Trigger a PLI if the duration of non-decodable frames exceeds a threshold.
BUG=1663 R=mikhal@webrtc.org, ronghuawu@chromium.org Review URL: https://webrtc-codereview.appspot.com/1359004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@3975 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
@ -29,7 +29,7 @@
|
||||
namespace webrtc {
|
||||
|
||||
// Use this rtt if no value has been reported.
|
||||
static uint32_t kDefaultRtt = 200;
|
||||
static const uint32_t kDefaultRtt = 200;
|
||||
|
||||
// Predicates used when searching for frames in the frame buffer list
|
||||
class FrameSmallerTimestamp {
|
||||
@ -54,6 +54,13 @@ class FrameEqualTimestamp {
|
||||
uint32_t timestamp_;
|
||||
};
|
||||
|
||||
class KeyFrameCriteria {
|
||||
public:
|
||||
bool operator()(VCMFrameBuffer* frame) {
|
||||
return frame->FrameType() == kVideoFrameKey;
|
||||
}
|
||||
};
|
||||
|
||||
class CompleteKeyFrameCriteria {
|
||||
public:
|
||||
bool operator()(VCMFrameBuffer* frame) {
|
||||
@ -105,6 +112,7 @@ VCMJitterBuffer::VCMJitterBuffer(Clock* clock,
|
||||
nack_seq_nums_(),
|
||||
max_nack_list_size_(0),
|
||||
max_packet_age_to_nack_(0),
|
||||
max_incomplete_time_ms_(0),
|
||||
decode_with_errors_(false) {
|
||||
memset(frame_buffers_, 0, sizeof(frame_buffers_));
|
||||
memset(receive_statistics_, 0, sizeof(receive_statistics_));
|
||||
@ -152,6 +160,7 @@ void VCMJitterBuffer::CopyFrom(const VCMJitterBuffer& rhs) {
|
||||
decode_with_errors_ = rhs.decode_with_errors_;
|
||||
assert(max_nack_list_size_ == rhs.max_nack_list_size_);
|
||||
assert(max_packet_age_to_nack_ == rhs.max_packet_age_to_nack_);
|
||||
assert(max_incomplete_time_ms_ == rhs.max_incomplete_time_ms_);
|
||||
memcpy(receive_statistics_, rhs.receive_statistics_,
|
||||
sizeof(receive_statistics_));
|
||||
nack_seq_nums_.resize(rhs.nack_seq_nums_.size());
|
||||
@ -391,7 +400,8 @@ bool VCMJitterBuffer::NextCompleteTimestamp(
|
||||
}
|
||||
CleanUpOldOrEmptyFrames();
|
||||
|
||||
FrameList::iterator it = FindOldestCompleteContinuousFrame();
|
||||
FrameList::iterator it = FindOldestCompleteContinuousFrame(
|
||||
frame_list_.begin(), &last_decoded_state_);
|
||||
if (it == frame_list_.end()) {
|
||||
const int64_t end_wait_time_ms = clock_->TimeInMilliseconds() +
|
||||
max_wait_time_ms;
|
||||
@ -410,7 +420,8 @@ bool VCMJitterBuffer::NextCompleteTimestamp(
|
||||
// Finding oldest frame ready for decoder, but check
|
||||
// sequence number and size
|
||||
CleanUpOldOrEmptyFrames();
|
||||
it = FindOldestCompleteContinuousFrame();
|
||||
it = FindOldestCompleteContinuousFrame(
|
||||
frame_list_.begin(), &last_decoded_state_);
|
||||
if (it == frame_list_.end()) {
|
||||
wait_time_ms = end_wait_time_ms - clock_->TimeInMilliseconds();
|
||||
} else {
|
||||
@ -800,14 +811,14 @@ void VCMJitterBuffer::SetNackMode(VCMNackMode mode,
|
||||
}
|
||||
|
||||
void VCMJitterBuffer::SetNackSettings(size_t max_nack_list_size,
|
||||
int max_packet_age_to_nack) {
|
||||
int max_packet_age_to_nack,
|
||||
int max_incomplete_time_ms) {
|
||||
CriticalSectionScoped cs(crit_sect_);
|
||||
assert(max_packet_age_to_nack >= 0);
|
||||
if (max_packet_age_to_nack <= 0) {
|
||||
return;
|
||||
}
|
||||
assert(max_incomplete_time_ms_ >= 0);
|
||||
max_nack_list_size_ = max_nack_list_size;
|
||||
max_packet_age_to_nack_ = max_packet_age_to_nack;
|
||||
max_incomplete_time_ms_ = max_incomplete_time_ms;
|
||||
nack_seq_nums_.resize(max_nack_list_size_);
|
||||
}
|
||||
|
||||
@ -816,6 +827,27 @@ VCMNackMode VCMJitterBuffer::nack_mode() const {
|
||||
return nack_mode_;
|
||||
}
|
||||
|
||||
int VCMJitterBuffer::NonContinuousOrIncompleteDuration() {
|
||||
if (frame_list_.empty()) {
|
||||
return 0;
|
||||
}
|
||||
FrameList::iterator start_it;
|
||||
FrameList::iterator end_it;
|
||||
RenderBuffer(&start_it, &end_it);
|
||||
if (end_it == frame_list_.end())
|
||||
end_it = frame_list_.begin();
|
||||
return frame_list_.back()->TimeStamp() -
|
||||
(*end_it)->TimeStamp();
|
||||
}
|
||||
|
||||
uint16_t VCMJitterBuffer::EstimatedLowSequenceNumber(
|
||||
const VCMFrameBuffer& frame) const {
|
||||
assert(frame.GetLowSeqNum() >= 0);
|
||||
if (frame.HaveFirstPacket())
|
||||
return frame.GetLowSeqNum();
|
||||
return frame.GetLowSeqNum() - 1;
|
||||
}
|
||||
|
||||
uint16_t* VCMJitterBuffer::GetNackList(uint16_t* nack_list_size,
|
||||
bool* request_key_frame) {
|
||||
CriticalSectionScoped cs(crit_sect_);
|
||||
@ -844,6 +876,28 @@ uint16_t* VCMJitterBuffer::GetNackList(uint16_t* nack_list_size,
|
||||
"size", missing_sequence_numbers_.size());
|
||||
*request_key_frame = !HandleTooLargeNackList();
|
||||
}
|
||||
if (max_incomplete_time_ms_ > 0) {
|
||||
int non_continuous_incomplete_duration =
|
||||
NonContinuousOrIncompleteDuration();
|
||||
if (non_continuous_incomplete_duration > 90 * max_incomplete_time_ms_) {
|
||||
TRACE_EVENT_INSTANT1("webrtc", "JB::NonContinuousOrIncompleteDuration",
|
||||
"duration", non_continuous_incomplete_duration);
|
||||
FrameList::reverse_iterator rit = find_if(frame_list_.rbegin(),
|
||||
frame_list_.rend(),
|
||||
KeyFrameCriteria());
|
||||
if (rit == frame_list_.rend()) {
|
||||
// Request a key frame if we don't have one already.
|
||||
*request_key_frame = true;
|
||||
*nack_list_size = 0;
|
||||
return NULL;
|
||||
} else {
|
||||
// Skip to the last key frame. If it's incomplete we will start
|
||||
// NACKing it.
|
||||
last_decoded_state_.Reset();
|
||||
DropPacketsFromNackList(EstimatedLowSequenceNumber(**rit));
|
||||
}
|
||||
}
|
||||
}
|
||||
unsigned int i = 0;
|
||||
SequenceNumberSet::iterator it = missing_sequence_numbers_.begin();
|
||||
for (; it != missing_sequence_numbers_.end(); ++it, ++i) {
|
||||
@ -939,47 +993,78 @@ int64_t VCMJitterBuffer::LastDecodedTimestamp() const {
|
||||
return last_decoded_state_.time_stamp();
|
||||
}
|
||||
|
||||
void VCMJitterBuffer::RenderBufferSize(
|
||||
uint32_t* timestamp_start, uint32_t* timestamp_end) {
|
||||
CriticalSectionScoped cs(crit_sect_);
|
||||
CleanUpOldOrEmptyFrames();
|
||||
*timestamp_start = 0u;
|
||||
*timestamp_end = 0u;
|
||||
|
||||
if (frame_list_.empty()) {
|
||||
return;
|
||||
}
|
||||
FrameList::iterator frame_it = frame_list_.begin();
|
||||
VCMFrameBuffer* current_frame = *frame_it;
|
||||
FrameList::iterator VCMJitterBuffer::FindLastContinuousAndComplete(
|
||||
FrameList::iterator start_it) {
|
||||
// Search for a complete and continuous sequence (starting from the last
|
||||
// decoded state or current frame if in initial state).
|
||||
VCMDecodingState previous_state;
|
||||
if (last_decoded_state_.in_initial_state()) {
|
||||
// Start with a key frame.
|
||||
frame_it = find_if(frame_list_.begin(), frame_list_.end(),
|
||||
CompleteKeyFrameCriteria());
|
||||
if (frame_it == frame_list_.end()) {
|
||||
return;
|
||||
}
|
||||
*timestamp_start = last_decoded_state_.time_stamp();
|
||||
current_frame = *frame_it;
|
||||
previous_state.SetState(current_frame);
|
||||
++frame_it;
|
||||
previous_state.SetState(*start_it);
|
||||
} else {
|
||||
previous_state.CopyFrom(last_decoded_state_);
|
||||
}
|
||||
bool continuous_complete = true;
|
||||
while (frame_it != frame_list_.end() && continuous_complete) {
|
||||
current_frame = *frame_it;
|
||||
continuous_complete = current_frame->IsSessionComplete() &&
|
||||
previous_state.ContinuousFrame(current_frame);
|
||||
previous_state.SetState(current_frame);
|
||||
++frame_it;
|
||||
FrameList::iterator previous_it = start_it;
|
||||
++start_it;
|
||||
while (start_it != frame_list_.end() && continuous_complete) {
|
||||
start_it = FindOldestCompleteContinuousFrame(start_it, &previous_state);
|
||||
if (start_it == frame_list_.end())
|
||||
break;
|
||||
previous_state.SetState(*start_it);
|
||||
previous_it = start_it;
|
||||
++start_it;
|
||||
}
|
||||
// Desired frame is the previous one.
|
||||
--frame_it;
|
||||
current_frame = *frame_it;
|
||||
*timestamp_end = current_frame->TimeStamp();
|
||||
return previous_it;
|
||||
}
|
||||
|
||||
void VCMJitterBuffer::RenderBuffer(FrameList::iterator* start_it,
|
||||
FrameList::iterator* end_it) {
|
||||
*start_it = FindOldestCompleteContinuousFrame(
|
||||
frame_list_.begin(), &last_decoded_state_);
|
||||
if (!decode_with_errors_ && *start_it == frame_list_.end()) {
|
||||
// No complete continuous frame found.
|
||||
// Look for a complete key frame if we're not decoding with errors.
|
||||
*start_it = find_if(frame_list_.begin(), frame_list_.end(),
|
||||
CompleteKeyFrameCriteria());
|
||||
}
|
||||
if (*start_it == frame_list_.end()) {
|
||||
*end_it = *start_it;
|
||||
} else {
|
||||
*end_it = *start_it;
|
||||
// Look for the last complete key frame and use that as the end of the
|
||||
// render buffer it's later than the last complete continuous frame.
|
||||
FrameList::reverse_iterator rend(*end_it);
|
||||
FrameList::reverse_iterator rit = find_if(frame_list_.rbegin(),
|
||||
rend,
|
||||
CompleteKeyFrameCriteria());
|
||||
if (rit != rend) {
|
||||
// A key frame was found. The reverse iterator base points to the
|
||||
// frame after it, so subtracting 1.
|
||||
*end_it = rit.base();
|
||||
--*end_it;
|
||||
}
|
||||
*end_it = FindLastContinuousAndComplete(*end_it);
|
||||
}
|
||||
}
|
||||
|
||||
void VCMJitterBuffer::RenderBufferSize(uint32_t* timestamp_start,
|
||||
uint32_t* timestamp_end) {
|
||||
CriticalSectionScoped cs(crit_sect_);
|
||||
CleanUpOldOrEmptyFrames();
|
||||
*timestamp_start = 0;
|
||||
*timestamp_end = 0;
|
||||
if (frame_list_.empty()) {
|
||||
return;
|
||||
}
|
||||
FrameList::iterator start_it;
|
||||
FrameList::iterator end_it;
|
||||
RenderBuffer(&start_it, &end_it);
|
||||
if (start_it == frame_list_.end()) {
|
||||
return;
|
||||
}
|
||||
*timestamp_start = (*start_it)->TimeStamp();
|
||||
*timestamp_end = (*end_it)->TimeStamp();
|
||||
}
|
||||
|
||||
// Set the frame state to free and remove it from the sorted
|
||||
@ -1031,7 +1116,7 @@ VCMFrameBuffer* VCMJitterBuffer::GetEmptyFrame() {
|
||||
// full.
|
||||
bool VCMJitterBuffer::RecycleFramesUntilKeyFrame() {
|
||||
// Remove up to oldest key frame
|
||||
while (frame_list_.size() > 0) {
|
||||
while (!frame_list_.empty()) {
|
||||
// Throw at least one frame.
|
||||
drop_count_++;
|
||||
FrameList::iterator it = frame_list_.begin();
|
||||
@ -1043,9 +1128,10 @@ bool VCMJitterBuffer::RecycleFramesUntilKeyFrame() {
|
||||
ReleaseFrameIfNotDecoding(*it);
|
||||
it = frame_list_.erase(it);
|
||||
if (it != frame_list_.end() && (*it)->FrameType() == kVideoFrameKey) {
|
||||
// Fake the last_decoded_state to match this key frame.
|
||||
last_decoded_state_.SetStateOneBack(*it);
|
||||
DropPacketsFromNackList(last_decoded_state_.sequence_num());
|
||||
// Reset last decoded state to make sure the next frame decoded is a key
|
||||
// frame, and start NACKing from here.
|
||||
last_decoded_state_.Reset();
|
||||
DropPacketsFromNackList(EstimatedLowSequenceNumber(**it));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@ -1147,7 +1233,8 @@ VCMFrameBufferEnum VCMJitterBuffer::UpdateFrameState(VCMFrameBuffer* frame) {
|
||||
assert(false);
|
||||
}
|
||||
}
|
||||
const FrameList::iterator it = FindOldestCompleteContinuousFrame();
|
||||
const FrameList::iterator it = FindOldestCompleteContinuousFrame(
|
||||
frame_list_.begin(), &last_decoded_state_);
|
||||
VCMFrameBuffer* old_frame = NULL;
|
||||
if (it != frame_list_.end()) {
|
||||
old_frame = *it;
|
||||
@ -1163,22 +1250,23 @@ VCMFrameBufferEnum VCMJitterBuffer::UpdateFrameState(VCMFrameBuffer* frame) {
|
||||
|
||||
// Find oldest complete frame used for getting next frame to decode
|
||||
// Must be called under critical section
|
||||
FrameList::iterator VCMJitterBuffer::FindOldestCompleteContinuousFrame() {
|
||||
FrameList::iterator VCMJitterBuffer::FindOldestCompleteContinuousFrame(
|
||||
FrameList::iterator start_it,
|
||||
const VCMDecodingState* decoding_state) {
|
||||
// If we have more than one frame done since last time, pick oldest.
|
||||
VCMFrameBuffer* oldest_frame = NULL;
|
||||
FrameList::iterator it = frame_list_.begin();
|
||||
|
||||
// When temporal layers are available, we search for a complete or decodable
|
||||
// frame until we hit one of the following:
|
||||
// 1. Continuous base or sync layer.
|
||||
// 2. The end of the list was reached.
|
||||
for (; it != frame_list_.end(); ++it) {
|
||||
oldest_frame = *it;
|
||||
for (; start_it != frame_list_.end(); ++start_it) {
|
||||
oldest_frame = *start_it;
|
||||
VCMFrameBufferStateEnum state = oldest_frame->GetState();
|
||||
// Is this frame complete or decodable and continuous?
|
||||
if ((state == kStateComplete ||
|
||||
(decode_with_errors_ && state == kStateDecodable)) &&
|
||||
last_decoded_state_.ContinuousFrame(oldest_frame)) {
|
||||
decoding_state->ContinuousFrame(oldest_frame)) {
|
||||
break;
|
||||
} else {
|
||||
int temporal_id = oldest_frame->TemporalId();
|
||||
@ -1197,7 +1285,7 @@ FrameList::iterator VCMJitterBuffer::FindOldestCompleteContinuousFrame() {
|
||||
}
|
||||
|
||||
// We have a complete continuous frame.
|
||||
return it;
|
||||
return start_it;
|
||||
}
|
||||
|
||||
// Must be called under the critical section |crit_sect_|.
|
||||
|
Reference in New Issue
Block a user