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
21namespace webrtc {
22
23VoEDtmf* 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
38VoEDtmfImpl::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
44VoEDtmfImpl::~VoEDtmfImpl() {
45  WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
46               "VoEDtmfImpl::~VoEDtmfImpl() - dtor");
47}
48
49int 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
131int 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
151int 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
168int 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
195int 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
208int 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