1/*
2 *  Copyright (c) 2015 The WebRTC project authors. All Rights Reserved.
3 *
4 *  Use of this source code is governed by a BSD-style license
5 *  that can be found in the LICENSE file in the root of the source
6 *  tree. An additional intellectual property rights grant can be found
7 *  in the file PATENTS.  All contributing project authors may
8 *  be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h"
12
13#include <algorithm>
14
15#include "webrtc/base/checks.h"
16#include "webrtc/base/logging.h"
17#include "webrtc/modules/rtp_rtcp/source/byte_io.h"
18
19using webrtc::RTCPUtility::RtcpCommonHeader;
20
21namespace webrtc {
22namespace rtcp {
23
24// RFC 4585: Feedback format.
25//
26// Common packet format:
27//
28//    0                   1                   2                   3
29//    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
30//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
31//   |V=2|P|   FMT   |       PT      |          length               |
32//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
33// 0 |                  SSRC of packet sender                        |
34//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
35// 4 |                  SSRC of media source                         |
36//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
37//   :            Feedback Control Information (FCI)                 :
38//   :                                                               :
39//
40// Generic NACK (RFC 4585).
41//
42// FCI:
43//    0                   1                   2                   3
44//    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
45//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
46//   |            PID                |             BLP               |
47//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
48bool Nack::Parse(const RtcpCommonHeader& header, const uint8_t* payload) {
49  RTC_DCHECK(header.packet_type == kPacketType);
50  RTC_DCHECK(header.count_or_format == kFeedbackMessageType);
51
52  if (header.payload_size_bytes < kCommonFeedbackLength + kNackItemLength) {
53    LOG(LS_WARNING) << "Payload length " << header.payload_size_bytes
54                    << " is too small for a Nack.";
55    return false;
56  }
57  size_t nack_items =
58      (header.payload_size_bytes - kCommonFeedbackLength) / kNackItemLength;
59
60  ParseCommonFeedback(payload);
61  const uint8_t* next_nack = payload + kCommonFeedbackLength;
62
63  packet_ids_.clear();
64  packed_.resize(nack_items);
65  for (size_t index = 0; index < nack_items; ++index) {
66    packed_[index].first_pid = ByteReader<uint16_t>::ReadBigEndian(next_nack);
67    packed_[index].bitmask = ByteReader<uint16_t>::ReadBigEndian(next_nack + 2);
68    next_nack += kNackItemLength;
69  }
70  Unpack();
71
72  return true;
73}
74
75bool Nack::Create(uint8_t* packet,
76                  size_t* index,
77                  size_t max_length,
78                  RtcpPacket::PacketReadyCallback* callback) const {
79  RTC_DCHECK(!packed_.empty());
80  // If nack list can't fit in packet, try to fragment.
81  size_t nack_index = 0;
82  const size_t kCommonFbFmtLength = kHeaderLength + kCommonFeedbackLength;
83  do {
84    size_t bytes_left_in_buffer = max_length - *index;
85    if (bytes_left_in_buffer < kCommonFbFmtLength + kNackItemLength) {
86      if (!OnBufferFull(packet, index, callback))
87        return false;
88      continue;
89    }
90    size_t num_nack_fields =
91        std::min((bytes_left_in_buffer - kCommonFbFmtLength) / kNackItemLength,
92                 packed_.size() - nack_index);
93
94    size_t size_bytes =
95        (num_nack_fields * kNackItemLength) + kCommonFbFmtLength;
96    size_t header_length = ((size_bytes + 3) / 4) - 1;  // As 32bit words - 1
97    CreateHeader(kFeedbackMessageType, kPacketType, header_length, packet,
98                 index);
99    CreateCommonFeedback(packet + *index);
100    *index += kCommonFeedbackLength;
101    size_t end_index = nack_index + num_nack_fields;
102    for (; nack_index < end_index; ++nack_index) {
103      const auto& item = packed_[nack_index];
104      ByteWriter<uint16_t>::WriteBigEndian(packet + *index + 0, item.first_pid);
105      ByteWriter<uint16_t>::WriteBigEndian(packet + *index + 2, item.bitmask);
106      *index += kNackItemLength;
107    }
108    RTC_DCHECK_LE(*index, max_length);
109  } while (nack_index < packed_.size());
110
111  return true;
112}
113
114size_t Nack::BlockLength() const {
115  return (packed_.size() * kNackItemLength) + kCommonFeedbackLength +
116         kHeaderLength;
117}
118
119void Nack::WithList(const uint16_t* nack_list, size_t length) {
120  RTC_DCHECK(nack_list);
121  RTC_DCHECK(packet_ids_.empty());
122  RTC_DCHECK(packed_.empty());
123  packet_ids_.assign(nack_list, nack_list + length);
124  Pack();
125}
126
127void Nack::Pack() {
128  RTC_DCHECK(!packet_ids_.empty());
129  RTC_DCHECK(packed_.empty());
130  auto it = packet_ids_.begin();
131  const auto end = packet_ids_.end();
132  while (it != end) {
133    PackedNack item;
134    item.first_pid = *it++;
135    // Bitmask specifies losses in any of the 16 packets following the pid.
136    item.bitmask = 0;
137    while (it != end) {
138      uint16_t shift = static_cast<uint16_t>(*it - item.first_pid - 1);
139      if (shift <= 15) {
140        item.bitmask |= (1 << shift);
141        ++it;
142      } else {
143        break;
144      }
145    }
146    packed_.push_back(item);
147  }
148}
149
150void Nack::Unpack() {
151  RTC_DCHECK(packet_ids_.empty());
152  RTC_DCHECK(!packed_.empty());
153  for (const PackedNack& item : packed_) {
154    packet_ids_.push_back(item.first_pid);
155    uint16_t pid = item.first_pid + 1;
156    for (uint16_t bitmask = item.bitmask; bitmask != 0; bitmask >>= 1, ++pid)
157      if (bitmask & 1)
158        packet_ids_.push_back(pid);
159  }
160}
161
162}  // namespace rtcp
163}  // namespace webrtc
164