| Index: webrtc/voice_engine/channel.cc
|
| diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc
|
| index 695aa1dee2434f85d6a1730771056b6ed9067fb6..11af45edc837fb8f94eb9da43edf9ba2be86dde9 100644
|
| --- a/webrtc/voice_engine/channel.cc
|
| +++ b/webrtc/voice_engine/channel.cc
|
| @@ -40,10 +40,12 @@
|
| #include "webrtc/voice_engine/transmit_mixer.h"
|
| #include "webrtc/voice_engine/utility.h"
|
|
|
| +#if defined(_WIN32)
|
| +#include <Qos.h>
|
| +#endif
|
| +
|
| namespace webrtc {
|
| namespace voe {
|
| -
|
| -const int kTelephoneEventAttenuationdB = 10;
|
|
|
| class TransportFeedbackProxy : public TransportFeedbackObserver {
|
| public:
|
| @@ -361,7 +363,7 @@
|
| " volume=%u)",
|
| event, lengthMs, volume);
|
|
|
| - if (event > 15) {
|
| + if (!_playOutbandDtmfEvent || (event > 15)) {
|
| // Ignore callback since feedback is disabled or event is not a
|
| // Dtmf tone event.
|
| return;
|
| @@ -759,11 +761,14 @@
|
| _outputFilePlayerId(VoEModuleId(instanceId, channelId) + 1025),
|
| _outputFileRecorderId(VoEModuleId(instanceId, channelId) + 1026),
|
| _outputFileRecording(false),
|
| + _inbandDtmfQueue(VoEModuleId(instanceId, channelId)),
|
| + _inbandDtmfGenerator(VoEModuleId(instanceId, channelId)),
|
| _outputExternalMedia(false),
|
| _inputExternalMediaCallbackPtr(NULL),
|
| _outputExternalMediaCallbackPtr(NULL),
|
| _timeStamp(0), // This is just an offset, RTP module will add it's own
|
| // random offset
|
| + _sendTelephoneEventPayloadType(106),
|
| ntp_estimator_(Clock::GetRealTimeClock()),
|
| jitter_buffer_playout_timestamp_(0),
|
| playout_timestamp_rtp_(0),
|
| @@ -791,6 +796,8 @@
|
| _panLeft(1.0f),
|
| _panRight(1.0f),
|
| _outputGain(1.0f),
|
| + _playOutbandDtmfEvent(false),
|
| + _playInbandDtmfEvent(false),
|
| _lastLocalTimeStamp(0),
|
| _lastPayloadType(0),
|
| _includeAudioLevelIndication(false),
|
| @@ -823,6 +830,8 @@
|
| config.Get<NetEqFastAccelerate>().enabled;
|
| audio_coding_.reset(AudioCodingModule::Create(acm_config));
|
|
|
| + _inbandDtmfQueue.ResetDtmf();
|
| + _inbandDtmfGenerator.Init();
|
| _outputAudioLevel.Clear();
|
|
|
| RtpRtcp::Configuration configuration;
|
| @@ -2203,18 +2212,21 @@
|
| return 0;
|
| }
|
|
|
| -int Channel::SendTelephoneEventOutband(int event, int duration_ms) {
|
| - WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
| - "Channel::SendTelephoneEventOutband(...)");
|
| - RTC_DCHECK_LE(0, event);
|
| - RTC_DCHECK_GE(255, event);
|
| - RTC_DCHECK_LE(0, duration_ms);
|
| - RTC_DCHECK_GE(65535, duration_ms);
|
| +int Channel::SendTelephoneEventOutband(unsigned char eventCode,
|
| + int lengthMs,
|
| + int attenuationDb,
|
| + bool playDtmfEvent) {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
| + "Channel::SendTelephoneEventOutband(..., playDtmfEvent=%d)",
|
| + playDtmfEvent);
|
| if (!Sending()) {
|
| return -1;
|
| }
|
| - if (_rtpRtcpModule->SendTelephoneEventOutband(
|
| - event, duration_ms, kTelephoneEventAttenuationdB) != 0) {
|
| +
|
| + _playOutbandDtmfEvent = playDtmfEvent;
|
| +
|
| + if (_rtpRtcpModule->SendTelephoneEventOutband(eventCode, lengthMs,
|
| + attenuationDb) != 0) {
|
| _engineStatisticsPtr->SetLastError(
|
| VE_SEND_DTMF_FAILED, kTraceWarning,
|
| "SendTelephoneEventOutband() failed to send event");
|
| @@ -2223,14 +2235,32 @@
|
| return 0;
|
| }
|
|
|
| -int Channel::SetSendTelephoneEventPayloadType(int payload_type) {
|
| +int Channel::SendTelephoneEventInband(unsigned char eventCode,
|
| + int lengthMs,
|
| + int attenuationDb,
|
| + bool playDtmfEvent) {
|
| + WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
| + "Channel::SendTelephoneEventInband(..., playDtmfEvent=%d)",
|
| + playDtmfEvent);
|
| +
|
| + _playInbandDtmfEvent = playDtmfEvent;
|
| + _inbandDtmfQueue.AddDtmf(eventCode, lengthMs, attenuationDb);
|
| +
|
| + return 0;
|
| +}
|
| +
|
| +int Channel::SetSendTelephoneEventPayloadType(unsigned char type) {
|
| WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
|
| "Channel::SetSendTelephoneEventPayloadType()");
|
| - RTC_DCHECK_LE(0, payload_type);
|
| - RTC_DCHECK_GE(127, payload_type);
|
| - CodecInst codec = {0};
|
| + if (type > 127) {
|
| + _engineStatisticsPtr->SetLastError(
|
| + VE_INVALID_ARGUMENT, kTraceError,
|
| + "SetSendTelephoneEventPayloadType() invalid type");
|
| + return -1;
|
| + }
|
| + CodecInst codec = {};
|
| codec.plfreq = 8000;
|
| - codec.pltype = payload_type;
|
| + codec.pltype = type;
|
| memcpy(codec.plname, "telephone-event", 16);
|
| if (_rtpRtcpModule->RegisterSendPayload(codec) != 0) {
|
| _rtpRtcpModule->DeRegisterSendPayload(codec.pltype);
|
| @@ -2242,6 +2272,12 @@
|
| return -1;
|
| }
|
| }
|
| + _sendTelephoneEventPayloadType = type;
|
| + return 0;
|
| +}
|
| +
|
| +int Channel::GetSendTelephoneEventPayloadType(unsigned char& type) {
|
| + type = _sendTelephoneEventPayloadType;
|
| return 0;
|
| }
|
|
|
| @@ -2991,6 +3027,8 @@
|
| }
|
| }
|
|
|
| + InsertInbandDtmfTone();
|
| +
|
| if (_includeAudioLevelIndication) {
|
| size_t length =
|
| _audioFrame.samples_per_channel_ * _audioFrame.num_channels_;
|
| @@ -3307,6 +3345,64 @@
|
| return -1;
|
| }
|
|
|
| + return 0;
|
| +}
|
| +
|
| +int Channel::InsertInbandDtmfTone() {
|
| + // Check if we should start a new tone.
|
| + if (_inbandDtmfQueue.PendingDtmf() && !_inbandDtmfGenerator.IsAddingTone() &&
|
| + _inbandDtmfGenerator.DelaySinceLastTone() >
|
| + kMinTelephoneEventSeparationMs) {
|
| + int8_t eventCode(0);
|
| + uint16_t lengthMs(0);
|
| + uint8_t attenuationDb(0);
|
| +
|
| + eventCode = _inbandDtmfQueue.NextDtmf(&lengthMs, &attenuationDb);
|
| + _inbandDtmfGenerator.AddTone(eventCode, lengthMs, attenuationDb);
|
| + if (_playInbandDtmfEvent) {
|
| + // Add tone to output mixer using a reduced length to minimize
|
| + // risk of echo.
|
| + _outputMixerPtr->PlayDtmfTone(eventCode, lengthMs - 80, attenuationDb);
|
| + }
|
| + }
|
| +
|
| + if (_inbandDtmfGenerator.IsAddingTone()) {
|
| + uint16_t frequency(0);
|
| + _inbandDtmfGenerator.GetSampleRate(frequency);
|
| +
|
| + if (frequency != _audioFrame.sample_rate_hz_) {
|
| + // Update sample rate of Dtmf tone since the mixing frequency
|
| + // has changed.
|
| + _inbandDtmfGenerator.SetSampleRate(
|
| + (uint16_t)(_audioFrame.sample_rate_hz_));
|
| + // Reset the tone to be added taking the new sample rate into
|
| + // account.
|
| + _inbandDtmfGenerator.ResetTone();
|
| + }
|
| +
|
| + int16_t toneBuffer[320];
|
| + uint16_t toneSamples(0);
|
| + // Get 10ms tone segment and set time since last tone to zero
|
| + if (_inbandDtmfGenerator.Get10msTone(toneBuffer, toneSamples) == -1) {
|
| + WEBRTC_TRACE(kTraceWarning, kTraceVoice, VoEId(_instanceId, _channelId),
|
| + "Channel::EncodeAndSend() inserting Dtmf failed");
|
| + return -1;
|
| + }
|
| +
|
| + // Replace mixed audio with DTMF tone.
|
| + for (size_t sample = 0; sample < _audioFrame.samples_per_channel_;
|
| + sample++) {
|
| + for (size_t channel = 0; channel < _audioFrame.num_channels_; channel++) {
|
| + const size_t index = sample * _audioFrame.num_channels_ + channel;
|
| + _audioFrame.data_[index] = toneBuffer[sample];
|
| + }
|
| + }
|
| +
|
| + assert(_audioFrame.samples_per_channel_ == toneSamples);
|
| + } else {
|
| + // Add 10ms to "delay-since-last-tone" counter
|
| + _inbandDtmfGenerator.UpdateDelaySinceLastTone();
|
| + }
|
| return 0;
|
| }
|
|
|
|
|