/* * 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 "rtp_player.h" #include "../source/internal_defines.h" #include "rtp_rtcp.h" #include "tick_time.h" #include #ifdef WIN32 #include #include #else #include #endif using namespace webrtc; RawRtpPacket::RawRtpPacket(WebRtc_UWord8* data, WebRtc_UWord16 len) : rtpData(), rtpLen(len), resendTimeMs(-1) { rtpData = new WebRtc_UWord8[rtpLen]; memcpy(rtpData, data, rtpLen); } RawRtpPacket::~RawRtpPacket() { delete [] rtpData; } LostPackets::LostPackets() : _critSect(*CriticalSectionWrapper::CreateCriticalSection()), _lossCount(0), ListWrapper(), _debugFile(NULL) { _debugFile = fopen("PacketLossDebug.txt", "w"); } LostPackets::~LostPackets() { if (_debugFile) { fclose(_debugFile); } ListItem* item = First(); while (item != NULL) { RawRtpPacket* packet = static_cast(item->GetItem()); if (packet != NULL) { delete packet; } Erase(item); item = First(); } delete &_critSect; } WebRtc_UWord32 LostPackets::AddPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen) { CriticalSectionScoped cs(_critSect); RawRtpPacket* packet = new RawRtpPacket(rtpData, rtpLen); ListItem* newItem = new ListItem(packet); InsertBefore(First(), newItem); const WebRtc_UWord16 seqNo = (rtpData[2] << 8) + rtpData[3]; if (_debugFile != NULL) { fprintf(_debugFile, "%u Lost packet: %u\n", _lossCount, seqNo); } _lossCount++; return 0; } WebRtc_UWord32 LostPackets::SetResendTime(WebRtc_UWord16 sequenceNumber, WebRtc_Word64 resendTime) { CriticalSectionScoped cs(_critSect); ListItem* item = First(); while (item != NULL) { RawRtpPacket* packet = static_cast(item->GetItem()); const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3]; const WebRtc_Word64 nowMs = VCMTickTime::MillisecondTimestamp(); if (sequenceNumber == seqNo && packet->resendTimeMs + 10 < nowMs) { if (_debugFile != NULL) { fprintf(_debugFile, "Resend %u at %u\n", seqNo, MaskWord64ToUWord32(resendTime)); } packet->resendTimeMs = resendTime; return 0; } item = Next(item); } fprintf(_debugFile, "Packet not lost %u\n", sequenceNumber); return -1; } WebRtc_UWord32 LostPackets::NumberOfPacketsToResend() const { CriticalSectionScoped cs(_critSect); WebRtc_UWord32 count = 0; ListItem* item = First(); while (item != NULL) { RawRtpPacket* packet = static_cast(item->GetItem()); if (packet->resendTimeMs >= 0) { count++; } item = Next(item); } return count; } void LostPackets::ResentPacket(WebRtc_UWord16 seqNo) { CriticalSectionScoped cs(_critSect); if (_debugFile != NULL) { fprintf(_debugFile, "Resent %u at %u\n", seqNo, MaskWord64ToUWord32(VCMTickTime::MillisecondTimestamp())); } } RTPPlayer::RTPPlayer(const char* filename, RtpData* callback) : _rtpModule(*RtpRtcp::CreateRtpRtcp(1, false)), _nextRtpTime(0), _dataCallback(callback), _firstPacket(true), _lossRate(0.0f), _nackEnabled(false), _resendPacketCount(0), _noLossStartup(100), _endOfFile(false), _rttMs(0), _firstPacketRtpTime(0), _firstPacketTimeMs(0), _reorderBuffer(NULL), _reordering(false), _nextPacket(), _nextPacketLength(0), _randVec(), _randVecPos(0) { _rtpFile = fopen(filename, "rb"); memset(_nextPacket, 0, sizeof(_nextPacket)); } RTPPlayer::~RTPPlayer() { RtpRtcp::DestroyRtpRtcp(&_rtpModule); if (_rtpFile != NULL) { fclose(_rtpFile); } if (_reorderBuffer != NULL) { delete _reorderBuffer; _reorderBuffer = NULL; } } WebRtc_Word32 RTPPlayer::Initialize(const ListWrapper& payloadList) { std::srand(321); for (int i=0; i < RAND_VEC_LENGTH; i++) { _randVec[i] = rand(); } _randVecPos = 0; WebRtc_Word32 ret = _rtpModule.SetNACKStatus(kNackOff); if (ret < 0) { return -1; } ret = _rtpModule.InitReceiver(); if (ret < 0) { return -1; } _rtpModule.InitSender(); _rtpModule.SetRTCPStatus(kRtcpNonCompound); _rtpModule.SetTMMBRStatus(true); ret = _rtpModule.RegisterIncomingDataCallback(_dataCallback); if (ret < 0) { return -1; } // Register payload types ListItem* item = payloadList.First(); while (item != NULL) { PayloadCodecTuple* payloadType = static_cast(item->GetItem()); if (payloadType != NULL) { if (_rtpModule.RegisterReceivePayload(payloadType->name.c_str(), payloadType->payloadType) < 0) { return -1; } } item = payloadList.Next(item); } if (ReadHeader() < 0) { return -1; } memset(_nextPacket, 0, sizeof(_nextPacket)); _nextPacketLength = ReadPacket(_nextPacket, &_nextRtpTime); return 0; } WebRtc_Word32 RTPPlayer::ReadHeader() { char firstline[FIRSTLINELEN]; if (_rtpFile == NULL) { return -1; } fgets(firstline, FIRSTLINELEN, _rtpFile); if(strncmp(firstline,"#!rtpplay",9) == 0) { if(strncmp(firstline,"#!rtpplay1.0",12) != 0){ printf("ERROR: wrong rtpplay version, must be 1.0\n"); return -1; } } else if (strncmp(firstline,"#!RTPencode",11) == 0) { if(strncmp(firstline,"#!RTPencode1.0",14) != 0){ printf("ERROR: wrong RTPencode version, must be 1.0\n"); return -1; } } else { printf("ERROR: wrong file format of input file\n"); return -1; } WebRtc_UWord32 start_sec; WebRtc_UWord32 start_usec; WebRtc_UWord32 source; WebRtc_UWord16 port; WebRtc_UWord16 padding; fread(&start_sec, 4, 1, _rtpFile); start_sec=ntohl(start_sec); fread(&start_usec, 4, 1, _rtpFile); start_usec=ntohl(start_usec); fread(&source, 4, 1, _rtpFile); source=ntohl(source); fread(&port, 2, 1, _rtpFile); port=ntohs(port); fread(&padding, 2, 1, _rtpFile); padding=ntohs(padding); return 0; } WebRtc_UWord32 RTPPlayer::TimeUntilNextPacket() const { WebRtc_Word64 timeLeft = (_nextRtpTime - _firstPacketRtpTime) - (VCMTickTime::MillisecondTimestamp() - _firstPacketTimeMs); if (timeLeft < 0) { return 0; } return static_cast(timeLeft); } WebRtc_Word32 RTPPlayer::NextPacket(const WebRtc_Word64 timeNow) { // Send any packets ready to be resent _lostPackets.Lock(); ListItem* item = _lostPackets.First(); _lostPackets.Unlock(); while (item != NULL) { _lostPackets.Lock(); RawRtpPacket* packet = static_cast(item->GetItem()); _lostPackets.Unlock(); if (timeNow >= packet->resendTimeMs && packet->resendTimeMs != -1) { const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3]; printf("Resend: %u\n", seqNo); WebRtc_Word32 ret = SendPacket(packet->rtpData, packet->rtpLen); ListItem* itemToRemove = item; _lostPackets.Lock(); item = _lostPackets.Next(item); _lostPackets.Erase(itemToRemove); delete packet; _lostPackets.Unlock(); _resendPacketCount++; if (ret > 0) { _lostPackets.ResentPacket(seqNo); } else if (ret < 0) { return ret; } } else { _lostPackets.Lock(); item = _lostPackets.Next(item); _lostPackets.Unlock(); } } // Send any packets from rtp file if (!_endOfFile && (TimeUntilNextPacket() == 0 || _firstPacket)) { _rtpModule.Process(); if (_firstPacket) { _firstPacketRtpTime = static_cast(_nextRtpTime); _firstPacketTimeMs = VCMTickTime::MillisecondTimestamp(); } if (_reordering && _reorderBuffer == NULL) { _reorderBuffer = new RawRtpPacket(reinterpret_cast(_nextPacket), static_cast(_nextPacketLength)); return 0; } WebRtc_Word32 ret = SendPacket(reinterpret_cast(_nextPacket), static_cast(_nextPacketLength)); if (_reordering && _reorderBuffer != NULL) { RawRtpPacket* rtpPacket = _reorderBuffer; _reorderBuffer = NULL; SendPacket(rtpPacket->rtpData, rtpPacket->rtpLen); delete rtpPacket; } _firstPacket = false; if (ret < 0) { return ret; } _nextPacketLength = ReadPacket(_nextPacket, &_nextRtpTime); if (_nextPacketLength < 0) { _endOfFile = true; return 0; } else if (_nextPacketLength == 0) { return 0; } } if (_endOfFile && _lostPackets.NumberOfPacketsToResend() == 0) { return 1; } return 0; } WebRtc_Word32 RTPPlayer::SendPacket(WebRtc_UWord8* rtpData, WebRtc_UWord16 rtpLen) { if ((_randVec[(_randVecPos++) % RAND_VEC_LENGTH] + 1.0)/(RAND_MAX + 1.0) < _lossRate && _noLossStartup < 0) { if (_nackEnabled) { const WebRtc_UWord16 seqNo = (rtpData[2] << 8) + rtpData[3]; printf("Throw: %u\n", seqNo); _lostPackets.AddPacket(rtpData, rtpLen); return 0; } } else { WebRtc_Word32 ret = _rtpModule.IncomingPacket(rtpData, rtpLen); if (ret < 0) { return -1; } } if (_noLossStartup >= 0) { _noLossStartup--; } return 1; } WebRtc_Word32 RTPPlayer::ReadPacket(WebRtc_Word16* rtpdata, WebRtc_UWord32* offset) { WebRtc_UWord16 length, plen; if (fread(&length,2,1,_rtpFile)==0) return(-1); length=ntohs(length); if (fread(&plen,2,1,_rtpFile)==0) return(-1); plen=ntohs(plen); if (fread(offset,4,1,_rtpFile)==0) return(-1); *offset=ntohl(*offset); // Use length here because a plen of 0 specifies rtcp length = (WebRtc_UWord16) (length - HDR_SIZE); if (fread((unsigned short *) rtpdata,1,length,_rtpFile) != length) return(-1); #ifdef JUNK_DATA // destroy the RTP payload with random data if (plen > 12) { // ensure that we have more than just a header for ( int ix = 12; ix < plen; ix=ix+2 ) { rtpdata[ix>>1] = (short) (rtpdata[ix>>1] + (short) rand()); } } #endif return plen; } WebRtc_Word32 RTPPlayer::SimulatePacketLoss(float lossRate, bool enableNack, WebRtc_UWord32 rttMs) { _nackEnabled = enableNack; _lossRate = lossRate; _rttMs = rttMs; return 0; } WebRtc_Word32 RTPPlayer::SetReordering(bool enabled) { _reordering = enabled; return 0; } WebRtc_Word32 RTPPlayer::ResendPackets(const WebRtc_UWord16* sequenceNumbers, WebRtc_UWord16 length) { if (sequenceNumbers == NULL) { return 0; } for (int i=0; i < length; i++) { _lostPackets.SetResendTime(sequenceNumbers[i], VCMTickTime::MillisecondTimestamp() + _rttMs); } return 0; } void RTPPlayer::Print() const { printf("Lost packets: %u, resent packets: %u\n", _lostPackets.TotalNumberOfLosses(), _resendPacketCount); printf("Packets still lost: %u\n", _lostPackets.GetSize()); printf("Packets waiting to be resent: %u\n", _lostPackets.NumberOfPacketsToResend()); printf("Sequence numbers:\n"); ListItem* item = _lostPackets.First(); while (item != NULL) { RawRtpPacket* packet = static_cast(item->GetItem()); const WebRtc_UWord16 seqNo = (packet->rtpData[2] << 8) + packet->rtpData[3]; printf("%u, ", seqNo); item = _lostPackets.Next(item); } printf("\n"); }