
TBR=niklas.enbom@webrtc.org Review URL: https://webrtc-codereview.appspot.com/915006 git-svn-id: http://webrtc.googlecode.com/svn/trunk@2963 4adac7df-926f-26a2-2b94-8c16560cd09d
368 lines
12 KiB
C++
368 lines
12 KiB
C++
/*
|
|
* Copyright (c) 2012 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 "rtp_receiver_audio.h"
|
|
|
|
#include <cassert> //assert
|
|
#include <cstring> // memcpy()
|
|
#include <math.h> // pow()
|
|
|
|
#include "critical_section_wrapper.h"
|
|
|
|
namespace webrtc {
|
|
RTPReceiverAudio::RTPReceiverAudio(const WebRtc_Word32 id):
|
|
_id(id),
|
|
_lastReceivedFrequency(8000),
|
|
_telephoneEvent(false),
|
|
_telephoneEventForwardToDecoder(false),
|
|
_telephoneEventDetectEndOfTone(false),
|
|
_telephoneEventPayloadType(-1),
|
|
_cngNBPayloadType(-1),
|
|
_cngWBPayloadType(-1),
|
|
_cngSWBPayloadType(-1),
|
|
_cngFBPayloadType(-1),
|
|
_cngPayloadType(-1),
|
|
_G722PayloadType(-1),
|
|
_lastReceivedG722(false),
|
|
_criticalSectionFeedback(CriticalSectionWrapper::CreateCriticalSection()),
|
|
_cbAudioFeedback(NULL)
|
|
{
|
|
}
|
|
|
|
RTPReceiverAudio::~RTPReceiverAudio()
|
|
{
|
|
delete _criticalSectionFeedback;
|
|
}
|
|
|
|
WebRtc_Word32
|
|
RTPReceiverAudio::RegisterIncomingAudioCallback(RtpAudioFeedback* incomingMessagesCallback)
|
|
{
|
|
CriticalSectionScoped lock(_criticalSectionFeedback);
|
|
_cbAudioFeedback = incomingMessagesCallback;
|
|
return 0;
|
|
}
|
|
|
|
WebRtc_UWord32
|
|
RTPReceiverAudio::AudioFrequency() const
|
|
{
|
|
if(_lastReceivedG722)
|
|
{
|
|
return 8000;
|
|
}
|
|
return _lastReceivedFrequency;
|
|
}
|
|
|
|
// Outband TelephoneEvent(DTMF) detection
|
|
WebRtc_Word32
|
|
RTPReceiverAudio::SetTelephoneEventStatus(const bool enable,
|
|
const bool forwardToDecoder,
|
|
const bool detectEndOfTone)
|
|
{
|
|
_telephoneEvent= enable;
|
|
_telephoneEventDetectEndOfTone = detectEndOfTone;
|
|
_telephoneEventForwardToDecoder = forwardToDecoder;
|
|
return 0;
|
|
}
|
|
|
|
// Is outband TelephoneEvent(DTMF) turned on/off?
|
|
bool
|
|
RTPReceiverAudio::TelephoneEvent() const
|
|
{
|
|
return _telephoneEvent;
|
|
}
|
|
|
|
// Is forwarding of outband telephone events turned on/off?
|
|
bool
|
|
RTPReceiverAudio::TelephoneEventForwardToDecoder() const
|
|
{
|
|
return _telephoneEventForwardToDecoder;
|
|
}
|
|
|
|
bool
|
|
RTPReceiverAudio::TelephoneEventPayloadType(const WebRtc_Word8 payloadType) const
|
|
{
|
|
return (_telephoneEventPayloadType == payloadType)?true:false;
|
|
}
|
|
|
|
bool
|
|
RTPReceiverAudio::CNGPayloadType(const WebRtc_Word8 payloadType,
|
|
WebRtc_UWord32& frequency)
|
|
{
|
|
// We can have four CNG on 8000Hz, 16000Hz, 32000Hz and 48000Hz.
|
|
if(_cngNBPayloadType == payloadType)
|
|
{
|
|
frequency = 8000;
|
|
if ((_cngPayloadType != -1) &&(_cngPayloadType !=_cngNBPayloadType))
|
|
{
|
|
ResetStatistics();
|
|
}
|
|
_cngPayloadType = _cngNBPayloadType;
|
|
return true;
|
|
} else if(_cngWBPayloadType == payloadType)
|
|
{
|
|
// if last received codec is G.722 we must use frequency 8000
|
|
if(_lastReceivedG722)
|
|
{
|
|
frequency = 8000;
|
|
} else
|
|
{
|
|
frequency = 16000;
|
|
}
|
|
if ((_cngPayloadType != -1) &&(_cngPayloadType !=_cngWBPayloadType))
|
|
{
|
|
ResetStatistics();
|
|
}
|
|
_cngPayloadType = _cngWBPayloadType;
|
|
return true;
|
|
}else if(_cngSWBPayloadType == payloadType)
|
|
{
|
|
frequency = 32000;
|
|
if ((_cngPayloadType != -1) &&(_cngPayloadType !=_cngSWBPayloadType))
|
|
{
|
|
ResetStatistics();
|
|
}
|
|
_cngPayloadType = _cngSWBPayloadType;
|
|
return true;
|
|
}else if(_cngFBPayloadType == payloadType)
|
|
{
|
|
frequency = 48000;
|
|
if ((_cngPayloadType != -1) &&(_cngPayloadType !=_cngFBPayloadType))
|
|
{
|
|
ResetStatistics();
|
|
}
|
|
_cngPayloadType = _cngFBPayloadType;
|
|
return true;
|
|
}else
|
|
{
|
|
// not CNG
|
|
if(_G722PayloadType == payloadType)
|
|
{
|
|
_lastReceivedG722 = true;
|
|
}else
|
|
{
|
|
_lastReceivedG722 = false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
Sample based or frame based codecs based on RFC 3551
|
|
|
|
NOTE! There is one error in the RFC, stating G.722 uses 8 bits/samples.
|
|
The correct rate is 4 bits/sample.
|
|
|
|
name of sampling default
|
|
encoding sample/frame bits/sample rate ms/frame ms/packet
|
|
|
|
Sample based audio codecs
|
|
DVI4 sample 4 var. 20
|
|
G722 sample 4 16,000 20
|
|
G726-40 sample 5 8,000 20
|
|
G726-32 sample 4 8,000 20
|
|
G726-24 sample 3 8,000 20
|
|
G726-16 sample 2 8,000 20
|
|
L8 sample 8 var. 20
|
|
L16 sample 16 var. 20
|
|
PCMA sample 8 var. 20
|
|
PCMU sample 8 var. 20
|
|
|
|
Frame based audio codecs
|
|
G723 frame N/A 8,000 30 30
|
|
G728 frame N/A 8,000 2.5 20
|
|
G729 frame N/A 8,000 10 20
|
|
G729D frame N/A 8,000 10 20
|
|
G729E frame N/A 8,000 10 20
|
|
GSM frame N/A 8,000 20 20
|
|
GSM-EFR frame N/A 8,000 20 20
|
|
LPC frame N/A 8,000 20 20
|
|
MPA frame N/A var. var.
|
|
|
|
G7221 frame N/A
|
|
*/
|
|
|
|
ModuleRTPUtility::Payload* RTPReceiverAudio::RegisterReceiveAudioPayload(
|
|
const char payloadName[RTP_PAYLOAD_NAME_SIZE],
|
|
const WebRtc_Word8 payloadType,
|
|
const WebRtc_UWord32 frequency,
|
|
const WebRtc_UWord8 channels,
|
|
const WebRtc_UWord32 rate) {
|
|
if (ModuleRTPUtility::StringCompare(payloadName, "telephone-event", 15)) {
|
|
_telephoneEventPayloadType = payloadType;
|
|
}
|
|
if (ModuleRTPUtility::StringCompare(payloadName, "cn", 2)) {
|
|
// we can have three CNG on 8000Hz, 16000Hz and 32000Hz
|
|
if(frequency == 8000){
|
|
_cngNBPayloadType = payloadType;
|
|
} else if(frequency == 16000) {
|
|
_cngWBPayloadType = payloadType;
|
|
} else if(frequency == 32000) {
|
|
_cngSWBPayloadType = payloadType;
|
|
} else if(frequency == 48000) {
|
|
_cngFBPayloadType = payloadType;
|
|
} else {
|
|
assert(false);
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
ModuleRTPUtility::Payload* payload = new ModuleRTPUtility::Payload;
|
|
payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
|
|
strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE - 1);
|
|
payload->typeSpecific.Audio.frequency = frequency;
|
|
payload->typeSpecific.Audio.channels = channels;
|
|
payload->typeSpecific.Audio.rate = rate;
|
|
payload->audio = true;
|
|
return payload;
|
|
}
|
|
|
|
// we are not allowed to have any critsects when calling CallbackOfReceivedPayloadData
|
|
WebRtc_Word32
|
|
RTPReceiverAudio::ParseAudioCodecSpecific(WebRtcRTPHeader* rtpHeader,
|
|
const WebRtc_UWord8* payloadData,
|
|
const WebRtc_UWord16 payloadLength,
|
|
const ModuleRTPUtility::AudioPayload& audioSpecific,
|
|
const bool isRED)
|
|
{
|
|
WebRtc_UWord8 newEvents[MAX_NUMBER_OF_PARALLEL_TELEPHONE_EVENTS];
|
|
WebRtc_UWord8 removedEvents[MAX_NUMBER_OF_PARALLEL_TELEPHONE_EVENTS];
|
|
WebRtc_UWord8 numberOfNewEvents = 0;
|
|
WebRtc_UWord8 numberOfRemovedEvents = 0;
|
|
bool telephoneEventPacket = TelephoneEventPayloadType(rtpHeader->header.payloadType);
|
|
|
|
if(payloadLength == 0)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
{
|
|
CriticalSectionScoped lock(_criticalSectionFeedback);
|
|
|
|
if(telephoneEventPacket)
|
|
{
|
|
// RFC 4733 2.3
|
|
/*
|
|
0 1 2 3
|
|
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
| event |E|R| volume | duration |
|
|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
|
*/
|
|
if(payloadLength % 4 != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
WebRtc_UWord8 numberOfEvents = payloadLength / 4;
|
|
|
|
// sanity
|
|
if(numberOfEvents >= MAX_NUMBER_OF_PARALLEL_TELEPHONE_EVENTS)
|
|
{
|
|
numberOfEvents = MAX_NUMBER_OF_PARALLEL_TELEPHONE_EVENTS;
|
|
}
|
|
for (int n = 0; n < numberOfEvents; n++)
|
|
{
|
|
bool end = (payloadData[(4*n)+1] & 0x80)? true:false;
|
|
|
|
std::set<WebRtc_UWord8>::iterator event =
|
|
_telephoneEventReported.find(payloadData[4*n]);
|
|
|
|
if(event != _telephoneEventReported.end())
|
|
{
|
|
// we have already seen this event
|
|
if(end)
|
|
{
|
|
removedEvents[numberOfRemovedEvents]= payloadData[4*n];
|
|
numberOfRemovedEvents++;
|
|
_telephoneEventReported.erase(payloadData[4*n]);
|
|
}
|
|
}else
|
|
{
|
|
if(end)
|
|
{
|
|
// don't add if it's a end of a tone
|
|
}else
|
|
{
|
|
newEvents[numberOfNewEvents] = payloadData[4*n];
|
|
numberOfNewEvents++;
|
|
_telephoneEventReported.insert(payloadData[4*n]);
|
|
}
|
|
}
|
|
}
|
|
|
|
// RFC 4733 2.5.1.3 & 2.5.2.3 Long-Duration Events
|
|
// should not be a problem since we don't care about the duration
|
|
|
|
// RFC 4733 See 2.5.1.5. & 2.5.2.4. Multiple Events in a Packet
|
|
}
|
|
|
|
if(_telephoneEvent && _cbAudioFeedback)
|
|
{
|
|
for (int n = 0; n < numberOfNewEvents; n++)
|
|
{
|
|
_cbAudioFeedback->OnReceivedTelephoneEvent(_id, newEvents[n], false);
|
|
}
|
|
if(_telephoneEventDetectEndOfTone)
|
|
{
|
|
for (int n = 0; n < numberOfRemovedEvents; n++)
|
|
{
|
|
_cbAudioFeedback->OnReceivedTelephoneEvent(_id, removedEvents[n], true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if(! telephoneEventPacket )
|
|
{
|
|
_lastReceivedFrequency = audioSpecific.frequency;
|
|
}
|
|
|
|
// Check if this is a CNG packet, receiver might want to know
|
|
WebRtc_UWord32 dummy;
|
|
if(CNGPayloadType(rtpHeader->header.payloadType, dummy))
|
|
{
|
|
rtpHeader->type.Audio.isCNG=true;
|
|
rtpHeader->frameType = kAudioFrameCN;
|
|
}else
|
|
{
|
|
rtpHeader->frameType = kAudioFrameSpeech;
|
|
rtpHeader->type.Audio.isCNG=false;
|
|
}
|
|
|
|
// check if it's a DTMF event, hence something we can playout
|
|
if(telephoneEventPacket)
|
|
{
|
|
if(!_telephoneEventForwardToDecoder)
|
|
{
|
|
// don't forward event to decoder
|
|
return 0;
|
|
}
|
|
std::set<WebRtc_UWord8>::iterator first =
|
|
_telephoneEventReported.begin();
|
|
if(first != _telephoneEventReported.end() && *first > 15)
|
|
{
|
|
// don't forward non DTMF events
|
|
return 0;
|
|
}
|
|
}
|
|
if(isRED && !(payloadData[0] & 0x80))
|
|
{
|
|
// we recive only one frame packed in a RED packet remove the RED wrapper
|
|
rtpHeader->header.payloadType = payloadData[0];
|
|
|
|
// only one frame in the RED strip the one byte to help NetEq
|
|
return CallbackOfReceivedPayloadData(payloadData+1,
|
|
payloadLength-1,
|
|
rtpHeader);
|
|
}
|
|
|
|
rtpHeader->type.Audio.channel = audioSpecific.channels;
|
|
return CallbackOfReceivedPayloadData(payloadData, payloadLength, rtpHeader);
|
|
}
|
|
} // namespace webrtc
|