Add metric for number of packets discarded by JB due to not being decodable
Also fixes a couple of bugs related to sequence number wrap found while testing. BUG= TEST= Review URL: http://webrtc-codereview.appspot.com/218001 git-svn-id: http://webrtc.googlecode.com/svn/trunk@732 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
@@ -793,18 +793,10 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
{
|
{
|
||||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||||
}
|
}
|
||||||
if (inputImage._buffer == NULL)
|
|
||||||
{
|
|
||||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
|
||||||
}
|
|
||||||
if (_decodeCompleteCallback == NULL)
|
if (_decodeCompleteCallback == NULL)
|
||||||
{
|
{
|
||||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||||
}
|
}
|
||||||
if (inputImage._length <= 0)
|
|
||||||
{
|
|
||||||
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
|
||||||
}
|
|
||||||
if (inputImage._completeFrame == false)
|
if (inputImage._completeFrame == false)
|
||||||
{
|
{
|
||||||
// future improvement
|
// future improvement
|
||||||
@@ -815,6 +807,11 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
}
|
}
|
||||||
// otherwise allow for incomplete frames to be decoded.
|
// otherwise allow for incomplete frames to be decoded.
|
||||||
}
|
}
|
||||||
|
if (inputImage._buffer == NULL && inputImage._length > 0)
|
||||||
|
{
|
||||||
|
return WEBRTC_VIDEO_CODEC_ERR_PARAMETER;
|
||||||
|
}
|
||||||
|
|
||||||
#ifdef INDEPENDENT_PARTITIONS
|
#ifdef INDEPENDENT_PARTITIONS
|
||||||
if (fragmentation == NULL)
|
if (fragmentation == NULL)
|
||||||
{
|
{
|
||||||
@@ -844,8 +841,13 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
|
WebRtc_UWord8* buffer = inputImage._buffer;
|
||||||
|
if (inputImage._length == 0)
|
||||||
|
{
|
||||||
|
buffer = NULL; // Triggers full frame concealment
|
||||||
|
}
|
||||||
if (vpx_codec_decode(_decoder,
|
if (vpx_codec_decode(_decoder,
|
||||||
inputImage._buffer,
|
buffer,
|
||||||
inputImage._length,
|
inputImage._length,
|
||||||
0,
|
0,
|
||||||
VPX_DL_REALTIME))
|
VPX_DL_REALTIME))
|
||||||
@@ -947,11 +949,12 @@ VP8Decoder::DecodePartitions(const EncodedImage& input_image,
|
|||||||
partition_length,
|
partition_length,
|
||||||
0,
|
0,
|
||||||
VPX_DL_REALTIME)) {
|
VPX_DL_REALTIME)) {
|
||||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Signal end of frame data
|
// Signal end of frame data. If there was no frame data this will trigger
|
||||||
|
// a full frame concealment.
|
||||||
if (vpx_codec_decode(_decoder, NULL, 0, 0, VPX_DL_REALTIME))
|
if (vpx_codec_decode(_decoder, NULL, 0, 0, VPX_DL_REALTIME))
|
||||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
|
|||||||
@@ -58,19 +58,19 @@ VCMFrameBuffer::SetPreviousFrameLoss()
|
|||||||
}
|
}
|
||||||
|
|
||||||
WebRtc_Word32
|
WebRtc_Word32
|
||||||
VCMFrameBuffer::GetLowSeqNum()
|
VCMFrameBuffer::GetLowSeqNum() const
|
||||||
{
|
{
|
||||||
return _sessionInfo.GetLowSeqNum();
|
return _sessionInfo.GetLowSeqNum();
|
||||||
}
|
}
|
||||||
|
|
||||||
WebRtc_Word32
|
WebRtc_Word32
|
||||||
VCMFrameBuffer::GetHighSeqNum()
|
VCMFrameBuffer::GetHighSeqNum() const
|
||||||
{
|
{
|
||||||
return _sessionInfo.GetHighSeqNum();
|
return _sessionInfo.GetHighSeqNum();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VCMFrameBuffer::IsSessionComplete()
|
VCMFrameBuffer::IsSessionComplete() const
|
||||||
{
|
{
|
||||||
return _sessionInfo.IsSessionComplete();
|
return _sessionInfo.IsSessionComplete();
|
||||||
}
|
}
|
||||||
@@ -229,7 +229,7 @@ VCMFrameBuffer::GetNackCount() const
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VCMFrameBuffer::HaveLastPacket()
|
VCMFrameBuffer::HaveLastPacket() const
|
||||||
{
|
{
|
||||||
return _sessionInfo.HaveLastPacket();
|
return _sessionInfo.HaveLastPacket();
|
||||||
}
|
}
|
||||||
@@ -311,9 +311,12 @@ VCMFrameBuffer::SetState(VCMFrameBufferStateEnum state)
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case kStateDecoding:
|
case kStateDecoding:
|
||||||
// we can go to this state from state kStateComplete kStateIncomplete
|
// A frame migth have received empty packets, or media packets might
|
||||||
|
// have been removed when making the frame decodable. The frame can
|
||||||
|
// still be set to decodable since it can be used to inform the
|
||||||
|
// decoder of a frame loss.
|
||||||
assert(_state == kStateComplete || _state == kStateIncomplete ||
|
assert(_state == kStateComplete || _state == kStateIncomplete ||
|
||||||
_state == kStateDecodable);
|
_state == kStateDecodable || _state == kStateEmpty);
|
||||||
// Transfer frame information to EncodedFrame and create any codec
|
// Transfer frame information to EncodedFrame and create any codec
|
||||||
// specific information
|
// specific information
|
||||||
RestructureFrameInformation();
|
RestructureFrameInformation();
|
||||||
@@ -372,13 +375,17 @@ VCMFrameBuffer::ExtractFromStorage(const EncodedVideoData& frameFromStorage)
|
|||||||
return VCM_OK;
|
return VCM_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int VCMFrameBuffer::NotDecodablePackets() const {
|
||||||
|
return _sessionInfo.NotDecodablePackets();
|
||||||
|
}
|
||||||
|
|
||||||
// Set counted status (as counted by JB or not)
|
// Set counted status (as counted by JB or not)
|
||||||
void VCMFrameBuffer::SetCountedFrame(bool frameCounted)
|
void VCMFrameBuffer::SetCountedFrame(bool frameCounted)
|
||||||
{
|
{
|
||||||
_frameCounted = frameCounted;
|
_frameCounted = frameCounted;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VCMFrameBuffer::GetCountedFrame()
|
bool VCMFrameBuffer::GetCountedFrame() const
|
||||||
{
|
{
|
||||||
return _frameCounted;
|
return _frameCounted;
|
||||||
}
|
}
|
||||||
@@ -399,7 +406,7 @@ VCMFrameBuffer::GetState(WebRtc_UWord32& timeStamp) const
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VCMFrameBuffer::IsRetransmitted()
|
VCMFrameBuffer::IsRetransmitted() const
|
||||||
{
|
{
|
||||||
return _sessionInfo.IsRetransmitted();
|
return _sessionInfo.IsRetransmitted();
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,22 +42,22 @@ public:
|
|||||||
VCMFrameBufferStateEnum GetState(WebRtc_UWord32& timeStamp) const;
|
VCMFrameBufferStateEnum GetState(WebRtc_UWord32& timeStamp) const;
|
||||||
void SetState(VCMFrameBufferStateEnum state); // Set state of frame
|
void SetState(VCMFrameBufferStateEnum state); // Set state of frame
|
||||||
|
|
||||||
bool IsRetransmitted();
|
bool IsRetransmitted() const;
|
||||||
bool IsSessionComplete();
|
bool IsSessionComplete() const;
|
||||||
bool HaveLastPacket();
|
bool HaveLastPacket() const;
|
||||||
bool ForceSetHaveLastPacket();
|
bool ForceSetHaveLastPacket();
|
||||||
// Makes sure the session contain a decodable stream.
|
// Makes sure the session contain a decodable stream.
|
||||||
void MakeSessionDecodable();
|
void MakeSessionDecodable();
|
||||||
|
|
||||||
// Sequence numbers
|
// Sequence numbers
|
||||||
// Get lowest packet sequence number in frame
|
// Get lowest packet sequence number in frame
|
||||||
WebRtc_Word32 GetLowSeqNum();
|
WebRtc_Word32 GetLowSeqNum() const;
|
||||||
// Get highest packet sequence number in frame
|
// Get highest packet sequence number in frame
|
||||||
WebRtc_Word32 GetHighSeqNum();
|
WebRtc_Word32 GetHighSeqNum() const;
|
||||||
|
|
||||||
// Set counted status (as counted by JB or not)
|
// Set counted status (as counted by JB or not)
|
||||||
void SetCountedFrame(bool frameCounted);
|
void SetCountedFrame(bool frameCounted);
|
||||||
bool GetCountedFrame();
|
bool GetCountedFrame() const;
|
||||||
|
|
||||||
// NACK
|
// NACK
|
||||||
// Zero out all entries in list up to and including _lowSeqNum
|
// Zero out all entries in list up to and including _lowSeqNum
|
||||||
@@ -76,6 +76,10 @@ public:
|
|||||||
|
|
||||||
WebRtc_Word32 ExtractFromStorage(const EncodedVideoData& frameFromStorage);
|
WebRtc_Word32 ExtractFromStorage(const EncodedVideoData& frameFromStorage);
|
||||||
|
|
||||||
|
// The number of packets discarded because the decoder can't make use of
|
||||||
|
// them.
|
||||||
|
int NotDecodablePackets() const;
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void RestructureFrameInformation();
|
void RestructureFrameInformation();
|
||||||
void PrepareForDecode();
|
void PrepareForDecode();
|
||||||
|
|||||||
@@ -26,7 +26,8 @@ VCMFrameListTimestampOrderAsc::Flush()
|
|||||||
while(Erase(First()) != -1) { }
|
while(Erase(First()) != -1) { }
|
||||||
}
|
}
|
||||||
|
|
||||||
// Inserts frame in timestamp order, with the oldest timestamp first. Takes wrap arounds into account
|
// Inserts frame in timestamp order, with the oldest timestamp first. Takes wrap
|
||||||
|
// arounds into account
|
||||||
WebRtc_Word32
|
WebRtc_Word32
|
||||||
VCMFrameListTimestampOrderAsc::Insert(VCMFrameBuffer* frame)
|
VCMFrameListTimestampOrderAsc::Insert(VCMFrameBuffer* frame)
|
||||||
{
|
{
|
||||||
@@ -40,7 +41,8 @@ VCMFrameListTimestampOrderAsc::Insert(VCMFrameBuffer* frame)
|
|||||||
while (item != NULL)
|
while (item != NULL)
|
||||||
{
|
{
|
||||||
const WebRtc_UWord32 itemTimestamp = item->GetItem()->TimeStamp();
|
const WebRtc_UWord32 itemTimestamp = item->GetItem()->TimeStamp();
|
||||||
if (VCMJitterBuffer::LatestTimestamp(itemTimestamp, frame->TimeStamp()) == itemTimestamp)
|
if (LatestTimestamp(itemTimestamp, frame->TimeStamp(), NULL) ==
|
||||||
|
itemTimestamp)
|
||||||
{
|
{
|
||||||
if (InsertBefore(item, newItem) < 0)
|
if (InsertBefore(item, newItem) < 0)
|
||||||
{
|
{
|
||||||
@@ -101,7 +103,9 @@ VCMFrameListTimestampOrderAsc::FindFrame(FindFrameCriteria criteria,
|
|||||||
const void* compareWith,
|
const void* compareWith,
|
||||||
VCMFrameListItem* startItem) const
|
VCMFrameListItem* startItem) const
|
||||||
{
|
{
|
||||||
const VCMFrameListItem* frameListItem = FindFrameListItem(criteria, compareWith, startItem);
|
const VCMFrameListItem* frameListItem = FindFrameListItem(criteria,
|
||||||
|
compareWith,
|
||||||
|
startItem);
|
||||||
if (frameListItem == NULL)
|
if (frameListItem == NULL)
|
||||||
{
|
{
|
||||||
return NULL;
|
return NULL;
|
||||||
|
|||||||
@@ -71,8 +71,9 @@ VCMJitterBuffer::VCMJitterBuffer(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId,
|
|||||||
_maxNumberOfFrames(kStartNumberOfFrames),
|
_maxNumberOfFrames(kStartNumberOfFrames),
|
||||||
_frameBuffers(),
|
_frameBuffers(),
|
||||||
_frameBuffersTSOrder(),
|
_frameBuffersTSOrder(),
|
||||||
_lastDecodedSeqNum(),
|
_lastDecodedSeqNum(-1),
|
||||||
_lastDecodedTimeStamp(-1),
|
_lastDecodedTimeStamp(-1),
|
||||||
|
_packetsNotDecodable(0),
|
||||||
_receiveStatistics(),
|
_receiveStatistics(),
|
||||||
_incomingFrameRate(0),
|
_incomingFrameRate(0),
|
||||||
_incomingFrameCount(0),
|
_incomingFrameCount(0),
|
||||||
@@ -92,7 +93,6 @@ VCMJitterBuffer::VCMJitterBuffer(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId,
|
|||||||
{
|
{
|
||||||
memset(_frameBuffers, 0, sizeof(_frameBuffers));
|
memset(_frameBuffers, 0, sizeof(_frameBuffers));
|
||||||
memset(_receiveStatistics, 0, sizeof(_receiveStatistics));
|
memset(_receiveStatistics, 0, sizeof(_receiveStatistics));
|
||||||
_lastDecodedSeqNum = -1;
|
|
||||||
memset(_NACKSeqNumInternal, -1, sizeof(_NACKSeqNumInternal));
|
memset(_NACKSeqNumInternal, -1, sizeof(_NACKSeqNumInternal));
|
||||||
|
|
||||||
for (int i = 0; i< kStartNumberOfFrames; i++)
|
for (int i = 0; i< kStartNumberOfFrames; i++)
|
||||||
@@ -127,7 +127,6 @@ VCMJitterBuffer::operator=(const VCMJitterBuffer& rhs)
|
|||||||
_running = rhs._running;
|
_running = rhs._running;
|
||||||
_master = !rhs._master;
|
_master = !rhs._master;
|
||||||
_maxNumberOfFrames = rhs._maxNumberOfFrames;
|
_maxNumberOfFrames = rhs._maxNumberOfFrames;
|
||||||
_lastDecodedTimeStamp = rhs._lastDecodedTimeStamp;
|
|
||||||
_incomingFrameRate = rhs._incomingFrameRate;
|
_incomingFrameRate = rhs._incomingFrameRate;
|
||||||
_incomingFrameCount = rhs._incomingFrameCount;
|
_incomingFrameCount = rhs._incomingFrameCount;
|
||||||
_timeLastIncomingFrameCount = rhs._timeLastIncomingFrameCount;
|
_timeLastIncomingFrameCount = rhs._timeLastIncomingFrameCount;
|
||||||
@@ -145,6 +144,8 @@ VCMJitterBuffer::operator=(const VCMJitterBuffer& rhs)
|
|||||||
_missingMarkerBits = rhs._missingMarkerBits;
|
_missingMarkerBits = rhs._missingMarkerBits;
|
||||||
_firstPacket = rhs._firstPacket;
|
_firstPacket = rhs._firstPacket;
|
||||||
_lastDecodedSeqNum = rhs._lastDecodedSeqNum;
|
_lastDecodedSeqNum = rhs._lastDecodedSeqNum;
|
||||||
|
_lastDecodedTimeStamp = rhs._lastDecodedTimeStamp;
|
||||||
|
_packetsNotDecodable = rhs._packetsNotDecodable;
|
||||||
memcpy(_receiveStatistics, rhs._receiveStatistics,
|
memcpy(_receiveStatistics, rhs._receiveStatistics,
|
||||||
sizeof(_receiveStatistics));
|
sizeof(_receiveStatistics));
|
||||||
memcpy(_NACKSeqNumInternal, rhs._NACKSeqNumInternal,
|
memcpy(_NACKSeqNumInternal, rhs._NACKSeqNumInternal,
|
||||||
@@ -174,30 +175,6 @@ VCMJitterBuffer::operator=(const VCMJitterBuffer& rhs)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebRtc_UWord32
|
|
||||||
VCMJitterBuffer::LatestTimestamp(const WebRtc_UWord32 existingTimestamp,
|
|
||||||
const WebRtc_UWord32 newTimestamp)
|
|
||||||
{
|
|
||||||
bool wrap = (newTimestamp < 0x0000ffff && existingTimestamp > 0xffff0000) ||
|
|
||||||
(newTimestamp > 0xffff0000 && existingTimestamp < 0x0000ffff);
|
|
||||||
if (existingTimestamp > newTimestamp && !wrap)
|
|
||||||
{
|
|
||||||
return existingTimestamp;
|
|
||||||
}
|
|
||||||
else if (existingTimestamp <= newTimestamp && !wrap)
|
|
||||||
{
|
|
||||||
return newTimestamp;
|
|
||||||
}
|
|
||||||
else if (existingTimestamp < newTimestamp && wrap)
|
|
||||||
{
|
|
||||||
return existingTimestamp;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return newTimestamp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start jitter buffer
|
// Start jitter buffer
|
||||||
void
|
void
|
||||||
VCMJitterBuffer::Start()
|
VCMJitterBuffer::Start()
|
||||||
@@ -223,6 +200,7 @@ VCMJitterBuffer::Start()
|
|||||||
_firstPacket = true;
|
_firstPacket = true;
|
||||||
_NACKSeqNumLength = 0;
|
_NACKSeqNumLength = 0;
|
||||||
_rttMs = 0;
|
_rttMs = 0;
|
||||||
|
_packetsNotDecodable = 0;
|
||||||
|
|
||||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId,
|
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideoCoding, VCMId(_vcmId,
|
||||||
_receiverId), "JB(0x%x): Jitter buffer: start", this);
|
_receiverId), "JB(0x%x): Jitter buffer: start", this);
|
||||||
@@ -280,6 +258,7 @@ VCMJitterBuffer::FlushInternal()
|
|||||||
}
|
}
|
||||||
_lastDecodedSeqNum = -1;
|
_lastDecodedSeqNum = -1;
|
||||||
_lastDecodedTimeStamp = -1;
|
_lastDecodedTimeStamp = -1;
|
||||||
|
_packetsNotDecodable = 0;
|
||||||
|
|
||||||
_frameEvent.Reset();
|
_frameEvent.Reset();
|
||||||
_packetEvent.Reset();
|
_packetEvent.Reset();
|
||||||
@@ -353,7 +332,7 @@ VCMJitterBuffer::UpdateFrameState(VCMFrameBuffer* frame)
|
|||||||
// an old complete frame can arrive too late
|
// an old complete frame can arrive too late
|
||||||
if (_lastDecodedTimeStamp > 0 &&
|
if (_lastDecodedTimeStamp > 0 &&
|
||||||
LatestTimestamp(static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp),
|
LatestTimestamp(static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp),
|
||||||
frame->TimeStamp()) == _lastDecodedTimeStamp)
|
frame->TimeStamp(), NULL) == _lastDecodedTimeStamp)
|
||||||
{
|
{
|
||||||
// Frame is older than the latest decoded frame, drop it.
|
// Frame is older than the latest decoded frame, drop it.
|
||||||
// This will trigger a release in CleanUpSizeZeroFrames later.
|
// This will trigger a release in CleanUpSizeZeroFrames later.
|
||||||
@@ -427,7 +406,6 @@ VCMJitterBuffer::UpdateFrameState(VCMFrameBuffer* frame)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Get received key and delta frames
|
// Get received key and delta frames
|
||||||
WebRtc_Word32
|
WebRtc_Word32
|
||||||
VCMJitterBuffer::GetFrameStatistics(WebRtc_UWord32& receivedDeltaFrames,
|
VCMJitterBuffer::GetFrameStatistics(WebRtc_UWord32& receivedDeltaFrames,
|
||||||
@@ -441,6 +419,11 @@ VCMJitterBuffer::GetFrameStatistics(WebRtc_UWord32& receivedDeltaFrames,
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
WebRtc_UWord32 VCMJitterBuffer::NumNotDecodablePackets() const {
|
||||||
|
CriticalSectionScoped cs(_critSect);
|
||||||
|
return _packetsNotDecodable;
|
||||||
|
}
|
||||||
|
|
||||||
WebRtc_UWord32 VCMJitterBuffer::DiscardedPackets() const {
|
WebRtc_UWord32 VCMJitterBuffer::DiscardedPackets() const {
|
||||||
CriticalSectionScoped cs(_critSect);
|
CriticalSectionScoped cs(_critSect);
|
||||||
return _discardedPackets;
|
return _discardedPackets;
|
||||||
@@ -458,7 +441,7 @@ VCMJitterBuffer::GetFrame(const VCMPacket& packet, VCMEncodedFrame*& frame)
|
|||||||
_critSect.Enter();
|
_critSect.Enter();
|
||||||
// Make sure that old empty packets are inserted.
|
// Make sure that old empty packets are inserted.
|
||||||
if (LatestTimestamp(static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp),
|
if (LatestTimestamp(static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp),
|
||||||
packet.timestamp) == _lastDecodedTimeStamp
|
packet.timestamp, NULL) == _lastDecodedTimeStamp
|
||||||
&& packet.sizeBytes > 0)
|
&& packet.sizeBytes > 0)
|
||||||
{
|
{
|
||||||
_discardedPackets++; // Only counts discarded media packets
|
_discardedPackets++; // Only counts discarded media packets
|
||||||
@@ -1136,6 +1119,8 @@ VCMJitterBuffer::GetFrameForDecoding()
|
|||||||
// Set as decoding. Propagates the missingFrame bit.
|
// Set as decoding. Propagates the missingFrame bit.
|
||||||
oldestFrame->SetState(kStateDecoding);
|
oldestFrame->SetState(kStateDecoding);
|
||||||
|
|
||||||
|
_packetsNotDecodable += oldestFrame->NotDecodablePackets();
|
||||||
|
|
||||||
// Store current seqnum & time
|
// Store current seqnum & time
|
||||||
_lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
|
_lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
|
||||||
_lastDecodedTimeStamp = oldestFrame->TimeStamp();
|
_lastDecodedTimeStamp = oldestFrame->TimeStamp();
|
||||||
@@ -1204,8 +1189,9 @@ VCMJitterBuffer::GetFrameForDecodingNACK()
|
|||||||
return oldestFrame;
|
return oldestFrame;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Must be called under the critical section _critSect. Should never be called with
|
// Must be called under the critical section _critSect. Should never be called
|
||||||
// retransmitted frames, they must be filtered out before this function is called.
|
// with retransmitted frames, they must be filtered out before this function is
|
||||||
|
// called.
|
||||||
void
|
void
|
||||||
VCMJitterBuffer::UpdateJitterAndDelayEstimates(VCMJitterSample& sample,
|
VCMJitterBuffer::UpdateJitterAndDelayEstimates(VCMJitterSample& sample,
|
||||||
bool incompleteFrame)
|
bool incompleteFrame)
|
||||||
@@ -1334,24 +1320,8 @@ VCMJitterBuffer::GetLowHighSequenceNumbers(WebRtc_Word32& lowSeqNum,
|
|||||||
(kStateDecoding != state) &&
|
(kStateDecoding != state) &&
|
||||||
seqNum != -1)
|
seqNum != -1)
|
||||||
{
|
{
|
||||||
if (highSeqNum == -1)
|
bool wrap;
|
||||||
{
|
highSeqNum = LatestSequenceNumber(seqNum, highSeqNum, &wrap);
|
||||||
// first
|
|
||||||
highSeqNum = seqNum;
|
|
||||||
}
|
|
||||||
else if (seqNum < 0x0fff && highSeqNum > 0xf000)
|
|
||||||
{
|
|
||||||
// wrap
|
|
||||||
highSeqNum = seqNum;
|
|
||||||
}
|
|
||||||
else if(seqNum > 0xf000 && highSeqNum < 0x0fff)
|
|
||||||
{
|
|
||||||
// Do nothing since it is a wrap and this one is older
|
|
||||||
}
|
|
||||||
else if (seqNum > highSeqNum)
|
|
||||||
{
|
|
||||||
highSeqNum = seqNum;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
} // for
|
} // for
|
||||||
return 0;
|
return 0;
|
||||||
@@ -1648,18 +1618,20 @@ VCMJitterBuffer::InsertPacket(VCMEncodedFrame* buffer, const VCMPacket& packet)
|
|||||||
VCMFrameBufferEnum ret = kSizeError;
|
VCMFrameBufferEnum ret = kSizeError;
|
||||||
VCMFrameBuffer* frame = static_cast<VCMFrameBuffer*>(buffer);
|
VCMFrameBuffer* frame = static_cast<VCMFrameBuffer*>(buffer);
|
||||||
|
|
||||||
|
// We are keeping track of the first seq num, the latest seq num and
|
||||||
|
// the number of wraps to be able to calculate how many packets we expect.
|
||||||
|
if (_firstPacket)
|
||||||
|
{
|
||||||
|
// Now it's time to start estimating jitter
|
||||||
|
// reset the delay estimate.
|
||||||
|
_delayEstimate.Reset();
|
||||||
|
_firstPacket = false;
|
||||||
|
}
|
||||||
|
|
||||||
// Empty packets may bias the jitter estimate (lacking size component),
|
// Empty packets may bias the jitter estimate (lacking size component),
|
||||||
// therefore don't let empty packet trigger the following updates:
|
// therefore don't let empty packet trigger the following updates:
|
||||||
if (packet.frameType != kFrameEmpty)
|
if (packet.frameType != kFrameEmpty)
|
||||||
{
|
{
|
||||||
if (_firstPacket)
|
|
||||||
{
|
|
||||||
// Now it's time to start estimating jitter
|
|
||||||
// reset the delay estimate.
|
|
||||||
_delayEstimate.Reset();
|
|
||||||
_firstPacket = false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (_waitingForCompletion.timestamp == packet.timestamp)
|
if (_waitingForCompletion.timestamp == packet.timestamp)
|
||||||
{
|
{
|
||||||
// This can get bad if we have a lot of duplicate packets,
|
// This can get bad if we have a lot of duplicate packets,
|
||||||
@@ -1681,24 +1653,19 @@ VCMJitterBuffer::InsertPacket(VCMEncodedFrame* buffer, const VCMPacket& packet)
|
|||||||
if (frame != NULL)
|
if (frame != NULL)
|
||||||
{
|
{
|
||||||
VCMFrameBufferStateEnum state = frame->GetState();
|
VCMFrameBufferStateEnum state = frame->GetState();
|
||||||
if ((packet.sizeBytes == 0) &&
|
if (packet.sizeBytes == 0 && packet.timestamp == _lastDecodedTimeStamp)
|
||||||
((state == kStateDecoding) ||
|
{
|
||||||
(state == kStateEmpty &&
|
|
||||||
_lastDecodedTimeStamp == packet.timestamp)))
|
|
||||||
{
|
|
||||||
// Empty packet (sizeBytes = 0), make sure we update the last
|
// Empty packet (sizeBytes = 0), make sure we update the last
|
||||||
// decoded seq num since this packet belongs either to a frame
|
// decoded sequence number
|
||||||
// being decoded (condition 1) or to a frame which was already
|
_lastDecodedSeqNum = LatestSequenceNumber(packet.seqNum,
|
||||||
// decoded and freed (condition 2). A new frame will be created
|
_lastDecodedSeqNum, NULL);
|
||||||
// for the empty packet. That frame will be empty and later on
|
|
||||||
// cleaned up.
|
|
||||||
UpdateLastDecodedWithEmpty(packet);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Insert packet
|
// Insert packet
|
||||||
// check for first packet
|
// Check for first packet
|
||||||
// high sequence number will not be set
|
// High sequence number will be -1 if neither an empty packet nor
|
||||||
bool first = frame->GetHighSeqNum() == -1;
|
// a media packet has been inserted.
|
||||||
|
bool first = (frame->GetHighSeqNum() == -1);
|
||||||
bufferReturn = frame->InsertPacket(packet, nowMs);
|
bufferReturn = frame->InsertPacket(packet, nowMs);
|
||||||
ret = bufferReturn;
|
ret = bufferReturn;
|
||||||
|
|
||||||
@@ -1762,26 +1729,13 @@ VCMJitterBuffer::InsertPacket(VCMEncodedFrame* buffer, const VCMPacket& packet)
|
|||||||
return ret;
|
return ret;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
VCMJitterBuffer::UpdateLastDecodedWithEmpty(const VCMPacket& packet)
|
|
||||||
{
|
|
||||||
// Empty packet inserted to a frame which
|
|
||||||
// is already decoding. Update the last decoded seq no.
|
|
||||||
if (_lastDecodedTimeStamp == packet.timestamp &&
|
|
||||||
(packet.seqNum > _lastDecodedSeqNum ||
|
|
||||||
(packet.seqNum < 0x0fff && _lastDecodedSeqNum > 0xf000)))
|
|
||||||
{
|
|
||||||
_lastDecodedSeqNum = packet.seqNum;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Must be called from within _critSect
|
// Must be called from within _critSect
|
||||||
void
|
void
|
||||||
VCMJitterBuffer::UpdateOldJitterSample(const VCMPacket& packet)
|
VCMJitterBuffer::UpdateOldJitterSample(const VCMPacket& packet)
|
||||||
{
|
{
|
||||||
if (_waitingForCompletion.timestamp != packet.timestamp &&
|
if (_waitingForCompletion.timestamp != packet.timestamp &&
|
||||||
LatestTimestamp(_waitingForCompletion.timestamp, packet.timestamp) ==
|
LatestTimestamp(_waitingForCompletion.timestamp, packet.timestamp,
|
||||||
packet.timestamp)
|
NULL) == packet.timestamp)
|
||||||
{
|
{
|
||||||
// This is a newer frame than the one waiting for completion.
|
// This is a newer frame than the one waiting for completion.
|
||||||
_waitingForCompletion.frameSize = packet.sizeBytes;
|
_waitingForCompletion.frameSize = packet.sizeBytes;
|
||||||
@@ -1904,8 +1858,8 @@ VCMJitterBuffer::CleanUpOldFrames()
|
|||||||
// Release the frame if it's older than the last decoded frame.
|
// Release the frame if it's older than the last decoded frame.
|
||||||
if (_lastDecodedTimeStamp > -1 &&
|
if (_lastDecodedTimeStamp > -1 &&
|
||||||
LatestTimestamp(static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp),
|
LatestTimestamp(static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp),
|
||||||
frameTimeStamp)
|
frameTimeStamp, NULL) ==
|
||||||
== static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp))
|
static_cast<WebRtc_UWord32>(_lastDecodedTimeStamp))
|
||||||
{
|
{
|
||||||
const WebRtc_Word32 frameLowSeqNum = oldestFrame->GetLowSeqNum();
|
const WebRtc_Word32 frameLowSeqNum = oldestFrame->GetLowSeqNum();
|
||||||
const WebRtc_Word32 frameHighSeqNum = oldestFrame->GetHighSeqNum();
|
const WebRtc_Word32 frameHighSeqNum = oldestFrame->GetHighSeqNum();
|
||||||
@@ -2027,13 +1981,17 @@ void
|
|||||||
VCMJitterBuffer::VerifyAndSetPreviousFrameLost(VCMFrameBuffer& frame)
|
VCMJitterBuffer::VerifyAndSetPreviousFrameLost(VCMFrameBuffer& frame)
|
||||||
{
|
{
|
||||||
frame.MakeSessionDecodable(); // make sure the session can be decoded.
|
frame.MakeSessionDecodable(); // make sure the session can be decoded.
|
||||||
|
if (frame.FrameType() == kVideoFrameKey)
|
||||||
|
return;
|
||||||
|
WebRtc_UWord16 nextExpectedSeqNum =
|
||||||
|
static_cast<WebRtc_UWord16>(_lastDecodedSeqNum + 1);
|
||||||
if (_lastDecodedSeqNum == -1)
|
if (_lastDecodedSeqNum == -1)
|
||||||
{
|
{
|
||||||
// First frame
|
// First frame
|
||||||
frame.SetPreviousFrameLoss();
|
frame.SetPreviousFrameLoss();
|
||||||
}
|
}
|
||||||
else if ((WebRtc_UWord16)frame.GetLowSeqNum() !=
|
else if (static_cast<WebRtc_UWord16>(frame.GetLowSeqNum()) !=
|
||||||
((WebRtc_UWord16)_lastDecodedSeqNum + (WebRtc_UWord16)1))
|
nextExpectedSeqNum)
|
||||||
{
|
{
|
||||||
// Frame loss
|
// Frame loss
|
||||||
frame.SetPreviousFrameLoss();
|
frame.SetPreviousFrameLoss();
|
||||||
|
|||||||
@@ -68,6 +68,9 @@ public:
|
|||||||
WebRtc_Word32 GetFrameStatistics(WebRtc_UWord32& receivedDeltaFrames,
|
WebRtc_Word32 GetFrameStatistics(WebRtc_UWord32& receivedDeltaFrames,
|
||||||
WebRtc_UWord32& receivedKeyFrames) const;
|
WebRtc_UWord32& receivedKeyFrames) const;
|
||||||
|
|
||||||
|
// The number of packets discarded by the jitter buffer because the decoder
|
||||||
|
// won't be able to decode them.
|
||||||
|
WebRtc_UWord32 NumNotDecodablePackets() const;
|
||||||
// Get number of packets discarded by the jitter buffer
|
// Get number of packets discarded by the jitter buffer
|
||||||
WebRtc_UWord32 DiscardedPackets() const;
|
WebRtc_UWord32 DiscardedPackets() const;
|
||||||
|
|
||||||
@@ -124,11 +127,8 @@ public:
|
|||||||
bool& listExtended);
|
bool& listExtended);
|
||||||
|
|
||||||
WebRtc_Word64 LastDecodedTimestamp() const;
|
WebRtc_Word64 LastDecodedTimestamp() const;
|
||||||
static WebRtc_UWord32 LatestTimestamp(const WebRtc_UWord32 existingTimestamp,
|
|
||||||
const WebRtc_UWord32 newTimestamp);
|
|
||||||
|
|
||||||
protected:
|
|
||||||
|
|
||||||
|
private:
|
||||||
// Misc help functions
|
// Misc help functions
|
||||||
// Recycle (release) frame, used if we didn't receive whole frame
|
// Recycle (release) frame, used if we didn't receive whole frame
|
||||||
void RecycleFrame(VCMFrameBuffer* frame);
|
void RecycleFrame(VCMFrameBuffer* frame);
|
||||||
@@ -159,6 +159,7 @@ protected:
|
|||||||
|
|
||||||
void VerifyAndSetPreviousFrameLost(VCMFrameBuffer& frame);
|
void VerifyAndSetPreviousFrameLost(VCMFrameBuffer& frame);
|
||||||
bool IsPacketRetransmitted(const VCMPacket& packet) const;
|
bool IsPacketRetransmitted(const VCMPacket& packet) const;
|
||||||
|
|
||||||
void UpdateJitterAndDelayEstimates(VCMJitterSample& sample,
|
void UpdateJitterAndDelayEstimates(VCMJitterSample& sample,
|
||||||
bool incompleteFrame);
|
bool incompleteFrame);
|
||||||
void UpdateJitterAndDelayEstimates(VCMFrameBuffer& frame,
|
void UpdateJitterAndDelayEstimates(VCMFrameBuffer& frame,
|
||||||
@@ -176,10 +177,6 @@ protected:
|
|||||||
WebRtc_Word32 GetLowHighSequenceNumbers(WebRtc_Word32& lowSeqNum,
|
WebRtc_Word32 GetLowHighSequenceNumbers(WebRtc_Word32& lowSeqNum,
|
||||||
WebRtc_Word32& highSeqNum) const;
|
WebRtc_Word32& highSeqNum) const;
|
||||||
|
|
||||||
void UpdateLastDecodedWithEmpty(const VCMPacket& packet);
|
|
||||||
|
|
||||||
private:
|
|
||||||
|
|
||||||
static bool FrameEqualTimestamp(VCMFrameBuffer* frame,
|
static bool FrameEqualTimestamp(VCMFrameBuffer* frame,
|
||||||
const void* timestamp);
|
const void* timestamp);
|
||||||
static bool CompleteDecodableKeyFrameCriteria(VCMFrameBuffer* frame,
|
static bool CompleteDecodableKeyFrameCriteria(VCMFrameBuffer* frame,
|
||||||
@@ -208,6 +205,7 @@ private:
|
|||||||
WebRtc_Word32 _lastDecodedSeqNum;
|
WebRtc_Word32 _lastDecodedSeqNum;
|
||||||
// Timestamp of last frame that was given to decoder
|
// Timestamp of last frame that was given to decoder
|
||||||
WebRtc_Word64 _lastDecodedTimeStamp;
|
WebRtc_Word64 _lastDecodedTimeStamp;
|
||||||
|
WebRtc_UWord32 _packetsNotDecodable;
|
||||||
|
|
||||||
// Statistics
|
// Statistics
|
||||||
// Frame counter for each type (key, delta, golden, key-delta)
|
// Frame counter for each type (key, delta, golden, key-delta)
|
||||||
|
|||||||
60
src/modules/video_coding/main/source/jitter_buffer_common.cc
Normal file
60
src/modules/video_coding/main/source/jitter_buffer_common.cc
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
/*
|
||||||
|
* 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 "jitter_buffer_common.h"
|
||||||
|
|
||||||
|
#include <cstdlib>
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
WebRtc_UWord32 LatestTimestamp(WebRtc_UWord32 timestamp1,
|
||||||
|
WebRtc_UWord32 timestamp2,
|
||||||
|
bool* has_wrapped) {
|
||||||
|
bool wrap = (timestamp2 < 0x0000ffff && timestamp1 > 0xffff0000) ||
|
||||||
|
(timestamp2 > 0xffff0000 && timestamp1 < 0x0000ffff);
|
||||||
|
if (has_wrapped != NULL)
|
||||||
|
*has_wrapped = wrap;
|
||||||
|
if (timestamp1 > timestamp2 && !wrap)
|
||||||
|
return timestamp1;
|
||||||
|
else if (timestamp1 <= timestamp2 && !wrap)
|
||||||
|
return timestamp2;
|
||||||
|
else if (timestamp1 < timestamp2 && wrap)
|
||||||
|
return timestamp1;
|
||||||
|
else
|
||||||
|
return timestamp2;
|
||||||
|
}
|
||||||
|
|
||||||
|
WebRtc_Word32 LatestSequenceNumber(WebRtc_Word32 seq_num1,
|
||||||
|
WebRtc_Word32 seq_num2,
|
||||||
|
bool* has_wrapped) {
|
||||||
|
if (seq_num1 < 0 && seq_num2 < 0)
|
||||||
|
return -1;
|
||||||
|
else if (seq_num1 < 0)
|
||||||
|
return seq_num2;
|
||||||
|
else if (seq_num2 < 0)
|
||||||
|
return seq_num1;
|
||||||
|
|
||||||
|
bool wrap = (seq_num1 < 0x00ff && seq_num2 > 0xff00) ||
|
||||||
|
(seq_num1 > 0xff00 && seq_num2 < 0x00ff);
|
||||||
|
|
||||||
|
if (has_wrapped != NULL)
|
||||||
|
*has_wrapped = wrap;
|
||||||
|
|
||||||
|
if (seq_num2 > seq_num1 && !wrap)
|
||||||
|
return seq_num2;
|
||||||
|
else if (seq_num2 <= seq_num1 && !wrap)
|
||||||
|
return seq_num1;
|
||||||
|
else if (seq_num2 < seq_num1 && wrap)
|
||||||
|
return seq_num2;
|
||||||
|
else
|
||||||
|
return seq_num1;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace webrtc
|
||||||
@@ -11,6 +11,8 @@
|
|||||||
#ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_
|
#ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_
|
||||||
#define WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_
|
#define WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_
|
||||||
|
|
||||||
|
#include "typedefs.h"
|
||||||
|
|
||||||
namespace webrtc
|
namespace webrtc
|
||||||
{
|
{
|
||||||
|
|
||||||
@@ -62,6 +64,19 @@ enum VCMNaluCompleteness
|
|||||||
kNaluEnd, // Packet is the end of a NALU
|
kNaluEnd, // Packet is the end of a NALU
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Returns the latest of the two timestamps, compensating for wrap arounds.
|
||||||
|
// This function assumes that the two timestamps are close in time.
|
||||||
|
WebRtc_UWord32 LatestTimestamp(WebRtc_UWord32 timestamp1,
|
||||||
|
WebRtc_UWord32 timestamp2,
|
||||||
|
bool* has_wrapped);
|
||||||
|
|
||||||
|
// Returns the latest of the two sequence numbers, compensating for wrap
|
||||||
|
// arounds. This function assumes that the two sequence numbers are close in
|
||||||
|
// time.
|
||||||
|
WebRtc_Word32 LatestSequenceNumber(WebRtc_Word32 seq_num1,
|
||||||
|
WebRtc_Word32 seq_num2,
|
||||||
|
bool* has_wrapped);
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|
||||||
#endif // WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_
|
#endif // WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_COMMON_H_
|
||||||
|
|||||||
@@ -8,13 +8,9 @@
|
|||||||
* be found in the AUTHORS file in the root of the source tree.
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#include "packet.h"
|
|
||||||
#include "session_info.h"
|
#include "session_info.h"
|
||||||
|
|
||||||
#include <string.h>
|
#include "packet.h"
|
||||||
#include <cassert>
|
|
||||||
|
|
||||||
#include "internal_defines.h"
|
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@@ -29,7 +25,8 @@ VCMSessionInfo::VCMSessionInfo():
|
|||||||
_highestPacketIndex(0),
|
_highestPacketIndex(0),
|
||||||
_emptySeqNumLow(-1),
|
_emptySeqNumLow(-1),
|
||||||
_emptySeqNumHigh(-1),
|
_emptySeqNumHigh(-1),
|
||||||
_markerSeqNum(-1)
|
_markerSeqNum(-1),
|
||||||
|
_packetsNotDecodable(0)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +52,7 @@ VCMSessionInfo::GetLowSeqNum() const
|
|||||||
WebRtc_Word32
|
WebRtc_Word32
|
||||||
VCMSessionInfo::GetHighSeqNum() const
|
VCMSessionInfo::GetHighSeqNum() const
|
||||||
{
|
{
|
||||||
return VCM_MAX(_emptySeqNumHigh, _highSeqNum);
|
return LatestSequenceNumber(_emptySeqNumHigh, _highSeqNum, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
@@ -73,6 +70,7 @@ VCMSessionInfo::Reset() {
|
|||||||
_sessionNACK = false;
|
_sessionNACK = false;
|
||||||
_highestPacketIndex = 0;
|
_highestPacketIndex = 0;
|
||||||
_markerSeqNum = -1;
|
_markerSeqNum = -1;
|
||||||
|
_packetsNotDecodable = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebRtc_UWord32
|
WebRtc_UWord32
|
||||||
@@ -202,7 +200,7 @@ VCMSessionInfo::UpdateCompleteSession()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VCMSessionInfo::IsSessionComplete()
|
bool VCMSessionInfo::IsSessionComplete() const
|
||||||
{
|
{
|
||||||
return _completeSession;
|
return _completeSession;
|
||||||
}
|
}
|
||||||
@@ -287,6 +285,7 @@ VCMSessionInfo::DeletePackets(WebRtc_UWord8* ptrStartOfLayer,
|
|||||||
{
|
{
|
||||||
bytesToDelete += _packets[j].sizeBytes;
|
bytesToDelete += _packets[j].sizeBytes;
|
||||||
_packets[j].Reset();
|
_packets[j].Reset();
|
||||||
|
++_packetsNotDecodable;
|
||||||
}
|
}
|
||||||
if (bytesToDelete > 0)
|
if (bytesToDelete > 0)
|
||||||
{
|
{
|
||||||
@@ -362,7 +361,7 @@ VCMSessionInfo::BuildVP8FragmentationHeader(
|
|||||||
return new_length;
|
return new_length;
|
||||||
}
|
}
|
||||||
|
|
||||||
int VCMSessionInfo::FindNextPartitionBeginning(int packet_index) const {
|
int VCMSessionInfo::FindNextPartitionBeginning(int packet_index) {
|
||||||
while (packet_index <= _highestPacketIndex) {
|
while (packet_index <= _highestPacketIndex) {
|
||||||
if (_packets[packet_index].completeNALU == kNaluUnset) {
|
if (_packets[packet_index].completeNALU == kNaluUnset) {
|
||||||
// Missing packet
|
// Missing packet
|
||||||
@@ -371,8 +370,13 @@ int VCMSessionInfo::FindNextPartitionBeginning(int packet_index) const {
|
|||||||
}
|
}
|
||||||
const bool beginning = _packets[packet_index].codecSpecificHeader.
|
const bool beginning = _packets[packet_index].codecSpecificHeader.
|
||||||
codecHeader.VP8.beginningOfPartition;
|
codecHeader.VP8.beginningOfPartition;
|
||||||
if (beginning)
|
if (beginning) {
|
||||||
return packet_index;
|
return packet_index;
|
||||||
|
} else {
|
||||||
|
// This packet belongs to a partition with a previous loss and can't
|
||||||
|
// be decoded.
|
||||||
|
++_packetsNotDecodable;
|
||||||
|
}
|
||||||
++packet_index;
|
++packet_index;
|
||||||
}
|
}
|
||||||
return packet_index;
|
return packet_index;
|
||||||
@@ -433,7 +437,7 @@ VCMSessionInfo::MakeDecodable(WebRtc_UWord8* ptrStartOfLayer)
|
|||||||
}
|
}
|
||||||
|
|
||||||
returnLength += DeletePackets(ptrStartOfLayer,
|
returnLength += DeletePackets(ptrStartOfLayer,
|
||||||
packetIndex, endIndex);
|
packetIndex + 1, endIndex);
|
||||||
packetIndex = endIndex;
|
packetIndex = endIndex;
|
||||||
}// end lost packet
|
}// end lost packet
|
||||||
}
|
}
|
||||||
@@ -636,7 +640,7 @@ VCMSessionInfo::GetHighestPacketIndex()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VCMSessionInfo::HaveLastPacket()
|
VCMSessionInfo::HaveLastPacket() const
|
||||||
{
|
{
|
||||||
return _markerBit;
|
return _markerBit;
|
||||||
}
|
}
|
||||||
@@ -649,25 +653,11 @@ VCMSessionInfo::ForceSetHaveLastPacket()
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VCMSessionInfo::IsRetransmitted()
|
VCMSessionInfo::IsRetransmitted() const
|
||||||
{
|
{
|
||||||
return _sessionNACK;
|
return _sessionNACK;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
VCMSessionInfo::UpdatePacketSize(WebRtc_Word32 packetIndex,
|
|
||||||
WebRtc_UWord32 length)
|
|
||||||
{
|
|
||||||
// sanity
|
|
||||||
if (packetIndex >= kMaxPacketsInJitterBuffer || packetIndex < 0)
|
|
||||||
{
|
|
||||||
// not allowed
|
|
||||||
assert(!"SessionInfo::UpdatePacketSize Error: invalid packetIndex");
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
_packets[packetIndex].sizeBytes = length;
|
|
||||||
}
|
|
||||||
|
|
||||||
WebRtc_Word64
|
WebRtc_Word64
|
||||||
VCMSessionInfo::InsertPacket(const VCMPacket& packet,
|
VCMSessionInfo::InsertPacket(const VCMPacket& packet,
|
||||||
WebRtc_UWord8* ptrStartOfLayer)
|
WebRtc_UWord8* ptrStartOfLayer)
|
||||||
@@ -848,6 +838,7 @@ VCMSessionInfo::PrepareForDecode(WebRtc_UWord8* ptrStartOfLayer,
|
|||||||
// missing the previous packet.
|
// missing the previous packet.
|
||||||
memset(ptrFirstByte, 0, _packets[i].sizeBytes);
|
memset(ptrFirstByte, 0, _packets[i].sizeBytes);
|
||||||
previousLost = true;
|
previousLost = true;
|
||||||
|
++_packetsNotDecodable;
|
||||||
}
|
}
|
||||||
else if (_packets[i].sizeBytes > 0) // Ignore if empty packet
|
else if (_packets[i].sizeBytes > 0) // Ignore if empty packet
|
||||||
{
|
{
|
||||||
@@ -870,6 +861,7 @@ VCMSessionInfo::PrepareForDecode(WebRtc_UWord8* ptrStartOfLayer,
|
|||||||
{
|
{
|
||||||
memset(ptrStartOfLayer, 0, _packets[i].sizeBytes);
|
memset(ptrStartOfLayer, 0, _packets[i].sizeBytes);
|
||||||
previousLost = true;
|
previousLost = true;
|
||||||
|
++_packetsNotDecodable;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (_packets[i].sizeBytes == 0 && codec == kVideoCodecH263)
|
else if (_packets[i].sizeBytes == 0 && codec == kVideoCodecH263)
|
||||||
@@ -899,4 +891,8 @@ VCMSessionInfo::PrepareForDecode(WebRtc_UWord8* ptrStartOfLayer,
|
|||||||
return length;
|
return length;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int VCMSessionInfo::NotDecodablePackets() const {
|
||||||
|
return _packetsNotDecodable;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -44,7 +44,7 @@ public:
|
|||||||
WebRtc_UWord8* ptrStartOfLayer);
|
WebRtc_UWord8* ptrStartOfLayer);
|
||||||
WebRtc_Word32 InformOfEmptyPacket(const WebRtc_UWord16 seqNum);
|
WebRtc_Word32 InformOfEmptyPacket(const WebRtc_UWord16 seqNum);
|
||||||
|
|
||||||
virtual bool IsSessionComplete();
|
virtual bool IsSessionComplete() const;
|
||||||
|
|
||||||
// Builds fragmentation headers for VP8, each fragment being a decodable
|
// Builds fragmentation headers for VP8, each fragment being a decodable
|
||||||
// VP8 partition. Returns the total number of bytes which are decodable. Is
|
// VP8 partition. Returns the total number of bytes which are decodable. Is
|
||||||
@@ -60,14 +60,12 @@ public:
|
|||||||
WebRtc_UWord32 MakeDecodable(WebRtc_UWord8* ptrStartOfLayer);
|
WebRtc_UWord32 MakeDecodable(WebRtc_UWord8* ptrStartOfLayer);
|
||||||
|
|
||||||
WebRtc_UWord32 GetSessionLength();
|
WebRtc_UWord32 GetSessionLength();
|
||||||
bool HaveLastPacket();
|
bool HaveLastPacket() const;
|
||||||
void ForceSetHaveLastPacket();
|
void ForceSetHaveLastPacket();
|
||||||
bool IsRetransmitted();
|
bool IsRetransmitted() const;
|
||||||
webrtc::FrameType FrameType() const { return _frameType; }
|
webrtc::FrameType FrameType() const { return _frameType; }
|
||||||
|
|
||||||
virtual WebRtc_Word32 GetHighestPacketIndex();
|
virtual WebRtc_Word32 GetHighestPacketIndex();
|
||||||
virtual void UpdatePacketSize(WebRtc_Word32 packetIndex,
|
|
||||||
WebRtc_UWord32 length);
|
|
||||||
|
|
||||||
void SetStartSeqNumber(WebRtc_UWord16 seqNumber);
|
void SetStartSeqNumber(WebRtc_UWord16 seqNumber);
|
||||||
|
|
||||||
@@ -83,10 +81,17 @@ public:
|
|||||||
void SetPreviousFrameLoss() { _previousFrameLoss = true; }
|
void SetPreviousFrameLoss() { _previousFrameLoss = true; }
|
||||||
bool PreviousFrameLoss() const { return _previousFrameLoss; }
|
bool PreviousFrameLoss() const { return _previousFrameLoss; }
|
||||||
|
|
||||||
protected:
|
// The number of packets discarded because the decoder can't make use of
|
||||||
// Finds the packet index of the next VP8 partition. If none is found
|
// them.
|
||||||
// _highestPacketIndex + 1 is returned.
|
int NotDecodablePackets() const;
|
||||||
int FindNextPartitionBeginning(int packet_index) const;
|
|
||||||
|
private:
|
||||||
|
// Finds the packet index of the beginning of the next VP8 partition. If
|
||||||
|
// none is found _highestPacketIndex + 1 is returned. packet_index is
|
||||||
|
// expected to be the index of the last decodable packet of the previous
|
||||||
|
// partitions + 1, or 0 for the first partition.
|
||||||
|
int FindNextPartitionBeginning(int packet_index);
|
||||||
|
|
||||||
// Finds the packet index of the end of the partition with index
|
// Finds the packet index of the end of the partition with index
|
||||||
// partitionIndex.
|
// partitionIndex.
|
||||||
int FindPartitionEnd(int packet_index) const;
|
int FindPartitionEnd(int packet_index) const;
|
||||||
@@ -123,6 +128,8 @@ protected:
|
|||||||
WebRtc_Word32 _emptySeqNumHigh;
|
WebRtc_Word32 _emptySeqNumHigh;
|
||||||
// Store the sequence number that marks the last media packet
|
// Store the sequence number that marks the last media packet
|
||||||
WebRtc_Word32 _markerSeqNum;
|
WebRtc_Word32 _markerSeqNum;
|
||||||
|
// Number of packets discarded because the decoder can't use them.
|
||||||
|
int _packetsNotDecodable;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@@ -50,8 +50,8 @@
|
|||||||
'generic_encoder.h',
|
'generic_encoder.h',
|
||||||
'inter_frame_delay.h',
|
'inter_frame_delay.h',
|
||||||
'internal_defines.h',
|
'internal_defines.h',
|
||||||
'jitter_buffer_common.h',
|
|
||||||
'jitter_buffer.h',
|
'jitter_buffer.h',
|
||||||
|
'jitter_buffer_common.h',
|
||||||
'jitter_estimator.h',
|
'jitter_estimator.h',
|
||||||
'media_opt_util.h',
|
'media_opt_util.h',
|
||||||
'media_optimization.h',
|
'media_optimization.h',
|
||||||
@@ -81,6 +81,7 @@
|
|||||||
'generic_encoder.cc',
|
'generic_encoder.cc',
|
||||||
'inter_frame_delay.cc',
|
'inter_frame_delay.cc',
|
||||||
'jitter_buffer.cc',
|
'jitter_buffer.cc',
|
||||||
|
'jitter_buffer_common.cc',
|
||||||
'jitter_estimator.cc',
|
'jitter_estimator.cc',
|
||||||
'media_opt_util.cc',
|
'media_opt_util.cc',
|
||||||
'media_optimization.cc',
|
'media_optimization.cc',
|
||||||
|
|||||||
@@ -819,6 +819,94 @@ int JitterBufferTest(CmdArgs& args)
|
|||||||
TEST(bitRate > 10000000);
|
TEST(bitRate > 10000000);
|
||||||
|
|
||||||
|
|
||||||
|
jb.Flush();
|
||||||
|
|
||||||
|
//
|
||||||
|
// TEST packet loss. Verify missing packets statistics and not decodable
|
||||||
|
// packets statistics.
|
||||||
|
// Insert 10 frames consisting of 4 packets and remove one from all of them.
|
||||||
|
// The last packet is a empty (non-media) packet
|
||||||
|
//
|
||||||
|
|
||||||
|
// Select a start seqNum which triggers a difficult wrap situation
|
||||||
|
seqNum = 0xffff - 4;
|
||||||
|
for (int i=0; i < 10; ++i) {
|
||||||
|
webrtc::FrameType frametype = kVideoFrameDelta;
|
||||||
|
if (i == 0)
|
||||||
|
frametype = kVideoFrameKey;
|
||||||
|
seqNum++;
|
||||||
|
timeStamp += 33*90;
|
||||||
|
packet.frameType = frametype;
|
||||||
|
if (i == 0)
|
||||||
|
packet.frameType = frametype;
|
||||||
|
packet.isFirstPacket = true;
|
||||||
|
packet.markerBit = false;
|
||||||
|
packet.seqNum = seqNum;
|
||||||
|
packet.timestamp = timeStamp;
|
||||||
|
packet.completeNALU = kNaluStart;
|
||||||
|
|
||||||
|
frameIn = jb.GetFrame(packet);
|
||||||
|
TEST(frameIn != 0);
|
||||||
|
|
||||||
|
// Insert a packet into a frame
|
||||||
|
TEST(kFirstPacket == jb.InsertPacket(frameIn, packet));
|
||||||
|
|
||||||
|
// get packet notification
|
||||||
|
TEST(timeStamp == jb.GetNextTimeStamp(10, incomingFrameType, renderTimeMs));
|
||||||
|
|
||||||
|
// check incoming frame type
|
||||||
|
TEST(incomingFrameType == frametype);
|
||||||
|
|
||||||
|
// get the frame
|
||||||
|
frameOut = jb.GetCompleteFrameForDecoding(10);
|
||||||
|
|
||||||
|
// it should not be complete
|
||||||
|
TEST(frameOut == 0);
|
||||||
|
|
||||||
|
seqNum += 2;
|
||||||
|
packet.isFirstPacket = false;
|
||||||
|
packet.markerBit = true;
|
||||||
|
packet.seqNum = seqNum;
|
||||||
|
packet.completeNALU = kNaluEnd;
|
||||||
|
|
||||||
|
frameIn = jb.GetFrame(packet);
|
||||||
|
TEST(frameIn != 0);
|
||||||
|
|
||||||
|
// Insert a packet into a frame
|
||||||
|
TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
|
||||||
|
|
||||||
|
|
||||||
|
// Insert an empty (non-media) packet
|
||||||
|
seqNum++;
|
||||||
|
packet.isFirstPacket = false;
|
||||||
|
packet.markerBit = false;
|
||||||
|
packet.seqNum = seqNum;
|
||||||
|
packet.completeNALU = kNaluEnd;
|
||||||
|
packet.frameType = kFrameEmpty;
|
||||||
|
|
||||||
|
frameIn = jb.GetFrame(packet);
|
||||||
|
TEST(frameIn != 0);
|
||||||
|
|
||||||
|
// Insert a packet into a frame
|
||||||
|
TEST(kIncomplete == jb.InsertPacket(frameIn, packet));
|
||||||
|
|
||||||
|
// get the frame
|
||||||
|
frameOut = jb.GetFrameForDecoding();
|
||||||
|
|
||||||
|
// One of the packets has been discarded by the jitter buffer
|
||||||
|
CheckOutFrame(frameOut, size, false);
|
||||||
|
|
||||||
|
// check the frame type
|
||||||
|
TEST(frameOut->FrameType() == frametype);
|
||||||
|
TEST(frameOut->Complete() == false);
|
||||||
|
TEST(frameOut->MissingFrame() == false);
|
||||||
|
|
||||||
|
// Release frame (when done with decoding)
|
||||||
|
jb.ReleaseFrame(frameOut);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(jb.NumNotDecodablePackets() == 10);
|
||||||
|
|
||||||
// Insert 3 old packets and verify that we have 3 discarded packets
|
// Insert 3 old packets and verify that we have 3 discarded packets
|
||||||
packet.timestamp = timeStamp - 1000;
|
packet.timestamp = timeStamp - 1000;
|
||||||
frameIn = jb.GetFrame(packet);
|
frameIn = jb.GetFrame(packet);
|
||||||
|
|||||||
Reference in New Issue
Block a user