Files
platform-external-webrtc/webrtc/modules/audio_coding/neteq4/test/NETEQTEST_RTPpacket.cc
henrik.lundin@webrtc.org d94659dc27 Initial upload of NetEq4
This is the first public upload of the new NetEq, version 4.

It has been through extensive internal review during the course of
the project.

TEST=trybots

Review URL: https://webrtc-codereview.appspot.com/1073005

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3425 4adac7df-926f-26a2-2b94-8c16560cd09d
2013-01-29 12:09:21 +00:00

871 lines
19 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 "NETEQTEST_RTPpacket.h"
#include <assert.h>
#include <stdlib.h> // rand
#include <string.h>
#ifdef WIN32
#include <winsock2.h>
#else
#include <netinet/in.h> // for htons, htonl, etc
#endif
const int NETEQTEST_RTPpacket::_kRDHeaderLen = 8;
const int NETEQTEST_RTPpacket::_kBasicHeaderLen = 12;
NETEQTEST_RTPpacket::NETEQTEST_RTPpacket()
:
_datagram(NULL),
_payloadPtr(NULL),
_memSize(0),
_datagramLen(-1),
_payloadLen(0),
_rtpParsed(false),
_receiveTime(0),
_lost(false)
{
memset(&_rtpInfo, 0, sizeof(_rtpInfo));
_blockList.clear();
}
NETEQTEST_RTPpacket::~NETEQTEST_RTPpacket()
{
if(_datagram)
{
delete [] _datagram;
}
}
void NETEQTEST_RTPpacket::reset()
{
if(_datagram) {
delete [] _datagram;
}
_datagram = NULL;
_memSize = 0;
_datagramLen = -1;
_payloadLen = 0;
_payloadPtr = NULL;
_receiveTime = 0;
memset(&_rtpInfo, 0, sizeof(_rtpInfo));
_rtpParsed = false;
}
int NETEQTEST_RTPpacket::skipFileHeader(FILE *fp)
{
if (!fp) {
return -1;
}
const int kFirstLineLength = 40;
char firstline[kFirstLineLength];
if (fgets(firstline, kFirstLineLength, fp) == NULL) {
return -1;
}
if (strncmp(firstline, "#!rtpplay", 9) == 0) {
if (strncmp(firstline, "#!rtpplay1.0", 12) != 0) {
return -1;
}
}
else if (strncmp(firstline, "#!RTPencode", 11) == 0) {
if (strncmp(firstline, "#!RTPencode1.0", 14) != 0) {
return -1;
}
}
else
{
return -1;
}
const int kRtpDumpHeaderSize = 4 + 4 + 4 + 2 + 2;
if (fseek(fp, kRtpDumpHeaderSize, SEEK_CUR) != 0)
{
return -1;
}
return 0;
}
int NETEQTEST_RTPpacket::readFromFile(FILE *fp)
{
if(!fp)
{
return(-1);
}
WebRtc_UWord16 length, plen;
WebRtc_UWord32 offset;
if (fread(&length,2,1,fp)==0)
{
reset();
return(-2);
}
length = ntohs(length);
if (fread(&plen,2,1,fp)==0)
{
reset();
return(-1);
}
int packetLen = ntohs(plen);
if (fread(&offset,4,1,fp)==0)
{
reset();
return(-1);
}
WebRtc_UWord32 receiveTime = ntohl(offset); // store in local variable until we have passed the reset below
// Use length here because a plen of 0 specifies rtcp
length = (WebRtc_UWord16) (length - _kRDHeaderLen);
// check buffer size
if (_datagram && _memSize < length)
{
reset();
}
if (!_datagram)
{
_datagram = new WebRtc_UWord8[length];
_memSize = length;
}
if (fread((unsigned short *) _datagram,1,length,fp) != length)
{
reset();
return(-1);
}
_datagramLen = length;
_receiveTime = receiveTime;
if (!_blockList.empty() && _blockList.count(payloadType()) > 0)
{
// discard this payload
return(readFromFile(fp));
}
_rtpParsed = false;
return(packetLen);
}
int NETEQTEST_RTPpacket::readFixedFromFile(FILE *fp, size_t length)
{
if (!fp)
{
return -1;
}
// check buffer size
if (_datagram && _memSize < static_cast<int>(length))
{
reset();
}
if (!_datagram)
{
_datagram = new WebRtc_UWord8[length];
_memSize = length;
}
if (fread(_datagram, 1, length, fp) != length)
{
reset();
return -1;
}
_datagramLen = length;
_receiveTime = 0;
if (!_blockList.empty() && _blockList.count(payloadType()) > 0)
{
// discard this payload
return readFromFile(fp);
}
_rtpParsed = false;
return length;
}
int NETEQTEST_RTPpacket::writeToFile(FILE *fp)
{
if (!fp)
{
return -1;
}
WebRtc_UWord16 length, plen;
WebRtc_UWord32 offset;
// length including RTPplay header
length = htons(_datagramLen + _kRDHeaderLen);
if (fwrite(&length, 2, 1, fp) != 1)
{
return -1;
}
// payload length
plen = htons(_datagramLen);
if (fwrite(&plen, 2, 1, fp) != 1)
{
return -1;
}
// offset (=receive time)
offset = htonl(_receiveTime);
if (fwrite(&offset, 4, 1, fp) != 1)
{
return -1;
}
// write packet data
if (fwrite(_datagram, 1, _datagramLen, fp) !=
static_cast<size_t>(_datagramLen))
{
return -1;
}
return _datagramLen + _kRDHeaderLen; // total number of bytes written
}
void NETEQTEST_RTPpacket::blockPT(WebRtc_UWord8 pt)
{
_blockList[pt] = true;
}
void NETEQTEST_RTPpacket::parseHeader()
{
if (_rtpParsed)
{
// nothing to do
return;
}
if (_datagramLen < _kBasicHeaderLen)
{
// corrupt packet?
return;
}
_payloadLen = parseRTPheader(&_payloadPtr);
_rtpParsed = true;
return;
}
void NETEQTEST_RTPpacket::parseHeader(webrtc::WebRtcRTPHeader* rtp_header) {
if (!_rtpParsed) {
parseHeader();
}
if (rtp_header) {
rtp_header->header.markerBit = _rtpInfo.header.markerBit;
rtp_header->header.payloadType = _rtpInfo.header.payloadType;
rtp_header->header.sequenceNumber = _rtpInfo.header.sequenceNumber;
rtp_header->header.timestamp = _rtpInfo.header.timestamp;
rtp_header->header.ssrc = _rtpInfo.header.ssrc;
}
}
const webrtc::WebRtcRTPHeader* NETEQTEST_RTPpacket::RTPinfo() const
{
if (_rtpParsed)
{
return &_rtpInfo;
}
else
{
return NULL;
}
}
WebRtc_UWord8 * NETEQTEST_RTPpacket::datagram() const
{
if (_datagramLen > 0)
{
return _datagram;
}
else
{
return NULL;
}
}
WebRtc_UWord8 * NETEQTEST_RTPpacket::payload() const
{
if (_payloadLen > 0)
{
return _payloadPtr;
}
else
{
return NULL;
}
}
WebRtc_Word16 NETEQTEST_RTPpacket::payloadLen()
{
parseHeader();
return _payloadLen;
}
WebRtc_Word16 NETEQTEST_RTPpacket::dataLen() const
{
return _datagramLen;
}
bool NETEQTEST_RTPpacket::isParsed() const
{
return _rtpParsed;
}
bool NETEQTEST_RTPpacket::isLost() const
{
return _lost;
}
WebRtc_UWord8 NETEQTEST_RTPpacket::payloadType() const
{
webrtc::WebRtcRTPHeader tempRTPinfo;
if(_datagram && _datagramLen >= _kBasicHeaderLen)
{
parseRTPheader(&tempRTPinfo);
}
else
{
return 0;
}
return tempRTPinfo.header.payloadType;
}
WebRtc_UWord16 NETEQTEST_RTPpacket::sequenceNumber() const
{
webrtc::WebRtcRTPHeader tempRTPinfo;
if(_datagram && _datagramLen >= _kBasicHeaderLen)
{
parseRTPheader(&tempRTPinfo);
}
else
{
return 0;
}
return tempRTPinfo.header.sequenceNumber;
}
WebRtc_UWord32 NETEQTEST_RTPpacket::timeStamp() const
{
webrtc::WebRtcRTPHeader tempRTPinfo;
if(_datagram && _datagramLen >= _kBasicHeaderLen)
{
parseRTPheader(&tempRTPinfo);
}
else
{
return 0;
}
return tempRTPinfo.header.timestamp;
}
WebRtc_UWord32 NETEQTEST_RTPpacket::SSRC() const
{
webrtc::WebRtcRTPHeader tempRTPinfo;
if(_datagram && _datagramLen >= _kBasicHeaderLen)
{
parseRTPheader(&tempRTPinfo);
}
else
{
return 0;
}
return tempRTPinfo.header.ssrc;
}
WebRtc_UWord8 NETEQTEST_RTPpacket::markerBit() const
{
webrtc::WebRtcRTPHeader tempRTPinfo;
if(_datagram && _datagramLen >= _kBasicHeaderLen)
{
parseRTPheader(&tempRTPinfo);
}
else
{
return 0;
}
return tempRTPinfo.header.markerBit;
}
int NETEQTEST_RTPpacket::setPayloadType(WebRtc_UWord8 pt)
{
if (_datagramLen < 12)
{
return -1;
}
if (!_rtpParsed)
{
_rtpInfo.header.payloadType = pt;
}
_datagram[1]=(unsigned char)(pt & 0xFF);
return 0;
}
int NETEQTEST_RTPpacket::setSequenceNumber(WebRtc_UWord16 sn)
{
if (_datagramLen < 12)
{
return -1;
}
if (!_rtpParsed)
{
_rtpInfo.header.sequenceNumber = sn;
}
_datagram[2]=(unsigned char)((sn>>8)&0xFF);
_datagram[3]=(unsigned char)((sn)&0xFF);
return 0;
}
int NETEQTEST_RTPpacket::setTimeStamp(WebRtc_UWord32 ts)
{
if (_datagramLen < 12)
{
return -1;
}
if (!_rtpParsed)
{
_rtpInfo.header.timestamp = ts;
}
_datagram[4]=(unsigned char)((ts>>24)&0xFF);
_datagram[5]=(unsigned char)((ts>>16)&0xFF);
_datagram[6]=(unsigned char)((ts>>8)&0xFF);
_datagram[7]=(unsigned char)(ts & 0xFF);
return 0;
}
int NETEQTEST_RTPpacket::setSSRC(WebRtc_UWord32 ssrc)
{
if (_datagramLen < 12)
{
return -1;
}
if (!_rtpParsed)
{
_rtpInfo.header.ssrc = ssrc;
}
_datagram[8]=(unsigned char)((ssrc>>24)&0xFF);
_datagram[9]=(unsigned char)((ssrc>>16)&0xFF);
_datagram[10]=(unsigned char)((ssrc>>8)&0xFF);
_datagram[11]=(unsigned char)(ssrc & 0xFF);
return 0;
}
int NETEQTEST_RTPpacket::setMarkerBit(WebRtc_UWord8 mb)
{
if (_datagramLen < 12)
{
return -1;
}
if (_rtpParsed)
{
_rtpInfo.header.markerBit = mb;
}
if (mb)
{
_datagram[0] |= 0x01;
}
else
{
_datagram[0] &= 0xFE;
}
return 0;
}
int NETEQTEST_RTPpacket::setRTPheader(const webrtc::WebRtcRTPHeader* RTPinfo)
{
if (_datagramLen < 12)
{
// this packet is not ok
return -1;
}
makeRTPheader(_datagram,
RTPinfo->header.payloadType,
RTPinfo->header.sequenceNumber,
RTPinfo->header.timestamp,
RTPinfo->header.ssrc,
RTPinfo->header.markerBit);
return 0;
}
int NETEQTEST_RTPpacket::splitStereo(NETEQTEST_RTPpacket* slaveRtp,
enum stereoModes mode)
{
// if mono, do nothing
if (mode == stereoModeMono)
{
return 0;
}
// check that the RTP header info is parsed
parseHeader();
// start by copying the main rtp packet
*slaveRtp = *this;
if(_payloadLen == 0)
{
// do no more
return 0;
}
if(_payloadLen%2 != 0)
{
// length must be a factor of 2
return -1;
}
switch(mode)
{
case stereoModeSample1:
{
// sample based codec with 1-byte samples
splitStereoSample(slaveRtp, 1 /* 1 byte/sample */);
break;
}
case stereoModeSample2:
{
// sample based codec with 2-byte samples
splitStereoSample(slaveRtp, 2 /* 2 bytes/sample */);
break;
}
case stereoModeFrame:
{
// frame based codec
splitStereoFrame(slaveRtp);
break;
}
case stereoModeDuplicate:
{
// frame based codec, send the whole packet to both master and slave
splitStereoDouble(slaveRtp);
break;
}
case stereoModeMono:
{
assert(false);
return -1;
}
}
return 0;
}
void NETEQTEST_RTPpacket::makeRTPheader(unsigned char* rtp_data, WebRtc_UWord8 payloadType, WebRtc_UWord16 seqNo, WebRtc_UWord32 timestamp, WebRtc_UWord32 ssrc, WebRtc_UWord8 markerBit) const
{
rtp_data[0]=(unsigned char)0x80;
if (markerBit)
{
rtp_data[0] |= 0x01;
}
else
{
rtp_data[0] &= 0xFE;
}
rtp_data[1]=(unsigned char)(payloadType & 0xFF);
rtp_data[2]=(unsigned char)((seqNo>>8)&0xFF);
rtp_data[3]=(unsigned char)((seqNo)&0xFF);
rtp_data[4]=(unsigned char)((timestamp>>24)&0xFF);
rtp_data[5]=(unsigned char)((timestamp>>16)&0xFF);
rtp_data[6]=(unsigned char)((timestamp>>8)&0xFF);
rtp_data[7]=(unsigned char)(timestamp & 0xFF);
rtp_data[8]=(unsigned char)((ssrc>>24)&0xFF);
rtp_data[9]=(unsigned char)((ssrc>>16)&0xFF);
rtp_data[10]=(unsigned char)((ssrc>>8)&0xFF);
rtp_data[11]=(unsigned char)(ssrc & 0xFF);
}
WebRtc_UWord16
NETEQTEST_RTPpacket::parseRTPheader(webrtc::WebRtcRTPHeader* RTPinfo,
WebRtc_UWord8 **payloadPtr) const
{
WebRtc_Word16 *rtp_data = (WebRtc_Word16 *) _datagram;
int i_P, i_X, i_CC;
assert(_datagramLen >= 12);
parseBasicHeader(RTPinfo, &i_P, &i_X, &i_CC);
int i_startPosition = calcHeaderLength(i_X, i_CC);
int i_padlength = calcPadLength(i_P);
if (payloadPtr)
{
*payloadPtr = (WebRtc_UWord8*) &rtp_data[i_startPosition >> 1];
}
return (WebRtc_UWord16) (_datagramLen - i_startPosition - i_padlength);
}
void NETEQTEST_RTPpacket::parseBasicHeader(webrtc::WebRtcRTPHeader* RTPinfo,
int *i_P, int *i_X, int *i_CC) const
{
WebRtc_Word16 *rtp_data = (WebRtc_Word16 *) _datagram;
if (_datagramLen < 12)
{
assert(false);
return;
}
*i_P=(((WebRtc_UWord16)(rtp_data[0] & 0x20))>>5); /* Extract the P bit */
*i_X=(((WebRtc_UWord16)(rtp_data[0] & 0x10))>>4); /* Extract the X bit */
*i_CC=(WebRtc_UWord16)(rtp_data[0] & 0xF); /* Get the CC number */
/* Get the marker bit */
RTPinfo->header.markerBit = (WebRtc_UWord8) ((rtp_data[0] >> 15) & 0x01);
/* Get the coder type */
RTPinfo->header.payloadType = (WebRtc_UWord8) ((rtp_data[0] >> 8) & 0x7F);
/* Get the packet number */
RTPinfo->header.sequenceNumber =
((( ((WebRtc_UWord16)rtp_data[1]) >> 8) & 0xFF) |
( ((WebRtc_UWord16)(rtp_data[1] & 0xFF)) << 8));
/* Get timestamp */
RTPinfo->header.timestamp = ((((WebRtc_UWord16)rtp_data[2]) & 0xFF) << 24) |
((((WebRtc_UWord16)rtp_data[2]) & 0xFF00) << 8) |
((((WebRtc_UWord16)rtp_data[3]) >> 8) & 0xFF) |
((((WebRtc_UWord16)rtp_data[3]) & 0xFF) << 8);
/* Get the SSRC */
RTPinfo->header.ssrc = ((((WebRtc_UWord16)rtp_data[4]) & 0xFF) << 24) |
((((WebRtc_UWord16)rtp_data[4]) & 0xFF00) << 8) |
((((WebRtc_UWord16)rtp_data[5]) >> 8) & 0xFF) |
((((WebRtc_UWord16)rtp_data[5]) & 0xFF) << 8);
}
int NETEQTEST_RTPpacket::calcHeaderLength(int i_X, int i_CC) const
{
int i_extlength = 0;
WebRtc_Word16 *rtp_data = (WebRtc_Word16 *) _datagram;
if (i_X == 1)
{
// Extension header exists.
// Find out how many WebRtc_Word32 it consists of.
assert(_datagramLen > 2 * (7 + 2 * i_CC));
if (_datagramLen > 2 * (7 + 2 * i_CC))
{
i_extlength = (((((WebRtc_UWord16) rtp_data[7 + 2 * i_CC]) >> 8)
& 0xFF) | (((WebRtc_UWord16) (rtp_data[7 + 2 * i_CC] & 0xFF))
<< 8)) + 1;
}
}
return 12 + 4 * i_extlength + 4 * i_CC;
}
int NETEQTEST_RTPpacket::calcPadLength(int i_P) const
{
WebRtc_Word16 *rtp_data = (WebRtc_Word16 *) _datagram;
if (i_P == 1)
{
/* Padding exists. Find out how many bytes the padding consists of. */
if (_datagramLen & 0x1)
{
/* odd number of bytes => last byte in higher byte */
return rtp_data[_datagramLen >> 1] & 0xFF;
}
else
{
/* even number of bytes => last byte in lower byte */
return ((WebRtc_UWord16) rtp_data[(_datagramLen >> 1) - 1]) >> 8;
}
}
return 0;
}
void NETEQTEST_RTPpacket::splitStereoSample(NETEQTEST_RTPpacket* slaveRtp,
int stride)
{
if(!_payloadPtr || !slaveRtp || !slaveRtp->_payloadPtr
|| _payloadLen <= 0 || slaveRtp->_memSize < _memSize)
{
return;
}
WebRtc_UWord8 *readDataPtr = _payloadPtr;
WebRtc_UWord8 *writeDataPtr = _payloadPtr;
WebRtc_UWord8 *slaveData = slaveRtp->_payloadPtr;
while (readDataPtr - _payloadPtr < _payloadLen)
{
// master data
for (int ix = 0; ix < stride; ix++) {
*writeDataPtr = *readDataPtr;
writeDataPtr++;
readDataPtr++;
}
// slave data
for (int ix = 0; ix < stride; ix++) {
*slaveData = *readDataPtr;
slaveData++;
readDataPtr++;
}
}
_payloadLen /= 2;
slaveRtp->_payloadLen = _payloadLen;
}
void NETEQTEST_RTPpacket::splitStereoFrame(NETEQTEST_RTPpacket* slaveRtp)
{
if(!_payloadPtr || !slaveRtp || !slaveRtp->_payloadPtr
|| _payloadLen <= 0 || slaveRtp->_memSize < _memSize)
{
return;
}
memmove(slaveRtp->_payloadPtr, _payloadPtr + _payloadLen/2, _payloadLen/2);
_payloadLen /= 2;
slaveRtp->_payloadLen = _payloadLen;
}
void NETEQTEST_RTPpacket::splitStereoDouble(NETEQTEST_RTPpacket* slaveRtp)
{
if(!_payloadPtr || !slaveRtp || !slaveRtp->_payloadPtr
|| _payloadLen <= 0 || slaveRtp->_memSize < _memSize)
{
return;
}
memcpy(slaveRtp->_payloadPtr, _payloadPtr, _payloadLen);
slaveRtp->_payloadLen = _payloadLen;
}
// Get the RTP header for the RED payload indicated by argument index.
// The first RED payload is index = 0.
int NETEQTEST_RTPpacket::extractRED(int index, webrtc::WebRtcRTPHeader& red)
{
//
// 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
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |1| block PT | timestamp offset | block length |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |1| ... |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// |0| block PT |
// +-+-+-+-+-+-+-+-+
//
parseHeader();
WebRtc_UWord8* ptr = payload();
WebRtc_UWord8* payloadEndPtr = ptr + payloadLen();
int num_encodings = 0;
int total_len = 0;
while ((ptr < payloadEndPtr) && (*ptr & 0x80))
{
int len = ((ptr[2] & 0x03) << 8) + ptr[3];
if (num_encodings == index)
{
// Header found.
red.header.payloadType = ptr[0] & 0x7F;
WebRtc_UWord32 offset = (ptr[1] << 6) + ((ptr[2] & 0xFC) >> 2);
red.header.sequenceNumber = sequenceNumber();
red.header.timestamp = timeStamp() - offset;
red.header.markerBit = markerBit();
red.header.ssrc = SSRC();
return len;
}
++num_encodings;
total_len += len;
ptr += 4;
}
if ((ptr < payloadEndPtr) && (num_encodings == index))
{
// Last header.
red.header.payloadType = ptr[0] & 0x7F;
red.header.sequenceNumber = sequenceNumber();
red.header.timestamp = timeStamp();
red.header.markerBit = markerBit();
red.header.ssrc = SSRC();
++ptr;
return payloadLen() - (ptr - payload()) - total_len;
}
return -1;
}
// Randomize the payload, not the RTP header.
void NETEQTEST_RTPpacket::scramblePayload(void)
{
parseHeader();
for (int i = 0; i < _payloadLen; ++i)
{
_payloadPtr[i] = static_cast<WebRtc_UWord8>(rand());
}
}