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