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:
parent
d0752c370d
commit
ffd28f95c5
@ -318,11 +318,7 @@ int RtpFormatVp8::WriteTIDFields(WebRtc_UWord8* x_field,
|
|||||||
|
|
||||||
int RtpFormatVp8::PayloadDescriptorExtraLength() const
|
int RtpFormatVp8::PayloadDescriptorExtraLength() const
|
||||||
{
|
{
|
||||||
int length_bytes = 0;
|
int length_bytes = PictureIdLength();
|
||||||
if (beginning_)
|
|
||||||
{
|
|
||||||
length_bytes = PictureIdLength();
|
|
||||||
}
|
|
||||||
if (TL0PicIdxFieldPresent()) ++length_bytes;
|
if (TL0PicIdxFieldPresent()) ++length_bytes;
|
||||||
if (TIDFieldPresent()) ++length_bytes;
|
if (TIDFieldPresent()) ++length_bytes;
|
||||||
if (length_bytes > 0) ++length_bytes; // Include the extension field.
|
if (length_bytes > 0) ++length_bytes; // Include the extension field.
|
||||||
@ -331,7 +327,7 @@ int RtpFormatVp8::PayloadDescriptorExtraLength() const
|
|||||||
|
|
||||||
int RtpFormatVp8::PictureIdLength() const
|
int RtpFormatVp8::PictureIdLength() const
|
||||||
{
|
{
|
||||||
if (!beginning_ || hdr_info_.pictureId == kNoPictureId)
|
if (hdr_info_.pictureId == kNoPictureId)
|
||||||
{
|
{
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
@ -268,6 +268,7 @@ private:
|
|||||||
EncodedImage _lastKeyFrame;
|
EncodedImage _lastKeyFrame;
|
||||||
int _imageFormat;
|
int _imageFormat;
|
||||||
vpx_ref_frame_t* _refFrame;
|
vpx_ref_frame_t* _refFrame;
|
||||||
|
int _propagationCnt;
|
||||||
|
|
||||||
};// end of VP8Decoder class
|
};// end of VP8Decoder class
|
||||||
|
|
||||||
|
@ -28,7 +28,7 @@
|
|||||||
|
|
||||||
#include "module_common_types.h"
|
#include "module_common_types.h"
|
||||||
|
|
||||||
#define VP8_FREQ_HZ 90000
|
enum { kVp8ErrorPropagationTh = 60 };
|
||||||
//#define DEV_PIC_LOSS
|
//#define DEV_PIC_LOSS
|
||||||
|
|
||||||
namespace webrtc
|
namespace webrtc
|
||||||
@ -725,7 +725,8 @@ VP8Decoder::VP8Decoder():
|
|||||||
_numCores(1),
|
_numCores(1),
|
||||||
_lastKeyFrame(),
|
_lastKeyFrame(),
|
||||||
_imageFormat(VPX_IMG_FMT_NONE),
|
_imageFormat(VPX_IMG_FMT_NONE),
|
||||||
_refFrame(NULL)
|
_refFrame(NULL),
|
||||||
|
_propagationCnt(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -752,6 +753,7 @@ VP8Decoder::Reset()
|
|||||||
{
|
{
|
||||||
InitDecode(NULL, _numCores);
|
InitDecode(NULL, _numCores);
|
||||||
}
|
}
|
||||||
|
_propagationCnt = -1;
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -811,6 +813,7 @@ VP8Decoder::InitDecode(const VideoCodec* inst,
|
|||||||
*_inst = *inst;
|
*_inst = *inst;
|
||||||
}
|
}
|
||||||
_numCores = numberOfCores;
|
_numCores = numberOfCores;
|
||||||
|
_propagationCnt = -1;
|
||||||
|
|
||||||
_inited = true;
|
_inited = true;
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
@ -822,7 +825,7 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
const RTPFragmentationHeader* fragmentation,
|
const RTPFragmentationHeader* fragmentation,
|
||||||
const CodecSpecificInfo* codecSpecificInfo,
|
const CodecSpecificInfo* codecSpecificInfo,
|
||||||
WebRtc_Word64 /*renderTimeMs*/)
|
WebRtc_Word64 /*renderTimeMs*/)
|
||||||
{
|
{
|
||||||
if (!_inited)
|
if (!_inited)
|
||||||
{
|
{
|
||||||
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
return WEBRTC_VIDEO_CODEC_UNINITIALIZED;
|
||||||
@ -853,6 +856,17 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Restrict error propagation
|
||||||
|
// Reset on a key frame refresh
|
||||||
|
if (inputImage._frameType == kKeyFrame && inputImage._completeFrame)
|
||||||
|
_propagationCnt = -1;
|
||||||
|
// Start count on first loss
|
||||||
|
else if ((!inputImage._completeFrame || missingFrames) &&
|
||||||
|
_propagationCnt == -1)
|
||||||
|
_propagationCnt = 0;
|
||||||
|
if (_propagationCnt >= 0)
|
||||||
|
_propagationCnt++;
|
||||||
|
|
||||||
vpx_dec_iter_t iter = NULL;
|
vpx_dec_iter_t iter = NULL;
|
||||||
vpx_image_t* img;
|
vpx_image_t* img;
|
||||||
WebRtc_Word32 ret;
|
WebRtc_Word32 ret;
|
||||||
@ -863,6 +877,9 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
// call decoder with zero data length to signal missing frames
|
// call decoder with zero data length to signal missing frames
|
||||||
if (vpx_codec_decode(_decoder, NULL, 0, 0, VPX_DL_REALTIME))
|
if (vpx_codec_decode(_decoder, NULL, 0, 0, VPX_DL_REALTIME))
|
||||||
{
|
{
|
||||||
|
// Reset to avoid requesting key frames too often.
|
||||||
|
if (_propagationCnt > 0)
|
||||||
|
_propagationCnt = 0;
|
||||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
}
|
}
|
||||||
img = vpx_codec_get_frame(_decoder, &iter);
|
img = vpx_codec_get_frame(_decoder, &iter);
|
||||||
@ -872,6 +889,9 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
#ifdef INDEPENDENT_PARTITIONS
|
#ifdef INDEPENDENT_PARTITIONS
|
||||||
if (DecodePartitions(inputImage, fragmentation))
|
if (DecodePartitions(inputImage, fragmentation))
|
||||||
{
|
{
|
||||||
|
// Reset to avoid requesting key frames too often.
|
||||||
|
if (_propagationCnt > 0)
|
||||||
|
_propagationCnt = 0;
|
||||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
}
|
}
|
||||||
#else
|
#else
|
||||||
@ -886,6 +906,9 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
0,
|
0,
|
||||||
VPX_DL_REALTIME))
|
VPX_DL_REALTIME))
|
||||||
{
|
{
|
||||||
|
// Reset to avoid requesting key frames too often.
|
||||||
|
if (_propagationCnt > 0)
|
||||||
|
_propagationCnt = 0;
|
||||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -922,11 +945,17 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
#ifdef DEV_PIC_LOSS
|
#ifdef DEV_PIC_LOSS
|
||||||
if (vpx_codec_control(_decoder, VP8D_GET_LAST_REF_UPDATES, &lastRefUpdates))
|
if (vpx_codec_control(_decoder, VP8D_GET_LAST_REF_UPDATES, &lastRefUpdates))
|
||||||
{
|
{
|
||||||
|
// Reset to avoid requesting key frames too often.
|
||||||
|
if (_propagationCnt > 0)
|
||||||
|
_propagationCnt = 0;
|
||||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
}
|
}
|
||||||
int corrupted = 0;
|
int corrupted = 0;
|
||||||
if (vpx_codec_control(_decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted))
|
if (vpx_codec_control(_decoder, VP8D_GET_FRAME_CORRUPTED, &corrupted))
|
||||||
{
|
{
|
||||||
|
// Reset to avoid requesting key frames too often.
|
||||||
|
if (_propagationCnt > 0)
|
||||||
|
_propagationCnt = 0;
|
||||||
return WEBRTC_VIDEO_CODEC_ERROR;
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@ -934,7 +963,12 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
img = vpx_codec_get_frame(_decoder, &iter);
|
img = vpx_codec_get_frame(_decoder, &iter);
|
||||||
ret = ReturnFrame(img, inputImage._timeStamp);
|
ret = ReturnFrame(img, inputImage._timeStamp);
|
||||||
if (ret != 0)
|
if (ret != 0)
|
||||||
|
{
|
||||||
|
// Reset to avoid requesting key frames too often.
|
||||||
|
if (ret < 0 && _propagationCnt > 0)
|
||||||
|
_propagationCnt = 0;
|
||||||
return ret;
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
// we need to communicate that we should send a RPSI with a specific picture ID
|
// we need to communicate that we should send a RPSI with a specific picture ID
|
||||||
|
|
||||||
@ -967,6 +1001,14 @@ VP8Decoder::Decode(const EncodedImage& inputImage,
|
|||||||
return WEBRTC_VIDEO_CODEC_REQUEST_SLI;
|
return WEBRTC_VIDEO_CODEC_REQUEST_SLI;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// Check Vs. threshold
|
||||||
|
if (_propagationCnt > kVp8ErrorPropagationTh)
|
||||||
|
{
|
||||||
|
// Reset to avoid requesting key frames too often.
|
||||||
|
_propagationCnt = 0;
|
||||||
|
return WEBRTC_VIDEO_CODEC_ERROR;
|
||||||
|
}
|
||||||
return WEBRTC_VIDEO_CODEC_OK;
|
return WEBRTC_VIDEO_CODEC_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -69,6 +69,10 @@ VCMFrameBuffer::GetHighSeqNum() const
|
|||||||
return _sessionInfo.GetHighSeqNum();
|
return _sessionInfo.GetHighSeqNum();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int VCMFrameBuffer::PictureId() const {
|
||||||
|
return _sessionInfo.PictureId();
|
||||||
|
}
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VCMFrameBuffer::IsSessionComplete() const
|
VCMFrameBuffer::IsSessionComplete() const
|
||||||
{
|
{
|
||||||
@ -234,13 +238,6 @@ VCMFrameBuffer::HaveLastPacket() const
|
|||||||
return _sessionInfo.HaveLastPacket();
|
return _sessionInfo.HaveLastPacket();
|
||||||
}
|
}
|
||||||
|
|
||||||
bool
|
|
||||||
VCMFrameBuffer::ForceSetHaveLastPacket()
|
|
||||||
{
|
|
||||||
_sessionInfo.ForceSetHaveLastPacket();
|
|
||||||
return _sessionInfo.IsSessionComplete();
|
|
||||||
}
|
|
||||||
|
|
||||||
void
|
void
|
||||||
VCMFrameBuffer::Reset()
|
VCMFrameBuffer::Reset()
|
||||||
{
|
{
|
||||||
@ -346,10 +343,7 @@ VCMFrameBuffer::RestructureFrameInformation()
|
|||||||
PrepareForDecode();
|
PrepareForDecode();
|
||||||
_frameType = ConvertFrameType(_sessionInfo.FrameType());
|
_frameType = ConvertFrameType(_sessionInfo.FrameType());
|
||||||
_completeFrame = _sessionInfo.IsSessionComplete();
|
_completeFrame = _sessionInfo.IsSessionComplete();
|
||||||
// TODO(holmer): This bit is disabled for now since we can't tell whether
|
_missingFrame = _sessionInfo.PreviousFrameLoss();
|
||||||
// we have had a full frame loss or if we've just lost an FEC/empty packet.
|
|
||||||
// _missingFrame = _sessionInfo.PreviousFrameLoss();
|
|
||||||
_missingFrame = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
WebRtc_Word32
|
WebRtc_Word32
|
||||||
|
@ -45,7 +45,6 @@ public:
|
|||||||
bool IsRetransmitted() const;
|
bool IsRetransmitted() const;
|
||||||
bool IsSessionComplete() const;
|
bool IsSessionComplete() const;
|
||||||
bool HaveLastPacket() const;
|
bool HaveLastPacket() const;
|
||||||
bool ForceSetHaveLastPacket();
|
|
||||||
// Makes sure the session contain a decodable stream.
|
// Makes sure the session contain a decodable stream.
|
||||||
void MakeSessionDecodable();
|
void MakeSessionDecodable();
|
||||||
|
|
||||||
@ -55,6 +54,8 @@ public:
|
|||||||
// Get highest packet sequence number in frame
|
// Get highest packet sequence number in frame
|
||||||
WebRtc_Word32 GetHighSeqNum() const;
|
WebRtc_Word32 GetHighSeqNum() const;
|
||||||
|
|
||||||
|
int PictureId() 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() const;
|
bool GetCountedFrame() const;
|
||||||
|
@ -73,6 +73,7 @@ VCMJitterBuffer::VCMJitterBuffer(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId,
|
|||||||
_frameBuffersTSOrder(),
|
_frameBuffersTSOrder(),
|
||||||
_lastDecodedSeqNum(-1),
|
_lastDecodedSeqNum(-1),
|
||||||
_lastDecodedTimeStamp(-1),
|
_lastDecodedTimeStamp(-1),
|
||||||
|
_lastDecodedPictureId(-1),
|
||||||
_packetsNotDecodable(0),
|
_packetsNotDecodable(0),
|
||||||
_receiveStatistics(),
|
_receiveStatistics(),
|
||||||
_incomingFrameRate(0),
|
_incomingFrameRate(0),
|
||||||
@ -88,7 +89,6 @@ VCMJitterBuffer::VCMJitterBuffer(WebRtc_Word32 vcmId, WebRtc_Word32 receiverId,
|
|||||||
_nackMode(kNoNack),
|
_nackMode(kNoNack),
|
||||||
_NACKSeqNum(),
|
_NACKSeqNum(),
|
||||||
_NACKSeqNumLength(0),
|
_NACKSeqNumLength(0),
|
||||||
_missingMarkerBits(false),
|
|
||||||
_firstPacket(true)
|
_firstPacket(true)
|
||||||
{
|
{
|
||||||
memset(_frameBuffers, 0, sizeof(_frameBuffers));
|
memset(_frameBuffers, 0, sizeof(_frameBuffers));
|
||||||
@ -141,10 +141,10 @@ VCMJitterBuffer::operator=(const VCMJitterBuffer& rhs)
|
|||||||
_nackMode = rhs._nackMode;
|
_nackMode = rhs._nackMode;
|
||||||
_rttMs = rhs._rttMs;
|
_rttMs = rhs._rttMs;
|
||||||
_NACKSeqNumLength = rhs._NACKSeqNumLength;
|
_NACKSeqNumLength = rhs._NACKSeqNumLength;
|
||||||
_missingMarkerBits = rhs._missingMarkerBits;
|
|
||||||
_firstPacket = rhs._firstPacket;
|
_firstPacket = rhs._firstPacket;
|
||||||
_lastDecodedSeqNum = rhs._lastDecodedSeqNum;
|
_lastDecodedSeqNum = rhs._lastDecodedSeqNum;
|
||||||
_lastDecodedTimeStamp = rhs._lastDecodedTimeStamp;
|
_lastDecodedTimeStamp = rhs._lastDecodedTimeStamp;
|
||||||
|
_lastDecodedPictureId = rhs._lastDecodedPictureId;
|
||||||
_packetsNotDecodable = rhs._packetsNotDecodable;
|
_packetsNotDecodable = rhs._packetsNotDecodable;
|
||||||
memcpy(_receiveStatistics, rhs._receiveStatistics,
|
memcpy(_receiveStatistics, rhs._receiveStatistics,
|
||||||
sizeof(_receiveStatistics));
|
sizeof(_receiveStatistics));
|
||||||
@ -196,7 +196,6 @@ VCMJitterBuffer::Start()
|
|||||||
_waitingForCompletion.frameSize = 0;
|
_waitingForCompletion.frameSize = 0;
|
||||||
_waitingForCompletion.timestamp = 0;
|
_waitingForCompletion.timestamp = 0;
|
||||||
_waitingForCompletion.latestPacketTime = -1;
|
_waitingForCompletion.latestPacketTime = -1;
|
||||||
_missingMarkerBits = false;
|
|
||||||
_firstPacket = true;
|
_firstPacket = true;
|
||||||
_NACKSeqNumLength = 0;
|
_NACKSeqNumLength = 0;
|
||||||
_rttMs = 0;
|
_rttMs = 0;
|
||||||
@ -215,6 +214,7 @@ VCMJitterBuffer::Stop()
|
|||||||
_running = false;
|
_running = false;
|
||||||
_lastDecodedTimeStamp = -1;
|
_lastDecodedTimeStamp = -1;
|
||||||
_lastDecodedSeqNum = -1;
|
_lastDecodedSeqNum = -1;
|
||||||
|
_lastDecodedPictureId = -1;
|
||||||
_frameBuffersTSOrder.Flush();
|
_frameBuffersTSOrder.Flush();
|
||||||
for (int i = 0; i < kMaxNumberOfFrames; i++)
|
for (int i = 0; i < kMaxNumberOfFrames; i++)
|
||||||
{
|
{
|
||||||
@ -258,6 +258,7 @@ VCMJitterBuffer::FlushInternal()
|
|||||||
}
|
}
|
||||||
_lastDecodedSeqNum = -1;
|
_lastDecodedSeqNum = -1;
|
||||||
_lastDecodedTimeStamp = -1;
|
_lastDecodedTimeStamp = -1;
|
||||||
|
_lastDecodedPictureId = -1;
|
||||||
_packetsNotDecodable = 0;
|
_packetsNotDecodable = 0;
|
||||||
|
|
||||||
_frameEvent.Reset();
|
_frameEvent.Reset();
|
||||||
@ -274,7 +275,6 @@ VCMJitterBuffer::FlushInternal()
|
|||||||
_waitingForCompletion.timestamp = 0;
|
_waitingForCompletion.timestamp = 0;
|
||||||
_waitingForCompletion.latestPacketTime = -1;
|
_waitingForCompletion.latestPacketTime = -1;
|
||||||
|
|
||||||
_missingMarkerBits = false;
|
|
||||||
_firstPacket = true;
|
_firstPacket = true;
|
||||||
|
|
||||||
_NACKSeqNumLength = 0;
|
_NACKSeqNumLength = 0;
|
||||||
@ -602,22 +602,9 @@ VCMJitterBuffer::FindOldestCompleteContinuousFrame()
|
|||||||
oldestFrame = oldestFrameItem->GetItem();
|
oldestFrame = oldestFrameItem->GetItem();
|
||||||
}
|
}
|
||||||
// is the frame complete?
|
// is the frame complete?
|
||||||
if (oldestFrame != NULL)
|
if (oldestFrame != NULL && kStateComplete != oldestFrame->GetState())
|
||||||
{
|
{
|
||||||
if (kStateComplete != oldestFrame->GetState())
|
oldestFrame = NULL;
|
||||||
{
|
|
||||||
// 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();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
if (oldestFrame == NULL)
|
if (oldestFrame == NULL)
|
||||||
{
|
{
|
||||||
@ -647,52 +634,6 @@ VCMJitterBuffer::FindOldestCompleteContinuousFrame()
|
|||||||
return oldestFrameItem;
|
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
|
// Call from inside the critical section _critSect
|
||||||
void
|
void
|
||||||
VCMJitterBuffer::RecycleFrame(VCMFrameBuffer* frame)
|
VCMJitterBuffer::RecycleFrame(VCMFrameBuffer* frame)
|
||||||
@ -710,7 +651,6 @@ VCMJitterBuffer::RecycleFrame(VCMFrameBuffer* frame)
|
|||||||
ReleaseFrameInternal(frame);
|
ReleaseFrameInternal(frame);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Calculate frame and bit rates
|
// Calculate frame and bit rates
|
||||||
WebRtc_Word32
|
WebRtc_Word32
|
||||||
VCMJitterBuffer::GetUpdate(WebRtc_UWord32& frameRate, WebRtc_UWord32& bitRate)
|
VCMJitterBuffer::GetUpdate(WebRtc_UWord32& frameRate, WebRtc_UWord32& bitRate)
|
||||||
@ -878,13 +818,11 @@ VCMJitterBuffer::GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS)
|
|||||||
// Ignore retransmitted and empty frames.
|
// Ignore retransmitted and empty frames.
|
||||||
UpdateJitterAndDelayEstimates(*oldestFrame, false);
|
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);
|
_frameBuffersTSOrder.Erase(oldestFrameListItem);
|
||||||
oldestFrameListItem = NULL;
|
oldestFrameListItem = NULL;
|
||||||
|
|
||||||
|
oldestFrame->SetState(kStateDecoding);
|
||||||
|
|
||||||
CleanUpOldFrames();
|
CleanUpOldFrames();
|
||||||
CleanUpSizeZeroFrames();
|
CleanUpSizeZeroFrames();
|
||||||
|
|
||||||
@ -893,6 +831,7 @@ VCMJitterBuffer::GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS)
|
|||||||
// We have a frame - store seqnum & timestamp
|
// We have a frame - store seqnum & timestamp
|
||||||
_lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
|
_lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
|
||||||
_lastDecodedTimeStamp = oldestFrame->TimeStamp();
|
_lastDecodedTimeStamp = oldestFrame->TimeStamp();
|
||||||
|
_lastDecodedPictureId = oldestFrame->PictureId();
|
||||||
|
|
||||||
return oldestFrame;
|
return oldestFrame;
|
||||||
}
|
}
|
||||||
@ -918,17 +857,6 @@ VCMJitterBuffer::GetEstimatedJitterMsInternal()
|
|||||||
}
|
}
|
||||||
estimate += static_cast<WebRtc_UWord32>
|
estimate += static_cast<WebRtc_UWord32>
|
||||||
(_jitterEstimate.GetJitterEstimate(rttMult) + 0.5);
|
(_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;
|
return estimate;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1045,8 +973,18 @@ VCMJitterBuffer::CompleteSequenceWithNextFrame()
|
|||||||
{
|
{
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
else if (oldestFrame->GetLowSeqNum() != (_lastDecodedSeqNum + 1)
|
// We can't use sequence numbers to detect frame loss when FEC is enabled.
|
||||||
% 0x00010000)
|
// 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;
|
return false;
|
||||||
}
|
}
|
||||||
@ -1111,19 +1049,24 @@ VCMJitterBuffer::GetFrameForDecoding()
|
|||||||
_frameBuffersTSOrder.Erase(oldestFrameListItem);
|
_frameBuffersTSOrder.Erase(oldestFrameListItem);
|
||||||
oldestFrameListItem = NULL;
|
oldestFrameListItem = NULL;
|
||||||
|
|
||||||
CleanUpOldFrames();
|
|
||||||
CleanUpSizeZeroFrames();
|
|
||||||
|
|
||||||
// Look for previous frame loss
|
// Look for previous frame loss
|
||||||
VerifyAndSetPreviousFrameLost(*oldestFrame);
|
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.
|
// Set as decoding. Propagates the missingFrame bit.
|
||||||
oldestFrame->SetState(kStateDecoding);
|
oldestFrame->SetState(kStateDecoding);
|
||||||
|
|
||||||
|
CleanUpOldFrames();
|
||||||
|
CleanUpSizeZeroFrames();
|
||||||
|
|
||||||
_packetsNotDecodable += oldestFrame->NotDecodablePackets();
|
_packetsNotDecodable += oldestFrame->NotDecodablePackets();
|
||||||
|
|
||||||
// Store current seqnum & time
|
// Store current seqnum & time
|
||||||
_lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
|
_lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
|
||||||
_lastDecodedTimeStamp = oldestFrame->TimeStamp();
|
_lastDecodedTimeStamp = oldestFrame->TimeStamp();
|
||||||
|
_lastDecodedPictureId = oldestFrame->PictureId();
|
||||||
|
|
||||||
return oldestFrame;
|
return oldestFrame;
|
||||||
}
|
}
|
||||||
@ -1171,11 +1114,16 @@ VCMJitterBuffer::GetFrameForDecodingNACK()
|
|||||||
// Ignore retransmitted and empty frames.
|
// Ignore retransmitted and empty frames.
|
||||||
UpdateJitterAndDelayEstimates(*oldestFrame, false);
|
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);
|
_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
|
// Clean up old frames and empty frames
|
||||||
CleanUpOldFrames();
|
CleanUpOldFrames();
|
||||||
@ -1185,6 +1133,7 @@ VCMJitterBuffer::GetFrameForDecodingNACK()
|
|||||||
// Store seqnum & timestamp
|
// Store seqnum & timestamp
|
||||||
_lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
|
_lastDecodedSeqNum = oldestFrame->GetHighSeqNum();
|
||||||
_lastDecodedTimeStamp = oldestFrame->TimeStamp();
|
_lastDecodedTimeStamp = oldestFrame->TimeStamp();
|
||||||
|
_lastDecodedPictureId = oldestFrame->PictureId();
|
||||||
|
|
||||||
return oldestFrame;
|
return oldestFrame;
|
||||||
}
|
}
|
||||||
@ -1903,7 +1852,8 @@ VCMJitterBuffer::CleanUpSizeZeroFrames()
|
|||||||
VCMFrameBuffer* ptrTempBuffer = frameListItem->GetItem();
|
VCMFrameBuffer* ptrTempBuffer = frameListItem->GetItem();
|
||||||
|
|
||||||
// pop frame if its size zero but store seqnum
|
// 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();
|
WebRtc_Word32 frameHighSeqNum = ptrTempBuffer->GetHighSeqNum();
|
||||||
if (frameHighSeqNum == -1)
|
if (frameHighSeqNum == -1)
|
||||||
@ -1983,21 +1933,47 @@ 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)
|
if (frame.FrameType() == kVideoFrameKey)
|
||||||
return;
|
return;
|
||||||
WebRtc_UWord16 nextExpectedSeqNum =
|
|
||||||
static_cast<WebRtc_UWord16>(_lastDecodedSeqNum + 1);
|
|
||||||
if (_lastDecodedSeqNum == -1)
|
if (_lastDecodedSeqNum == -1)
|
||||||
{
|
{
|
||||||
// First frame
|
// First frame is not a key frame
|
||||||
frame.SetPreviousFrameLoss();
|
frame.SetPreviousFrameLoss();
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else if (static_cast<WebRtc_UWord16>(frame.GetLowSeqNum()) !=
|
// We can't use sequence numbers to detect frame loss when FEC is enabled.
|
||||||
nextExpectedSeqNum)
|
// 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();
|
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
|
bool
|
||||||
VCMJitterBuffer::WaitForNack()
|
VCMJitterBuffer::WaitForNack()
|
||||||
{
|
{
|
||||||
|
@ -150,10 +150,6 @@ private:
|
|||||||
// Find oldest complete frame, used for getting next frame to decode
|
// Find oldest complete frame, used for getting next frame to decode
|
||||||
VCMFrameListItem* FindOldestCompleteContinuousFrame();
|
VCMFrameListItem* FindOldestCompleteContinuousFrame();
|
||||||
|
|
||||||
// Check if a frame is missing the markerbit but is complete
|
|
||||||
bool CheckForCompleteFrame(VCMFrameListItem* oldestFrameItem);
|
|
||||||
|
|
||||||
|
|
||||||
void CleanUpOldFrames();
|
void CleanUpOldFrames();
|
||||||
void CleanUpSizeZeroFrames();
|
void CleanUpSizeZeroFrames();
|
||||||
|
|
||||||
@ -181,6 +177,7 @@ private:
|
|||||||
const void* timestamp);
|
const void* timestamp);
|
||||||
static bool CompleteDecodableKeyFrameCriteria(VCMFrameBuffer* frame,
|
static bool CompleteDecodableKeyFrameCriteria(VCMFrameBuffer* frame,
|
||||||
const void* notUsed);
|
const void* notUsed);
|
||||||
|
bool ContinuousPictureId(int pictureId) const;
|
||||||
// Decide whether should wait for NACK (mainly relevant for hybrid mode)
|
// Decide whether should wait for NACK (mainly relevant for hybrid mode)
|
||||||
bool WaitForNack();
|
bool WaitForNack();
|
||||||
|
|
||||||
@ -205,6 +202,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;
|
||||||
|
int _lastDecodedPictureId;
|
||||||
WebRtc_UWord32 _packetsNotDecodable;
|
WebRtc_UWord32 _packetsNotDecodable;
|
||||||
|
|
||||||
// Statistics
|
// Statistics
|
||||||
@ -241,7 +239,6 @@ private:
|
|||||||
WebRtc_UWord16 _NACKSeqNum[kNackHistoryLength];
|
WebRtc_UWord16 _NACKSeqNum[kNackHistoryLength];
|
||||||
WebRtc_UWord32 _NACKSeqNumLength;
|
WebRtc_UWord32 _NACKSeqNumLength;
|
||||||
|
|
||||||
bool _missingMarkerBits;
|
|
||||||
bool _firstPacket;
|
bool _firstPacket;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -26,7 +26,8 @@ VCMSessionInfo::VCMSessionInfo():
|
|||||||
_emptySeqNumLow(-1),
|
_emptySeqNumLow(-1),
|
||||||
_emptySeqNumHigh(-1),
|
_emptySeqNumHigh(-1),
|
||||||
_markerSeqNum(-1),
|
_markerSeqNum(-1),
|
||||||
_packetsNotDecodable(0)
|
_packetsNotDecodable(0),
|
||||||
|
_pictureId(-1)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -55,6 +56,10 @@ VCMSessionInfo::GetHighSeqNum() const
|
|||||||
return LatestSequenceNumber(_emptySeqNumHigh, _highSeqNum, NULL);
|
return LatestSequenceNumber(_emptySeqNumHigh, _highSeqNum, NULL);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int VCMSessionInfo::PictureId() const {
|
||||||
|
return _pictureId;
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
VCMSessionInfo::Reset() {
|
VCMSessionInfo::Reset() {
|
||||||
for (int i = 0; i <= _highestPacketIndex; ++i)
|
for (int i = 0; i <= _highestPacketIndex; ++i)
|
||||||
@ -71,6 +76,7 @@ VCMSessionInfo::Reset() {
|
|||||||
_highestPacketIndex = 0;
|
_highestPacketIndex = 0;
|
||||||
_markerSeqNum = -1;
|
_markerSeqNum = -1;
|
||||||
_packetsNotDecodable = 0;
|
_packetsNotDecodable = 0;
|
||||||
|
_pictureId = -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
WebRtc_UWord32
|
WebRtc_UWord32
|
||||||
@ -169,6 +175,11 @@ VCMSessionInfo::InsertBuffer(WebRtc_UWord8* ptrStartOfLayer,
|
|||||||
}
|
}
|
||||||
returnLength = packetSize;
|
returnLength = packetSize;
|
||||||
|
|
||||||
|
if (packet.codecSpecificHeader.codec == kRTPVideoVP8)
|
||||||
|
{
|
||||||
|
_pictureId = packet.codecSpecificHeader.codecHeader.VP8.pictureId;
|
||||||
|
}
|
||||||
|
|
||||||
if (packet.markerBit)
|
if (packet.markerBit)
|
||||||
{
|
{
|
||||||
_markerBit = true;
|
_markerBit = true;
|
||||||
@ -645,13 +656,6 @@ VCMSessionInfo::HaveLastPacket() const
|
|||||||
return _markerBit;
|
return _markerBit;
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
|
||||||
VCMSessionInfo::ForceSetHaveLastPacket()
|
|
||||||
{
|
|
||||||
_markerBit = true;
|
|
||||||
UpdateCompleteSession();
|
|
||||||
}
|
|
||||||
|
|
||||||
bool
|
bool
|
||||||
VCMSessionInfo::IsRetransmitted() const
|
VCMSessionInfo::IsRetransmitted() const
|
||||||
{
|
{
|
||||||
|
@ -61,7 +61,6 @@ public:
|
|||||||
|
|
||||||
WebRtc_UWord32 GetSessionLength();
|
WebRtc_UWord32 GetSessionLength();
|
||||||
bool HaveLastPacket() const;
|
bool HaveLastPacket() const;
|
||||||
void ForceSetHaveLastPacket();
|
|
||||||
bool IsRetransmitted() const;
|
bool IsRetransmitted() const;
|
||||||
webrtc::FrameType FrameType() const { return _frameType; }
|
webrtc::FrameType FrameType() const { return _frameType; }
|
||||||
|
|
||||||
@ -74,6 +73,7 @@ public:
|
|||||||
WebRtc_Word32 GetLowSeqNum() const;
|
WebRtc_Word32 GetLowSeqNum() const;
|
||||||
// returns highest seqNum, media or empty
|
// returns highest seqNum, media or empty
|
||||||
WebRtc_Word32 GetHighSeqNum() const;
|
WebRtc_Word32 GetHighSeqNum() const;
|
||||||
|
int PictureId() const;
|
||||||
|
|
||||||
WebRtc_UWord32 PrepareForDecode(WebRtc_UWord8* ptrStartOfLayer,
|
WebRtc_UWord32 PrepareForDecode(WebRtc_UWord8* ptrStartOfLayer,
|
||||||
VideoCodecType codec);
|
VideoCodecType codec);
|
||||||
@ -130,6 +130,7 @@ private:
|
|||||||
WebRtc_Word32 _markerSeqNum;
|
WebRtc_Word32 _markerSeqNum;
|
||||||
// Number of packets discarded because the decoder can't use them.
|
// Number of packets discarded because the decoder can't use them.
|
||||||
int _packetsNotDecodable;
|
int _packetsNotDecodable;
|
||||||
|
int _pictureId;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
Loading…
x
Reference in New Issue
Block a user