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
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 = static_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::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
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::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
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::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
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::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
244int 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
261int 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
283int 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