blob: b61157d67234c136f00a821dd7202c8072fa1518 [file] [log] [blame]
/*
* 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),
_cngPayloadType(-1),
_G722PayloadType(-1),
_lastReceivedG722(false),
_criticalSectionFeedback(CriticalSectionWrapper::CreateCriticalSection()),
_cbAudioFeedback(NULL)
{
}
RTPReceiverAudio::~RTPReceiverAudio()
{
delete _criticalSectionFeedback;
}
WebRtc_Word32 RTPReceiverAudio::Init() {
_lastReceivedFrequency = 8000;
_telephoneEvent = false;
_telephoneEventForwardToDecoder = false;
_telephoneEventDetectEndOfTone = false;
_telephoneEventPayloadType = -1;
_telephoneEventReported.clear();
_cngNBPayloadType = -1;
_cngWBPayloadType = -1;
_cngSWBPayloadType = -1;
_cngPayloadType = -1;
_G722PayloadType = -1;
_lastReceivedG722 = false;
return 0;
}
void
RTPReceiverAudio::ChangeUniqueId(const WebRtc_Word32 id)
{
_id = id;
}
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 three CNG on 8000Hz, 16000Hz and 32000Hz
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
{
// 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 {
assert(false);
return NULL;
}
}
WebRtc_UWord8 bitsPerSample = 0; // zero implies frame based
bool isTrueStereo = false; // Default value
if (ModuleRTPUtility::StringCompare(payloadName, "DVI4", 4)) {
bitsPerSample = 4;
} else if(ModuleRTPUtility::StringCompare(payloadName, "G722", 4)) {
if(ModuleRTPUtility::StringCompare(payloadName, "G7221", 5)) {
// frame based
} else {
_G722PayloadType = payloadType;
bitsPerSample = 4;
}
} else if(ModuleRTPUtility::StringCompare(payloadName,"G726-40",7)) {
bitsPerSample = 5;
} else if(ModuleRTPUtility::StringCompare(payloadName,"G726-32",7)) {
bitsPerSample = 4;
} else if(ModuleRTPUtility::StringCompare(payloadName,"G726-24",7)) {
bitsPerSample = 3;
} else if(ModuleRTPUtility::StringCompare(payloadName,"G726-16",7)) {
bitsPerSample = 2;
} else if(ModuleRTPUtility::StringCompare(payloadName,"L8",2)) {
bitsPerSample = 8;
} else if(ModuleRTPUtility::StringCompare(payloadName,"L16",3)) {
bitsPerSample = 16;
} else if(ModuleRTPUtility::StringCompare(payloadName,"PCMU",4)) {
bitsPerSample = 8;
} else if(ModuleRTPUtility::StringCompare(payloadName,"PCMA",4)) {
bitsPerSample = 8;
} else if(ModuleRTPUtility::StringCompare(payloadName,"CELT",4))
{
isTrueStereo = true;
}
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.bitsPerSample = bitsPerSample;
payload->typeSpecific.Audio.rate = rate;
payload->typeSpecific.Audio.trueStereoCodec = isTrueStereo;
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);
}
if(audioSpecific.channels > 1)
{
WebRtc_Word32 retVal = 0;
WebRtc_UWord16 channelLength = payloadLength/audioSpecific.channels;
if(audioSpecific.bitsPerSample > 0)
{
// sanity
assert((payloadLength*8)%audioSpecific.bitsPerSample == 0);
// sample based codec
// build matrix
WebRtc_UWord8 matrix[IP_PACKET_SIZE];
WebRtc_UWord32 offsetBytes = 0;
WebRtc_UWord32 offsetBytesInsert = 0;
// initialize matrix to 0
memset(matrix, 0, audioSpecific.channels*channelLength);
switch(audioSpecific.bitsPerSample)
{
case 1:
case 2:
case 3:
case 4:
case 5:
case 6:
case 7:
{
WebRtc_UWord32 offsetSamples = 0;
WebRtc_UWord32 offsetSamplesInsert = 0;
WebRtc_UWord16 bitMask = (WebRtc_UWord16)ModuleRTPUtility::pow2(audioSpecific.bitsPerSample)-1;
WebRtc_UWord16 samplesPerChannel =payloadLength*8/audioSpecific.bitsPerSample/audioSpecific.channels;
for(WebRtc_UWord32 i = 0; i < samplesPerChannel; i++)
{
WebRtc_UWord8 insertShift = (WebRtc_UWord8)((offsetSamplesInsert+audioSpecific.bitsPerSample)%16);
insertShift = 16 - insertShift; // inverse the calculation
for(WebRtc_UWord32 j = 0; j < audioSpecific.channels; j++)
{
// get sample
WebRtc_UWord16 s = payloadData[offsetBytes] << 8;
// check that we don't read outside the memory
if(offsetBytes < (WebRtc_UWord32)payloadLength -2)
{
s += payloadData[offsetBytes+1];
}
WebRtc_UWord8 readShift = (WebRtc_UWord8)((offsetSamples+audioSpecific.bitsPerSample)%16);
readShift = 16 - readShift; // inverse the calculation
s >>= readShift;
s &= bitMask;
// prepare for reading next sample
offsetSamples += audioSpecific.bitsPerSample;
if(readShift <= audioSpecific.bitsPerSample)
{
// next does not fitt
// or fitt exactly
offsetSamples -= 8;
offsetBytes++;
}
// insert sample into matrix
WebRtc_UWord32 columOffset = j*channelLength;
WebRtc_UWord16 insert = s << insertShift;
#if defined(WEBRTC_LITTLE_ENDIAN)
matrix[columOffset+offsetBytesInsert] |= static_cast<WebRtc_UWord8>(insert>>8);
matrix[columOffset+offsetBytesInsert+1] |= static_cast<WebRtc_UWord8>(insert);
#else
WebRtc_UWord16* matrixU16 = (WebRtc_UWord16*)&(matrix[columOffset+offsetBytesInsert]);
matrixU16[0] |= (s << insertShift);
#endif
}
// prepare for writing next sample
offsetSamplesInsert += audioSpecific.bitsPerSample;
if(insertShift <= audioSpecific.bitsPerSample)
{
// next does not fitt
// or fitt exactly
offsetSamplesInsert -= 8;
offsetBytesInsert++;
}
}
}
break;
case 8:
{
WebRtc_UWord32 sample = 0;
for(WebRtc_UWord32 i = 0; i < channelLength; i++)
{
for(WebRtc_UWord32 j = 0; j < audioSpecific.channels; j++)
{
WebRtc_UWord32 columOffset = j*channelLength;
matrix[columOffset + i] = payloadData[sample++];
}
}
}
break;
case 16:
{
WebRtc_UWord32 sample = 0;
for(WebRtc_UWord32 i = 0; i < channelLength; i +=2)
{
for(WebRtc_UWord32 j = 0; j < audioSpecific.channels; j++)
{
WebRtc_UWord32 columOffset = j*channelLength;
matrix[columOffset + i] = payloadData[sample++];
matrix[columOffset + i + 1] = payloadData[sample++];
}
}
}
break;
default:
assert(false);
return -1;
}
// we support 16 bits sample
// callback for all channels
for(int channel = 0; channel < audioSpecific.channels && retVal == 0; channel++)
{
// one callback per channel
rtpHeader->type.Audio.channel = channel+1;
if(channel == 0)
{
// include the original packet only in the first callback
retVal = CallbackOfReceivedPayloadData(&matrix[channel*channelLength],
channelLength,
rtpHeader);
} else
{
retVal = CallbackOfReceivedPayloadData(&matrix[channel*channelLength],
channelLength,
rtpHeader);
}
}
} else if (audioSpecific.trueStereoCodec)
{
// One callback with the whole payload for each channel.
for(int channel = 1; (channel <= audioSpecific.channels) &&
(retVal == 0); channel++)
{
// One callback per channel.
rtpHeader->type.Audio.channel = channel;
retVal = CallbackOfReceivedPayloadData(payloadData,
payloadLength,
rtpHeader);
}
} else
{
for(int channel = 1; channel <= audioSpecific.channels && retVal == 0; channel++)
{
// one callback per channel
rtpHeader->type.Audio.channel = channel;
if(channel == 1)
{
// include the original packet only in the first callback
retVal = CallbackOfReceivedPayloadData(payloadData,
channelLength,
rtpHeader);
} else
{
retVal = CallbackOfReceivedPayloadData(payloadData,
channelLength,
rtpHeader);
}
payloadData += channelLength;
}
}
return retVal;
}else
{
rtpHeader->type.Audio.channel = 1;
return CallbackOfReceivedPayloadData(payloadData,
payloadLength,
rtpHeader);
}
}
} // namespace webrtc