| /* |
| * 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 <cstring> |
| #include <map> |
| #include <set> |
| #include <utility> |
| |
| #include "webrtc/base/random.h" |
| #include "webrtc/modules/video_coding/frame_object.h" |
| #include "webrtc/modules/video_coding/packet_buffer.h" |
| #include "webrtc/system_wrappers/include/clock.h" |
| #include "webrtc/test/gtest.h" |
| |
| namespace webrtc { |
| namespace video_coding { |
| |
| class TestPacketBuffer : public ::testing::Test, |
| public OnReceivedFrameCallback { |
| protected: |
| TestPacketBuffer() |
| : rand_(0x7732213), |
| clock_(new SimulatedClock(0)), |
| packet_buffer_( |
| PacketBuffer::Create(clock_.get(), kStartSize, kMaxSize, this)) {} |
| |
| uint16_t Rand() { return rand_.Rand<uint16_t>(); } |
| |
| void OnReceivedFrame(std::unique_ptr<RtpFrameObject> frame) override { |
| uint16_t first_seq_num = frame->first_seq_num(); |
| if (frames_from_callback_.find(first_seq_num) != |
| frames_from_callback_.end()) { |
| ADD_FAILURE() << "Already received frame with first sequence number " |
| << first_seq_num << "."; |
| return; |
| } |
| frames_from_callback_.insert( |
| std::make_pair(frame->first_seq_num(), std::move(frame))); |
| } |
| |
| enum IsKeyFrame { kKeyFrame, kDeltaFrame }; |
| enum IsFirst { kFirst, kNotFirst }; |
| enum IsLast { kLast, kNotLast }; |
| |
| bool 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 |
| VCMPacket packet; |
| packet.codec = kVideoCodecGeneric; |
| packet.seqNum = seq_num; |
| packet.frameType = keyframe ? kVideoFrameKey : kVideoFrameDelta; |
| packet.isFirstPacket = first == kFirst; |
| packet.markerBit = last == kLast; |
| packet.sizeBytes = data_size; |
| packet.dataPtr = data; |
| |
| return packet_buffer_->InsertPacket(packet); |
| } |
| |
| void CheckFrame(uint16_t first_seq_num) { |
| auto frame_it = frames_from_callback_.find(first_seq_num); |
| ASSERT_FALSE(frame_it == frames_from_callback_.end()) |
| << "Could not find frame with first sequence number " << first_seq_num |
| << "."; |
| } |
| |
| const int kStartSize = 16; |
| const int kMaxSize = 64; |
| |
| Random rand_; |
| std::unique_ptr<Clock> clock_; |
| rtc::scoped_refptr<PacketBuffer> packet_buffer_; |
| std::map<uint16_t, std::unique_ptr<RtpFrameObject>> frames_from_callback_; |
| }; |
| |
| TEST_F(TestPacketBuffer, InsertOnePacket) { |
| const uint16_t seq_num = Rand(); |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kLast)); |
| } |
| |
| TEST_F(TestPacketBuffer, InsertMultiplePackets) { |
| const uint16_t seq_num = Rand(); |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 1, kKeyFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 2, kKeyFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 3, kKeyFrame, kFirst, kLast)); |
| } |
| |
| TEST_F(TestPacketBuffer, InsertDuplicatePacket) { |
| const uint16_t seq_num = Rand(); |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kLast)); |
| } |
| |
| TEST_F(TestPacketBuffer, SeqNumWrap) { |
| EXPECT_TRUE(Insert(0xFFFF, kKeyFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(0x0, kKeyFrame, kFirst, kLast)); |
| |
| CheckFrame(0xFFFF); |
| } |
| |
| TEST_F(TestPacketBuffer, InsertOldPackets) { |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + 2, kDeltaFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast)); |
| ASSERT_EQ(2UL, frames_from_callback_.size()); |
| |
| frames_from_callback_.erase(seq_num + 2); |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kNotLast)); |
| ASSERT_EQ(1UL, frames_from_callback_.size()); |
| |
| frames_from_callback_.erase(frames_from_callback_.find(seq_num)); |
| ASSERT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + 2, kDeltaFrame, kFirst, kLast)); |
| |
| packet_buffer_->ClearTo(seq_num + 2); |
| EXPECT_FALSE(Insert(seq_num + 2, kDeltaFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 3, kDeltaFrame, kFirst, kLast)); |
| ASSERT_EQ(2UL, frames_from_callback_.size()); |
| } |
| |
| TEST_F(TestPacketBuffer, NackCount) { |
| const uint16_t seq_num = Rand(); |
| |
| VCMPacket packet; |
| packet.codec = kVideoCodecGeneric; |
| packet.seqNum = seq_num; |
| packet.frameType = kVideoFrameKey; |
| packet.isFirstPacket = true; |
| packet.markerBit = false; |
| packet.timesNacked = 0; |
| |
| packet_buffer_->InsertPacket(packet); |
| |
| packet.seqNum++; |
| packet.isFirstPacket = false; |
| packet.timesNacked = 1; |
| packet_buffer_->InsertPacket(packet); |
| |
| packet.seqNum++; |
| packet.timesNacked = 3; |
| packet_buffer_->InsertPacket(packet); |
| |
| packet.seqNum++; |
| packet.markerBit = true; |
| packet.timesNacked = 1; |
| packet_buffer_->InsertPacket(packet); |
| |
| ASSERT_EQ(1UL, frames_from_callback_.size()); |
| RtpFrameObject* frame = frames_from_callback_.begin()->second.get(); |
| EXPECT_EQ(3, frame->times_nacked()); |
| } |
| |
| TEST_F(TestPacketBuffer, FrameSize) { |
| const uint16_t seq_num = Rand(); |
| uint8_t data[] = {1, 2, 3, 4, 5}; |
| |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kNotLast, 5, data)); |
| EXPECT_TRUE(Insert(seq_num + 1, kKeyFrame, kNotFirst, kNotLast, 5, data)); |
| EXPECT_TRUE(Insert(seq_num + 2, kKeyFrame, kNotFirst, kNotLast, 5, data)); |
| EXPECT_TRUE(Insert(seq_num + 3, kKeyFrame, kNotFirst, kLast, 5, data)); |
| |
| ASSERT_EQ(1UL, frames_from_callback_.size()); |
| EXPECT_EQ(20UL, frames_from_callback_.begin()->second->size()); |
| } |
| |
| TEST_F(TestPacketBuffer, ExpandBuffer) { |
| const uint16_t seq_num = Rand(); |
| |
| for (int i = 0; i < kStartSize + 1; ++i) { |
| EXPECT_TRUE(Insert(seq_num + i, kKeyFrame, kFirst, kLast)); |
| } |
| } |
| |
| TEST_F(TestPacketBuffer, SingleFrameExpandsBuffer) { |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kNotLast)); |
| for (int i = 1; i < kStartSize; ++i) |
| EXPECT_TRUE(Insert(seq_num + i, kKeyFrame, kNotFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + kStartSize, kKeyFrame, kNotFirst, kLast)); |
| |
| ASSERT_EQ(1UL, frames_from_callback_.size()); |
| CheckFrame(seq_num); |
| } |
| |
| TEST_F(TestPacketBuffer, ExpandBufferOverflow) { |
| const uint16_t seq_num = Rand(); |
| |
| for (int i = 0; i < kMaxSize; ++i) |
| EXPECT_TRUE(Insert(seq_num + i, kKeyFrame, kFirst, kLast)); |
| EXPECT_FALSE(Insert(seq_num + kMaxSize + 1, kKeyFrame, kFirst, kLast)); |
| } |
| |
| TEST_F(TestPacketBuffer, OnePacketOneFrame) { |
| const uint16_t seq_num = Rand(); |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kLast)); |
| ASSERT_EQ(1UL, frames_from_callback_.size()); |
| CheckFrame(seq_num); |
| } |
| |
| TEST_F(TestPacketBuffer, TwoPacketsTwoFrames) { |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 1, kKeyFrame, kFirst, kLast)); |
| |
| EXPECT_EQ(2UL, frames_from_callback_.size()); |
| CheckFrame(seq_num); |
| CheckFrame(seq_num + 1); |
| } |
| |
| TEST_F(TestPacketBuffer, TwoPacketsOneFrames) { |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + 1, kKeyFrame, kNotFirst, kLast)); |
| |
| EXPECT_EQ(1UL, frames_from_callback_.size()); |
| CheckFrame(seq_num); |
| } |
| |
| TEST_F(TestPacketBuffer, ThreePacketReorderingOneFrame) { |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + 2, kKeyFrame, kNotFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 1, kKeyFrame, kNotFirst, kNotLast)); |
| |
| EXPECT_EQ(1UL, frames_from_callback_.size()); |
| CheckFrame(seq_num); |
| } |
| |
| TEST_F(TestPacketBuffer, Frames) { |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 1, kDeltaFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 2, kDeltaFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 3, kDeltaFrame, kFirst, kLast)); |
| |
| ASSERT_EQ(4UL, frames_from_callback_.size()); |
| CheckFrame(seq_num); |
| CheckFrame(seq_num + 1); |
| CheckFrame(seq_num + 2); |
| CheckFrame(seq_num + 3); |
| } |
| |
| TEST_F(TestPacketBuffer, ClearSinglePacket) { |
| const uint16_t seq_num = Rand(); |
| |
| for (int i = 0; i < kMaxSize; ++i) |
| EXPECT_TRUE(Insert(seq_num + i, kDeltaFrame, kFirst, kLast)); |
| |
| packet_buffer_->ClearTo(seq_num); |
| EXPECT_TRUE(Insert(seq_num + kMaxSize, kDeltaFrame, kFirst, kLast)); |
| } |
| |
| TEST_F(TestPacketBuffer, OneIncompleteFrame) { |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num, kDeltaFrame, kFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + 1, kDeltaFrame, kNotFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num - 1, kDeltaFrame, kNotFirst, kLast)); |
| |
| ASSERT_EQ(1UL, frames_from_callback_.size()); |
| CheckFrame(seq_num); |
| } |
| |
| TEST_F(TestPacketBuffer, TwoIncompleteFramesFullBuffer) { |
| const uint16_t seq_num = Rand(); |
| |
| for (int i = 1; i < kMaxSize - 1; ++i) |
| EXPECT_TRUE(Insert(seq_num + i, kDeltaFrame, kNotFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num, kDeltaFrame, kFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num - 1, kDeltaFrame, kNotFirst, kLast)); |
| |
| ASSERT_EQ(0UL, frames_from_callback_.size()); |
| } |
| |
| TEST_F(TestPacketBuffer, FramesReordered) { |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num + 1, kDeltaFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 3, kDeltaFrame, kFirst, kLast)); |
| EXPECT_TRUE(Insert(seq_num + 2, kDeltaFrame, kFirst, kLast)); |
| |
| ASSERT_EQ(4UL, frames_from_callback_.size()); |
| CheckFrame(seq_num); |
| CheckFrame(seq_num + 1); |
| CheckFrame(seq_num + 2); |
| CheckFrame(seq_num + 3); |
| } |
| |
| TEST_F(TestPacketBuffer, GetBitstream) { |
| // "many bitstream, such data" with null termination. |
| uint8_t many[] = {0x6d, 0x61, 0x6e, 0x79, 0x20}; |
| uint8_t bitstream[] = {0x62, 0x69, 0x74, 0x73, 0x74, 0x72, |
| 0x65, 0x61, 0x6d, 0x2c, 0x20}; |
| uint8_t such[] = {0x73, 0x75, 0x63, 0x68, 0x20}; |
| uint8_t data[] = {0x64, 0x61, 0x74, 0x61, 0x0}; |
| uint8_t |
| result[sizeof(many) + sizeof(bitstream) + sizeof(such) + sizeof(data)]; |
| |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kNotLast, sizeof(many), many)); |
| EXPECT_TRUE(Insert(seq_num + 1, kDeltaFrame, kNotFirst, kNotLast, |
| sizeof(bitstream), bitstream)); |
| EXPECT_TRUE(Insert(seq_num + 2, kDeltaFrame, kNotFirst, kNotLast, |
| sizeof(such), such)); |
| EXPECT_TRUE( |
| Insert(seq_num + 3, kDeltaFrame, kNotFirst, kLast, sizeof(data), data)); |
| |
| ASSERT_EQ(1UL, frames_from_callback_.size()); |
| CheckFrame(seq_num); |
| EXPECT_TRUE(frames_from_callback_[seq_num]->GetBitstream(result)); |
| EXPECT_EQ(memcmp(result, "many bitstream, such data", sizeof(result)), 0); |
| } |
| |
| TEST_F(TestPacketBuffer, GetBitstreamH264BufferPadding) { |
| uint16_t seq_num = Rand(); |
| uint8_t data[] = "some plain old data"; |
| |
| // EncodedImage::kBufferPaddingBytesH264 is unknown at compile time. |
| uint8_t* result = |
| new uint8_t[sizeof(data) + EncodedImage::kBufferPaddingBytesH264]; |
| |
| VCMPacket packet; |
| packet.seqNum = seq_num; |
| packet.codec = kVideoCodecH264; |
| packet.insertStartCode = true; |
| packet.video_header.codecHeader.H264.packetization_type = kH264SingleNalu; |
| packet.dataPtr = data; |
| packet.sizeBytes = sizeof(data); |
| packet.isFirstPacket = true; |
| packet.markerBit = true; |
| packet_buffer_->InsertPacket(packet); |
| |
| ASSERT_EQ(1UL, frames_from_callback_.size()); |
| EXPECT_EQ(frames_from_callback_[seq_num]->EncodedImage()._length, |
| sizeof(data)); |
| EXPECT_EQ(frames_from_callback_[seq_num]->EncodedImage()._size, |
| sizeof(data) + EncodedImage::kBufferPaddingBytesH264); |
| EXPECT_TRUE(frames_from_callback_[seq_num]->GetBitstream(result)); |
| EXPECT_EQ(memcmp(result, data, sizeof(data)), 0); |
| delete[] result; |
| } |
| |
| TEST_F(TestPacketBuffer, FreeSlotsOnFrameDestruction) { |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + 1, kDeltaFrame, kNotFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + 2, kDeltaFrame, kNotFirst, kLast)); |
| EXPECT_EQ(1UL, frames_from_callback_.size()); |
| CheckFrame(seq_num); |
| |
| frames_from_callback_.clear(); |
| |
| // Insert frame that fills the whole buffer. |
| EXPECT_TRUE(Insert(seq_num + 3, kKeyFrame, kFirst, kNotLast)); |
| for (int i = 0; i < kMaxSize - 2; ++i) |
| EXPECT_TRUE(Insert(seq_num + i + 4, kDeltaFrame, kNotFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + kMaxSize + 2, kKeyFrame, kNotFirst, kLast)); |
| EXPECT_EQ(1UL, frames_from_callback_.size()); |
| CheckFrame(seq_num + 3); |
| } |
| |
| TEST_F(TestPacketBuffer, Clear) { |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + 1, kDeltaFrame, kNotFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + 2, kDeltaFrame, kNotFirst, kLast)); |
| EXPECT_EQ(1UL, frames_from_callback_.size()); |
| CheckFrame(seq_num); |
| |
| packet_buffer_->Clear(); |
| |
| EXPECT_TRUE(Insert(seq_num + kStartSize, kKeyFrame, kFirst, kNotLast)); |
| EXPECT_TRUE( |
| Insert(seq_num + kStartSize + 1, kDeltaFrame, kNotFirst, kNotLast)); |
| EXPECT_TRUE(Insert(seq_num + kStartSize + 2, kDeltaFrame, kNotFirst, kLast)); |
| EXPECT_EQ(2UL, frames_from_callback_.size()); |
| CheckFrame(seq_num + kStartSize); |
| } |
| |
| TEST_F(TestPacketBuffer, InvalidateFrameByClearing) { |
| const uint16_t seq_num = Rand(); |
| |
| EXPECT_TRUE(Insert(seq_num, kKeyFrame, kFirst, kLast)); |
| ASSERT_EQ(1UL, frames_from_callback_.size()); |
| |
| packet_buffer_->Clear(); |
| EXPECT_FALSE(frames_from_callback_.begin()->second->GetBitstream(nullptr)); |
| } |
| |
| } // namespace video_coding |
| } // namespace webrtc |