You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
161 lines
5.4 KiB
161 lines
5.4 KiB
// Copyright 2019 The Chromium Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style license that can be
|
|
// found in the LICENSE file.
|
|
|
|
#include "cast/streaming/frame_collector.h"
|
|
|
|
#include <algorithm>
|
|
#include <limits>
|
|
#include <numeric>
|
|
|
|
#include "cast/streaming/frame_id.h"
|
|
#include "cast/streaming/rtp_defines.h"
|
|
#include "util/osp_logging.h"
|
|
|
|
namespace openscreen {
|
|
namespace cast {
|
|
|
|
namespace {
|
|
|
|
// Integer constant representing that the number of packets is not yet known.
|
|
constexpr int kUnknownNumberOfPackets = std::numeric_limits<int>::max();
|
|
|
|
} // namespace
|
|
|
|
FrameCollector::FrameCollector()
|
|
: num_missing_packets_(kUnknownNumberOfPackets) {}
|
|
|
|
FrameCollector::~FrameCollector() = default;
|
|
|
|
bool FrameCollector::CollectRtpPacket(const RtpPacketParser::ParseResult& part,
|
|
std::vector<uint8_t>* buffer) {
|
|
OSP_DCHECK(!frame_.frame_id.is_null());
|
|
|
|
if (part.frame_id != frame_.frame_id) {
|
|
OSP_LOG_WARN
|
|
<< "Ignoring potentially corrupt packet (frame ID mismatch). Expected: "
|
|
<< frame_.frame_id << " Got: " << part.frame_id;
|
|
return false;
|
|
}
|
|
|
|
const int frame_packet_count = static_cast<int>(part.max_packet_id) + 1;
|
|
if (num_missing_packets_ == kUnknownNumberOfPackets) {
|
|
// This is the first packet being processed for the frame.
|
|
num_missing_packets_ = frame_packet_count;
|
|
chunks_.resize(num_missing_packets_);
|
|
} else {
|
|
// Since this is not the first packet being processed, sanity-check that the
|
|
// "frame ID" and "max packet ID" are the expected values.
|
|
if (frame_packet_count != static_cast<int>(chunks_.size())) {
|
|
OSP_LOG_WARN << "Ignoring potentially corrupt packet (packet count "
|
|
"mismatch). packet_count="
|
|
<< chunks_.size() << " is not equal to 1 + max_packet_id="
|
|
<< part.max_packet_id;
|
|
return false;
|
|
}
|
|
}
|
|
|
|
// The packet ID must not be greater than the max packet ID.
|
|
if (part.packet_id >= chunks_.size()) {
|
|
OSP_LOG_WARN
|
|
<< "Ignoring potentially corrupt packet having invalid packet ID "
|
|
<< part.packet_id << " (should be less than " << chunks_.size() << ").";
|
|
return false;
|
|
}
|
|
|
|
// Don't process duplicate packets.
|
|
if (chunks_[part.packet_id].has_data()) {
|
|
// Note: No logging here because this is a common occurrence that is not
|
|
// indicative of any problem in the system.
|
|
return true;
|
|
}
|
|
|
|
// Populate metadata from packet 0 only, which is the only packet that must
|
|
// contain a complete set of values.
|
|
if (part.packet_id == FramePacketId{0}) {
|
|
if (part.is_key_frame) {
|
|
frame_.dependency = EncodedFrame::KEY_FRAME;
|
|
} else if (part.frame_id == part.referenced_frame_id) {
|
|
frame_.dependency = EncodedFrame::INDEPENDENTLY_DECODABLE;
|
|
} else {
|
|
frame_.dependency = EncodedFrame::DEPENDS_ON_ANOTHER;
|
|
}
|
|
frame_.referenced_frame_id = part.referenced_frame_id;
|
|
frame_.rtp_timestamp = part.rtp_timestamp;
|
|
frame_.new_playout_delay = part.new_playout_delay;
|
|
}
|
|
|
|
// Take ownership of the contents of the |buffer| (no copy!), and record the
|
|
// region of the buffer containing the payload data. The payload region is
|
|
// usually all but the first few dozen bytes of the buffer.
|
|
PayloadChunk& chunk = chunks_[part.packet_id];
|
|
chunk.buffer.swap(*buffer);
|
|
chunk.payload = part.payload;
|
|
OSP_DCHECK_GE(chunk.payload.data(), chunk.buffer.data());
|
|
OSP_DCHECK_LE(chunk.payload.data() + chunk.payload.size(),
|
|
chunk.buffer.data() + chunk.buffer.size());
|
|
|
|
// Success!
|
|
--num_missing_packets_;
|
|
OSP_DCHECK_GE(num_missing_packets_, 0);
|
|
return true;
|
|
}
|
|
|
|
void FrameCollector::GetMissingPackets(std::vector<PacketNack>* nacks) const {
|
|
OSP_DCHECK(!frame_.frame_id.is_null());
|
|
|
|
if (num_missing_packets_ == 0) {
|
|
return;
|
|
}
|
|
|
|
const int frame_packet_count = chunks_.size();
|
|
if (num_missing_packets_ >= frame_packet_count) {
|
|
nacks->push_back(PacketNack{frame_.frame_id, kAllPacketsLost});
|
|
return;
|
|
}
|
|
|
|
for (int packet_id = 0; packet_id < frame_packet_count; ++packet_id) {
|
|
if (!chunks_[packet_id].has_data()) {
|
|
nacks->push_back(
|
|
PacketNack{frame_.frame_id, static_cast<FramePacketId>(packet_id)});
|
|
}
|
|
}
|
|
}
|
|
|
|
const EncryptedFrame& FrameCollector::PeekAtAssembledFrame() {
|
|
OSP_DCHECK_EQ(num_missing_packets_, 0);
|
|
|
|
if (!frame_.data.data()) {
|
|
// Allocate the frame's payload buffer once, right-sized to the sum of all
|
|
// chunk sizes.
|
|
frame_.owned_data_.reserve(
|
|
std::accumulate(chunks_.cbegin(), chunks_.cend(), size_t{0},
|
|
[](size_t num_bytes_so_far, const PayloadChunk& chunk) {
|
|
return num_bytes_so_far + chunk.payload.size();
|
|
}));
|
|
// Now, populate the frame's payload buffer with each chunk of data.
|
|
for (const PayloadChunk& chunk : chunks_) {
|
|
frame_.owned_data_.insert(frame_.owned_data_.end(), chunk.payload.begin(),
|
|
chunk.payload.end());
|
|
}
|
|
frame_.data = absl::Span<uint8_t>(frame_.owned_data_);
|
|
}
|
|
|
|
return frame_;
|
|
}
|
|
|
|
void FrameCollector::Reset() {
|
|
num_missing_packets_ = kUnknownNumberOfPackets;
|
|
frame_.frame_id = FrameId();
|
|
frame_.owned_data_.clear();
|
|
frame_.owned_data_.shrink_to_fit();
|
|
frame_.data = absl::Span<uint8_t>();
|
|
chunks_.clear();
|
|
}
|
|
|
|
FrameCollector::PayloadChunk::PayloadChunk() = default;
|
|
FrameCollector::PayloadChunk::~PayloadChunk() = default;
|
|
|
|
} // namespace cast
|
|
} // namespace openscreen
|