blob: 05817378ff3589702b23e5f013a07085ef0b0dfa [file] [log] [blame]
andrew@webrtc.orga7b57da2012-10-22 18:19:23 +00001/*
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 "voe_dtmf_impl.h"
12
13#include "channel.h"
14#include "critical_section_wrapper.h"
15#include "output_mixer.h"
16#include "trace.h"
17#include "transmit_mixer.h"
18#include "voe_errors.h"
19#include "voice_engine_impl.h"
20
21namespace webrtc {
22
23VoEDtmf* 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 = reinterpret_cast<VoiceEngineImpl*>(voiceEngine);
33 s->AddRef();
34 return s;
35#endif
36}
37
38#ifdef WEBRTC_VOICE_ENGINE_DTMF_API
39
40VoEDtmfImpl::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
49VoEDtmfImpl::~VoEDtmfImpl()
50{
51 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
52 "VoEDtmfImpl::~VoEDtmfImpl() - dtor");
53}
54
55int 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::ScopedChannel sc(_shared->channel_manager(), channel);
71 voe::Channel* channelPtr = sc.ChannelPtr();
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
153int 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::ScopedChannel sc(_shared->channel_manager(), channel);
165 voe::Channel* channelPtr = sc.ChannelPtr();
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
175int 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::ScopedChannel sc(_shared->channel_manager(), channel);
186 voe::Channel* channelPtr = sc.ChannelPtr();
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
196int 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
230int VoEDtmfImpl::StartPlayingDtmfTone(int eventCode,
231 int attenuationDb)
232{
233 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
234 "StartPlayingDtmfTone(eventCode=%d, attenuationDb=%d)",
235 eventCode, attenuationDb);
236
237 if (!_shared->statistics().Initialized())
238 {
239 _shared->SetLastError(VE_NOT_INITED, kTraceError);
240 return -1;
241 }
242 if (!_shared->audio_device()->Playing())
243 {
244 _shared->SetLastError(VE_NOT_PLAYING, kTraceError,
245 "StartPlayingDtmfTone() no channel is playing out");
246 return -1;
247 }
248 if ((eventCode < kMinDtmfEventCode) ||
249 (eventCode > kMaxDtmfEventCode) ||
250 (attenuationDb < kMinTelephoneEventAttenuation) ||
251 (attenuationDb > kMaxTelephoneEventAttenuation))
252 {
253 _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
254 "StartPlayingDtmfTone() invalid tone parameter(s)");
255 return -1;
256 }
257 return _shared->output_mixer()->StartPlayingDtmfTone(eventCode,
258 attenuationDb);
259}
260
261int VoEDtmfImpl::StopPlayingDtmfTone()
262{
263 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
264 "StopPlayingDtmfTone()");
265
266 if (!_shared->statistics().Initialized())
267 {
268 _shared->SetLastError(VE_NOT_INITED, kTraceError);
269 return -1;
270 }
271 return _shared->output_mixer()->StopPlayingDtmfTone();
272}
273
274int VoEDtmfImpl::RegisterTelephoneEventDetection(
275 int channel,
276 TelephoneEventDetectionMethods detectionMethod,
277 VoETelephoneEventObserver& observer)
278{
279 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
280 "RegisterTelephoneEventDetection(channel=%d, detectionMethod=%d,"
281 "observer=0x%x)", channel, detectionMethod, &observer);
282#ifdef WEBRTC_DTMF_DETECTION
283 if (!_shared->statistics().Initialized())
284 {
285 _shared->SetLastError(VE_NOT_INITED, kTraceError);
286 return -1;
287 }
288 voe::ScopedChannel sc(_shared->channel_manager(), channel);
289 voe::Channel* channelPtr = sc.ChannelPtr();
290 if (channelPtr == NULL)
291 {
292 _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
293 "RegisterTelephoneEventDetection() failed to locate channel");
294 return -1;
295 }
296 return channelPtr->RegisterTelephoneEventDetection(detectionMethod,
297 observer);
298#else
299 _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
300 "SetTelephoneEventDetectionStatus() Dtmf detection is not supported");
301 return -1;
302#endif
303}
304
305int VoEDtmfImpl::DeRegisterTelephoneEventDetection(int channel)
306{
307 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
308 "DeRegisterTelephoneEventDetection(channel=%d)", channel);
309#ifdef WEBRTC_DTMF_DETECTION
310 if (!_shared->statistics().Initialized())
311 {
312 _shared->SetLastError(VE_NOT_INITED, kTraceError);
313 return -1;
314 }
315 voe::ScopedChannel sc(_shared->channel_manager(), channel);
316 voe::Channel* channelPtr = sc.ChannelPtr();
317 if (channelPtr == NULL)
318 {
319 _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
320 "DeRegisterTelephoneEventDe tection() failed to locate channel");
321 return -1;
322 }
323 return channelPtr->DeRegisterTelephoneEventDetection();
324#else
325 _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
326 "DeRegisterTelephoneEventDetection() Dtmf detection is not supported");
327 return -1;
328#endif
329}
330
331
332int VoEDtmfImpl::GetTelephoneEventDetectionStatus(
333 int channel,
334 bool& enabled,
335 TelephoneEventDetectionMethods& detectionMethod)
336{
337 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
338 "GetTelephoneEventDetectionStatus(channel=%d)", channel);
339#ifdef WEBRTC_DTMF_DETECTION
340 if (!_shared->statistics().Initialized())
341 {
342 _shared->SetLastError(VE_NOT_INITED, kTraceError);
343 return -1;
344 }
345 voe::ScopedChannel sc(_shared->channel_manager(), channel);
346 voe::Channel* channelPtr = sc.ChannelPtr();
347 if (channelPtr == NULL)
348 {
349 _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
350 "GetTelephoneEventDetectionStatus() failed to locate channel");
351 return -1;
352 }
353 return channelPtr->GetTelephoneEventDetectionStatus(enabled,
354 detectionMethod);
355#else
356 _shared->SetLastError(VE_FUNC_NOT_SUPPORTED, kTraceError,
357 "GetTelephoneEventDetectionStatus() Dtmf detection is not supported");
358 return -1;
359#endif
360}
361
362int VoEDtmfImpl::SetDtmfFeedbackStatus(bool enable, bool directFeedback)
363{
364 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
365 "SetDtmfFeedbackStatus(enable=%d, directFeeback=%d)",
366 (int)enable, (int)directFeedback);
367
368 CriticalSectionScoped sc(_shared->crit_sec());
369
370 _dtmfFeedback = enable;
371 _dtmfDirectFeedback = directFeedback;
372
373 return 0;
374}
375
376int VoEDtmfImpl::GetDtmfFeedbackStatus(bool& enabled, bool& directFeedback)
377{
378 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
379 "GetDtmfFeedbackStatus()");
380
381 CriticalSectionScoped sc(_shared->crit_sec());
382
383 enabled = _dtmfFeedback;
384 directFeedback = _dtmfDirectFeedback;
385
386 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
387 VoEId(_shared->instance_id(), -1),
388 "GetDtmfFeedbackStatus() => enabled=%d, directFeedback=%d",
389 enabled, directFeedback);
390 return 0;
391}
392
393int VoEDtmfImpl::SetDtmfPlayoutStatus(int channel, bool enable)
394{
395 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
396 "SetDtmfPlayoutStatus(channel=%d, enable=%d)",
397 channel, enable);
398 IPHONE_NOT_SUPPORTED(_shared->statistics());
399
400 if (!_shared->statistics().Initialized())
401 {
402 _shared->SetLastError(VE_NOT_INITED, kTraceError);
403 return -1;
404 }
405 voe::ScopedChannel sc(_shared->channel_manager(), channel);
406 voe::Channel* channelPtr = sc.ChannelPtr();
407 if (channelPtr == NULL)
408 {
409 _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
410 "SetDtmfPlayoutStatus() failed to locate channel");
411 return -1;
412 }
413 return channelPtr->SetDtmfPlayoutStatus(enable);
414}
415
416int VoEDtmfImpl::GetDtmfPlayoutStatus(int channel, bool& enabled)
417{
418 WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
419 "GetDtmfPlayoutStatus(channel=%d, enabled=?)", channel);
420 IPHONE_NOT_SUPPORTED(_shared->statistics());
421 if (!_shared->statistics().Initialized())
422 {
423 _shared->SetLastError(VE_NOT_INITED, kTraceError);
424 return -1;
425 }
426 voe::ScopedChannel sc(_shared->channel_manager(), channel);
427 voe::Channel* channelPtr = sc.ChannelPtr();
428 if (channelPtr == NULL)
429 {
430 _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
431 "GetDtmfPlayoutStatus() failed to locate channel");
432 return -1;
433 }
434 enabled = channelPtr->DtmfPlayoutStatus();
435 WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
436 VoEId(_shared->instance_id(), -1),
437 "GetDtmfPlayoutStatus() => enabled=%d", enabled);
438 return 0;
439}
440
441#endif // #ifdef WEBRTC_VOICE_ENGINE_DTMF_API
442
443} // namespace webrtc