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.
212 lines
8.0 KiB
212 lines
8.0 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 <stdint.h>
|
|
|
|
#include <algorithm>
|
|
#include <vector>
|
|
|
|
#include "cast/streaming/encoded_frame.h"
|
|
#include "cast/streaming/frame_id.h"
|
|
#include "cast/streaming/rtcp_common.h"
|
|
#include "cast/streaming/rtp_time.h"
|
|
#include "gtest/gtest.h"
|
|
|
|
namespace openscreen {
|
|
namespace cast {
|
|
namespace {
|
|
|
|
const FrameId kSomeFrameId = FrameId::first() + 39;
|
|
constexpr RtpTimeTicks kSomeRtpTimestamp =
|
|
RtpTimeTicks() + RtpTimeDelta::FromTicks(90000);
|
|
|
|
// Convenience macro to check that the |collector| generates an expected set of
|
|
// NACKs.
|
|
#define EXPECT_HAS_NACKS(expected_nacks, collector) \
|
|
do { \
|
|
std::vector<PacketNack> nacks; \
|
|
(collector).GetMissingPackets(&nacks); \
|
|
EXPECT_EQ((expected_nacks), nacks); \
|
|
} while (false);
|
|
|
|
TEST(FrameCollectorTest, CollectsFrameWithOnlyOnePart) {
|
|
FrameCollector collector;
|
|
|
|
// Run for two frames to test that the collector can be re-used.
|
|
for (int i = 0; i < 2; ++i) {
|
|
const FrameId frame_id = kSomeFrameId + i;
|
|
collector.set_frame_id(frame_id);
|
|
EXPECT_FALSE(collector.is_complete());
|
|
|
|
// With no packets seen yet, the collector should provide the "all packets
|
|
// lost" NACK.
|
|
EXPECT_HAS_NACKS((std::vector<PacketNack>{{frame_id, kAllPacketsLost}}),
|
|
collector);
|
|
|
|
// Collect the single packet of the frame.
|
|
RtpPacketParser::ParseResult part;
|
|
part.rtp_timestamp = kSomeRtpTimestamp + (RtpTimeDelta::FromTicks(200) * i);
|
|
part.frame_id = frame_id;
|
|
part.packet_id = 0;
|
|
part.max_packet_id = 0;
|
|
if (i == 0) {
|
|
part.is_key_frame = true;
|
|
// Do not set |part.new_playout_delay|.
|
|
} else {
|
|
part.is_key_frame = false;
|
|
part.new_playout_delay = std::chrono::milliseconds(800);
|
|
}
|
|
part.referenced_frame_id = kSomeFrameId;
|
|
std::vector<uint8_t> buffer(255);
|
|
for (int j = 0; j < 255; ++j) {
|
|
buffer[j] = static_cast<uint8_t>(j);
|
|
}
|
|
part.payload = absl::Span<uint8_t>(buffer);
|
|
EXPECT_TRUE(collector.CollectRtpPacket(part, &buffer));
|
|
|
|
// At this point, the collector should feel complete.
|
|
EXPECT_TRUE(collector.is_complete());
|
|
EXPECT_HAS_NACKS(std::vector<PacketNack>(), collector);
|
|
|
|
// Examine the assembled frame, and confirm its metadata and payload match
|
|
// what was put into the collector via the packet above.
|
|
const auto& frame = collector.PeekAtAssembledFrame();
|
|
if (i == 0) {
|
|
EXPECT_EQ(EncodedFrame::KEY_FRAME, frame.dependency);
|
|
EXPECT_EQ(std::chrono::milliseconds(), frame.new_playout_delay);
|
|
} else {
|
|
EXPECT_EQ(EncodedFrame::DEPENDS_ON_ANOTHER, frame.dependency);
|
|
EXPECT_EQ(std::chrono::milliseconds(800), frame.new_playout_delay);
|
|
}
|
|
EXPECT_EQ(part.frame_id, frame.frame_id);
|
|
EXPECT_EQ(kSomeFrameId, frame.referenced_frame_id);
|
|
EXPECT_EQ(part.rtp_timestamp, frame.rtp_timestamp);
|
|
for (int j = 0; j < 255; ++j) {
|
|
EXPECT_EQ(static_cast<uint8_t>(j), frame.data[j]);
|
|
}
|
|
|
|
collector.Reset();
|
|
}
|
|
}
|
|
|
|
TEST(FrameCollectorTest, CollectsFrameWithMultiplePartsArrivingOutOfOrder) {
|
|
FrameCollector collector;
|
|
collector.set_frame_id(kSomeFrameId);
|
|
|
|
// With no packets seen yet, the collector should provide the "all packets
|
|
// lost" NACK.
|
|
EXPECT_HAS_NACKS((std::vector<PacketNack>{{kSomeFrameId, kAllPacketsLost}}),
|
|
collector);
|
|
|
|
// Prepare the six packet payloads, and the list of remaining NACKs (checked
|
|
// after each part is collected).
|
|
const int kPayloadSizes[] = {999, 998, 998, 998, 42, 0};
|
|
std::vector<uint8_t> payloads[6];
|
|
std::vector<PacketNack> remaining_nacks;
|
|
for (int i = 0; i < 6; ++i) {
|
|
payloads[i].resize(kPayloadSizes[i], static_cast<uint8_t>(i));
|
|
remaining_nacks.push_back(
|
|
PacketNack{kSomeFrameId, static_cast<FramePacketId>(i)});
|
|
}
|
|
|
|
// Collect all six packets, out-of-order, and with some duplicates.
|
|
constexpr FramePacketId kPacketIds[] = {2, 0, 1, 2, 4, 3, 5, 5, 5, 0};
|
|
for (FramePacketId packet_id : kPacketIds) {
|
|
RtpPacketParser::ParseResult part{};
|
|
part.rtp_timestamp = kSomeRtpTimestamp;
|
|
part.is_key_frame = true;
|
|
part.frame_id = kSomeFrameId;
|
|
part.packet_id = packet_id;
|
|
part.max_packet_id = 5;
|
|
part.referenced_frame_id = kSomeFrameId;
|
|
// Prepare a copy of the payload to pass into the FrameCollector. Place 24
|
|
// bytes of bogus data at the start of the buffer to simulate the
|
|
// non-payload part of the RTP packet.
|
|
std::vector<uint8_t> buffer(24, uint8_t{0xab});
|
|
buffer.insert(buffer.end(), payloads[packet_id].begin(),
|
|
payloads[packet_id].end());
|
|
part.payload = absl::Span<uint8_t>(buffer.data() + 24, buffer.size() - 24);
|
|
EXPECT_TRUE(collector.CollectRtpPacket(part, &buffer));
|
|
|
|
// Remove the packet from the list of expected remaining NACKs, and then
|
|
// check that the collector agrees.
|
|
remaining_nacks.erase(
|
|
std::remove_if(remaining_nacks.begin(), remaining_nacks.end(),
|
|
[packet_id](const PacketNack& nack) {
|
|
return nack.packet_id == packet_id;
|
|
}),
|
|
remaining_nacks.end());
|
|
EXPECT_HAS_NACKS(remaining_nacks, collector);
|
|
}
|
|
|
|
// Confirm there are no missing packets and no NACKs generated.
|
|
EXPECT_TRUE(collector.is_complete());
|
|
EXPECT_HAS_NACKS(std::vector<PacketNack>(), collector);
|
|
|
|
// Examine the assembled frame, and confirm its metadata and payload match
|
|
// what was put into the collector via the packets above, and that the payload
|
|
// bytes are in-order.
|
|
const auto& frame = collector.PeekAtAssembledFrame();
|
|
EXPECT_EQ(EncodedFrame::KEY_FRAME, frame.dependency);
|
|
EXPECT_EQ(kSomeFrameId, frame.frame_id);
|
|
EXPECT_EQ(kSomeFrameId, frame.referenced_frame_id);
|
|
EXPECT_EQ(kSomeRtpTimestamp, frame.rtp_timestamp);
|
|
absl::Span<const uint8_t> remaining_data = frame.data;
|
|
for (int i = 0; i < 6; ++i) {
|
|
ASSERT_LE(kPayloadSizes[i], static_cast<int>(remaining_data.size()));
|
|
EXPECT_EQ(absl::Span<const uint8_t>(payloads[i]),
|
|
remaining_data.subspan(0, kPayloadSizes[i]))
|
|
<< "i=" << i;
|
|
remaining_data.remove_prefix(kPayloadSizes[i]);
|
|
}
|
|
ASSERT_TRUE(remaining_data.empty());
|
|
}
|
|
|
|
TEST(FrameCollectorTest, RejectsInvalidParts) {
|
|
FrameCollector collector;
|
|
|
|
// Expect the collector rejects a part not having the correct FrameId.
|
|
collector.set_frame_id(kSomeFrameId + 256);
|
|
RtpPacketParser::ParseResult part{};
|
|
part.rtp_timestamp = kSomeRtpTimestamp;
|
|
part.is_key_frame = false;
|
|
part.frame_id = kSomeFrameId;
|
|
part.packet_id = 0;
|
|
part.max_packet_id = 3;
|
|
std::vector<uint8_t> buffer(1, 'A');
|
|
part.payload = absl::Span<uint8_t>(buffer);
|
|
EXPECT_FALSE(collector.CollectRtpPacket(part, &buffer));
|
|
// Note: When CollectRtpPacket() returns false, it does not take ownership of
|
|
// the buffer memory.
|
|
ASSERT_TRUE(buffer.size() == 1 && buffer[0] == 'A');
|
|
|
|
// The collector should accept a part having the correct FrameId.
|
|
collector.set_frame_id(kSomeFrameId);
|
|
part.frame_id = kSomeFrameId;
|
|
EXPECT_TRUE(collector.CollectRtpPacket(part, &buffer));
|
|
// Note: Re-assign the buffer and payload pointer since the buffer was just
|
|
// consumed.
|
|
buffer.assign(1, 'A');
|
|
part.payload = absl::Span<uint8_t>(buffer);
|
|
|
|
// The collector should reject a part where the packet_id is greater than the
|
|
// previously-established max_packet_id.
|
|
part.packet_id = 5; // BAD, since max_packet_id is 3 (see above).
|
|
EXPECT_FALSE(collector.CollectRtpPacket(part, &buffer));
|
|
ASSERT_TRUE(buffer.size() == 1 && buffer[0] == 'A');
|
|
|
|
// The collector should reject a part where the max_packet_id disagrees with
|
|
// previously-established max_packet_id.
|
|
part.packet_id = 2;
|
|
part.max_packet_id = 5; // BAD, since max_packet_id is 3 (see above).
|
|
EXPECT_FALSE(collector.CollectRtpPacket(part, &buffer));
|
|
ASSERT_TRUE(buffer.size() == 1 && buffer[0] == 'A');
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace cast
|
|
} // namespace openscreen
|