/* * 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 "packet.h" #include "session_info.h" #include #include namespace webrtc { VCMSessionInfo::VCMSessionInfo(): _haveFirstPacket(false), _markerBit(false), _sessionNACK(false), _completeSession(false), _frameType(kVideoFrameDelta), _previousFrameLoss(false), _lowSeqNum(-1), _highSeqNum(-1), _highestPacketIndex(0) { memset(_packetSizeBytes, 0, sizeof(_packetSizeBytes)); memset(_naluCompleteness, kNaluUnset, sizeof(_naluCompleteness)); memset(_ORwithPrevByte, 0, sizeof(_ORwithPrevByte)); } VCMSessionInfo::~VCMSessionInfo() { } WebRtc_Word32 VCMSessionInfo::GetLowSeqNum() const { return _lowSeqNum; } WebRtc_Word32 VCMSessionInfo::GetHighSeqNum() const { return _highSeqNum; } void VCMSessionInfo::Reset() { _lowSeqNum = -1; _highSeqNum = -1; _markerBit = false; _haveFirstPacket = false; _completeSession = false; _frameType = kVideoFrameDelta; _previousFrameLoss = false; _sessionNACK = false; _highestPacketIndex = 0; memset(_packetSizeBytes, 0, sizeof(_packetSizeBytes)); memset(_naluCompleteness, kNaluUnset, sizeof(_naluCompleteness)); memset(_ORwithPrevByte, 0, sizeof(_ORwithPrevByte)); } WebRtc_UWord32 VCMSessionInfo::GetSessionLength() { WebRtc_UWord32 length = 0; for (WebRtc_Word32 i=0; i<=_highestPacketIndex; ++i) { length += _packetSizeBytes[i]; } return length; } void VCMSessionInfo::SetStartSeqNumber(WebRtc_UWord16 seqNumber) { _lowSeqNum = seqNumber; _highSeqNum = seqNumber; } bool VCMSessionInfo::HaveStartSeqNumber() { if(_lowSeqNum == -1 || _highSeqNum == -1) { return false; } return true; } WebRtc_UWord32 VCMSessionInfo::InsertBuffer(WebRtc_UWord8* ptrStartOfLayer, WebRtc_Word32 packetIndex, const VCMPacket& packet) { WebRtc_UWord32 moveLength = 0; WebRtc_UWord32 returnLength = 0; int i = 0; // need to calc offset before updating _packetSizeBytes WebRtc_UWord32 offset = 0; WebRtc_UWord32 packetSize = 0; // Store this packet length. Add length since we could have data present already (e.g. multicall case). if (packet.bits) { packetSize = packet.sizeBytes; } else { packetSize = packet.sizeBytes + (packet.insertStartCode?kH264StartCodeLengthBytes:0); } _packetSizeBytes[packetIndex] += packetSize; // count only the one in our layer for (i=0; i 0) { memmove((void*)(ptrStartOfLayer + offset + packetSize), ptrStartOfLayer + offset, moveLength); } if (packet.bits) { // Add the packet without ORing end and start bytes together. // This is done when the frame is fetched for decoding by calling // GlueTogether(). _ORwithPrevByte[packetIndex] = true; if (packet.dataPtr != NULL) { memcpy((void*)(ptrStartOfLayer + offset), packet.dataPtr, packetSize); } returnLength = packetSize; } else { _ORwithPrevByte[packetIndex] = false; if (packet.dataPtr != NULL) { const unsigned char startCode[] = {0, 0, 0, 1}; if(packet.insertStartCode) { memcpy((void*)(ptrStartOfLayer + offset), startCode, kH264StartCodeLengthBytes); } memcpy((void*)(ptrStartOfLayer + offset + (packet.insertStartCode?kH264StartCodeLengthBytes:0)), packet.dataPtr, packet.sizeBytes); } returnLength = packetSize; } if (packet.isFirstPacket) { _haveFirstPacket = true; } if (packet.markerBit) { _markerBit = true; } // Store information about if the packet is decodable as is or not. _naluCompleteness[packetIndex]=packet.completeNALU; UpdateCompleteSession(); return returnLength; } void VCMSessionInfo::UpdateCompleteSession() { if (_haveFirstPacket && _markerBit) { // do we have all packets in this session? bool completeSession = true; for (int i=0; i<= _highestPacketIndex; ++i) { if (_naluCompleteness[i] == kNaluUnset) { completeSession = false; break; } } _completeSession = completeSession; } } bool VCMSessionInfo::IsSessionComplete() { return _completeSession; } // Find the start and end index of packetIndex packet. // startIndex -1 if start not found endIndex=-1 if end index not found void VCMSessionInfo::FindNaluBorder(WebRtc_Word32 packetIndex,WebRtc_Word32& startIndex, WebRtc_Word32& endIndex) { if(_naluCompleteness[packetIndex]==kNaluStart || _naluCompleteness[packetIndex]==kNaluComplete) { startIndex=packetIndex; } else // Need to find the start { for(startIndex=packetIndex-1;startIndex>=0;--startIndex) { if( (_naluCompleteness[startIndex]==kNaluComplete && _packetSizeBytes[startIndex]>0) ||(_naluCompleteness[startIndex]==kNaluEnd && startIndex>0)) // Found previous NALU. { startIndex++; break; } if( _naluCompleteness[startIndex]==kNaluStart) // This is where the NALU start. { break; } } } if(_naluCompleteness[packetIndex]==kNaluEnd || _naluCompleteness[packetIndex]==kNaluComplete) { endIndex=packetIndex; } else { // Find the next NALU for(endIndex=packetIndex+1;endIndex<=_highestPacketIndex;++endIndex) { if((_naluCompleteness[endIndex]==kNaluComplete && _packetSizeBytes[endIndex]>0) || _naluCompleteness[endIndex]==kNaluStart) // Found next NALU. { endIndex--; break; } if( _naluCompleteness[endIndex]==kNaluEnd) // This is where the NALU end. { break; } } if(endIndex>_highestPacketIndex) endIndex=-1; } } // Deletes all packets between startIndex and endIndex WebRtc_UWord32 VCMSessionInfo::DeletePackets(WebRtc_UWord8* ptrStartOfLayer,WebRtc_Word32 startIndex,WebRtc_Word32 endIndex) { //Get the number of bytes to delete. //Clear the size of these packets. WebRtc_UWord32 bytesToDelete=0; /// The number of bytes to delete. for(int j=startIndex;j<=endIndex;++j) { bytesToDelete+=_packetSizeBytes[j]; _packetSizeBytes[j]=0; } if (bytesToDelete > 0) { // Get the offset we want to move to. int destOffset=0; for(int j=0;j0) { switch(_naluCompleteness[0]) { case kNaluComplete: //Packet can be decoded as is. break; case kNaluStart: // Packet contain beginning of NALU- No need to do anything. break; case kNaluIncomplete: //Packet is not beginning or end of NALU //Need to find the end of this fua NALU and delete all packets. FindNaluBorder(0,startIndex,endIndex); if(endIndex==-1) // No end found. Delete { endIndex=_highestPacketIndex; } returnLength+=DeletePackets(ptrStartOfLayer,0,endIndex);//Delete this NALU. break; case kNaluEnd: // Packet is the end of a NALU //Need to delete this packet returnLength+=DeletePackets(ptrStartOfLayer,0,0);//Delete this NALU. break; default: assert(false); } } return returnLength; } WebRtc_Word32 VCMSessionInfo::ZeroOutSeqNum(WebRtc_Word32* list, WebRtc_Word32 num) { if ((NULL == list) || (num < 1)) { return -1; } if (_lowSeqNum == -1) { // no packets in this frame return 0; } // Find end point (index of entry that equals _lowSeqNum) int index = 0; for (; index = kMaxPacketsInJitterBuffer || packetIndex < 0) { //not allowed assert(!"SessionInfo::UpdatePacketSize Error: invalid packetIndex"); return; } _packetSizeBytes[packetIndex] = length; } void VCMSessionInfo::PrependPacketIndices(WebRtc_Word32 numberOfPacketIndices) { // sanity if((numberOfPacketIndices + GetHighestPacketIndex() >= kMaxPacketsInJitterBuffer) || numberOfPacketIndices < 0) { //not allowed assert(!"SessionInfo::PrependPacketIndexes Error: invalid packetIndex"); return; } // Works if we have new packets before packetIndex = 0 int numOfPacketsToMove = GetHighestPacketIndex()+1; memmove(&_packetSizeBytes[numberOfPacketIndices], &_packetSizeBytes[0], (numOfPacketsToMove)*sizeof(WebRtc_UWord16)); memset(&_packetSizeBytes[0], 0, numberOfPacketIndices*sizeof(WebRtc_UWord16)); _highestPacketIndex += (WebRtc_UWord16)numberOfPacketIndices; } void VCMSessionInfo::ClearPacketSize(WebRtc_Word32 packetIndex) { // sanity if(packetIndex >= kMaxPacketsInJitterBuffer || packetIndex < 0) { //not allowed assert(!"SessionInfo::ClearPacketSize Error: invalid packetIndex"); return; } _packetSizeBytes[packetIndex] =0; } WebRtc_UWord32 VCMSessionInfo::GetPacketSize(WebRtc_Word32 packetIndex) { // sanity if(packetIndex >= kMaxPacketsInJitterBuffer || packetIndex < 0) { //not allowed assert(!"SessionInfo::GetPacketSize Error: invalid packetIndex"); return 0; } return _packetSizeBytes[packetIndex]; } WebRtc_Word64 VCMSessionInfo::InsertPacket(const VCMPacket& packet, WebRtc_UWord8* ptrStartOfLayer) { //not allowed assert(!packet.insertStartCode || !packet.bits); // Check if this is first packet (only valid for some codecs) if (packet.isFirstPacket) { // the first packet in the frame always signals the frametype _frameType = packet.frameType; } // Check sequence number and update highest and lowest sequence numbers received. // Move data if this seq num is lower than previously lowest. if (packet.seqNum > _highSeqNum) { // This packet's seq num is higher than previously highest seq num; normal case // if we have a wrap, only update with wrapped values if (!(_highSeqNum < 0x00ff && packet.seqNum > 0xff00)) { _highSeqNum = packet.seqNum; } } else if (_highSeqNum > 0xff00 && packet.seqNum < 0x00ff) { // wrap _highSeqNum = packet.seqNum; } int packetIndex = packet.seqNum - (WebRtc_UWord16)_lowSeqNum; if(_lowSeqNum < 0x00ff && packet.seqNum > 0xff00) { // negative wrap packetIndex = packet.seqNum - 0x10000 - _lowSeqNum; } if (packetIndex < 0) { if (_lowSeqNum > 0xff00 && packet.seqNum < 0x00ff) { // we have a false detect due to the wrap packetIndex = (0xffff - (WebRtc_UWord16)_lowSeqNum) + packet.seqNum + (WebRtc_UWord16)1; } else { // This packet's seq num is lower than previously lowest seq num, but no wrap // We need to move the data in all arrays indexed by packetIndex and insert the new // packet's info // How many packets should we leave room for (positions to shift)? // Example - this seq num is 3 lower than previously lowest seq num // Before: |--prev packet with lowest seq num--|--|...| // After: |--new lowest seq num--|--|--|--prev packet with lowest seq num--|--|...| WebRtc_UWord16 positionsToShift = (WebRtc_UWord16)_lowSeqNum - packet.seqNum; WebRtc_UWord16 numOfPacketsToMove = _highestPacketIndex + 1; // sanity, do we have room for the shift? if ((positionsToShift + numOfPacketsToMove) > kMaxPacketsInJitterBuffer) { return -1; } // Shift _ORwithPrevByte array memmove(&_ORwithPrevByte[positionsToShift], &_ORwithPrevByte[0], numOfPacketsToMove*sizeof(bool)); memset(&_ORwithPrevByte[0], false, positionsToShift*sizeof(bool)); // Shift _packetSizeBytes array memmove(&_packetSizeBytes[positionsToShift], &_packetSizeBytes[0], numOfPacketsToMove*sizeof(WebRtc_UWord32)); memset(&_packetSizeBytes[0], 0, positionsToShift*sizeof(WebRtc_UWord32)); //Shift _naluCompleteness memmove(&_naluCompleteness[positionsToShift], &_naluCompleteness[0], numOfPacketsToMove*sizeof(WebRtc_UWord8)); memset(&_naluCompleteness[0], kNaluUnset, positionsToShift*sizeof(WebRtc_UWord8)); _highestPacketIndex += positionsToShift; _lowSeqNum = packet.seqNum; packetIndex = 0; // (seqNum - _lowSeqNum) = 0 } } // if (_lowSeqNum > seqNum) // sanity if (packetIndex >= kMaxPacketsInJitterBuffer ) { return -1; } if (packetIndex < 0 ) { return -1; } // Check for duplicate packets if (_packetSizeBytes[packetIndex] != 0) { // We have already received a packet with this sequence number, ignore it. return -2; } // update highest packet index _highestPacketIndex = packetIndex > _highestPacketIndex ? packetIndex :_highestPacketIndex; return InsertBuffer(ptrStartOfLayer, packetIndex, packet); } WebRtc_UWord32 VCMSessionInfo::PrepareForDecode(WebRtc_UWord8* ptrStartOfLayer, VideoCodecType codec) { WebRtc_UWord32 currentPacketOffset = 0; WebRtc_UWord32 length = GetSessionLength(); WebRtc_UWord32 idSum = 0; WebRtc_UWord32 realDataBytes = 0; if (length == 0) { return length; } bool previousLost = false; for (int i=0; i <= _highestPacketIndex; i++) { if (_ORwithPrevByte[i]) { if (currentPacketOffset > 0) { WebRtc_UWord8* ptrFirstByte = ptrStartOfLayer + currentPacketOffset; if (_packetSizeBytes[i-1] == 0 || previousLost) { // It is be better to throw away this packet if we are missing the // previous packet. memset(ptrFirstByte, 0, _packetSizeBytes[i]); previousLost = true; } else if (_packetSizeBytes[i] > 0) // Ignore if empty packet { // Glue with previous byte // Move everything from [this packet start + 1, end of buffer] one byte to the left WebRtc_UWord8* ptrPrevByte = ptrFirstByte - 1; *ptrPrevByte = (*ptrPrevByte) | (*ptrFirstByte); WebRtc_UWord32 lengthToEnd = length - (currentPacketOffset + 1); memmove((void*)ptrFirstByte, (void*)(ptrFirstByte + 1), lengthToEnd); _packetSizeBytes[i]--; length--; previousLost = false; realDataBytes += _packetSizeBytes[i]; } } else { memset(ptrStartOfLayer, 0, _packetSizeBytes[i]); previousLost = true; } } else if (_packetSizeBytes[i] == 0 && codec == kVideoCodecH263) { WebRtc_UWord8* ptrFirstByte = ptrStartOfLayer + currentPacketOffset; memmove(ptrFirstByte + 10, ptrFirstByte, length - currentPacketOffset); memset(ptrFirstByte, 0, 10); _packetSizeBytes[i] = 10; length += _packetSizeBytes[i]; previousLost = true; } else { realDataBytes += _packetSizeBytes[i]; previousLost = false; } currentPacketOffset += _packetSizeBytes[i]; } if (realDataBytes == 0) { // Drop the frame since all it contains are zeros length = 0; memset(_packetSizeBytes, 0, sizeof(_packetSizeBytes)); } return length; } }