474 lines
16 KiB
C++
474 lines
16 KiB
C++
/*
|
|
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
|
|
#include "voe_dtmf_impl.h"
|
|
|
|
#include "channel.h"
|
|
#include "critical_section_wrapper.h"
|
|
#include "output_mixer.h"
|
|
#include "trace.h"
|
|
#include "transmit_mixer.h"
|
|
#include "voe_errors.h"
|
|
#include "voice_engine_impl.h"
|
|
|
|
namespace webrtc {
|
|
|
|
VoEDtmf* VoEDtmf::GetInterface(VoiceEngine* voiceEngine)
|
|
{
|
|
#ifndef WEBRTC_VOICE_ENGINE_DTMF_API
|
|
return NULL;
|
|
#else
|
|
if (NULL == voiceEngine)
|
|
{
|
|
return NULL;
|
|
}
|
|
VoiceEngineImpl* s =
|
|
reinterpret_cast<VoiceEngineImpl*> (voiceEngine);
|
|
VoEDtmfImpl* d = s;
|
|
( *d)++;
|
|
return (d);
|
|
#endif
|
|
}
|
|
|
|
#ifdef WEBRTC_VOICE_ENGINE_DTMF_API
|
|
|
|
VoEDtmfImpl::VoEDtmfImpl() :
|
|
_dtmfFeedback(true),
|
|
_dtmfDirectFeedback(false)
|
|
{
|
|
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1 ),
|
|
"VoEDtmfImpl::VoEDtmfImpl() - ctor");
|
|
}
|
|
|
|
VoEDtmfImpl::~VoEDtmfImpl()
|
|
{
|
|
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,-1),
|
|
"VoEDtmfImpl::~VoEDtmfImpl() - dtor");
|
|
}
|
|
|
|
int VoEDtmfImpl::Release()
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"VoEDtmf::Release()");
|
|
(*this)--;
|
|
int refCount = GetCount();
|
|
if (refCount < 0)
|
|
{
|
|
Reset(); // reset reference counter to zero => OK to delete VE
|
|
_engineStatistics.SetLastError(
|
|
VE_INTERFACE_NOT_FOUND, kTraceWarning);
|
|
return (-1);
|
|
}
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
|
|
"VoEDtmf reference counter = %d", refCount);
|
|
return (refCount);
|
|
}
|
|
|
|
int VoEDtmfImpl::SendTelephoneEvent(int channel,
|
|
unsigned char eventCode,
|
|
bool outOfBand,
|
|
int lengthMs,
|
|
int attenuationDb)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"SendTelephoneEvent(channel=%d, eventCode=%d, outOfBand=%d,"
|
|
"length=%d, attenuationDb=%d)",
|
|
channel, eventCode, (int)outOfBand, lengthMs, attenuationDb);
|
|
if (!_engineStatistics.Initialized())
|
|
{
|
|
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
voe::ScopedChannel sc(_channelManager, channel);
|
|
voe::Channel* channelPtr = sc.ChannelPtr();
|
|
if (channelPtr == NULL)
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_CHANNEL_NOT_VALID, kTraceError,
|
|
"SendTelephoneEvent() failed to locate channel");
|
|
return -1;
|
|
}
|
|
if (!channelPtr->Sending())
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_NOT_SENDING, kTraceError,
|
|
"SendTelephoneEvent() sending is not active");
|
|
return -1;
|
|
}
|
|
|
|
// Sanity check
|
|
const int maxEventCode = outOfBand ?
|
|
static_cast<int>(kMaxTelephoneEventCode) :
|
|
static_cast<int>(kMaxDtmfEventCode);
|
|
const bool testFailed = ((eventCode < 0) ||
|
|
(eventCode > maxEventCode) ||
|
|
(lengthMs < kMinTelephoneEventDuration) ||
|
|
(lengthMs > kMaxTelephoneEventDuration) ||
|
|
(attenuationDb < kMinTelephoneEventAttenuation) ||
|
|
(attenuationDb > kMaxTelephoneEventAttenuation));
|
|
if (testFailed)
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
"SendTelephoneEvent() invalid parameter(s)");
|
|
return -1;
|
|
}
|
|
|
|
const bool isDtmf =
|
|
(eventCode >= 0) && (eventCode <= kMaxDtmfEventCode);
|
|
const bool playDtmfToneDirect =
|
|
isDtmf && (_dtmfFeedback && _dtmfDirectFeedback);
|
|
|
|
if (playDtmfToneDirect)
|
|
{
|
|
// Mute the microphone signal while playing back the tone directly.
|
|
// This is to reduce the risk of introducing echo from the added output.
|
|
_transmitMixerPtr->UpdateMuteMicrophoneTime(lengthMs);
|
|
|
|
// Play out local feedback tone directly (same approach for both inband
|
|
// and outband).
|
|
// Reduce the length of the the tone with 80ms to reduce risk of echo.
|
|
// For non-direct feedback, outband and inband cases are handled
|
|
// differently.
|
|
_outputMixerPtr->PlayDtmfTone(eventCode, lengthMs-80, attenuationDb);
|
|
}
|
|
|
|
if (outOfBand)
|
|
{
|
|
// The RTP/RTCP module will always deliver OnPlayTelephoneEvent when
|
|
// an event is transmitted. It is up to the VoE to utilize it or not.
|
|
// This flag ensures that feedback/playout is enabled; however, the
|
|
// channel object must still parse out the Dtmf events (0-15) from
|
|
// all possible events (0-255).
|
|
const bool playDTFMEvent = (_dtmfFeedback && !_dtmfDirectFeedback);
|
|
|
|
return channelPtr->SendTelephoneEventOutband(eventCode,
|
|
lengthMs,
|
|
attenuationDb,
|
|
playDTFMEvent);
|
|
}
|
|
else
|
|
{
|
|
// For Dtmf tones, we want to ensure that inband tones are played out
|
|
// in sync with the transmitted audio. This flag is utilized by the
|
|
// channel object to determine if the queued Dtmf e vent shall also
|
|
// be fed to the output mixer in the same step as input audio is
|
|
// replaced by inband Dtmf tones.
|
|
const bool playDTFMEvent =
|
|
(isDtmf && _dtmfFeedback && !_dtmfDirectFeedback);
|
|
|
|
return channelPtr->SendTelephoneEventInband(eventCode,
|
|
lengthMs,
|
|
attenuationDb,
|
|
playDTFMEvent);
|
|
}
|
|
}
|
|
|
|
int VoEDtmfImpl::SetSendTelephoneEventPayloadType(int channel,
|
|
unsigned char type)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"SetSendTelephoneEventPayloadType(channel=%d, type=%u)",
|
|
channel, type);
|
|
if (!_engineStatistics.Initialized())
|
|
{
|
|
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
voe::ScopedChannel sc(_channelManager, channel);
|
|
voe::Channel* channelPtr = sc.ChannelPtr();
|
|
if (channelPtr == NULL)
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_CHANNEL_NOT_VALID, kTraceError,
|
|
"SetSendTelephoneEventPayloadType() failed to locate channel");
|
|
return -1;
|
|
}
|
|
return channelPtr->SetSendTelephoneEventPayloadType(type);
|
|
}
|
|
|
|
int VoEDtmfImpl::GetSendTelephoneEventPayloadType(int channel,
|
|
unsigned char& type)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"GetSendTelephoneEventPayloadType(channel=%d)", channel);
|
|
if (!_engineStatistics.Initialized())
|
|
{
|
|
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
voe::ScopedChannel sc(_channelManager, channel);
|
|
voe::Channel* channelPtr = sc.ChannelPtr();
|
|
if (channelPtr == NULL)
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_CHANNEL_NOT_VALID, kTraceError,
|
|
"GetSendTelephoneEventPayloadType() failed to locate channel");
|
|
return -1;
|
|
}
|
|
return channelPtr->GetSendTelephoneEventPayloadType(type);
|
|
}
|
|
|
|
int VoEDtmfImpl::PlayDtmfTone(unsigned char eventCode,
|
|
int lengthMs,
|
|
int attenuationDb)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"PlayDtmfTone(eventCode=%d, lengthMs=%d, attenuationDb=%d)",
|
|
eventCode, lengthMs, attenuationDb);
|
|
|
|
if (!_engineStatistics.Initialized())
|
|
{
|
|
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
if (!_audioDevicePtr->Playing())
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_NOT_PLAYING, kTraceError,
|
|
"PlayDtmfTone() no channel is playing out");
|
|
return -1;
|
|
}
|
|
if ((eventCode < kMinDtmfEventCode) ||
|
|
(eventCode > kMaxDtmfEventCode) ||
|
|
(lengthMs < kMinTelephoneEventDuration) ||
|
|
(lengthMs > kMaxTelephoneEventDuration) ||
|
|
(attenuationDb <kMinTelephoneEventAttenuation) ||
|
|
(attenuationDb > kMaxTelephoneEventAttenuation))
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
"PlayDtmfTone() invalid tone parameter(s)");
|
|
return -1;
|
|
}
|
|
return _outputMixerPtr->PlayDtmfTone(eventCode, lengthMs, attenuationDb);
|
|
}
|
|
|
|
int VoEDtmfImpl::StartPlayingDtmfTone(unsigned char eventCode,
|
|
int attenuationDb)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"StartPlayingDtmfTone(eventCode=%d, attenuationDb=%d)",
|
|
eventCode, attenuationDb);
|
|
|
|
if (!_engineStatistics.Initialized())
|
|
{
|
|
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
if (!_audioDevicePtr->Playing())
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_NOT_PLAYING, kTraceError,
|
|
"StartPlayingDtmfTone() no channel is playing out");
|
|
return -1;
|
|
}
|
|
if ((eventCode < kMinDtmfEventCode) ||
|
|
(eventCode > kMaxDtmfEventCode) ||
|
|
(attenuationDb < kMinTelephoneEventAttenuation) ||
|
|
(attenuationDb > kMaxTelephoneEventAttenuation))
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_INVALID_ARGUMENT, kTraceError,
|
|
"StartPlayingDtmfTone() invalid tone parameter(s)");
|
|
return -1;
|
|
}
|
|
return _outputMixerPtr->StartPlayingDtmfTone(eventCode, attenuationDb);
|
|
}
|
|
|
|
int VoEDtmfImpl::StopPlayingDtmfTone()
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"StopPlayingDtmfTone()");
|
|
|
|
if (!_engineStatistics.Initialized())
|
|
{
|
|
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
return _outputMixerPtr->StopPlayingDtmfTone();
|
|
}
|
|
|
|
int VoEDtmfImpl::RegisterTelephoneEventDetection(
|
|
int channel,
|
|
TelephoneEventDetectionMethods detectionMethod,
|
|
VoETelephoneEventObserver& observer)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"RegisterTelephoneEventDetection(channel=%d, detectionMethod=%d,"
|
|
"observer=0x%x)", channel, detectionMethod, &observer);
|
|
#ifdef WEBRTC_DTMF_DETECTION
|
|
if (!_engineStatistics.Initialized())
|
|
{
|
|
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
voe::ScopedChannel sc(_channelManager, channel);
|
|
voe::Channel* channelPtr = sc.ChannelPtr();
|
|
if (channelPtr == NULL)
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_CHANNEL_NOT_VALID, kTraceError,
|
|
"RegisterTelephoneEventDetection() failed to locate channel");
|
|
return -1;
|
|
}
|
|
return channelPtr->RegisterTelephoneEventDetection(detectionMethod,
|
|
observer);
|
|
#else
|
|
_engineStatistics.SetLastError(
|
|
VE_FUNC_NOT_SUPPORTED, kTraceError,
|
|
"SetTelephoneEventDetectionStatus() Dtmf detection is not supported");
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int VoEDtmfImpl::DeRegisterTelephoneEventDetection(int channel)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"DeRegisterTelephoneEventDetection(channel=%d)", channel);
|
|
#ifdef WEBRTC_DTMF_DETECTION
|
|
if (!_engineStatistics.Initialized())
|
|
{
|
|
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
voe::ScopedChannel sc(_channelManager, channel);
|
|
voe::Channel* channelPtr = sc.ChannelPtr();
|
|
if (channelPtr == NULL)
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_CHANNEL_NOT_VALID, kTraceError,
|
|
"DeRegisterTelephoneEventDe tection() failed to locate channel");
|
|
return -1;
|
|
}
|
|
return channelPtr->DeRegisterTelephoneEventDetection();
|
|
#else
|
|
_engineStatistics.SetLastError(
|
|
VE_FUNC_NOT_SUPPORTED, kTraceError,
|
|
"DeRegisterTelephoneEventDetection() Dtmf detection is not supported");
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
|
|
int VoEDtmfImpl::GetTelephoneEventDetectionStatus(
|
|
int channel,
|
|
bool& enabled,
|
|
TelephoneEventDetectionMethods& detectionMethod)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"GetTelephoneEventDetectionStatus(channel=%d)", channel);
|
|
#ifdef WEBRTC_DTMF_DETECTION
|
|
if (!_engineStatistics.Initialized())
|
|
{
|
|
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
voe::ScopedChannel sc(_channelManager, channel);
|
|
voe::Channel* channelPtr = sc.ChannelPtr();
|
|
if (channelPtr == NULL)
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_CHANNEL_NOT_VALID, kTraceError,
|
|
"GetTelephoneEventDetectionStatus() failed to locate channel");
|
|
return -1;
|
|
}
|
|
return channelPtr->GetTelephoneEventDetectionStatus(enabled, detectionMethod);
|
|
#else
|
|
_engineStatistics.SetLastError(
|
|
VE_FUNC_NOT_SUPPORTED, kTraceError,
|
|
"GetTelephoneEventDetectionStatus() Dtmf detection is not supported");
|
|
return -1;
|
|
#endif
|
|
}
|
|
|
|
int VoEDtmfImpl::SetDtmfFeedbackStatus(bool enable, bool directFeedback)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"SetDtmfFeedbackStatus(enable=%d, directFeeback=%d)",
|
|
(int)enable, (int)directFeedback);
|
|
|
|
CriticalSectionScoped sc(*_apiCritPtr);
|
|
|
|
_dtmfFeedback = enable;
|
|
_dtmfDirectFeedback = directFeedback;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int VoEDtmfImpl::GetDtmfFeedbackStatus(bool& enabled, bool& directFeedback)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"GetDtmfFeedbackStatus()");
|
|
|
|
CriticalSectionScoped sc(*_apiCritPtr);
|
|
|
|
enabled = _dtmfFeedback;
|
|
directFeedback = _dtmfDirectFeedback;
|
|
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
|
|
"GetDtmfFeedbackStatus() => enabled=%d, directFeedback=%d",
|
|
enabled, directFeedback);
|
|
return 0;
|
|
}
|
|
|
|
int VoEDtmfImpl::SetDtmfPlayoutStatus(int channel, bool enable)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"SetDtmfPlayoutStatus(channel=%d, enable=%d)",
|
|
channel, enable);
|
|
IPHONE_NOT_SUPPORTED();
|
|
|
|
if (!_engineStatistics.Initialized())
|
|
{
|
|
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
voe::ScopedChannel sc(_channelManager, channel);
|
|
voe::Channel* channelPtr = sc.ChannelPtr();
|
|
if (channelPtr == NULL)
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_CHANNEL_NOT_VALID, kTraceError,
|
|
"SetDtmfPlayoutStatus() failed to locate channel");
|
|
return -1;
|
|
}
|
|
return channelPtr->SetDtmfPlayoutStatus(enable);
|
|
}
|
|
|
|
int VoEDtmfImpl::GetDtmfPlayoutStatus(int channel, bool& enabled)
|
|
{
|
|
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_instanceId,-1),
|
|
"GetDtmfPlayoutStatus(channel=%d, enabled=?)", channel);
|
|
IPHONE_NOT_SUPPORTED();
|
|
if (!_engineStatistics.Initialized())
|
|
{
|
|
_engineStatistics.SetLastError(VE_NOT_INITED, kTraceError);
|
|
return -1;
|
|
}
|
|
voe::ScopedChannel sc(_channelManager, channel);
|
|
voe::Channel* channelPtr = sc.ChannelPtr();
|
|
if (channelPtr == NULL)
|
|
{
|
|
_engineStatistics.SetLastError(
|
|
VE_CHANNEL_NOT_VALID, kTraceError,
|
|
"GetDtmfPlayoutStatus() failed to locate channel");
|
|
return -1;
|
|
}
|
|
enabled = channelPtr->DtmfPlayoutStatus();
|
|
WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, VoEId(_instanceId,-1),
|
|
"GetDtmfPlayoutStatus() => enabled=%d", enabled);
|
|
return 0;
|
|
}
|
|
|
|
#endif // #ifdef WEBRTC_VOICE_ENGINE_DTMF_API
|
|
|
|
} // namespace webrtc
|