1 // Copyright 2019 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "cast/streaming/frame_collector.h"
6 
7 #include <algorithm>
8 #include <limits>
9 #include <numeric>
10 
11 #include "cast/streaming/frame_id.h"
12 #include "cast/streaming/rtp_defines.h"
13 #include "util/osp_logging.h"
14 
15 namespace openscreen {
16 namespace cast {
17 
18 namespace {
19 
20 // Integer constant representing that the number of packets is not yet known.
21 constexpr int kUnknownNumberOfPackets = std::numeric_limits<int>::max();
22 
23 }  // namespace
24 
FrameCollector()25 FrameCollector::FrameCollector()
26     : num_missing_packets_(kUnknownNumberOfPackets) {}
27 
28 FrameCollector::~FrameCollector() = default;
29 
CollectRtpPacket(const RtpPacketParser::ParseResult & part,std::vector<uint8_t> * buffer)30 bool FrameCollector::CollectRtpPacket(const RtpPacketParser::ParseResult& part,
31                                       std::vector<uint8_t>* buffer) {
32   OSP_DCHECK(!frame_.frame_id.is_null());
33 
34   if (part.frame_id != frame_.frame_id) {
35     OSP_LOG_WARN
36         << "Ignoring potentially corrupt packet (frame ID mismatch). Expected: "
37         << frame_.frame_id << " Got: " << part.frame_id;
38     return false;
39   }
40 
41   const int frame_packet_count = static_cast<int>(part.max_packet_id) + 1;
42   if (num_missing_packets_ == kUnknownNumberOfPackets) {
43     // This is the first packet being processed for the frame.
44     num_missing_packets_ = frame_packet_count;
45     chunks_.resize(num_missing_packets_);
46   } else {
47     // Since this is not the first packet being processed, sanity-check that the
48     // "frame ID" and "max packet ID" are the expected values.
49     if (frame_packet_count != static_cast<int>(chunks_.size())) {
50       OSP_LOG_WARN << "Ignoring potentially corrupt packet (packet count "
51                       "mismatch). packet_count="
52                    << chunks_.size() << " is not equal to 1 + max_packet_id="
53                    << part.max_packet_id;
54       return false;
55     }
56   }
57 
58   // The packet ID must not be greater than the max packet ID.
59   if (part.packet_id >= chunks_.size()) {
60     OSP_LOG_WARN
61         << "Ignoring potentially corrupt packet having invalid packet ID "
62         << part.packet_id << " (should be less than " << chunks_.size() << ").";
63     return false;
64   }
65 
66   // Don't process duplicate packets.
67   if (chunks_[part.packet_id].has_data()) {
68     // Note: No logging here because this is a common occurrence that is not
69     // indicative of any problem in the system.
70     return true;
71   }
72 
73   // Populate metadata from packet 0 only, which is the only packet that must
74   // contain a complete set of values.
75   if (part.packet_id == FramePacketId{0}) {
76     if (part.is_key_frame) {
77       frame_.dependency = EncodedFrame::KEY_FRAME;
78     } else if (part.frame_id == part.referenced_frame_id) {
79       frame_.dependency = EncodedFrame::INDEPENDENTLY_DECODABLE;
80     } else {
81       frame_.dependency = EncodedFrame::DEPENDS_ON_ANOTHER;
82     }
83     frame_.referenced_frame_id = part.referenced_frame_id;
84     frame_.rtp_timestamp = part.rtp_timestamp;
85     frame_.new_playout_delay = part.new_playout_delay;
86   }
87 
88   // Take ownership of the contents of the |buffer| (no copy!), and record the
89   // region of the buffer containing the payload data. The payload region is
90   // usually all but the first few dozen bytes of the buffer.
91   PayloadChunk& chunk = chunks_[part.packet_id];
92   chunk.buffer.swap(*buffer);
93   chunk.payload = part.payload;
94   OSP_DCHECK_GE(chunk.payload.data(), chunk.buffer.data());
95   OSP_DCHECK_LE(chunk.payload.data() + chunk.payload.size(),
96                 chunk.buffer.data() + chunk.buffer.size());
97 
98   // Success!
99   --num_missing_packets_;
100   OSP_DCHECK_GE(num_missing_packets_, 0);
101   return true;
102 }
103 
GetMissingPackets(std::vector<PacketNack> * nacks) const104 void FrameCollector::GetMissingPackets(std::vector<PacketNack>* nacks) const {
105   OSP_DCHECK(!frame_.frame_id.is_null());
106 
107   if (num_missing_packets_ == 0) {
108     return;
109   }
110 
111   const int frame_packet_count = chunks_.size();
112   if (num_missing_packets_ >= frame_packet_count) {
113     nacks->push_back(PacketNack{frame_.frame_id, kAllPacketsLost});
114     return;
115   }
116 
117   for (int packet_id = 0; packet_id < frame_packet_count; ++packet_id) {
118     if (!chunks_[packet_id].has_data()) {
119       nacks->push_back(
120           PacketNack{frame_.frame_id, static_cast<FramePacketId>(packet_id)});
121     }
122   }
123 }
124 
PeekAtAssembledFrame()125 const EncryptedFrame& FrameCollector::PeekAtAssembledFrame() {
126   OSP_DCHECK_EQ(num_missing_packets_, 0);
127 
128   if (!frame_.data.data()) {
129     // Allocate the frame's payload buffer once, right-sized to the sum of all
130     // chunk sizes.
131     frame_.owned_data_.reserve(
132         std::accumulate(chunks_.cbegin(), chunks_.cend(), size_t{0},
133                         [](size_t num_bytes_so_far, const PayloadChunk& chunk) {
134                           return num_bytes_so_far + chunk.payload.size();
135                         }));
136     // Now, populate the frame's payload buffer with each chunk of data.
137     for (const PayloadChunk& chunk : chunks_) {
138       frame_.owned_data_.insert(frame_.owned_data_.end(), chunk.payload.begin(),
139                                 chunk.payload.end());
140     }
141     frame_.data = absl::Span<uint8_t>(frame_.owned_data_);
142   }
143 
144   return frame_;
145 }
146 
Reset()147 void FrameCollector::Reset() {
148   num_missing_packets_ = kUnknownNumberOfPackets;
149   frame_.frame_id = FrameId();
150   frame_.owned_data_.clear();
151   frame_.owned_data_.shrink_to_fit();
152   frame_.data = absl::Span<uint8_t>();
153   chunks_.clear();
154 }
155 
156 FrameCollector::PayloadChunk::PayloadChunk() = default;
157 FrameCollector::PayloadChunk::~PayloadChunk() = default;
158 
159 }  // namespace cast
160 }  // namespace openscreen
161