Home | History | Annotate | Download | only in voice_engine
      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