mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 13:50:40 +01:00

delete traces of the OnAssembledFrame callback relax some expectation to better match test purpose, in particular avoid verifying every test buffer is not cleared on new packet. switch comparisons from EXPECT_EQ(constant, value) to more natural EXPECT_EQ(value, constant) Bug: None Change-Id: I81e2c9d0133221435cb2bb02c9190d9f32abd548 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/158140 Commit-Queue: Danil Chapovalov <danilchap@webrtc.org> Reviewed-by: Philip Eliasson <philipel@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29610}
1019 lines
37 KiB
C++
1019 lines
37 KiB
C++
/*
|
|
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
#include "modules/video_coding/packet_buffer.h"
|
|
|
|
#include <cstring>
|
|
#include <limits>
|
|
#include <ostream>
|
|
#include <string>
|
|
#include <utility>
|
|
|
|
#include "api/array_view.h"
|
|
#include "common_video/h264/h264_common.h"
|
|
#include "modules/video_coding/frame_object.h"
|
|
#include "rtc_base/random.h"
|
|
#include "system_wrappers/include/clock.h"
|
|
#include "test/field_trial.h"
|
|
#include "test/gmock.h"
|
|
#include "test/gtest.h"
|
|
|
|
namespace webrtc {
|
|
namespace video_coding {
|
|
namespace {
|
|
|
|
using ::testing::ElementsAre;
|
|
using ::testing::ElementsAreArray;
|
|
using ::testing::IsEmpty;
|
|
using ::testing::Matches;
|
|
using ::testing::Pointee;
|
|
using ::testing::SizeIs;
|
|
|
|
constexpr int kStartSize = 16;
|
|
constexpr int kMaxSize = 64;
|
|
|
|
void IgnoreResult(PacketBuffer::InsertResult /*result*/) {}
|
|
|
|
std::vector<uint16_t> StartSeqNums(
|
|
rtc::ArrayView<const std::unique_ptr<RtpFrameObject>> frames) {
|
|
std::vector<uint16_t> result;
|
|
for (const auto& frame : frames) {
|
|
result.push_back(frame->first_seq_num());
|
|
}
|
|
return result;
|
|
}
|
|
|
|
MATCHER_P(StartSeqNumsAre, seq_num, "") {
|
|
return Matches(ElementsAre(seq_num))(StartSeqNums(arg.frames));
|
|
}
|
|
|
|
MATCHER_P2(StartSeqNumsAre, seq_num1, seq_num2, "") {
|
|
return Matches(ElementsAre(seq_num1, seq_num2))(StartSeqNums(arg.frames));
|
|
}
|
|
|
|
MATCHER(KeyFrame, "") {
|
|
return arg->frame_type() == VideoFrameType::kVideoFrameKey;
|
|
}
|
|
|
|
MATCHER(DeltaFrame, "") {
|
|
return arg->frame_type() == VideoFrameType::kVideoFrameDelta;
|
|
}
|
|
|
|
struct PacketBufferInsertResult : public PacketBuffer::InsertResult {
|
|
explicit PacketBufferInsertResult(PacketBuffer::InsertResult result)
|
|
: InsertResult(std::move(result)) {}
|
|
};
|
|
|
|
void PrintTo(const PacketBufferInsertResult& result, std::ostream* os) {
|
|
*os << "frames: { ";
|
|
for (size_t i = 0; i < result.frames.size(); ++i) {
|
|
const RtpFrameObject& frame = *result.frames[i];
|
|
if (i > 0) {
|
|
*os << ", ";
|
|
}
|
|
*os << "{sn: ";
|
|
if (frame.first_seq_num() == frame.last_seq_num()) {
|
|
*os << frame.first_seq_num();
|
|
} else {
|
|
*os << "[" << frame.first_seq_num() << "-" << frame.last_seq_num() << "]";
|
|
}
|
|
*os << "}";
|
|
}
|
|
*os << " }";
|
|
if (result.buffer_cleared) {
|
|
*os << ", buffer_cleared";
|
|
}
|
|
}
|
|
|
|
class PacketBufferTest : public ::testing::Test {
|
|
protected:
|
|
explicit PacketBufferTest(std::string field_trials = "")
|
|
: scoped_field_trials_(field_trials),
|
|
rand_(0x7732213),
|
|
clock_(new SimulatedClock(0)),
|
|
packet_buffer_(clock_.get(), kStartSize, kMaxSize) {}
|
|
|
|
uint16_t Rand() { return rand_.Rand<uint16_t>(); }
|
|
|
|
enum IsKeyFrame { kKeyFrame, kDeltaFrame };
|
|
enum IsFirst { kFirst, kNotFirst };
|
|
enum IsLast { kLast, kNotLast };
|
|
|
|
PacketBufferInsertResult Insert(uint16_t seq_num, // packet sequence number
|
|
IsKeyFrame keyframe, // is keyframe
|
|
IsFirst first, // is first packet of frame
|
|
IsLast last, // is last packet of frame
|
|
int data_size = 0, // size of data
|
|
uint8_t* data = nullptr, // data pointer
|
|
uint32_t timestamp = 123u) { // rtp timestamp
|
|
VCMPacket packet;
|
|
packet.video_header.codec = kVideoCodecGeneric;
|
|
packet.timestamp = timestamp;
|
|
packet.seqNum = seq_num;
|
|
packet.video_header.frame_type = keyframe == kKeyFrame
|
|
? VideoFrameType::kVideoFrameKey
|
|
: VideoFrameType::kVideoFrameDelta;
|
|
packet.video_header.is_first_packet_in_frame = first == kFirst;
|
|
packet.video_header.is_last_packet_in_frame = last == kLast;
|
|
packet.sizeBytes = data_size;
|
|
packet.dataPtr = data;
|
|
|
|
return PacketBufferInsertResult(packet_buffer_.InsertPacket(&packet));
|
|
}
|
|
|
|
const test::ScopedFieldTrials scoped_field_trials_;
|
|
Random rand_;
|
|
std::unique_ptr<SimulatedClock> clock_;
|
|
PacketBuffer packet_buffer_;
|
|
};
|
|
|
|
TEST_F(PacketBufferTest, InsertOnePacket) {
|
|
const uint16_t seq_num = Rand();
|
|
EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast).frames, SizeIs(1));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, InsertMultiplePackets) {
|
|
const uint16_t seq_num = Rand();
|
|
EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast).frames, SizeIs(1));
|
|
EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kFirst, kLast).frames, SizeIs(1));
|
|
EXPECT_THAT(Insert(seq_num + 2, kKeyFrame, kFirst, kLast).frames, SizeIs(1));
|
|
EXPECT_THAT(Insert(seq_num + 3, kKeyFrame, kFirst, kLast).frames, SizeIs(1));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, InsertDuplicatePacket) {
|
|
const uint16_t seq_num = Rand();
|
|
EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).frames, IsEmpty());
|
|
EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).frames, IsEmpty());
|
|
EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast).frames,
|
|
SizeIs(1));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, SeqNumWrapOneFrame) {
|
|
Insert(0xFFFF, kKeyFrame, kFirst, kNotLast);
|
|
EXPECT_THAT(Insert(0x0, kKeyFrame, kNotFirst, kLast),
|
|
StartSeqNumsAre(0xFFFF));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, SeqNumWrapTwoFrames) {
|
|
EXPECT_THAT(Insert(0xFFFF, kKeyFrame, kFirst, kLast),
|
|
StartSeqNumsAre(0xFFFF));
|
|
EXPECT_THAT(Insert(0x0, kKeyFrame, kFirst, kLast), StartSeqNumsAre(0x0));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, InsertOldPackets) {
|
|
EXPECT_THAT(Insert(100, kKeyFrame, kFirst, kNotLast).frames, IsEmpty());
|
|
EXPECT_THAT(Insert(102, kDeltaFrame, kFirst, kLast).frames, SizeIs(1));
|
|
EXPECT_THAT(Insert(101, kKeyFrame, kNotFirst, kLast).frames, SizeIs(1));
|
|
|
|
EXPECT_THAT(Insert(100, kKeyFrame, kFirst, kNotLast).frames, IsEmpty());
|
|
EXPECT_THAT(Insert(100, kKeyFrame, kFirst, kNotLast).frames, IsEmpty());
|
|
EXPECT_THAT(Insert(102, kDeltaFrame, kFirst, kLast).frames, SizeIs(1));
|
|
|
|
packet_buffer_.ClearTo(102);
|
|
EXPECT_THAT(Insert(102, kDeltaFrame, kFirst, kLast).frames, IsEmpty());
|
|
EXPECT_THAT(Insert(103, kDeltaFrame, kFirst, kLast).frames, SizeIs(1));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, NackCount) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
VCMPacket packet;
|
|
packet.video_header.codec = kVideoCodecGeneric;
|
|
packet.seqNum = seq_num;
|
|
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
|
|
packet.video_header.is_first_packet_in_frame = true;
|
|
packet.video_header.is_last_packet_in_frame = false;
|
|
packet.timesNacked = 0;
|
|
|
|
IgnoreResult(packet_buffer_.InsertPacket(&packet));
|
|
|
|
packet.seqNum++;
|
|
packet.video_header.is_first_packet_in_frame = false;
|
|
packet.timesNacked = 1;
|
|
IgnoreResult(packet_buffer_.InsertPacket(&packet));
|
|
|
|
packet.seqNum++;
|
|
packet.timesNacked = 3;
|
|
IgnoreResult(packet_buffer_.InsertPacket(&packet));
|
|
|
|
packet.seqNum++;
|
|
packet.video_header.is_last_packet_in_frame = true;
|
|
packet.timesNacked = 1;
|
|
auto frames = packet_buffer_.InsertPacket(&packet).frames;
|
|
|
|
ASSERT_THAT(frames, SizeIs(1));
|
|
EXPECT_EQ(frames.front()->times_nacked(), 3);
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, FrameSize) {
|
|
const uint16_t seq_num = Rand();
|
|
uint8_t* data1 = new uint8_t[5]();
|
|
uint8_t* data2 = new uint8_t[5]();
|
|
uint8_t* data3 = new uint8_t[5]();
|
|
uint8_t* data4 = new uint8_t[5]();
|
|
|
|
Insert(seq_num, kKeyFrame, kFirst, kNotLast, 5, data1);
|
|
Insert(seq_num + 1, kKeyFrame, kNotFirst, kNotLast, 5, data2);
|
|
Insert(seq_num + 2, kKeyFrame, kNotFirst, kNotLast, 5, data3);
|
|
EXPECT_THAT(Insert(seq_num + 3, kKeyFrame, kNotFirst, kLast, 5, data4).frames,
|
|
ElementsAre(Pointee(SizeIs(20))));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, CountsUniqueFrames) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
ASSERT_EQ(packet_buffer_.GetUniqueFramesSeen(), 0);
|
|
|
|
Insert(seq_num, kKeyFrame, kFirst, kNotLast, 0, nullptr, 100);
|
|
ASSERT_EQ(packet_buffer_.GetUniqueFramesSeen(), 1);
|
|
// Still the same frame.
|
|
Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast, 0, nullptr, 100);
|
|
ASSERT_EQ(packet_buffer_.GetUniqueFramesSeen(), 1);
|
|
|
|
// Second frame.
|
|
Insert(seq_num + 2, kKeyFrame, kFirst, kNotLast, 0, nullptr, 200);
|
|
ASSERT_EQ(packet_buffer_.GetUniqueFramesSeen(), 2);
|
|
Insert(seq_num + 3, kKeyFrame, kNotFirst, kLast, 0, nullptr, 200);
|
|
ASSERT_EQ(packet_buffer_.GetUniqueFramesSeen(), 2);
|
|
|
|
// Old packet.
|
|
Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast, 0, nullptr, 100);
|
|
ASSERT_EQ(packet_buffer_.GetUniqueFramesSeen(), 2);
|
|
|
|
// Missing middle packet.
|
|
Insert(seq_num + 4, kKeyFrame, kFirst, kNotLast, 0, nullptr, 300);
|
|
Insert(seq_num + 6, kKeyFrame, kNotFirst, kLast, 0, nullptr, 300);
|
|
ASSERT_EQ(packet_buffer_.GetUniqueFramesSeen(), 3);
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, HasHistoryOfUniqueFrames) {
|
|
const int kNumFrames = 1500;
|
|
const int kRequiredHistoryLength = 1000;
|
|
const uint16_t seq_num = Rand();
|
|
const uint32_t timestamp = 0xFFFFFFF0; // Large enough to cause wrap-around.
|
|
|
|
for (int i = 0; i < kNumFrames; ++i) {
|
|
Insert(seq_num + i, kKeyFrame, kFirst, kNotLast, 0, nullptr,
|
|
timestamp + 10 * i);
|
|
}
|
|
EXPECT_EQ(packet_buffer_.GetUniqueFramesSeen(), kNumFrames);
|
|
|
|
// Old packets within history should not affect number of seen unique frames.
|
|
for (int i = kNumFrames - kRequiredHistoryLength; i < kNumFrames; ++i) {
|
|
Insert(seq_num + i, kKeyFrame, kFirst, kNotLast, 0, nullptr,
|
|
timestamp + 10 * i);
|
|
}
|
|
EXPECT_EQ(packet_buffer_.GetUniqueFramesSeen(), kNumFrames);
|
|
|
|
// Very old packets should be treated as unique.
|
|
Insert(seq_num, kKeyFrame, kFirst, kNotLast, 0, nullptr, timestamp);
|
|
EXPECT_EQ(packet_buffer_.GetUniqueFramesSeen(), kNumFrames + 1);
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, ExpandBuffer) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
Insert(seq_num, kKeyFrame, kFirst, kNotLast);
|
|
for (int i = 1; i < kStartSize; ++i)
|
|
EXPECT_FALSE(
|
|
Insert(seq_num + i, kKeyFrame, kNotFirst, kNotLast).buffer_cleared);
|
|
|
|
// Already inserted kStartSize number of packets, inserting the last packet
|
|
// should increase the buffer size and also result in an assembled frame.
|
|
EXPECT_FALSE(
|
|
Insert(seq_num + kStartSize, kKeyFrame, kNotFirst, kLast).buffer_cleared);
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, SingleFrameExpandsBuffer) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
Insert(seq_num, kKeyFrame, kFirst, kNotLast);
|
|
for (int i = 1; i < kStartSize; ++i)
|
|
Insert(seq_num + i, kKeyFrame, kNotFirst, kNotLast);
|
|
EXPECT_THAT(Insert(seq_num + kStartSize, kKeyFrame, kNotFirst, kLast),
|
|
StartSeqNumsAre(seq_num));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, ExpandBufferOverflow) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
EXPECT_FALSE(Insert(seq_num, kKeyFrame, kFirst, kNotLast).buffer_cleared);
|
|
for (int i = 1; i < kMaxSize; ++i)
|
|
EXPECT_FALSE(
|
|
Insert(seq_num + i, kKeyFrame, kNotFirst, kNotLast).buffer_cleared);
|
|
|
|
// Already inserted kMaxSize number of packets, inserting the last packet
|
|
// should overflow the buffer and result in false being returned.
|
|
EXPECT_TRUE(
|
|
Insert(seq_num + kMaxSize, kKeyFrame, kNotFirst, kLast).buffer_cleared);
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, OnePacketOneFrame) {
|
|
const uint16_t seq_num = Rand();
|
|
EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast),
|
|
StartSeqNumsAre(seq_num));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, TwoPacketsTwoFrames) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast),
|
|
StartSeqNumsAre(seq_num));
|
|
EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kFirst, kLast),
|
|
StartSeqNumsAre(seq_num + 1));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, TwoPacketsOneFrames) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).frames, IsEmpty());
|
|
EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast),
|
|
StartSeqNumsAre(seq_num));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, ThreePacketReorderingOneFrame) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kNotLast).frames, IsEmpty());
|
|
EXPECT_THAT(Insert(seq_num + 2, kKeyFrame, kNotFirst, kLast).frames,
|
|
IsEmpty());
|
|
EXPECT_THAT(Insert(seq_num + 1, kKeyFrame, kNotFirst, kNotLast),
|
|
StartSeqNumsAre(seq_num));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, Frames) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast),
|
|
StartSeqNumsAre(seq_num));
|
|
EXPECT_THAT(Insert(seq_num + 1, kDeltaFrame, kFirst, kLast),
|
|
StartSeqNumsAre(seq_num + 1));
|
|
EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kFirst, kLast),
|
|
StartSeqNumsAre(seq_num + 2));
|
|
EXPECT_THAT(Insert(seq_num + 3, kDeltaFrame, kFirst, kLast),
|
|
StartSeqNumsAre(seq_num + 3));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, ClearSinglePacket) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
for (int i = 0; i < kMaxSize; ++i)
|
|
Insert(seq_num + i, kDeltaFrame, kFirst, kLast);
|
|
|
|
packet_buffer_.ClearTo(seq_num);
|
|
EXPECT_FALSE(
|
|
Insert(seq_num + kMaxSize, kDeltaFrame, kFirst, kLast).buffer_cleared);
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, ClearFullBuffer) {
|
|
for (int i = 0; i < kMaxSize; ++i)
|
|
Insert(i, kDeltaFrame, kFirst, kLast);
|
|
|
|
packet_buffer_.ClearTo(kMaxSize - 1);
|
|
|
|
for (int i = kMaxSize; i < 2 * kMaxSize; ++i)
|
|
EXPECT_FALSE(Insert(i, kDeltaFrame, kFirst, kLast).buffer_cleared);
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, DontClearNewerPacket) {
|
|
EXPECT_THAT(Insert(0, kKeyFrame, kFirst, kLast), StartSeqNumsAre(0));
|
|
packet_buffer_.ClearTo(0);
|
|
EXPECT_THAT(Insert(2 * kStartSize, kKeyFrame, kFirst, kLast),
|
|
StartSeqNumsAre(2 * kStartSize));
|
|
EXPECT_THAT(Insert(3 * kStartSize + 1, kKeyFrame, kFirst, kNotLast).frames,
|
|
IsEmpty());
|
|
packet_buffer_.ClearTo(2 * kStartSize);
|
|
EXPECT_THAT(Insert(3 * kStartSize + 2, kKeyFrame, kNotFirst, kLast),
|
|
StartSeqNumsAre(3 * kStartSize + 1));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, OneIncompleteFrame) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
EXPECT_THAT(Insert(seq_num, kDeltaFrame, kFirst, kNotLast).frames, IsEmpty());
|
|
EXPECT_THAT(Insert(seq_num + 1, kDeltaFrame, kNotFirst, kLast),
|
|
StartSeqNumsAre(seq_num));
|
|
EXPECT_THAT(Insert(seq_num - 1, kDeltaFrame, kNotFirst, kLast).frames,
|
|
IsEmpty());
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, TwoIncompleteFramesFullBuffer) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
for (int i = 1; i < kMaxSize - 1; ++i)
|
|
Insert(seq_num + i, kDeltaFrame, kNotFirst, kNotLast);
|
|
EXPECT_THAT(Insert(seq_num, kDeltaFrame, kFirst, kNotLast).frames, IsEmpty());
|
|
EXPECT_THAT(Insert(seq_num - 1, kDeltaFrame, kNotFirst, kLast).frames,
|
|
IsEmpty());
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, FramesReordered) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
EXPECT_THAT(Insert(seq_num + 1, kDeltaFrame, kFirst, kLast),
|
|
StartSeqNumsAre(seq_num + 1));
|
|
EXPECT_THAT(Insert(seq_num, kKeyFrame, kFirst, kLast),
|
|
StartSeqNumsAre(seq_num));
|
|
EXPECT_THAT(Insert(seq_num + 3, kDeltaFrame, kFirst, kLast),
|
|
StartSeqNumsAre(seq_num + 3));
|
|
EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kFirst, kLast),
|
|
StartSeqNumsAre(seq_num + 2));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, GetBitstream) {
|
|
// "many bitstream, such data" with null termination.
|
|
uint8_t many_data[] = {0x6d, 0x61, 0x6e, 0x79, 0x20};
|
|
uint8_t bitstream_data[] = {0x62, 0x69, 0x74, 0x73, 0x74, 0x72,
|
|
0x65, 0x61, 0x6d, 0x2c, 0x20};
|
|
uint8_t such_data[] = {0x73, 0x75, 0x63, 0x68, 0x20};
|
|
uint8_t data_data[] = {0x64, 0x61, 0x74, 0x61, 0x0};
|
|
|
|
uint8_t* many = new uint8_t[sizeof(many_data)];
|
|
uint8_t* bitstream = new uint8_t[sizeof(bitstream_data)];
|
|
uint8_t* such = new uint8_t[sizeof(such_data)];
|
|
uint8_t* data = new uint8_t[sizeof(data_data)];
|
|
|
|
memcpy(many, many_data, sizeof(many_data));
|
|
memcpy(bitstream, bitstream_data, sizeof(bitstream_data));
|
|
memcpy(such, such_data, sizeof(such_data));
|
|
memcpy(data, data_data, sizeof(data_data));
|
|
|
|
const uint16_t seq_num = Rand();
|
|
|
|
Insert(seq_num, kKeyFrame, kFirst, kNotLast, sizeof(many_data), many);
|
|
Insert(seq_num + 1, kDeltaFrame, kNotFirst, kNotLast, sizeof(bitstream_data),
|
|
bitstream);
|
|
Insert(seq_num + 2, kDeltaFrame, kNotFirst, kNotLast, sizeof(such_data),
|
|
such);
|
|
auto frames = Insert(seq_num + 3, kDeltaFrame, kNotFirst, kLast,
|
|
sizeof(data_data), data)
|
|
.frames;
|
|
|
|
ASSERT_THAT(frames, SizeIs(1));
|
|
EXPECT_EQ(frames[0]->first_seq_num(), seq_num);
|
|
EXPECT_THAT(rtc::MakeArrayView(frames[0]->data(), frames[0]->size()),
|
|
ElementsAreArray("many bitstream, such data"));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, GetBitstreamOneFrameOnePacket) {
|
|
uint8_t bitstream_data[] = "All the bitstream data for this frame!";
|
|
uint8_t* data = new uint8_t[sizeof(bitstream_data)];
|
|
memcpy(data, bitstream_data, sizeof(bitstream_data));
|
|
|
|
auto frames =
|
|
Insert(0, kKeyFrame, kFirst, kLast, sizeof(bitstream_data), data).frames;
|
|
ASSERT_THAT(StartSeqNums(frames), ElementsAre(0));
|
|
EXPECT_THAT(rtc::MakeArrayView(frames[0]->data(), frames[0]->size()),
|
|
ElementsAreArray(bitstream_data));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, GetBitstreamOneFrameFullBuffer) {
|
|
uint8_t* data_arr[kStartSize];
|
|
uint8_t expected[kStartSize];
|
|
|
|
for (uint8_t i = 0; i < kStartSize; ++i) {
|
|
data_arr[i] = new uint8_t[1];
|
|
data_arr[i][0] = i;
|
|
expected[i] = i;
|
|
}
|
|
|
|
Insert(0, kKeyFrame, kFirst, kNotLast, 1, data_arr[0]);
|
|
for (uint8_t i = 1; i < kStartSize - 1; ++i)
|
|
Insert(i, kKeyFrame, kNotFirst, kNotLast, 1, data_arr[i]);
|
|
auto frames = Insert(kStartSize - 1, kKeyFrame, kNotFirst, kLast, 1,
|
|
data_arr[kStartSize - 1])
|
|
.frames;
|
|
|
|
ASSERT_THAT(StartSeqNums(frames), ElementsAre(0));
|
|
EXPECT_THAT(rtc::MakeArrayView(frames[0]->data(), frames[0]->size()),
|
|
ElementsAreArray(expected));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, InsertPacketAfterSequenceNumberWrapAround) {
|
|
uint16_t kFirstSeqNum = 0;
|
|
uint32_t kTimestampDelta = 100;
|
|
uint32_t timestamp = 10000;
|
|
uint16_t seq_num = kFirstSeqNum;
|
|
|
|
// Loop until seq_num wraps around.
|
|
SeqNumUnwrapper<uint16_t> unwrapper;
|
|
while (unwrapper.Unwrap(seq_num) < std::numeric_limits<uint16_t>::max()) {
|
|
Insert(seq_num++, kKeyFrame, kFirst, kNotLast, 0, nullptr, timestamp);
|
|
for (int i = 0; i < 5; ++i) {
|
|
Insert(seq_num++, kKeyFrame, kNotFirst, kNotLast, 0, nullptr, timestamp);
|
|
}
|
|
Insert(seq_num++, kKeyFrame, kNotFirst, kLast, 0, nullptr, timestamp);
|
|
timestamp += kTimestampDelta;
|
|
}
|
|
|
|
// Receive frame with overlapping sequence numbers.
|
|
Insert(seq_num++, kKeyFrame, kFirst, kNotLast, 0, nullptr, timestamp);
|
|
for (int i = 0; i < 5; ++i) {
|
|
Insert(seq_num++, kKeyFrame, kNotFirst, kNotLast, 0, nullptr, timestamp);
|
|
}
|
|
EXPECT_THAT(
|
|
Insert(seq_num++, kKeyFrame, kNotFirst, kLast, 0, nullptr, timestamp)
|
|
.frames,
|
|
SizeIs(1));
|
|
}
|
|
|
|
// If |sps_pps_idr_is_keyframe| is true, we require keyframes to contain
|
|
// SPS/PPS/IDR and the keyframes we create as part of the test do contain
|
|
// SPS/PPS/IDR. If |sps_pps_idr_is_keyframe| is false, we only require and
|
|
// create keyframes containing only IDR.
|
|
class PacketBufferH264Test : public PacketBufferTest {
|
|
protected:
|
|
explicit PacketBufferH264Test(bool sps_pps_idr_is_keyframe)
|
|
: PacketBufferTest(sps_pps_idr_is_keyframe
|
|
? "WebRTC-SpsPpsIdrIsH264Keyframe/Enabled/"
|
|
: ""),
|
|
sps_pps_idr_is_keyframe_(sps_pps_idr_is_keyframe) {}
|
|
|
|
PacketBufferInsertResult InsertH264(
|
|
uint16_t seq_num, // packet sequence number
|
|
IsKeyFrame keyframe, // is keyframe
|
|
IsFirst first, // is first packet of frame
|
|
IsLast last, // is last packet of frame
|
|
uint32_t timestamp, // rtp timestamp
|
|
int data_size = 0, // size of data
|
|
uint8_t* data = nullptr, // data pointer
|
|
uint32_t width = 0, // width of frame (SPS/IDR)
|
|
uint32_t height = 0) { // height of frame (SPS/IDR)
|
|
VCMPacket packet;
|
|
packet.video_header.codec = kVideoCodecH264;
|
|
auto& h264_header =
|
|
packet.video_header.video_type_header.emplace<RTPVideoHeaderH264>();
|
|
packet.seqNum = seq_num;
|
|
packet.timestamp = timestamp;
|
|
if (keyframe == kKeyFrame) {
|
|
if (sps_pps_idr_is_keyframe_) {
|
|
h264_header.nalus[0].type = H264::NaluType::kSps;
|
|
h264_header.nalus[1].type = H264::NaluType::kPps;
|
|
h264_header.nalus[2].type = H264::NaluType::kIdr;
|
|
h264_header.nalus_length = 3;
|
|
} else {
|
|
h264_header.nalus[0].type = H264::NaluType::kIdr;
|
|
h264_header.nalus_length = 1;
|
|
}
|
|
}
|
|
packet.video_header.width = width;
|
|
packet.video_header.height = height;
|
|
packet.video_header.is_first_packet_in_frame = first == kFirst;
|
|
packet.video_header.is_last_packet_in_frame = last == kLast;
|
|
packet.sizeBytes = data_size;
|
|
packet.dataPtr = data;
|
|
|
|
return PacketBufferInsertResult(packet_buffer_.InsertPacket(&packet));
|
|
}
|
|
|
|
PacketBufferInsertResult InsertH264KeyFrameWithAud(
|
|
uint16_t seq_num, // packet sequence number
|
|
IsKeyFrame keyframe, // is keyframe
|
|
IsFirst first, // is first packet of frame
|
|
IsLast last, // is last packet of frame
|
|
uint32_t timestamp, // rtp timestamp
|
|
int data_size = 0, // size of data
|
|
uint8_t* data = nullptr, // data pointer
|
|
uint32_t width = 0, // width of frame (SPS/IDR)
|
|
uint32_t height = 0) { // height of frame (SPS/IDR)
|
|
VCMPacket packet;
|
|
packet.video_header.codec = kVideoCodecH264;
|
|
auto& h264_header =
|
|
packet.video_header.video_type_header.emplace<RTPVideoHeaderH264>();
|
|
packet.seqNum = seq_num;
|
|
packet.timestamp = timestamp;
|
|
|
|
// this should be the start of frame.
|
|
RTC_CHECK(first == kFirst);
|
|
|
|
// Insert a AUD NALU / packet without width/height.
|
|
h264_header.nalus[0].type = H264::NaluType::kAud;
|
|
h264_header.nalus_length = 1;
|
|
packet.video_header.is_first_packet_in_frame = true;
|
|
packet.video_header.is_last_packet_in_frame = false;
|
|
packet.sizeBytes = 0;
|
|
packet.dataPtr = nullptr;
|
|
IgnoreResult(packet_buffer_.InsertPacket(&packet));
|
|
// insert IDR
|
|
return InsertH264(seq_num + 1, keyframe, kNotFirst, last, timestamp,
|
|
data_size, data, width, height);
|
|
}
|
|
|
|
const bool sps_pps_idr_is_keyframe_;
|
|
};
|
|
|
|
// This fixture is used to test the general behaviour of the packet buffer
|
|
// in both configurations.
|
|
class PacketBufferH264ParameterizedTest
|
|
: public ::testing::WithParamInterface<bool>,
|
|
public PacketBufferH264Test {
|
|
protected:
|
|
PacketBufferH264ParameterizedTest() : PacketBufferH264Test(GetParam()) {}
|
|
};
|
|
|
|
INSTANTIATE_TEST_SUITE_P(SpsPpsIdrIsKeyframe,
|
|
PacketBufferH264ParameterizedTest,
|
|
::testing::Bool());
|
|
|
|
TEST_P(PacketBufferH264ParameterizedTest, DontRemoveMissingPacketOnClearTo) {
|
|
InsertH264(0, kKeyFrame, kFirst, kLast, 0);
|
|
InsertH264(2, kDeltaFrame, kFirst, kNotLast, 2);
|
|
packet_buffer_.ClearTo(0);
|
|
// Expect no frame because of missing of packet #1
|
|
EXPECT_THAT(InsertH264(3, kDeltaFrame, kNotFirst, kLast, 2).frames,
|
|
IsEmpty());
|
|
}
|
|
|
|
TEST_P(PacketBufferH264ParameterizedTest, GetBitstreamOneFrameFullBuffer) {
|
|
uint8_t* data_arr[kStartSize];
|
|
uint8_t expected[kStartSize];
|
|
|
|
for (uint8_t i = 0; i < kStartSize; ++i) {
|
|
data_arr[i] = new uint8_t[1];
|
|
data_arr[i][0] = i;
|
|
expected[i] = i;
|
|
}
|
|
|
|
InsertH264(0, kKeyFrame, kFirst, kNotLast, 1, 1, data_arr[0]);
|
|
for (uint8_t i = 1; i < kStartSize - 1; ++i) {
|
|
InsertH264(i, kKeyFrame, kNotFirst, kNotLast, 1, 1, data_arr[i]);
|
|
}
|
|
|
|
auto frames = InsertH264(kStartSize - 1, kKeyFrame, kNotFirst, kLast, 1, 1,
|
|
data_arr[kStartSize - 1])
|
|
.frames;
|
|
ASSERT_THAT(StartSeqNums(frames), ElementsAre(0));
|
|
EXPECT_THAT(rtc::MakeArrayView(frames[0]->data(), frames[0]->size()),
|
|
ElementsAreArray(expected));
|
|
}
|
|
|
|
TEST_P(PacketBufferH264ParameterizedTest, GetBitstreamBufferPadding) {
|
|
uint16_t seq_num = Rand();
|
|
uint8_t data_data[] = "some plain old data";
|
|
uint8_t* data = new uint8_t[sizeof(data_data)];
|
|
memcpy(data, data_data, sizeof(data_data));
|
|
|
|
VCMPacket packet;
|
|
auto& h264_header =
|
|
packet.video_header.video_type_header.emplace<RTPVideoHeaderH264>();
|
|
h264_header.nalus_length = 1;
|
|
h264_header.nalus[0].type = H264::NaluType::kIdr;
|
|
h264_header.packetization_type = kH264SingleNalu;
|
|
packet.seqNum = seq_num;
|
|
packet.video_header.codec = kVideoCodecH264;
|
|
packet.insertStartCode = true;
|
|
packet.dataPtr = data;
|
|
packet.sizeBytes = sizeof(data_data);
|
|
packet.video_header.is_first_packet_in_frame = true;
|
|
packet.video_header.is_last_packet_in_frame = true;
|
|
auto frames = packet_buffer_.InsertPacket(&packet).frames;
|
|
|
|
ASSERT_THAT(frames, SizeIs(1));
|
|
EXPECT_EQ(frames[0]->first_seq_num(), seq_num);
|
|
EXPECT_EQ(frames[0]->EncodedImage().size(), sizeof(data_data));
|
|
EXPECT_EQ(frames[0]->EncodedImage().capacity(), sizeof(data_data));
|
|
EXPECT_THAT(rtc::MakeArrayView(frames[0]->data(), frames[0]->size()),
|
|
ElementsAreArray(data_data));
|
|
}
|
|
|
|
TEST_P(PacketBufferH264ParameterizedTest, FrameResolution) {
|
|
uint16_t seq_num = 100;
|
|
uint8_t data_data[] = "some plain old data";
|
|
uint8_t* data = new uint8_t[sizeof(data_data)];
|
|
memcpy(data, data_data, sizeof(data_data));
|
|
uint32_t width = 640;
|
|
uint32_t height = 360;
|
|
uint32_t timestamp = 1000;
|
|
|
|
auto frames = InsertH264(seq_num, kKeyFrame, kFirst, kLast, timestamp,
|
|
sizeof(data_data), data, width, height)
|
|
.frames;
|
|
|
|
ASSERT_THAT(frames, SizeIs(1));
|
|
EXPECT_THAT(rtc::MakeArrayView(frames[0]->data(), frames[0]->size()),
|
|
ElementsAreArray(data_data));
|
|
EXPECT_EQ(frames[0]->EncodedImage()._encodedWidth, width);
|
|
EXPECT_EQ(frames[0]->EncodedImage()._encodedHeight, height);
|
|
}
|
|
|
|
TEST_P(PacketBufferH264ParameterizedTest, FrameResolutionNaluBeforeSPS) {
|
|
uint16_t seq_num = 100;
|
|
uint8_t data_data[] = "some plain old data";
|
|
uint8_t* data = new uint8_t[sizeof(data_data)];
|
|
memcpy(data, data_data, sizeof(data_data));
|
|
uint32_t width = 640;
|
|
uint32_t height = 360;
|
|
uint32_t timestamp = 1000;
|
|
|
|
auto frames =
|
|
InsertH264KeyFrameWithAud(seq_num, kKeyFrame, kFirst, kLast, timestamp,
|
|
sizeof(data_data), data, width, height)
|
|
.frames;
|
|
|
|
ASSERT_THAT(StartSeqNums(frames), ElementsAre(seq_num));
|
|
|
|
EXPECT_EQ(frames[0]->EncodedImage().size(), sizeof(data_data));
|
|
EXPECT_EQ(frames[0]->EncodedImage().capacity(), sizeof(data_data));
|
|
EXPECT_EQ(frames[0]->EncodedImage()._encodedWidth, width);
|
|
EXPECT_EQ(frames[0]->EncodedImage()._encodedHeight, height);
|
|
EXPECT_THAT(rtc::MakeArrayView(frames[0]->data(), frames[0]->size()),
|
|
ElementsAreArray(data_data));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, FreeSlotsOnFrameCreation) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
Insert(seq_num, kKeyFrame, kFirst, kNotLast);
|
|
Insert(seq_num + 1, kDeltaFrame, kNotFirst, kNotLast);
|
|
EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kNotFirst, kLast),
|
|
StartSeqNumsAre(seq_num));
|
|
|
|
// Insert frame that fills the whole buffer.
|
|
Insert(seq_num + 3, kKeyFrame, kFirst, kNotLast);
|
|
for (int i = 0; i < kMaxSize - 2; ++i)
|
|
Insert(seq_num + i + 4, kDeltaFrame, kNotFirst, kNotLast);
|
|
EXPECT_THAT(Insert(seq_num + kMaxSize + 2, kKeyFrame, kNotFirst, kLast),
|
|
StartSeqNumsAre(seq_num + 3));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, Clear) {
|
|
const uint16_t seq_num = Rand();
|
|
|
|
Insert(seq_num, kKeyFrame, kFirst, kNotLast);
|
|
Insert(seq_num + 1, kDeltaFrame, kNotFirst, kNotLast);
|
|
EXPECT_THAT(Insert(seq_num + 2, kDeltaFrame, kNotFirst, kLast),
|
|
StartSeqNumsAre(seq_num));
|
|
|
|
packet_buffer_.Clear();
|
|
|
|
Insert(seq_num + kStartSize, kKeyFrame, kFirst, kNotLast);
|
|
Insert(seq_num + kStartSize + 1, kDeltaFrame, kNotFirst, kNotLast);
|
|
EXPECT_THAT(Insert(seq_num + kStartSize + 2, kDeltaFrame, kNotFirst, kLast),
|
|
StartSeqNumsAre(seq_num + kStartSize));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, FramesAfterClear) {
|
|
Insert(9025, kDeltaFrame, kFirst, kLast);
|
|
Insert(9024, kKeyFrame, kFirst, kLast);
|
|
packet_buffer_.ClearTo(9025);
|
|
EXPECT_THAT(Insert(9057, kDeltaFrame, kFirst, kLast).frames, SizeIs(1));
|
|
EXPECT_THAT(Insert(9026, kDeltaFrame, kFirst, kLast).frames, SizeIs(1));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, SameFrameDifferentTimestamps) {
|
|
Insert(0, kKeyFrame, kFirst, kNotLast, 0, nullptr, 1000);
|
|
EXPECT_THAT(Insert(1, kKeyFrame, kNotFirst, kLast, 0, nullptr, 1001).frames,
|
|
IsEmpty());
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, DontLeakPayloadData) {
|
|
// NOTE! Any eventual leak is suppose to be detected by valgrind
|
|
// or any other similar tool.
|
|
uint8_t* data1 = new uint8_t[5];
|
|
uint8_t* data2 = new uint8_t[5];
|
|
uint8_t* data3 = new uint8_t[5];
|
|
uint8_t* data4 = new uint8_t[5];
|
|
|
|
// Expected to free data1 upon PacketBuffer destruction.
|
|
Insert(2, kKeyFrame, kFirst, kNotLast, 5, data1);
|
|
|
|
// Expect to free data2 upon insertion.
|
|
Insert(2, kKeyFrame, kFirst, kNotLast, 5, data2);
|
|
|
|
// Expect to free data3 upon insertion (old packet).
|
|
packet_buffer_.ClearTo(1);
|
|
Insert(1, kKeyFrame, kFirst, kNotLast, 5, data3);
|
|
|
|
// Expect to free data4 upon insertion (packet buffer is full).
|
|
Insert(2 + kMaxSize, kKeyFrame, kFirst, kNotLast, 5, data4);
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, ContinuousSeqNumDoubleMarkerBit) {
|
|
Insert(2, kKeyFrame, kNotFirst, kNotLast);
|
|
Insert(1, kKeyFrame, kFirst, kLast);
|
|
EXPECT_THAT(Insert(3, kKeyFrame, kNotFirst, kLast).frames, IsEmpty());
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, PacketTimestamps) {
|
|
absl::optional<int64_t> packet_ms;
|
|
absl::optional<int64_t> packet_keyframe_ms;
|
|
|
|
packet_ms = packet_buffer_.LastReceivedPacketMs();
|
|
packet_keyframe_ms = packet_buffer_.LastReceivedKeyframePacketMs();
|
|
EXPECT_FALSE(packet_ms);
|
|
EXPECT_FALSE(packet_keyframe_ms);
|
|
|
|
int64_t keyframe_ms = clock_->TimeInMilliseconds();
|
|
Insert(100, kKeyFrame, kFirst, kLast);
|
|
packet_ms = packet_buffer_.LastReceivedPacketMs();
|
|
packet_keyframe_ms = packet_buffer_.LastReceivedKeyframePacketMs();
|
|
EXPECT_TRUE(packet_ms);
|
|
EXPECT_TRUE(packet_keyframe_ms);
|
|
EXPECT_EQ(keyframe_ms, *packet_ms);
|
|
EXPECT_EQ(keyframe_ms, *packet_keyframe_ms);
|
|
|
|
clock_->AdvanceTimeMilliseconds(100);
|
|
int64_t delta_ms = clock_->TimeInMilliseconds();
|
|
Insert(101, kDeltaFrame, kFirst, kLast);
|
|
packet_ms = packet_buffer_.LastReceivedPacketMs();
|
|
packet_keyframe_ms = packet_buffer_.LastReceivedKeyframePacketMs();
|
|
EXPECT_TRUE(packet_ms);
|
|
EXPECT_TRUE(packet_keyframe_ms);
|
|
EXPECT_EQ(delta_ms, *packet_ms);
|
|
EXPECT_EQ(keyframe_ms, *packet_keyframe_ms);
|
|
|
|
packet_buffer_.Clear();
|
|
packet_ms = packet_buffer_.LastReceivedPacketMs();
|
|
packet_keyframe_ms = packet_buffer_.LastReceivedKeyframePacketMs();
|
|
EXPECT_FALSE(packet_ms);
|
|
EXPECT_FALSE(packet_keyframe_ms);
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, IncomingCodecChange) {
|
|
VCMPacket packet;
|
|
packet.video_header.is_first_packet_in_frame = true;
|
|
packet.video_header.is_last_packet_in_frame = true;
|
|
packet.sizeBytes = 0;
|
|
packet.dataPtr = nullptr;
|
|
|
|
packet.video_header.codec = kVideoCodecVP8;
|
|
packet.video_header.video_type_header.emplace<RTPVideoHeaderVP8>();
|
|
packet.timestamp = 1;
|
|
packet.seqNum = 1;
|
|
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
|
|
EXPECT_THAT(packet_buffer_.InsertPacket(&packet).frames, SizeIs(1));
|
|
|
|
packet.video_header.codec = kVideoCodecH264;
|
|
auto& h264_header =
|
|
packet.video_header.video_type_header.emplace<RTPVideoHeaderH264>();
|
|
h264_header.nalus_length = 1;
|
|
packet.timestamp = 3;
|
|
packet.seqNum = 3;
|
|
EXPECT_THAT(packet_buffer_.InsertPacket(&packet).frames, IsEmpty());
|
|
|
|
packet.video_header.codec = kVideoCodecVP8;
|
|
packet.video_header.video_type_header.emplace<RTPVideoHeaderVP8>();
|
|
packet.timestamp = 2;
|
|
packet.seqNum = 2;
|
|
packet.video_header.frame_type = VideoFrameType::kVideoFrameDelta;
|
|
EXPECT_THAT(packet_buffer_.InsertPacket(&packet).frames, SizeIs(2));
|
|
}
|
|
|
|
TEST_F(PacketBufferTest, TooManyNalusInPacket) {
|
|
VCMPacket packet;
|
|
packet.video_header.codec = kVideoCodecH264;
|
|
packet.timestamp = 1;
|
|
packet.seqNum = 1;
|
|
packet.video_header.frame_type = VideoFrameType::kVideoFrameKey;
|
|
packet.video_header.is_first_packet_in_frame = true;
|
|
packet.video_header.is_last_packet_in_frame = true;
|
|
auto& h264_header =
|
|
packet.video_header.video_type_header.emplace<RTPVideoHeaderH264>();
|
|
h264_header.nalus_length = kMaxNalusPerPacket;
|
|
packet.sizeBytes = 0;
|
|
packet.dataPtr = nullptr;
|
|
EXPECT_THAT(packet_buffer_.InsertPacket(&packet).frames, IsEmpty());
|
|
}
|
|
|
|
TEST_P(PacketBufferH264ParameterizedTest, OneFrameFillBuffer) {
|
|
InsertH264(0, kKeyFrame, kFirst, kNotLast, 1000);
|
|
for (int i = 1; i < kStartSize - 1; ++i)
|
|
InsertH264(i, kKeyFrame, kNotFirst, kNotLast, 1000);
|
|
EXPECT_THAT(InsertH264(kStartSize - 1, kKeyFrame, kNotFirst, kLast, 1000),
|
|
StartSeqNumsAre(0));
|
|
}
|
|
|
|
TEST_P(PacketBufferH264ParameterizedTest, CreateFramesAfterFilledBuffer) {
|
|
EXPECT_THAT(InsertH264(kStartSize - 2, kKeyFrame, kFirst, kLast, 0).frames,
|
|
SizeIs(1));
|
|
|
|
InsertH264(kStartSize, kDeltaFrame, kFirst, kNotLast, 2000);
|
|
for (int i = 1; i < kStartSize; ++i)
|
|
InsertH264(kStartSize + i, kDeltaFrame, kNotFirst, kNotLast, 2000);
|
|
EXPECT_THAT(
|
|
InsertH264(kStartSize + kStartSize, kDeltaFrame, kNotFirst, kLast, 2000)
|
|
.frames,
|
|
IsEmpty());
|
|
|
|
EXPECT_THAT(InsertH264(kStartSize - 1, kKeyFrame, kFirst, kLast, 1000),
|
|
StartSeqNumsAre(kStartSize - 1, kStartSize));
|
|
}
|
|
|
|
TEST_P(PacketBufferH264ParameterizedTest, OneFrameMaxSeqNum) {
|
|
InsertH264(65534, kKeyFrame, kFirst, kNotLast, 1000);
|
|
EXPECT_THAT(InsertH264(65535, kKeyFrame, kNotFirst, kLast, 1000),
|
|
StartSeqNumsAre(65534));
|
|
}
|
|
|
|
TEST_P(PacketBufferH264ParameterizedTest, ClearMissingPacketsOnKeyframe) {
|
|
InsertH264(0, kKeyFrame, kFirst, kLast, 1000);
|
|
InsertH264(2, kKeyFrame, kFirst, kLast, 3000);
|
|
InsertH264(3, kDeltaFrame, kFirst, kNotLast, 4000);
|
|
InsertH264(4, kDeltaFrame, kNotFirst, kLast, 4000);
|
|
|
|
EXPECT_THAT(InsertH264(kStartSize + 1, kKeyFrame, kFirst, kLast, 18000),
|
|
StartSeqNumsAre(kStartSize + 1));
|
|
}
|
|
|
|
TEST_P(PacketBufferH264ParameterizedTest, FindFramesOnPadding) {
|
|
EXPECT_THAT(InsertH264(0, kKeyFrame, kFirst, kLast, 1000),
|
|
StartSeqNumsAre(0));
|
|
EXPECT_THAT(InsertH264(2, kDeltaFrame, kFirst, kLast, 1000).frames,
|
|
IsEmpty());
|
|
|
|
EXPECT_THAT(packet_buffer_.InsertPadding(1), StartSeqNumsAre(2));
|
|
}
|
|
|
|
class PacketBufferH264XIsKeyframeTest : public PacketBufferH264Test {
|
|
protected:
|
|
const uint16_t kSeqNum = 5;
|
|
|
|
explicit PacketBufferH264XIsKeyframeTest(bool sps_pps_idr_is_keyframe)
|
|
: PacketBufferH264Test(sps_pps_idr_is_keyframe) {
|
|
packet_.video_header.codec = kVideoCodecH264;
|
|
packet_.seqNum = kSeqNum;
|
|
|
|
packet_.video_header.is_first_packet_in_frame = true;
|
|
packet_.video_header.is_last_packet_in_frame = true;
|
|
}
|
|
|
|
VCMPacket packet_;
|
|
};
|
|
|
|
class PacketBufferH264IdrIsKeyframeTest
|
|
: public PacketBufferH264XIsKeyframeTest {
|
|
protected:
|
|
PacketBufferH264IdrIsKeyframeTest()
|
|
: PacketBufferH264XIsKeyframeTest(false) {}
|
|
};
|
|
|
|
TEST_F(PacketBufferH264IdrIsKeyframeTest, IdrIsKeyframe) {
|
|
auto& h264_header =
|
|
packet_.video_header.video_type_header.emplace<RTPVideoHeaderH264>();
|
|
h264_header.nalus[0].type = H264::NaluType::kIdr;
|
|
h264_header.nalus_length = 1;
|
|
EXPECT_THAT(packet_buffer_.InsertPacket(&packet_).frames,
|
|
ElementsAre(KeyFrame()));
|
|
}
|
|
|
|
TEST_F(PacketBufferH264IdrIsKeyframeTest, SpsPpsIdrIsKeyframe) {
|
|
auto& h264_header =
|
|
packet_.video_header.video_type_header.emplace<RTPVideoHeaderH264>();
|
|
h264_header.nalus[0].type = H264::NaluType::kSps;
|
|
h264_header.nalus[1].type = H264::NaluType::kPps;
|
|
h264_header.nalus[2].type = H264::NaluType::kIdr;
|
|
h264_header.nalus_length = 3;
|
|
|
|
EXPECT_THAT(packet_buffer_.InsertPacket(&packet_).frames,
|
|
ElementsAre(KeyFrame()));
|
|
}
|
|
|
|
class PacketBufferH264SpsPpsIdrIsKeyframeTest
|
|
: public PacketBufferH264XIsKeyframeTest {
|
|
protected:
|
|
PacketBufferH264SpsPpsIdrIsKeyframeTest()
|
|
: PacketBufferH264XIsKeyframeTest(true) {}
|
|
};
|
|
|
|
TEST_F(PacketBufferH264SpsPpsIdrIsKeyframeTest, IdrIsNotKeyframe) {
|
|
auto& h264_header =
|
|
packet_.video_header.video_type_header.emplace<RTPVideoHeaderH264>();
|
|
h264_header.nalus[0].type = H264::NaluType::kIdr;
|
|
h264_header.nalus_length = 1;
|
|
|
|
EXPECT_THAT(packet_buffer_.InsertPacket(&packet_).frames,
|
|
ElementsAre(DeltaFrame()));
|
|
}
|
|
|
|
TEST_F(PacketBufferH264SpsPpsIdrIsKeyframeTest, SpsPpsIsNotKeyframe) {
|
|
auto& h264_header =
|
|
packet_.video_header.video_type_header.emplace<RTPVideoHeaderH264>();
|
|
h264_header.nalus[0].type = H264::NaluType::kSps;
|
|
h264_header.nalus[1].type = H264::NaluType::kPps;
|
|
h264_header.nalus_length = 2;
|
|
|
|
EXPECT_THAT(packet_buffer_.InsertPacket(&packet_).frames,
|
|
ElementsAre(DeltaFrame()));
|
|
}
|
|
|
|
TEST_F(PacketBufferH264SpsPpsIdrIsKeyframeTest, SpsPpsIdrIsKeyframe) {
|
|
auto& h264_header =
|
|
packet_.video_header.video_type_header.emplace<RTPVideoHeaderH264>();
|
|
h264_header.nalus[0].type = H264::NaluType::kSps;
|
|
h264_header.nalus[1].type = H264::NaluType::kPps;
|
|
h264_header.nalus[2].type = H264::NaluType::kIdr;
|
|
h264_header.nalus_length = 3;
|
|
|
|
EXPECT_THAT(packet_buffer_.InsertPacket(&packet_).frames,
|
|
ElementsAre(KeyFrame()));
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace video_coding
|
|
} // namespace webrtc
|