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
GetInterface(VoiceEngine * voiceEngine)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
VoEDtmfImpl(voe::SharedData * shared)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
~VoEDtmfImpl()49 VoEDtmfImpl::~VoEDtmfImpl()
50 {
51 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
52 "VoEDtmfImpl::~VoEDtmfImpl() - dtor");
53 }
54
SendTelephoneEvent(int channel,int eventCode,bool outOfBand,int lengthMs,int attenuationDb)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
SetSendTelephoneEventPayloadType(int channel,unsigned char type)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
GetSendTelephoneEventPayloadType(int channel,unsigned char & type)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
PlayDtmfTone(int eventCode,int lengthMs,int attenuationDb)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
SetDtmfFeedbackStatus(bool enable,bool directFeedback)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
GetDtmfFeedbackStatus(bool & enabled,bool & directFeedback)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
SetDtmfPlayoutStatus(int channel,bool enable)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
GetDtmfPlayoutStatus(int channel,bool & enabled)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