| /* |
| * 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_dtmf_impl.h" |
| |
| #include "webrtc/system_wrappers/include/critical_section_wrapper.h" |
| #include "webrtc/system_wrappers/include/trace.h" |
| #include "webrtc/voice_engine/channel.h" |
| #include "webrtc/voice_engine/include/voe_errors.h" |
| #include "webrtc/voice_engine/output_mixer.h" |
| #include "webrtc/voice_engine/transmit_mixer.h" |
| #include "webrtc/voice_engine/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 = static_cast<VoiceEngineImpl*>(voiceEngine); |
| s->AddRef(); |
| return s; |
| #endif |
| } |
| |
| #ifdef WEBRTC_VOICE_ENGINE_DTMF_API |
| |
| VoEDtmfImpl::VoEDtmfImpl(voe::SharedData* shared) |
| : _dtmfFeedback(true), _dtmfDirectFeedback(false), _shared(shared) { |
| WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), |
| "VoEDtmfImpl::VoEDtmfImpl() - ctor"); |
| } |
| |
| VoEDtmfImpl::~VoEDtmfImpl() { |
| WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1), |
| "VoEDtmfImpl::~VoEDtmfImpl() - dtor"); |
| } |
| |
| int VoEDtmfImpl::SendTelephoneEvent(int channel, |
| int eventCode, |
| bool outOfBand, |
| int lengthMs, |
| int attenuationDb) { |
| WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), |
| "SendTelephoneEvent(channel=%d, eventCode=%d, outOfBand=%d," |
| "length=%d, attenuationDb=%d)", |
| channel, eventCode, (int)outOfBand, lengthMs, attenuationDb); |
| 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, |
| "SendTelephoneEvent() failed to locate channel"); |
| return -1; |
| } |
| if (!channelPtr->Sending()) { |
| _shared->SetLastError(VE_NOT_SENDING, kTraceError, |
| "SendTelephoneEvent() sending is not active"); |
| return -1; |
| } |
| |
| // Sanity check |
| const int maxEventCode = outOfBand ? static_cast<int>(kMaxTelephoneEventCode) |
| : static_cast<int>(kMaxDtmfEventCode); |
| const bool testFailed = ((eventCode < 0) || (eventCode > maxEventCode) || |
| (lengthMs < kMinTelephoneEventDuration) || |
| (lengthMs > kMaxTelephoneEventDuration) || |
| (attenuationDb < kMinTelephoneEventAttenuation) || |
| (attenuationDb > kMaxTelephoneEventAttenuation)); |
| if (testFailed) { |
| _shared->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. |
| _shared->transmit_mixer()->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. |
| _shared->output_mixer()->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(_shared->instance_id(), -1), |
| "SetSendTelephoneEventPayloadType(channel=%d, type=%u)", channel, |
| type); |
| 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, |
| "SetSendTelephoneEventPayloadType() failed to locate channel"); |
| return -1; |
| } |
| return channelPtr->SetSendTelephoneEventPayloadType(type); |
| } |
| |
| int VoEDtmfImpl::GetSendTelephoneEventPayloadType(int channel, |
| unsigned char& type) { |
| 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, |
| "GetSendTelephoneEventPayloadType() failed to locate channel"); |
| return -1; |
| } |
| return channelPtr->GetSendTelephoneEventPayloadType(type); |
| } |
| |
| int VoEDtmfImpl::PlayDtmfTone(int eventCode, int lengthMs, int attenuationDb) { |
| WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), |
| "PlayDtmfTone(eventCode=%d, lengthMs=%d, attenuationDb=%d)", |
| eventCode, lengthMs, attenuationDb); |
| |
| if (!_shared->statistics().Initialized()) { |
| _shared->SetLastError(VE_NOT_INITED, kTraceError); |
| return -1; |
| } |
| if (!_shared->audio_device()->Playing()) { |
| _shared->SetLastError(VE_NOT_PLAYING, kTraceError, |
| "PlayDtmfTone() no channel is playing out"); |
| return -1; |
| } |
| if ((eventCode < kMinDtmfEventCode) || (eventCode > kMaxDtmfEventCode) || |
| (lengthMs < kMinTelephoneEventDuration) || |
| (lengthMs > kMaxTelephoneEventDuration) || |
| (attenuationDb < kMinTelephoneEventAttenuation) || |
| (attenuationDb > kMaxTelephoneEventAttenuation)) { |
| _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, |
| "PlayDtmfTone() invalid tone parameter(s)"); |
| return -1; |
| } |
| return _shared->output_mixer()->PlayDtmfTone(eventCode, lengthMs, |
| attenuationDb); |
| } |
| |
| int VoEDtmfImpl::SetDtmfFeedbackStatus(bool enable, bool directFeedback) { |
| WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), |
| "SetDtmfFeedbackStatus(enable=%d, directFeeback=%d)", |
| (int)enable, (int)directFeedback); |
| |
| CriticalSectionScoped sc(_shared->crit_sec()); |
| |
| _dtmfFeedback = enable; |
| _dtmfDirectFeedback = directFeedback; |
| |
| return 0; |
| } |
| |
| int VoEDtmfImpl::GetDtmfFeedbackStatus(bool& enabled, bool& directFeedback) { |
| CriticalSectionScoped sc(_shared->crit_sec()); |
| |
| enabled = _dtmfFeedback; |
| directFeedback = _dtmfDirectFeedback; |
| return 0; |
| } |
| #endif // #ifdef WEBRTC_VOICE_ENGINE_DTMF_API |
| |
| } // namespace webrtc |