/* * 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(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(kMaxTelephoneEventCode) : static_cast(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