Refactored the jitter buffer to use std::list.

BUG=
TEST=

Review URL: https://webrtc-codereview.appspot.com/352016

git-svn-id: http://webrtc.googlecode.com/svn/trunk@1635 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
stefan@webrtc.org 2012-02-08 08:58:55 +00:00
parent 7dfa883954
commit 2979461595
7 changed files with 158 additions and 415 deletions

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
* *
* Use of this source code is governed by a BSD-style license * 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 * that can be found in the LICENSE file in the root of the source
@ -11,13 +11,11 @@
#ifndef WEBRTC_MODULES_VIDEO_CODING_FRAME_BUFFER_H_ #ifndef WEBRTC_MODULES_VIDEO_CODING_FRAME_BUFFER_H_
#define WEBRTC_MODULES_VIDEO_CODING_FRAME_BUFFER_H_ #define WEBRTC_MODULES_VIDEO_CODING_FRAME_BUFFER_H_
#include "modules/interface/module_common_types.h"
#include "modules/video_coding/main/source/encoded_frame.h"
#include "modules/video_coding/main/source/jitter_buffer_common.h"
#include "modules/video_coding/main/source/session_info.h"
#include "typedefs.h" #include "typedefs.h"
#include "module_common_types.h"
#include "encoded_frame.h"
#include "frame_list.h"
#include "jitter_buffer_common.h"
#include "session_info.h"
namespace webrtc namespace webrtc
{ {

View File

@ -1,117 +0,0 @@
/*
* 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 "frame_list.h"
#include "frame_buffer.h"
#include "jitter_buffer.h"
#include <cstdlib>
namespace webrtc {
VCMFrameListTimestampOrderAsc::~VCMFrameListTimestampOrderAsc()
{
Flush();
}
void
VCMFrameListTimestampOrderAsc::Flush()
{
while(Erase(First()) != -1) { }
}
// Inserts frame in timestamp order, with the oldest timestamp first. Takes wrap
// arounds into account
WebRtc_Word32
VCMFrameListTimestampOrderAsc::Insert(VCMFrameBuffer* frame)
{
VCMFrameListItem* item = static_cast<VCMFrameListItem*>(First());
VCMFrameListItem* newItem = new VCMFrameListItem(frame);
bool inserted = false;
if (newItem == NULL)
{
return -1;
}
while (item != NULL)
{
const WebRtc_UWord32 itemTimestamp = item->GetItem()->TimeStamp();
if (LatestTimestamp(itemTimestamp, frame->TimeStamp(), NULL) ==
itemTimestamp)
{
if (InsertBefore(item, newItem) < 0)
{
delete newItem;
return -1;
}
inserted = true;
break;
}
item = Next(item);
}
if (!inserted && ListWrapper::Insert(ListWrapper::Last(), newItem) < 0)
{
delete newItem;
return -1;
}
return 0;
}
VCMFrameBuffer*
VCMFrameListTimestampOrderAsc::FirstFrame() const
{
VCMFrameListItem* item = First();
if (item != NULL)
{
return item->GetItem();
}
return NULL;
}
VCMFrameListItem*
VCMFrameListTimestampOrderAsc::FindFrameListItem(FindFrameCriteria criteria,
const void* compareWith,
VCMFrameListItem* startItem) const
{
if (startItem == NULL)
{
startItem = First();
}
if (criteria == NULL)
{
return NULL;
}
while (startItem != NULL)
{
if (criteria(startItem->GetItem(), compareWith))
{
return startItem;
}
startItem = Next(startItem);
}
// No frame found
return NULL;
}
VCMFrameBuffer*
VCMFrameListTimestampOrderAsc::FindFrame(FindFrameCriteria criteria,
const void* compareWith,
VCMFrameListItem* startItem) const
{
const VCMFrameListItem* frameListItem = FindFrameListItem(criteria,
compareWith,
startItem);
if (frameListItem == NULL)
{
return NULL;
}
return frameListItem->GetItem();
}
}

View File

@ -1,66 +0,0 @@
/*
* 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.
*/
#ifndef WEBRTC_MODULES_VIDEO_CODING_FRAME_LIST_H_
#define WEBRTC_MODULES_VIDEO_CODING_FRAME_LIST_H_
#include "list_wrapper.h"
#include "typedefs.h"
#include <stdlib.h>
namespace webrtc
{
class VCMFrameBuffer;
typedef bool (*FindFrameCriteria)(VCMFrameBuffer*, const void*);
class VCMFrameListItem : public ListItem
{
friend class VCMFrameListTimestampOrderAsc;
public:
VCMFrameListItem(const VCMFrameBuffer* ptr) : ListItem(ptr) {}
~VCMFrameListItem() {};
VCMFrameBuffer* GetItem() const
{ return static_cast<VCMFrameBuffer*>(ListItem::GetItem()); }
};
class VCMFrameListTimestampOrderAsc : public ListWrapper
{
public:
VCMFrameListTimestampOrderAsc() : ListWrapper() {};
~VCMFrameListTimestampOrderAsc();
void Flush();
// Inserts frame in timestamp order, with the oldest timestamp first.
// Takes wrap arounds into account.
WebRtc_Word32 Insert(VCMFrameBuffer* frame);
VCMFrameBuffer* FirstFrame() const;
VCMFrameListItem* Next(VCMFrameListItem* item) const
{ return static_cast<VCMFrameListItem*>(ListWrapper::Next(item)); }
VCMFrameListItem* Previous(VCMFrameListItem* item) const
{ return static_cast<VCMFrameListItem*>(ListWrapper::Previous(item)); }
VCMFrameListItem* First() const
{ return static_cast<VCMFrameListItem*>(ListWrapper::First()); }
VCMFrameListItem* Last() const
{ return static_cast<VCMFrameListItem*>(ListWrapper::Last()); }
VCMFrameListItem* FindFrameListItem(FindFrameCriteria criteria,
const void* compareWith = NULL,
VCMFrameListItem* startItem = NULL) const;
VCMFrameBuffer* FindFrame(FindFrameCriteria criteria,
const void* compareWith = NULL,
VCMFrameListItem* startItem = NULL) const;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_VIDEO_CODING_FRAME_LIST_H_

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
* *
* Use of this source code is governed by a BSD-style license * 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 * that can be found in the LICENSE file in the root of the source
@ -7,25 +7,21 @@
* in the file PATENTS. All contributing project authors may * in the file PATENTS. All contributing project authors may
* 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 "modules/video_coding/main/source/jitter_buffer.h"
#include "critical_section_wrapper.h" #include <algorithm>
#include "frame_buffer.h"
#include "inter_frame_delay.h"
#include "internal_defines.h"
#include "jitter_buffer.h"
#include "jitter_buffer_common.h"
#include "jitter_estimator.h"
#include "packet.h"
#include "event.h"
#include "trace.h"
#include "modules/video_coding/main/source/tick_time_base.h"
#include "list_wrapper.h"
#include <cassert> #include <cassert>
#include <string.h>
#include <cmath> #include "modules/video_coding/main/source/event.h"
#include "modules/video_coding/main/source/frame_buffer.h"
#include "modules/video_coding/main/source/inter_frame_delay.h"
#include "modules/video_coding/main/source/internal_defines.h"
#include "modules/video_coding/main/source/jitter_buffer_common.h"
#include "modules/video_coding/main/source/jitter_estimator.h"
#include "modules/video_coding/main/source/packet.h"
#include "modules/video_coding/main/source/tick_time_base.h"
#include "system_wrappers/interface/critical_section_wrapper.h"
#include "system_wrappers/interface/trace.h"
#if defined(_WIN32) #if defined(_WIN32)
// VS 2005: Don't warn for default initialized arrays. See help for more info. // VS 2005: Don't warn for default initialized arrays. See help for more info.
@ -34,27 +30,38 @@
namespace webrtc { namespace webrtc {
// Criteria used when searching for frames in the frame buffer list // Predicates used when searching for frames in the frame buffer list
bool class FrameSmallerTimestamp {
VCMJitterBuffer::FrameEqualTimestamp(VCMFrameBuffer* frame, public:
const void* timestamp) FrameSmallerTimestamp(uint32_t timestamp) : timestamp_(timestamp) {}
{ bool operator()(VCMFrameBuffer* frame) {
if (timestamp == NULL) return (LatestTimestamp(timestamp_, frame->TimeStamp(), NULL) ==
{ timestamp_);
return false; }
}
return (*static_cast<const WebRtc_UWord32*>(timestamp)) == frame->TimeStamp();
}
bool private:
VCMJitterBuffer::CompleteDecodableKeyFrameCriteria(VCMFrameBuffer* frame, uint32_t timestamp_;
const void* /*notUsed*/) };
{
const VCMFrameBufferStateEnum state = frame->GetState(); class FrameEqualTimestamp {
// We can decode key frame or decodable/complete frames. public:
FrameEqualTimestamp(uint32_t timestamp) : timestamp_(timestamp) {}
bool operator()(VCMFrameBuffer* frame) {
return (timestamp_ == frame->TimeStamp());
}
private:
uint32_t timestamp_;
};
class CompleteDecodableKeyFrameCriteria {
public:
bool operator()(VCMFrameBuffer* frame) {
return (frame->FrameType() == kVideoFrameKey) && return (frame->FrameType() == kVideoFrameKey) &&
(state == kStateComplete || state == kStateDecodable); (frame->GetState() == kStateComplete ||
} frame->GetState() == kStateDecodable);
}
};
// Constructor // Constructor
VCMJitterBuffer::VCMJitterBuffer(TickTimeBase* clock, VCMJitterBuffer::VCMJitterBuffer(TickTimeBase* clock,
@ -71,7 +78,7 @@ VCMJitterBuffer::VCMJitterBuffer(TickTimeBase* clock,
_packetEvent(), _packetEvent(),
_maxNumberOfFrames(kStartNumberOfFrames), _maxNumberOfFrames(kStartNumberOfFrames),
_frameBuffers(), _frameBuffers(),
_frameBuffersTSOrder(), _frameList(),
_lastDecodedState(), _lastDecodedState(),
_packetsNotDecodable(0), _packetsNotDecodable(0),
_receiveStatistics(), _receiveStatistics(),
@ -162,14 +169,16 @@ VCMJitterBuffer::CopyFrom(const VCMJitterBuffer& rhs)
_frameBuffers[i] = NULL; _frameBuffers[i] = NULL;
} }
} }
while(_frameBuffersTSOrder.Erase(_frameBuffersTSOrder.First()) != -1) _frameList.clear();
{ }
for (int i = 0; i < _maxNumberOfFrames; i++) for (int i = 0; i < _maxNumberOfFrames; i++)
{ {
_frameBuffers[i] = new VCMFrameBuffer(*(rhs._frameBuffers[i])); _frameBuffers[i] = new VCMFrameBuffer(*(rhs._frameBuffers[i]));
if (_frameBuffers[i]->Length() > 0) if (_frameBuffers[i]->Length() > 0)
{ {
_frameBuffersTSOrder.Insert(_frameBuffers[i]); FrameList::reverse_iterator rit = std::find_if(
_frameList.rbegin(), _frameList.rend(),
FrameSmallerTimestamp(_frameBuffers[i]->TimeStamp()));
_frameList.insert(rit.base(), _frameBuffers[i]);
} }
} }
rhs._critSect->Leave(); rhs._critSect->Leave();
@ -217,7 +226,7 @@ VCMJitterBuffer::Stop()
_critSect->Enter(); _critSect->Enter();
_running = false; _running = false;
_lastDecodedState.Reset(); _lastDecodedState.Reset();
_frameBuffersTSOrder.Flush(); _frameList.clear();
for (int i = 0; i < kMaxNumberOfFrames; i++) for (int i = 0; i < kMaxNumberOfFrames; i++)
{ {
if (_frameBuffers[i] != NULL) if (_frameBuffers[i] != NULL)
@ -253,7 +262,7 @@ void
VCMJitterBuffer::FlushInternal() VCMJitterBuffer::FlushInternal()
{ {
// Erase all frames from the sorted list and set their state to free. // Erase all frames from the sorted list and set their state to free.
_frameBuffersTSOrder.Flush(); _frameList.clear();
for (WebRtc_Word32 i = 0; i < _maxNumberOfFrames; i++) for (WebRtc_Word32 i = 0; i < _maxNumberOfFrames; i++)
{ {
ReleaseFrameInternal(_frameBuffers[i]); ReleaseFrameInternal(_frameBuffers[i]);
@ -389,12 +398,11 @@ VCMJitterBuffer::UpdateFrameState(VCMFrameBuffer* frame)
} }
} }
const VCMFrameListItem* const FrameList::iterator it = FindOldestCompleteContinuousFrame(false);
oldFrameListItem = FindOldestCompleteContinuousFrame(false);
VCMFrameBuffer* oldFrame = NULL; VCMFrameBuffer* oldFrame = NULL;
if (oldFrameListItem != NULL) if (it != _frameList.end())
{ {
oldFrame = oldFrameListItem->GetItem(); oldFrame = *it;
} }
// Only signal if this is the oldest frame. // Only signal if this is the oldest frame.
@ -464,16 +472,18 @@ VCMJitterBuffer::GetFrame(const VCMPacket& packet, VCMEncodedFrame*& frame)
} }
_numConsecutiveOldPackets = 0; _numConsecutiveOldPackets = 0;
frame = _frameBuffersTSOrder.FindFrame(FrameEqualTimestamp, FrameList::iterator it = std::find_if(
&packet.timestamp); _frameList.begin(),
_frameList.end(),
FrameEqualTimestamp(packet.timestamp));
if (it != _frameList.end()) {
frame = *it;
return VCM_OK;
}
_critSect->Leave(); _critSect->Leave();
if (frame != NULL)
{
return VCM_OK;
}
// No match, return empty frame // No match, return empty frame
frame = GetEmptyFrame(); frame = GetEmptyFrame();
if (frame != NULL) if (frame != NULL)
@ -550,51 +560,46 @@ VCMJitterBuffer::GetEmptyFrame()
// Find oldest complete frame used for getting next frame to decode // Find oldest complete frame used for getting next frame to decode
// Must be called under critical section // Must be called under critical section
VCMFrameListItem* FrameList::iterator
VCMJitterBuffer::FindOldestCompleteContinuousFrame(bool enable_decodable) { VCMJitterBuffer::FindOldestCompleteContinuousFrame(bool enable_decodable) {
// If we have more than one frame done since last time, pick oldest. // If we have more than one frame done since last time, pick oldest.
VCMFrameListItem* oldest_frame_item = _frameBuffersTSOrder.First();
VCMFrameBuffer* oldest_frame = NULL; VCMFrameBuffer* oldest_frame = NULL;
FrameList::iterator it = _frameList.begin();
// When temporal layers are available, we search for a complete or decodable // When temporal layers are available, we search for a complete or decodable
// frame until we hit one of the following: // frame until we hit one of the following:
// 1. Continuous base or sync layer. // 1. Continuous base or sync layer.
// 2. The end of the list was reached. // 2. The end of the list was reached.
while (oldest_frame_item != NULL) { for (; it != _frameList.end(); ++it) {
oldest_frame = oldest_frame_item->GetItem(); oldest_frame = *it;
if (oldest_frame) { VCMFrameBufferStateEnum state = oldest_frame->GetState();
VCMFrameBufferStateEnum state = oldest_frame->GetState(); // Is this frame complete or decodable and continuous?
// Is this frame complete or decodable and continuous? if ((state == kStateComplete ||
if ((state == kStateComplete || (enable_decodable && state == kStateDecodable)) &&
(enable_decodable && state == kStateDecodable)) && _lastDecodedState.ContinuousFrame(oldest_frame)) {
_lastDecodedState.ContinuousFrame(oldest_frame)) { break;
} else {
int temporal_id = oldest_frame->TemporalId();
oldest_frame = NULL;
if (temporal_id <= 0) {
// When temporal layers are disabled or we have hit a base layer
// we break (regardless of continuity and completeness).
break; break;
} else {
int temporal_id = oldest_frame->TemporalId();
oldest_frame = NULL;
if (temporal_id <= 0) {
// When temporal layers are disabled or we have hit a base layer
// we break (regardless of continuity and completeness).
break;
}
} }
} }
// Temporal layers are available, and we have yet to reach a base layer
// frame (complete/decodable or not) => Read next frame.
oldest_frame_item = _frameBuffersTSOrder.Next(oldest_frame_item);
} }
if (oldest_frame == NULL) { if (oldest_frame == NULL) {
// No complete frame no point to continue. // No complete frame no point to continue.
return NULL; return _frameList.end();
} else if (_waitingForKeyFrame && } else if (_waitingForKeyFrame &&
oldest_frame->FrameType() != kVideoFrameKey) { oldest_frame->FrameType() != kVideoFrameKey) {
// We are waiting for a key frame. // We are waiting for a key frame.
return NULL; return _frameList.end();
} }
// We have a complete continuous frame. // We have a complete continuous frame.
return oldest_frame_item; return it;
} }
// Call from inside the critical section _critSect // Call from inside the critical section _critSect
@ -700,15 +705,9 @@ VCMJitterBuffer::GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS)
if (_lastDecodedState.init() && WaitForNack()) { if (_lastDecodedState.init() && WaitForNack()) {
_waitingForKeyFrame = true; _waitingForKeyFrame = true;
} }
VCMFrameListItem*
oldestFrameListItem = FindOldestCompleteContinuousFrame(false);
VCMFrameBuffer* oldestFrame = NULL;
if (oldestFrameListItem != NULL)
{
oldestFrame = oldestFrameListItem->GetItem();
}
if (oldestFrame == NULL) FrameList::iterator it = FindOldestCompleteContinuousFrame(false);
if (it == _frameList.end())
{ {
if (maxWaitTimeMS == 0) if (maxWaitTimeMS == 0)
{ {
@ -736,12 +735,8 @@ VCMJitterBuffer::GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS)
// Finding oldest frame ready for decoder, but check // Finding oldest frame ready for decoder, but check
// sequence number and size // sequence number and size
CleanUpOldFrames(); CleanUpOldFrames();
oldestFrameListItem = FindOldestCompleteContinuousFrame(false); it = FindOldestCompleteContinuousFrame(false);
if (oldestFrameListItem != NULL) if (it == _frameList.end())
{
oldestFrame = oldestFrameListItem->GetItem();
}
if (oldestFrame == NULL)
{ {
waitTimeMs = endWaitTimeMs - waitTimeMs = endWaitTimeMs -
_clock->MillisecondTimestamp(); _clock->MillisecondTimestamp();
@ -765,13 +760,16 @@ VCMJitterBuffer::GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS)
_frameEvent.Reset(); _frameEvent.Reset();
} }
if (oldestFrame == NULL) if (it == _frameList.end())
{ {
// Even after signaling we're still missing a complete continuous frame // Even after signaling we're still missing a complete continuous frame
_critSect->Leave(); _critSect->Leave();
return NULL; return NULL;
} }
VCMFrameBuffer* oldestFrame = *it;
it = _frameList.erase(it);
// Update jitter estimate // Update jitter estimate
const bool retransmitted = (oldestFrame->GetNackCount() > 0); const bool retransmitted = (oldestFrame->GetNackCount() > 0);
if (retransmitted) if (retransmitted)
@ -783,8 +781,6 @@ VCMJitterBuffer::GetCompleteFrameForDecoding(WebRtc_UWord32 maxWaitTimeMS)
// Ignore retransmitted and empty frames. // Ignore retransmitted and empty frames.
UpdateJitterAndDelayEstimates(*oldestFrame, false); UpdateJitterAndDelayEstimates(*oldestFrame, false);
} }
_frameBuffersTSOrder.Erase(oldestFrameListItem);
oldestFrameListItem = NULL;
oldestFrame->SetState(kStateDecoding); oldestFrame->SetState(kStateDecoding);
@ -853,9 +849,9 @@ VCMJitterBuffer::GetNextTimeStamp(WebRtc_UWord32 maxWaitTimeMS,
// Finding oldest frame ready for decoder, check sequence number and size // Finding oldest frame ready for decoder, check sequence number and size
CleanUpOldFrames(); CleanUpOldFrames();
VCMFrameBuffer* oldestFrame = _frameBuffersTSOrder.FirstFrame(); FrameList::iterator it = _frameList.begin();
if (oldestFrame == NULL) if (it == _frameList.end())
{ {
_packetEvent.Reset(); _packetEvent.Reset();
_critSect->Leave(); _critSect->Leave();
@ -870,7 +866,7 @@ VCMJitterBuffer::GetNextTimeStamp(WebRtc_UWord32 maxWaitTimeMS,
_critSect->Enter(); _critSect->Enter();
CleanUpOldFrames(); CleanUpOldFrames();
oldestFrame = _frameBuffersTSOrder.FirstFrame(); it = _frameList.begin();
} }
else else
{ {
@ -878,7 +874,7 @@ VCMJitterBuffer::GetNextTimeStamp(WebRtc_UWord32 maxWaitTimeMS,
} }
} }
if (oldestFrame == NULL) if (it == _frameList.end())
{ {
_critSect->Leave(); _critSect->Leave();
return -1; return -1;
@ -887,11 +883,11 @@ VCMJitterBuffer::GetNextTimeStamp(WebRtc_UWord32 maxWaitTimeMS,
// return frame type // return frame type
// All layers are assumed to have the same type // All layers are assumed to have the same type
incomingFrameType = oldestFrame->FrameType(); incomingFrameType = (*it)->FrameType();
renderTimeMs = oldestFrame->RenderTimeMs(); renderTimeMs = (*it)->RenderTimeMs();
const WebRtc_UWord32 timestamp = oldestFrame->TimeStamp(); const WebRtc_UWord32 timestamp = (*it)->TimeStamp();
_critSect->Leave(); _critSect->Leave();
@ -911,17 +907,12 @@ VCMJitterBuffer::CompleteSequenceWithNextFrame()
// Finding oldest frame ready for decoder, check sequence number and size // Finding oldest frame ready for decoder, check sequence number and size
CleanUpOldFrames(); CleanUpOldFrames();
VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First(); if (_frameList.empty())
if (oldestFrameListItem == NULL) return true;
{
// No frame found
return true;
}
VCMFrameBuffer* oldestFrame = oldestFrameListItem->GetItem(); VCMFrameBuffer* oldestFrame = _frameList.front();
const VCMFrameListItem* nextFrameItem = if (_frameList.size() <= 1 &&
_frameBuffersTSOrder.Next(oldestFrameListItem); oldestFrame->GetState() != kStateComplete)
if (nextFrameItem == NULL && oldestFrame->GetState() != kStateComplete)
{ {
// Frame not ready to be decoded. // Frame not ready to be decoded.
return true; return true;
@ -968,19 +959,14 @@ VCMJitterBuffer::GetFrameForDecoding()
CleanUpOldFrames(); CleanUpOldFrames();
VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First(); if (_frameList.empty()) {
if (oldestFrameListItem == NULL) return NULL;
{
return NULL;
} }
VCMFrameBuffer* oldestFrame = oldestFrameListItem->GetItem();
const VCMFrameListItem* nextFrameItem = VCMFrameBuffer* oldestFrame = _frameList.front();
_frameBuffersTSOrder.Next(oldestFrameListItem); if (_frameList.size() <= 1 &&
// Don't output incomplete frames if subsequent frames haven't arrived yet. oldestFrame->GetState() != kStateComplete) {
if (nextFrameItem == NULL && oldestFrame->GetState() != kStateComplete) return NULL;
{
return NULL;
} }
// Incomplete frame pulled out from jitter buffer, // Incomplete frame pulled out from jitter buffer,
@ -1006,8 +992,7 @@ VCMJitterBuffer::GetFrameForDecoding()
oldestFrame->LatestPacketTimeMs(); oldestFrame->LatestPacketTimeMs();
_waitingForCompletion.timestamp = oldestFrame->TimeStamp(); _waitingForCompletion.timestamp = oldestFrame->TimeStamp();
} }
_frameBuffersTSOrder.Erase(oldestFrameListItem); _frameList.erase(_frameList.begin());
oldestFrameListItem = NULL;
// Look for previous frame loss // Look for previous frame loss
VerifyAndSetPreviousFrameLost(*oldestFrame); VerifyAndSetPreviousFrameLost(*oldestFrame);
@ -1052,27 +1037,18 @@ VCMJitterBuffer::GetFrameForDecodingNACK()
// Allow for a decodable frame when in Hybrid mode. // Allow for a decodable frame when in Hybrid mode.
bool enableDecodable = _nackMode == kNackHybrid ? true : false; bool enableDecodable = _nackMode == kNackHybrid ? true : false;
VCMFrameListItem* FrameList::iterator it = FindOldestCompleteContinuousFrame(enableDecodable);
oldestFrameListItem = FindOldestCompleteContinuousFrame(enableDecodable); if (it == _frameList.end())
VCMFrameBuffer* oldestFrame = NULL;
if (oldestFrameListItem != NULL)
{
oldestFrame = oldestFrameListItem->GetItem();
}
if (oldestFrame == NULL)
{ {
// If we didn't find one we're good with a complete key/decodable frame. // If we didn't find one we're good with a complete key/decodable frame.
oldestFrameListItem = _frameBuffersTSOrder.FindFrameListItem( it = find_if(_frameList.begin(), _frameList.end(),
CompleteDecodableKeyFrameCriteria); CompleteDecodableKeyFrameCriteria());
if (oldestFrameListItem != NULL) if (it == _frameList.end())
{
oldestFrame = oldestFrameListItem->GetItem();
}
if (oldestFrame == NULL)
{ {
return NULL; return NULL;
} }
} }
VCMFrameBuffer* oldestFrame = *it;
// Update jitter estimate // Update jitter estimate
const bool retransmitted = (oldestFrame->GetNackCount() > 0); const bool retransmitted = (oldestFrame->GetNackCount() > 0);
if (retransmitted) if (retransmitted)
@ -1084,8 +1060,7 @@ VCMJitterBuffer::GetFrameForDecodingNACK()
// Ignore retransmitted and empty frames. // Ignore retransmitted and empty frames.
UpdateJitterAndDelayEstimates(*oldestFrame, false); UpdateJitterAndDelayEstimates(*oldestFrame, false);
} }
_frameBuffersTSOrder.Erase(oldestFrameListItem); it = _frameList.erase(it);
oldestFrameListItem = NULL;
// Look for previous frame loss // Look for previous frame loss
VerifyAndSetPreviousFrameLost(*oldestFrame); VerifyAndSetPreviousFrameLost(*oldestFrame);
@ -1601,7 +1576,10 @@ VCMJitterBuffer::InsertPacket(VCMEncodedFrame* buffer, const VCMPacket& packet)
if (state == kStateEmpty && first) if (state == kStateEmpty && first)
{ {
ret = kFirstPacket; ret = kFirstPacket;
_frameBuffersTSOrder.Insert(frame); FrameList::reverse_iterator rit = std::find_if(
_frameList.rbegin(), _frameList.rend(),
FrameSmallerTimestamp(frame->TimeStamp()));
_frameList.insert(rit.base(), frame);
} }
} }
} }
@ -1719,35 +1697,22 @@ VCMJitterBuffer::SetNackMode(VCMNackMode mode,
bool bool
VCMJitterBuffer::RecycleFramesUntilKeyFrame() VCMJitterBuffer::RecycleFramesUntilKeyFrame()
{ {
// Throw at least one frame.
VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First();
VCMFrameBuffer* oldestFrame = NULL;
if (oldestFrameListItem != NULL)
{
oldestFrame = oldestFrameListItem->GetItem();
}
// Remove up to oldest key frame // Remove up to oldest key frame
while (oldestFrameListItem != NULL) while (_frameList.size() > 0)
{ {
// Throw at least one frame. // Throw at least one frame.
_dropCount++; _dropCount++;
FrameList::iterator it = _frameList.begin();
WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding, WEBRTC_TRACE(webrtc::kTraceWarning, webrtc::kTraceVideoCoding,
VCMId(_vcmId, _receiverId), VCMId(_vcmId, _receiverId),
"Jitter buffer drop count:%d, lowSeq %d", _dropCount, "Jitter buffer drop count:%d, lowSeq %d", _dropCount,
oldestFrame->GetLowSeqNum()); (*it)->GetLowSeqNum());
_frameBuffersTSOrder.Erase(oldestFrameListItem); RecycleFrame(*it);
RecycleFrame(oldestFrame); it = _frameList.erase(it);
if (it != _frameList.end() && (*it)->FrameType() == kVideoFrameKey)
oldestFrameListItem = _frameBuffersTSOrder.First();
if (oldestFrameListItem != NULL)
{
oldestFrame = oldestFrameListItem->GetItem();
}
if (oldestFrame != NULL && oldestFrame->FrameType() == kVideoFrameKey)
{ {
// Fake the lastDecodedState to match this key frame. // Fake the lastDecodedState to match this key frame.
_lastDecodedState.SetStateOneBack(oldestFrame); _lastDecodedState.SetStateOneBack(*it);
return true; return true;
} }
} }
@ -1758,18 +1723,14 @@ VCMJitterBuffer::RecycleFramesUntilKeyFrame()
// Must be called under the critical section _critSect. // Must be called under the critical section _critSect.
void VCMJitterBuffer::CleanUpOldFrames() { void VCMJitterBuffer::CleanUpOldFrames() {
VCMFrameListItem* oldestFrameListItem = _frameBuffersTSOrder.First(); while (_frameList.size() > 0) {
VCMFrameBuffer* oldestFrame = NULL; VCMFrameBuffer* oldestFrame = _frameList.front();
while (oldestFrameListItem != NULL) {
oldestFrame = oldestFrameListItem->GetItem();
bool nextFrameEmpty = (_lastDecodedState.ContinuousFrame(oldestFrame) && bool nextFrameEmpty = (_lastDecodedState.ContinuousFrame(oldestFrame) &&
oldestFrame->GetState() == kStateEmpty); oldestFrame->GetState() == kStateEmpty);
if (_lastDecodedState.IsOldFrame(oldestFrame) || (nextFrameEmpty && if (_lastDecodedState.IsOldFrame(oldestFrame) ||
_frameBuffersTSOrder.Next(oldestFrameListItem) != NULL)) { (nextFrameEmpty && _frameList.size() > 1)) {
_frameBuffersTSOrder.Erase(oldestFrameListItem); ReleaseFrameInternal(_frameList.front());
ReleaseFrameInternal(oldestFrame); _frameList.erase(_frameList.begin());
oldestFrameListItem = _frameBuffersTSOrder.First();
} else { } else {
break; break;
} }
@ -1786,7 +1747,6 @@ void VCMJitterBuffer::VerifyAndSetPreviousFrameLost(VCMFrameBuffer& frame) {
frame.SetPreviousFrameLoss(); frame.SetPreviousFrameLoss();
} }
bool bool
VCMJitterBuffer::WaitForNack() VCMJitterBuffer::WaitForNack()
{ {

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
* *
* Use of this source code is governed by a BSD-style license * 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 * that can be found in the LICENSE file in the root of the source
@ -11,16 +11,18 @@
#ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_H_ #ifndef WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_H_
#define WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_H_ #define WEBRTC_MODULES_VIDEO_CODING_JITTER_BUFFER_H_
#include <list>
#include "modules/interface/module_common_types.h"
#include "modules/video_coding/main/interface/video_coding_defines.h"
#include "modules/video_coding/main/source/decoding_state.h"
#include "modules/video_coding/main/source/event.h"
#include "modules/video_coding/main/source/inter_frame_delay.h"
#include "modules/video_coding/main/source/jitter_buffer_common.h"
#include "modules/video_coding/main/source/jitter_estimator.h"
#include "system_wrappers/interface/constructor_magic.h"
#include "system_wrappers/interface/critical_section_wrapper.h"
#include "typedefs.h" #include "typedefs.h"
#include "critical_section_wrapper.h"
#include "decoding_state.h"
#include "module_common_types.h"
#include "video_coding_defines.h"
#include "inter_frame_delay.h"
#include "event.h"
#include "frame_list.h"
#include "jitter_buffer_common.h"
#include "jitter_estimator.h"
namespace webrtc namespace webrtc
{ {
@ -32,6 +34,8 @@ enum VCMNackMode
kNoNack kNoNack
}; };
typedef std::list<VCMFrameBuffer*> FrameList;
// forward declarations // forward declarations
class TickTimeBase; class TickTimeBase;
class VCMFrameBuffer; class VCMFrameBuffer;
@ -160,7 +164,7 @@ private:
// Help functions for getting a frame // Help functions for getting a frame
// Find oldest complete frame, used for getting next frame to decode // Find oldest complete frame, used for getting next frame to decode
// When enabled, will return a decodable frame // When enabled, will return a decodable frame
VCMFrameListItem* FindOldestCompleteContinuousFrame(bool enableDecodable); FrameList::iterator FindOldestCompleteContinuousFrame(bool enableDecodable);
void CleanUpOldFrames(); void CleanUpOldFrames();
@ -184,10 +188,6 @@ private:
WebRtc_Word32 GetLowHighSequenceNumbers(WebRtc_Word32& lowSeqNum, WebRtc_Word32 GetLowHighSequenceNumbers(WebRtc_Word32& lowSeqNum,
WebRtc_Word32& highSeqNum) const; WebRtc_Word32& highSeqNum) const;
static bool FrameEqualTimestamp(VCMFrameBuffer* frame,
const void* timestamp);
static bool CompleteDecodableKeyFrameCriteria(VCMFrameBuffer* frame,
const void* notUsed);
// 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();
@ -206,7 +206,7 @@ private:
WebRtc_Word32 _maxNumberOfFrames; WebRtc_Word32 _maxNumberOfFrames;
// Array of pointers to the frames in JB // Array of pointers to the frames in JB
VCMFrameBuffer* _frameBuffers[kMaxNumberOfFrames]; VCMFrameBuffer* _frameBuffers[kMaxNumberOfFrames];
VCMFrameListTimestampOrderAsc _frameBuffersTSOrder; FrameList _frameList;
// timing // timing
VCMDecodingState _lastDecodedState; VCMDecodingState _lastDecodedState;

View File

@ -46,7 +46,6 @@
'fec_tables_xor.h', 'fec_tables_xor.h',
'frame_buffer.h', 'frame_buffer.h',
'frame_dropper.h', 'frame_dropper.h',
'frame_list.h',
'generic_decoder.h', 'generic_decoder.h',
'generic_encoder.h', 'generic_encoder.h',
'inter_frame_delay.h', 'inter_frame_delay.h',
@ -78,7 +77,6 @@
'exp_filter.cc', 'exp_filter.cc',
'frame_buffer.cc', 'frame_buffer.cc',
'frame_dropper.cc', 'frame_dropper.cc',
'frame_list.cc',
'generic_decoder.cc', 'generic_decoder.cc',
'generic_encoder.cc', 'generic_encoder.cc',
'inter_frame_delay.cc', 'inter_frame_delay.cc',

View File

@ -1,5 +1,5 @@
/* /*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
* *
* Use of this source code is governed by a BSD-style license * 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 * that can be found in the LICENSE file in the root of the source
@ -8,8 +8,8 @@
* 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 <stdio.h>
#include <math.h> #include <math.h>
#include <stdio.h>
#include "common_types.h" #include "common_types.h"
#include "../source/event.h" #include "../source/event.h"
@ -24,6 +24,7 @@
#include "test_util.h" #include "test_util.h"
#include "test_macros.h" #include "test_macros.h"
// TODO(holmer): Get rid of this to conform with style guide.
using namespace webrtc; using namespace webrtc;
// TODO (Mikhal/Stefan): Update as gtest and separate to specific tests. // TODO (Mikhal/Stefan): Update as gtest and separate to specific tests.
@ -105,37 +106,6 @@ int JitterBufferTest(CmdArgs& args)
WebRtc_UWord8 data[1500]; WebRtc_UWord8 data[1500];
VCMPacket packet(data, size, seqNum, timeStamp, true); VCMPacket packet(data, size, seqNum, timeStamp, true);
VCMFrameListTimestampOrderAsc frameList;
VCMFrameBuffer* fb = NULL;
for (int i=0; i < 100; i++)
{
fb = new VCMFrameBuffer();
fb->SetState(kStateEmpty);
packet.timestamp = 0xfffffff0 + i;
packet.seqNum = seqNum;
packet.payloadType = 126;
seqNum++;
fb->InsertPacket(packet, clock.MillisecondTimestamp(), false, 0);
TEST(frameList.Insert(fb) == 0);
}
VCMFrameListItem* item = NULL;
WebRtc_UWord32 prevTimestamp = 0;
int i = 0;
for (i=0; !frameList.Empty(); i++)
{
item = frameList.First();
fb = static_cast<VCMFrameBuffer*>(item->GetItem());
TEST(i > 0 || fb->TimeStamp() == 0xfffffff0); // Frame 0 has no prev
TEST(prevTimestamp - fb->TimeStamp() == static_cast<WebRtc_UWord32>(-1)
|| i == 0);
prevTimestamp = fb->TimeStamp();
frameList.Erase(item);
delete fb;
}
TEST(i == 100);
//printf("DONE timestamp ordered frame list\n");
VCMJitterBuffer jb(&clock); VCMJitterBuffer jb(&clock);
seqNum = 1234; seqNum = 1234;