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
21 namespace webrtc {
22
GetInterface(VoiceEngine * voiceEngine)23 VoEDtmf* 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
VoEDtmfImpl(voe::SharedData * shared)38 VoEDtmfImpl::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
~VoEDtmfImpl()44 VoEDtmfImpl::~VoEDtmfImpl() {
45 WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_shared->instance_id(), -1),
46 "VoEDtmfImpl::~VoEDtmfImpl() - dtor");
47 }
48
SendTelephoneEvent(int channel,int eventCode,bool outOfBand,int lengthMs,int attenuationDb)49 int 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
SetSendTelephoneEventPayloadType(int channel,unsigned char type)131 int 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
GetSendTelephoneEventPayloadType(int channel,unsigned char & type)151 int 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
PlayDtmfTone(int eventCode,int lengthMs,int attenuationDb)168 int 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
SetDtmfFeedbackStatus(bool enable,bool directFeedback)195 int 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
GetDtmfFeedbackStatus(bool & enabled,bool & directFeedback)208 int 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