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:
@ -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);
|
||||
|
||||
Reference in New Issue
Block a user