diff options
Diffstat (limited to 'webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc')
-rw-r--r-- | webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc new file mode 100644 index 0000000000..8b9b354a06 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2015 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 "webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h" + +#include <algorithm> + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +using webrtc::RTCPUtility::RtcpCommonHeader; + +namespace webrtc { +namespace rtcp { + +// RFC 4585: Feedback format. +// +// Common packet format: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| FMT | PT | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 0 | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 4 | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : Feedback Control Information (FCI) : +// : : +// +// Generic NACK (RFC 4585). +// +// FCI: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | PID | BLP | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +bool Nack::Parse(const RtcpCommonHeader& header, const uint8_t* payload) { + RTC_DCHECK(header.packet_type == kPacketType); + RTC_DCHECK(header.count_or_format == kFeedbackMessageType); + + if (header.payload_size_bytes < kCommonFeedbackLength + kNackItemLength) { + LOG(LS_WARNING) << "Payload length " << header.payload_size_bytes + << " is too small for a Nack."; + return false; + } + size_t nack_items = + (header.payload_size_bytes - kCommonFeedbackLength) / kNackItemLength; + + ParseCommonFeedback(payload); + const uint8_t* next_nack = payload + kCommonFeedbackLength; + + packet_ids_.clear(); + packed_.resize(nack_items); + for (size_t index = 0; index < nack_items; ++index) { + packed_[index].first_pid = ByteReader<uint16_t>::ReadBigEndian(next_nack); + packed_[index].bitmask = ByteReader<uint16_t>::ReadBigEndian(next_nack + 2); + next_nack += kNackItemLength; + } + Unpack(); + + return true; +} + +bool Nack::Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const { + RTC_DCHECK(!packed_.empty()); + // If nack list can't fit in packet, try to fragment. + size_t nack_index = 0; + const size_t kCommonFbFmtLength = kHeaderLength + kCommonFeedbackLength; + do { + size_t bytes_left_in_buffer = max_length - *index; + if (bytes_left_in_buffer < kCommonFbFmtLength + kNackItemLength) { + if (!OnBufferFull(packet, index, callback)) + return false; + continue; + } + size_t num_nack_fields = + std::min((bytes_left_in_buffer - kCommonFbFmtLength) / kNackItemLength, + packed_.size() - nack_index); + + size_t size_bytes = + (num_nack_fields * kNackItemLength) + kCommonFbFmtLength; + size_t header_length = ((size_bytes + 3) / 4) - 1; // As 32bit words - 1 + CreateHeader(kFeedbackMessageType, kPacketType, header_length, packet, + index); + CreateCommonFeedback(packet + *index); + *index += kCommonFeedbackLength; + size_t end_index = nack_index + num_nack_fields; + for (; nack_index < end_index; ++nack_index) { + const auto& item = packed_[nack_index]; + ByteWriter<uint16_t>::WriteBigEndian(packet + *index + 0, item.first_pid); + ByteWriter<uint16_t>::WriteBigEndian(packet + *index + 2, item.bitmask); + *index += kNackItemLength; + } + RTC_DCHECK_LE(*index, max_length); + } while (nack_index < packed_.size()); + + return true; +} + +size_t Nack::BlockLength() const { + return (packed_.size() * kNackItemLength) + kCommonFeedbackLength + + kHeaderLength; +} + +void Nack::WithList(const uint16_t* nack_list, size_t length) { + RTC_DCHECK(nack_list); + RTC_DCHECK(packet_ids_.empty()); + RTC_DCHECK(packed_.empty()); + packet_ids_.assign(nack_list, nack_list + length); + Pack(); +} + +void Nack::Pack() { + RTC_DCHECK(!packet_ids_.empty()); + RTC_DCHECK(packed_.empty()); + auto it = packet_ids_.begin(); + const auto end = packet_ids_.end(); + while (it != end) { + PackedNack item; + item.first_pid = *it++; + // Bitmask specifies losses in any of the 16 packets following the pid. + item.bitmask = 0; + while (it != end) { + uint16_t shift = static_cast<uint16_t>(*it - item.first_pid - 1); + if (shift <= 15) { + item.bitmask |= (1 << shift); + ++it; + } else { + break; + } + } + packed_.push_back(item); + } +} + +void Nack::Unpack() { + RTC_DCHECK(packet_ids_.empty()); + RTC_DCHECK(!packed_.empty()); + for (const PackedNack& item : packed_) { + packet_ids_.push_back(item.first_pid); + uint16_t pid = item.first_pid + 1; + for (uint16_t bitmask = item.bitmask; bitmask != 0; bitmask >>= 1, ++pid) + if (bitmask & 1) + packet_ids_.push_back(pid); + } +} + +} // namespace rtcp +} // namespace webrtc |