Refactor NACK list creation to build the NACK list as packets arrive.

Also fixes a timer bug related to NACKing in the RTP module which could cause packets to only be NACKed twice if there's frequent packet losses.

Note that I decided to remove any selective NACKing for now as I don't think the gain of doing it is big enough compared to the added complexity. The same reasoning for empty packets. None of them will be retransmitted by a smart sender since the sender would know that they aren't needed.

BUG=1420
TEST=video_coding_unittests, vie_auto_test, video_coding_integrationtests, trybots

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@3599 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
stefan@webrtc.org
2013-03-04 15:24:40 +00:00
parent 17b867ae00
commit a64300af50
22 changed files with 451 additions and 818 deletions

View File

@ -352,186 +352,6 @@ int VCMSessionInfo::MakeDecodable() {
return return_length;
}
int VCMSessionInfo::BuildHardNackList(int* seq_num_list,
int seq_num_list_length,
int nack_seq_nums_index) {
assert(seq_num_list && seq_num_list_length > 0);
if (packets_.empty() && empty_seq_num_low_ == -1) {
return nack_seq_nums_index;
}
// Find end point (index of entry equals the sequence number of the first
// packet).
int low_seq_num = (packets_.empty()) ? empty_seq_num_low_:
packets_.front().seqNum;
for (; nack_seq_nums_index < seq_num_list_length; ++nack_seq_nums_index) {
if (seq_num_list[nack_seq_nums_index] == low_seq_num) {
seq_num_list[nack_seq_nums_index] = -1;
++nack_seq_nums_index;
break;
}
}
if (!packets_.empty()) {
// Zero out between the first entry and the end point.
PacketIterator it = packets_.begin();
PacketIterator prev_it = it;
++it;
while (it != packets_.end() && nack_seq_nums_index < seq_num_list_length) {
if (!InSequence(it, prev_it)) {
// Found a sequence number gap due to packet loss.
nack_seq_nums_index += PacketsMissing(it, prev_it);
session_nack_ = true;
}
seq_num_list[nack_seq_nums_index] = -1;
++nack_seq_nums_index;
prev_it = it;
++it;
}
if (!packets_.front().isFirstPacket)
session_nack_ = true;
}
nack_seq_nums_index = ClearOutEmptyPacketSequenceNumbers(seq_num_list,
seq_num_list_length,
nack_seq_nums_index);
return nack_seq_nums_index;
}
int VCMSessionInfo::BuildSoftNackList(int* seq_num_list,
int seq_num_list_length,
int nack_seq_nums_index,
int rtt_ms) {
assert(seq_num_list && seq_num_list_length > 0);
if (packets_.empty() && empty_seq_num_low_ == -1) {
return nack_seq_nums_index;
}
int low_seq_num = (packets_.empty()) ? empty_seq_num_low_:
packets_.front().seqNum;
// Find entrance point (nack_seq_nums_index of entry equals the sequence
// number of the first packet).
for (; nack_seq_nums_index < seq_num_list_length; ++nack_seq_nums_index) {
if (seq_num_list[nack_seq_nums_index] == low_seq_num) {
seq_num_list[nack_seq_nums_index] = -1;
break;
}
}
// TODO(mikhal): 1. Update score based on RTT value 2. Add partition data.
// Use the previous available.
bool base_available = false;
if ((nack_seq_nums_index > 0) && (seq_num_list[nack_seq_nums_index] == -1)) {
// Found first packet, for now let's go only one back.
if ((seq_num_list[nack_seq_nums_index - 1] == -1) ||
(seq_num_list[nack_seq_nums_index - 1] == -2)) {
// This is indeed the first packet, as previous packet was populated.
base_available = true;
}
}
bool allow_nack = ((packets_.size() > 0 && !packets_.front().isFirstPacket)
|| !base_available);
// Zero out between first entry and end point.
int media_high_seq_num;
if (HaveLastPacket()) {
media_high_seq_num = packets_.back().seqNum;
} else {
// Estimation.
if (empty_seq_num_low_ >= 0) {
// Assuming empty packets have later sequence numbers than media packets.
media_high_seq_num = empty_seq_num_low_ - 1;
} else {
// Since this frame doesn't have the marker bit we can assume it should
// contain at least one more packet.
media_high_seq_num = static_cast<uint16_t>(packets_.back().seqNum + 1);
}
}
// Compute session/packet scores and thresholds:
// based on RTT and layer info (when available).
float nack_score_threshold = 0.25f;
float layer_score = TemporalId() > 0 ? 0.0f : 1.0f;
float rtt_score = 1.0f;
float score_multiplier = rtt_score * layer_score;
// Zero out between first entry and end point.
if (!packets_.empty()) {
PacketIterator it = packets_.begin();
PacketIterator prev_it = it;
++nack_seq_nums_index;
++it;
// TODO(holmer): Rewrite this in a way which better makes use of the list.
while (it != packets_.end() && nack_seq_nums_index < seq_num_list_length) {
// Only process media packet sequence numbers.
if (LatestSequenceNumber((*it).seqNum, media_high_seq_num, NULL) ==
(*it).seqNum && (*it).seqNum != media_high_seq_num)
break;
if (!InSequence(it, prev_it)) {
// Found a sequence number gap due to packet loss.
int num_lost = PacketsMissing(it, prev_it);
for (int i = 0 ; i < num_lost; ++i) {
// Compute score of the packet.
float score = 1.0f;
// Multiply internal score (packet) by score multiplier.
score *= score_multiplier;
if (score > nack_score_threshold) {
allow_nack = true;
} else {
seq_num_list[nack_seq_nums_index] = -1;
}
++nack_seq_nums_index;
}
}
seq_num_list[nack_seq_nums_index] = -1;
++nack_seq_nums_index;
prev_it = it;
++it;
}
}
nack_seq_nums_index = ClearOutEmptyPacketSequenceNumbers(seq_num_list,
seq_num_list_length,
nack_seq_nums_index);
session_nack_ = allow_nack;
return nack_seq_nums_index;
}
int VCMSessionInfo::ClearOutEmptyPacketSequenceNumbers(
int* seq_num_list,
int seq_num_list_length,
int index) const {
// Empty packets follow the data packets, and therefore have a higher
// sequence number. We do not want to NACK empty packets.
if (empty_seq_num_low_ != -1 && empty_seq_num_high_ != -1) {
// First make sure that we are at least at the minimum value (if not we are
// missing last packet(s)).
while (index < seq_num_list_length &&
seq_num_list[index] < empty_seq_num_low_) {
++index;
}
// Mark empty packets.
while (index < seq_num_list_length &&
seq_num_list[index] >= 0 &&
seq_num_list[index] <= empty_seq_num_high_) {
seq_num_list[index] = -2;
++index;
}
}
return index;
}
int VCMSessionInfo::PacketsMissing(const PacketIterator& packet_it,
const PacketIterator& prev_packet_it) {
if (packet_it == prev_packet_it)
return 0;
return static_cast<uint16_t>((*packet_it).seqNum -
(*prev_packet_it).seqNum - 1);
}
bool
VCMSessionInfo::HaveLastPacket() const {
return (!packets_.empty() && packets_.back().markerBit);