Request key frames to battle error propagation.
The VP8 decoder wrapper will request key frames 30 frames after seeing a packet loss, if it hasn't received a state refresh (only possible through key frames in this version). For this to be possible the jitter buffer has been made aware of picture ids to be able to detect frame losses. Legacy JB code to handle streams without marker bits was also removed since it conflicts with streams with FEC. BUG= TEST= Review URL: http://webrtc-codereview.appspot.com/239002 git-svn-id: http://webrtc.googlecode.com/svn/trunk@774 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
@@ -73,6 +73,7 @@ VCMJitterBuffer::VCMJitterBuffer(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId,
|
||||
_frameBuffersTSOrder(),
|
||||
_lastDecodedSeqNum(-1),
|
||||
_lastDecodedTimeStamp(-1),
|
||||
_lastDecodedPictureId(-1),
|
||||
_packetsNotDecodable(0),
|
||||
_receiveStatistics(),
|
||||
_incomingFrameRate(0),
|
||||
@@ -88,7 +89,6 @@ VCMJitterBuffer::VCMJitterBuffer(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId,
|
||||
_nackMode(kNoNack),
|
||||
_NACKSeqNum(),
|
||||
_NACKSeqNumLength(0),
|
||||
_missingMarkerBits(false),
|
||||
_firstPacket(true)
|
||||
{
|
||||
memset(_frameBuffers, 0, sizeof(_frameBuffers));
|
||||
@@ -141,10 +141,10 @@ VCMJitterBuffer::operator=(const VCMJitterBuffer& rhs)
|
||||
_nackMode = rhs._nackMode;
|
||||
_rttMs = rhs._rttMs;
|
||||
_NACKSeqNumLength = rhs._NACKSeqNumLength;
|
||||
_missingMarkerBits = rhs._missingMarkerBits;
|
||||
_firstPacket = rhs._firstPacket;
|
||||
_lastDecodedSeqNum = rhs._lastDecodedSeqNum;
|
||||
_lastDecodedTimeStamp = rhs._lastDecodedTimeStamp;
|
||||
_lastDecodedPictureId = rhs._lastDecodedPictureId;
|
||||
_packetsNotDecodable = rhs._packetsNotDecodable;
|
||||
memcpy(_receiveStatistics, rhs._receiveStatistics,
|
||||
sizeof(_receiveStatistics));
|
||||
@@ -196,7 +196,6 @@ VCMJitterBuffer::Start()
|
||||
_waitingForCompletion.frameSize = 0;
|
||||
_waitingForCompletion.timestamp = 0;
|
||||
_waitingForCompletion.latestPacketTime = -1;
|
||||
_missingMarkerBits = false;
|
||||
_firstPacket = true;
|
||||
_NACKSeqNumLength = 0;
|
||||
_rttMs = 0;
|
||||
@@ -215,6 +214,7 @@ VCMJitterBuffer::Stop()
|
||||
_running = false;
|
||||
_lastDecodedTimeStamp = -1;
|
||||
_lastDecodedSeqNum = -1;
|
||||
_lastDecodedPictureId = -1;
|
||||
_frameBuffersTSOrder.Flush();
|
||||
for (int i = 0; i < kMaxNumberOfFrames; i++)
|
||||
{
|
||||
@@ -258,6 +258,7 @@ VCMJitterBuffer::FlushInternal()
|
||||
}
|
||||
_lastDecodedSeqNum = -1;
|
||||
_lastDecodedTimeStamp = -1;
|
||||
_lastDecodedPictureId = -1;
|
||||
_packetsNotDecodable = 0;
|
||||
|
||||
_frameEvent.Reset();
|
||||
@@ -274,7 +275,6 @@ VCMJitterBuffer::FlushInternal()
|
||||
_waitingForCompletion.timestamp = 0;
|
||||
_waitingForCompletion.latestPacketTime = -1;
|
||||
|
||||
_missingMarkerBits = false;
|
||||
_firstPacket = true;
|
||||
|
||||
_NACKSeqNumLength = 0;
|
||||
@@ -602,22 +602,9 @@ VCMJitterBuffer::FindOldestCompleteContinuousFrame()
|
||||
oldestFrame = oldestFrameItem->GetItem();
|
||||
}
|
||||
// is the frame complete?
|
||||
if (oldestFrame != NULL)
|
||||
if (oldestFrame != NULL && kStateComplete != oldestFrame->GetState())
|
||||
{
|
||||
if (kStateComplete != oldestFrame->GetState())
|
||||
{
|
||||
// Try to see if the frame is complete even though the state is not
|
||||
// complete. Can happen if markerbit is not set.
|
||||
if (!CheckForCompleteFrame(oldestFrameItem))
|
||||
{
|
||||
oldestFrame = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// we have a complete frame
|
||||
currentLow = oldestFrame->GetLowSeqNum();
|
||||
}
|
||||
oldestFrame = NULL;
|
||||
}
|
||||
if (oldestFrame == NULL)
|
||||
{
|
||||
@@ -647,52 +634,6 @@ VCMJitterBuffer::FindOldestCompleteContinuousFrame()
|
||||
return oldestFrameItem;
|
||||
}
|
||||
|
||||
// Check if the oldest frame is complete even though it isn't complete.
|
||||
// This can happen when makerbit is not set
|
||||
// Must be called under the critical section _critSect.
|
||||
// Return false for lost packets
|
||||
bool
|
||||
VCMJitterBuffer::CheckForCompleteFrame(VCMFrameListItem* oldestFrameItem)
|
||||
{
|
||||
const VCMFrameListItem*
|
||||
nextFrameItem = _frameBuffersTSOrder.Next(oldestFrameItem);
|
||||
VCMFrameBuffer* oldestFrame = NULL;
|
||||
if (oldestFrameItem != NULL)
|
||||
{
|
||||
oldestFrame = oldestFrameItem->GetItem();
|
||||
}
|
||||
if (nextFrameItem != NULL)
|
||||
{
|
||||
// We have received at least one packet from a later frame.
|
||||
if(!oldestFrame->HaveLastPacket()) // If we don't have the markerbit
|
||||
{
|
||||
VCMFrameBuffer* nextFrame = nextFrameItem->GetItem();
|
||||
// Verify that we have received the first packet of the next frame,
|
||||
// so we're not missing the last packet.
|
||||
if (nextFrame != NULL && nextFrame->GetLowSeqNum() ==
|
||||
static_cast<WebRtc_UWord16>(oldestFrame->GetHighSeqNum() + 1))
|
||||
{
|
||||
_missingMarkerBits = true;
|
||||
bool completeSession = oldestFrame->ForceSetHaveLastPacket();
|
||||
if (completeSession)
|
||||
{
|
||||
UpdateFrameState(oldestFrame);
|
||||
}
|
||||
const VCMFrameBufferStateEnum state = oldestFrame->GetState();
|
||||
if (state == kStateComplete)
|
||||
{
|
||||
if (oldestFrame->Length() > 0)
|
||||
{
|
||||
UpdateJitterAndDelayEstimates(*oldestFrame, false);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Call from inside the critical section _critSect
|
||||
void
|
||||
VCMJitterBuffer::RecycleFrame(VCMFrameBuffer* frame)
|
||||
@@ -710,7 +651,6 @@ VCMJitterBuffer::RecycleFrame(VCMFrameBuffer* frame)
|
||||
ReleaseFrameInternal(frame);
|
||||
}
|
||||
|
||||
|
||||
// Calculate frame and bit rates
|
||||
WebRtc_Word32
|
||||
VCMJitterBuffer::GetUpdate(WebRtc_UWord32& frameRate, WebRtc_UWord32& bitRate)
|
||||
@@ -878,13 +818,11 @@ VCMJitterBuffer::GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS)
|
||||
// Ignore retransmitted and empty frames.
|
||||
UpdateJitterAndDelayEstimates(*oldestFrame, false);
|
||||
}
|
||||
|
||||
// This needs to be done before we clean up old frames,
|
||||
// otherwise we'll remove ourselves...
|
||||
oldestFrame->SetState(kStateDecoding);
|
||||
_frameBuffersTSOrder.Erase(oldestFrameListItem);
|
||||
oldestFrameListItem = NULL;
|
||||
|
||||
oldestFrame->SetState(kStateDecoding);
|
||||
|
||||
CleanUpOldFrames();
|
||||
CleanUpSizeZeroFrames();
|
||||
|
||||
@@ -893,6 +831,7 @@ VCMJitterBuffer::GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS)
|
||||
// We have a frame - store seqnum & timestamp
|
||||
_lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
|
||||
_lastDecodedTimeStamp = oldestFrame->TimeStamp();
|
||||
_lastDecodedPictureId = oldestFrame->PictureId();
|
||||
|
||||
return oldestFrame;
|
||||
}
|
||||
@@ -918,17 +857,6 @@ VCMJitterBuffer::GetEstimatedJitterMsInternal()
|
||||
}
|
||||
estimate += static_cast<WebRtc_UWord32>
|
||||
(_jitterEstimate.GetJitterEstimate(rttMult) + 0.5);
|
||||
if (_missingMarkerBits)
|
||||
{
|
||||
// Since the incoming packets are all missing marker bits we have to
|
||||
// wait until the first packet of the next frame arrives, before we can
|
||||
// safely say that the frame is complete. Therefore we have to
|
||||
// compensate the jitter buffer level with one frame period.
|
||||
// TODO(holmer): The timestamp diff should probably be filtered
|
||||
// (max filter) since the diff can alternate between e.g. 3000 and 6000
|
||||
// if we have a frame rate between 15 and 30 frames per seconds.
|
||||
estimate += _delayEstimate.CurrentTimeStampDiffMs();
|
||||
}
|
||||
return estimate;
|
||||
}
|
||||
|
||||
@@ -1045,8 +973,18 @@ VCMJitterBuffer::CompleteSequenceWithNextFrame()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
else if (oldestFrame->GetLowSeqNum() != (_lastDecodedSeqNum + 1)
|
||||
% 0x00010000)
|
||||
// We can't use sequence numbers to detect frame loss when FEC is enabled.
|
||||
// Assume FEC is only enabled for VP8 with picture ids, and use picture ids
|
||||
// to detect frame loss in that situation.
|
||||
else if (oldestFrame->PictureId() == kNoPictureId)
|
||||
{
|
||||
if (oldestFrame->GetLowSeqNum() !=
|
||||
(_lastDecodedSeqNum + 1) % 0x00010000)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
else if (!ContinuousPictureId(oldestFrame->PictureId()))
|
||||
{
|
||||
return false;
|
||||
}
|
||||
@@ -1111,19 +1049,24 @@ VCMJitterBuffer::GetFrameForDecoding()
|
||||
_frameBuffersTSOrder.Erase(oldestFrameListItem);
|
||||
oldestFrameListItem = NULL;
|
||||
|
||||
CleanUpOldFrames();
|
||||
CleanUpSizeZeroFrames();
|
||||
|
||||
// Look for previous frame loss
|
||||
VerifyAndSetPreviousFrameLost(*oldestFrame);
|
||||
|
||||
// The state must be changed to decoding before cleaning up zero sized
|
||||
// frames to avoid empty frames being cleaned up and then given to the
|
||||
// decoder.
|
||||
// Set as decoding. Propagates the missingFrame bit.
|
||||
oldestFrame->SetState(kStateDecoding);
|
||||
|
||||
CleanUpOldFrames();
|
||||
CleanUpSizeZeroFrames();
|
||||
|
||||
_packetsNotDecodable += oldestFrame->NotDecodablePackets();
|
||||
|
||||
// Store current seqnum & time
|
||||
_lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
|
||||
_lastDecodedTimeStamp = oldestFrame->TimeStamp();
|
||||
_lastDecodedPictureId = oldestFrame->PictureId();
|
||||
|
||||
return oldestFrame;
|
||||
}
|
||||
@@ -1171,11 +1114,16 @@ VCMJitterBuffer::GetFrameForDecodingNACK()
|
||||
// Ignore retransmitted and empty frames.
|
||||
UpdateJitterAndDelayEstimates(*oldestFrame, false);
|
||||
}
|
||||
|
||||
// This needs to be done before we clean up old frames,
|
||||
// otherwise we might release the frame we want to decode right now.
|
||||
oldestFrame->SetState(kStateDecoding);
|
||||
_frameBuffersTSOrder.Erase(oldestFrameListItem);
|
||||
oldestFrameListItem = NULL;
|
||||
|
||||
// Look for previous frame loss
|
||||
VerifyAndSetPreviousFrameLost(*oldestFrame);
|
||||
|
||||
// The state must be changed to decoding before cleaning up zero sized
|
||||
// frames to avoid empty frames being cleaned up and then given to the
|
||||
// decoder.
|
||||
oldestFrame->SetState(kStateDecoding);
|
||||
|
||||
// Clean up old frames and empty frames
|
||||
CleanUpOldFrames();
|
||||
@@ -1185,6 +1133,7 @@ VCMJitterBuffer::GetFrameForDecodingNACK()
|
||||
// Store seqnum & timestamp
|
||||
_lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
|
||||
_lastDecodedTimeStamp = oldestFrame->TimeStamp();
|
||||
_lastDecodedPictureId = oldestFrame->PictureId();
|
||||
|
||||
return oldestFrame;
|
||||
}
|
||||
@@ -1903,7 +1852,8 @@ VCMJitterBuffer::CleanUpSizeZeroFrames()
|
||||
VCMFrameBuffer* ptrTempBuffer = frameListItem->GetItem();
|
||||
|
||||
// pop frame if its size zero but store seqnum
|
||||
if (ptrTempBuffer->Length() == 0)
|
||||
if (ptrTempBuffer->Length() == 0 &&
|
||||
ptrTempBuffer->GetState() != kStateDecoding)
|
||||
{
|
||||
WebRtc_Word32 frameHighSeqNum = ptrTempBuffer->GetHighSeqNum();
|
||||
if (frameHighSeqNum == -1)
|
||||
@@ -1983,21 +1933,47 @@ VCMJitterBuffer::VerifyAndSetPreviousFrameLost(VCMFrameBuffer& frame)
|
||||
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)
|
||||
{
|
||||
// First frame
|
||||
// First frame is not a key frame
|
||||
frame.SetPreviousFrameLoss();
|
||||
return;
|
||||
}
|
||||
else if (static_cast<WebRtc_UWord16>(frame.GetLowSeqNum()) !=
|
||||
nextExpectedSeqNum)
|
||||
// We can't use sequence numbers to detect frame loss when FEC is enabled.
|
||||
// Assume FEC is only enabled for VP8 with picture ids, and use picture ids
|
||||
// to detect frame loss in that situation.
|
||||
if (frame.PictureId() == kNoPictureId)
|
||||
{
|
||||
WebRtc_UWord16 nextExpectedSeqNum =
|
||||
static_cast<WebRtc_UWord16>(_lastDecodedSeqNum + 1);
|
||||
if (static_cast<WebRtc_UWord16>(frame.GetLowSeqNum()) !=
|
||||
nextExpectedSeqNum)
|
||||
{
|
||||
frame.SetPreviousFrameLoss();
|
||||
}
|
||||
}
|
||||
else if (!ContinuousPictureId(frame.PictureId()))
|
||||
{
|
||||
// Frame loss
|
||||
frame.SetPreviousFrameLoss();
|
||||
}
|
||||
}
|
||||
|
||||
bool VCMJitterBuffer::ContinuousPictureId(int pictureId) const {
|
||||
if (pictureId < _lastDecodedPictureId) {
|
||||
// Wrap
|
||||
if (_lastDecodedPictureId >= (1<<8)) {
|
||||
// 15 bits used for picture id
|
||||
return (((_lastDecodedPictureId + 1) % 0x7FFF) == pictureId);
|
||||
} else {
|
||||
// 7 bits used for picture id
|
||||
return (((_lastDecodedPictureId + 1) % 0x7F) == pictureId);
|
||||
}
|
||||
}
|
||||
// No wrap
|
||||
return (_lastDecodedPictureId + 1 == pictureId);
|
||||
}
|
||||
|
||||
bool
|
||||
VCMJitterBuffer::WaitForNack()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user