diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index 08f455e294..d5df853905 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -370,6 +370,7 @@ 'video_coding/jitter_estimator_tests.cc', 'video_coding/media_optimization_unittest.cc', 'video_coding/nack_module_unittest.cc', + 'video_coding/packet_buffer_unittest.cc', 'video_coding/percentile_filter_unittest.cc', 'video_coding/receiver_unittest.cc', 'video_coding/session_info_unittest.cc', diff --git a/webrtc/modules/video_coding/BUILD.gn b/webrtc/modules/video_coding/BUILD.gn index 37a0d2cb8f..bc6f595b51 100644 --- a/webrtc/modules/video_coding/BUILD.gn +++ b/webrtc/modules/video_coding/BUILD.gn @@ -25,6 +25,8 @@ source_set("video_coding") { "fec_tables_xor.h", "frame_buffer.cc", "frame_buffer.h", + "frame_object.cc", + "frame_object.h", "generic_decoder.cc", "generic_decoder.h", "generic_encoder.cc", @@ -50,6 +52,8 @@ source_set("video_coding") { "nack_module.h", "packet.cc", "packet.h", + "packet_buffer.cc", + "packet_buffer.h", "percentile_filter.cc", "percentile_filter.h", "qm_select.cc", diff --git a/webrtc/modules/video_coding/frame_object.cc b/webrtc/modules/video_coding/frame_object.cc new file mode 100644 index 0000000000..363c8a7035 --- /dev/null +++ b/webrtc/modules/video_coding/frame_object.cc @@ -0,0 +1,47 @@ +/* + * 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 "webrtc/modules/video_coding/frame_object.h" +#include "webrtc/base/criticalsection.h" +#include "webrtc/modules/video_coding/packet_buffer.h" + +namespace webrtc { +namespace video_coding { + +RtpFrameObject::RtpFrameObject(PacketBuffer* packet_buffer, + uint16_t picture_id, + uint16_t first_packet, + uint16_t last_packet) + : packet_buffer_(packet_buffer), + first_packet_(first_packet), + last_packet_(last_packet) {} + +RtpFrameObject::~RtpFrameObject() { + packet_buffer_->ReturnFrame(this); +} + +uint16_t RtpFrameObject::first_packet() const { + return first_packet_; +} + +uint16_t RtpFrameObject::last_packet() const { + return last_packet_; +} + +uint16_t RtpFrameObject::picture_id() const { + return picture_id_; +} + +bool RtpFrameObject::GetBitstream(uint8_t* destination) const { + return packet_buffer_->GetBitstream(*this, destination); +} + +} // namespace video_coding +} // namespace webrtc diff --git a/webrtc/modules/video_coding/frame_object.h b/webrtc/modules/video_coding/frame_object.h new file mode 100644 index 0000000000..2a68293d63 --- /dev/null +++ b/webrtc/modules/video_coding/frame_object.h @@ -0,0 +1,50 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CODING_FRAME_OBJECT_H_ +#define WEBRTC_MODULES_VIDEO_CODING_FRAME_OBJECT_H_ + +#include "webrtc/modules/video_coding/packet.h" + +namespace webrtc { +namespace video_coding { + +class FrameObject { + public: + virtual uint16_t picture_id() const = 0; + virtual bool GetBitstream(uint8_t* destination) const = 0; + virtual ~FrameObject() {} +}; + +class PacketBuffer; + +class RtpFrameObject : public FrameObject { + public: + RtpFrameObject(PacketBuffer* packet_buffer, + uint16_t picture_id, + uint16_t first_packet, + uint16_t last_packet); + ~RtpFrameObject(); + uint16_t first_packet() const; + uint16_t last_packet() const; + uint16_t picture_id() const override; + bool GetBitstream(uint8_t* destination) const override; + + private: + PacketBuffer* packet_buffer_; + uint16_t picture_id_; + uint16_t first_packet_; + uint16_t last_packet_; +}; + +} // namespace video_coding +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CODING_FRAME_OBJECT_H_ diff --git a/webrtc/modules/video_coding/packet_buffer.cc b/webrtc/modules/video_coding/packet_buffer.cc new file mode 100644 index 0000000000..0a05baa16a --- /dev/null +++ b/webrtc/modules/video_coding/packet_buffer.cc @@ -0,0 +1,204 @@ +/* + * 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 "webrtc/modules/video_coding/packet_buffer.h" + +#include +#include + +#include "webrtc/base/checks.h" +#include "webrtc/modules/video_coding/frame_object.h" +#include "webrtc/modules/video_coding/sequence_number_util.h" + +namespace webrtc { +namespace video_coding { + +PacketBuffer::PacketBuffer(size_t start_buffer_size, + size_t max_buffer_size, + OnCompleteFrameCallback* frame_callback) + : size_(start_buffer_size), + max_size_(max_buffer_size), + last_seq_num_(0), + first_seq_num_(0), + initialized_(false), + data_buffer_(start_buffer_size), + sequence_buffer_(start_buffer_size), + frame_callback_(frame_callback) { + RTC_DCHECK_LE(start_buffer_size, max_buffer_size); + // Buffer size must always be a power of 2. + RTC_DCHECK((start_buffer_size & (start_buffer_size - 1)) == 0); + RTC_DCHECK((max_buffer_size & (max_buffer_size - 1)) == 0); +} + +bool PacketBuffer::InsertPacket(const VCMPacket& packet) { + rtc::CritScope lock(&crit_); + uint16_t seq_num = packet.seqNum; + int index = seq_num % size_; + + if (!initialized_) { + first_seq_num_ = seq_num - 1; + last_seq_num_ = seq_num; + initialized_ = true; + } + + if (sequence_buffer_[index].used) { + // Duplicate packet, do nothing. + if (data_buffer_[index].seqNum == packet.seqNum) + return true; + + // The packet buffer is full, try to expand the buffer. + while (ExpandBufferSize() && sequence_buffer_[seq_num % size_].used) { + } + index = seq_num % size_; + + // Packet buffer is still full. + if (sequence_buffer_[index].used) + return false; + } + + if (AheadOf(seq_num, last_seq_num_)) + last_seq_num_ = seq_num; + + sequence_buffer_[index].frame_begin = packet.isFirstPacket; + sequence_buffer_[index].frame_end = packet.markerBit; + sequence_buffer_[index].seq_num = packet.seqNum; + sequence_buffer_[index].continuous = false; + sequence_buffer_[index].used = true; + data_buffer_[index] = packet; + + FindCompleteFrames(seq_num); + return true; +} + +void PacketBuffer::ClearTo(uint16_t seq_num) { + rtc::CritScope lock(&crit_); + int index = first_seq_num_ % size_; + while (AheadOf(seq_num, first_seq_num_ + 1)) { + index = (index + 1) % size_; + first_seq_num_ = Add<1 << 16>(first_seq_num_, 1); + sequence_buffer_[index].used = false; + } +} + +bool PacketBuffer::ExpandBufferSize() { + if (size_ == max_size_) + return false; + + size_t new_size = std::min(max_size_, 2 * size_); + std::vector new_data_buffer(new_size); + std::vector new_sequence_buffer(new_size); + for (size_t i = 0; i < size_; ++i) { + if (sequence_buffer_[i].used) { + int index = sequence_buffer_[i].seq_num % new_size; + new_sequence_buffer[index] = sequence_buffer_[i]; + new_data_buffer[index] = data_buffer_[i]; + } + } + size_ = new_size; + sequence_buffer_ = std::move(new_sequence_buffer); + data_buffer_ = std::move(new_data_buffer); + return true; +} + +bool PacketBuffer::IsContinuous(uint16_t seq_num) const { + int index = seq_num % size_; + int prev_index = index > 0 ? index - 1 : size_ - 1; + if (!sequence_buffer_[index].used) + return false; + if (sequence_buffer_[index].frame_begin) + return true; + if (!sequence_buffer_[prev_index].used) + return false; + if (sequence_buffer_[prev_index].continuous) + return true; + + return false; +} + +void PacketBuffer::FindCompleteFrames(uint16_t seq_num) { + int index = seq_num % size_; + while (IsContinuous(seq_num)) { + sequence_buffer_[index].continuous = true; + + // If the frame is complete, find the first packet of the frame and + // create a FrameObject. + if (sequence_buffer_[index].frame_end) { + int rindex = index; + uint16_t start_seq_num = seq_num; + while (!sequence_buffer_[rindex].frame_begin) { + rindex = rindex > 0 ? rindex - 1 : size_ - 1; + start_seq_num--; + } + + std::unique_ptr frame( + new RtpFrameObject(this, 1, start_seq_num, seq_num)); + frame_callback_->OnCompleteFrame(std::move(frame)); + } + + index = (index + 1) % size_; + ++seq_num; + } +} + +void PacketBuffer::ReturnFrame(RtpFrameObject* frame) { + rtc::CritScope lock(&crit_); + int index = frame->first_packet() % size_; + int end = (frame->last_packet() + 1) % size_; + uint16_t seq_num = frame->first_packet(); + while (index != end) { + if (sequence_buffer_[index].seq_num == seq_num) { + sequence_buffer_[index].used = false; + sequence_buffer_[index].continuous = false; + } + index = (index + 1) % size_; + ++seq_num; + } + + index = first_seq_num_ % size_; + while (AheadOf(last_seq_num_, first_seq_num_) && + !sequence_buffer_[index].used) { + ++first_seq_num_; + index = (index + 1) % size_; + } +} + +bool PacketBuffer::GetBitstream(const RtpFrameObject& frame, + uint8_t* destination) { + rtc::CritScope lock(&crit_); + + int index = frame.first_packet() % size_; + int end = (frame.last_packet() + 1) % size_; + uint16_t seq_num = frame.first_packet(); + while (index != end) { + if (!sequence_buffer_[index].used || + sequence_buffer_[index].seq_num != seq_num) { + return false; + } + + const uint8_t* source = data_buffer_[index].dataPtr; + size_t length = data_buffer_[index].sizeBytes; + memcpy(destination, source, length); + destination += length; + index = (index + 1) % size_; + ++seq_num; + } + return true; +} + +void PacketBuffer::Flush() { + rtc::CritScope lock(&crit_); + for (size_t i = 0; i < size_; ++i) { + sequence_buffer_[i].used = false; + sequence_buffer_[i].continuous = false; + } +} + +} // namespace video_coding +} // namespace webrtc diff --git a/webrtc/modules/video_coding/packet_buffer.h b/webrtc/modules/video_coding/packet_buffer.h new file mode 100644 index 0000000000..6ca514536e --- /dev/null +++ b/webrtc/modules/video_coding/packet_buffer.h @@ -0,0 +1,81 @@ +/* + * 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. + */ + +#ifndef WEBRTC_MODULES_VIDEO_CODING_PACKET_BUFFER_H_ +#define WEBRTC_MODULES_VIDEO_CODING_PACKET_BUFFER_H_ + +#include + +#include "webrtc/base/criticalsection.h" +#include "webrtc/base/scoped_ptr.h" +#include "webrtc/base/thread_annotations.h" +#include "webrtc/modules/video_coding/packet.h" + +namespace webrtc { +namespace video_coding { + +class FrameObject; +class RtpFrameObject; + +class OnCompleteFrameCallback { + public: + virtual ~OnCompleteFrameCallback() {} + virtual void OnCompleteFrame(std::unique_ptr frame) = 0; +}; + +class PacketBuffer { + public: + // Both |start_buffer_size| and |max_buffer_size| must be a power of 2. + PacketBuffer(size_t start_buffer_size, + size_t max_buffer_size, + OnCompleteFrameCallback* frame_callback); + + bool InsertPacket(const VCMPacket& packet); + void ClearTo(uint16_t seq_num); + void Flush(); + + private: + friend RtpFrameObject; + // Since we want the packet buffer to be as packet type agnostic + // as possible we extract only the information needed in order + // to determin whether a sequence of packets is continuous or not. + struct ContinuityInfo { + uint16_t seq_num = 0; + bool frame_begin = false; + bool frame_end = false; + bool used = false; + bool continuous = false; + }; + + bool ExpandBufferSize() EXCLUSIVE_LOCKS_REQUIRED(crit_); + bool IsContinuous(uint16_t seq_num) const EXCLUSIVE_LOCKS_REQUIRED(crit_); + void FindCompleteFrames(uint16_t seq_num) EXCLUSIVE_LOCKS_REQUIRED(crit_); + bool GetBitstream(const RtpFrameObject& frame, uint8_t* destination); + void ReturnFrame(RtpFrameObject* frame); + + rtc::CriticalSection crit_; + + // Buffer size_ and max_size_ must always be a power of two. + size_t size_ GUARDED_BY(crit_); + const size_t max_size_; + + uint16_t last_seq_num_ GUARDED_BY(crit_); + uint16_t first_seq_num_ GUARDED_BY(crit_); + bool initialized_ GUARDED_BY(crit_); + std::vector data_buffer_ GUARDED_BY(crit_); + std::vector sequence_buffer_ GUARDED_BY(crit_); + + OnCompleteFrameCallback* const frame_callback_; +}; + +} // namespace video_coding +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CODING_PACKET_BUFFER_H_ diff --git a/webrtc/modules/video_coding/packet_buffer_unittest.cc b/webrtc/modules/video_coding/packet_buffer_unittest.cc new file mode 100644 index 0000000000..bc06940391 --- /dev/null +++ b/webrtc/modules/video_coding/packet_buffer_unittest.cc @@ -0,0 +1,305 @@ +/* + * 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 +#include + +#include "webrtc/modules/video_coding/frame_object.h" +#include "webrtc/modules/video_coding/packet_buffer.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/random.h" + +namespace webrtc { +namespace video_coding { + +class TestPacketBuffer : public ::testing::Test, + public OnCompleteFrameCallback { + protected: + TestPacketBuffer() + : rand_(0x8739211), packet_buffer_(kStartSize, kMaxSize, this) {} + + uint16_t Rand() { return rand_.Rand(std::numeric_limits::max()); } + + void OnCompleteFrame(std::unique_ptr frame) override { + frames_from_callback_.emplace_back(std::move(frame)); + } + + void TearDown() override { + // All FrameObjects must be destroyed before the PacketBuffer since + // a FrameObject will try to remove itself from the packet buffer + // upon destruction. + frames_from_callback_.clear(); + } + + const int kStartSize = 16; + const int kMaxSize = 64; + + Random rand_; + PacketBuffer packet_buffer_; + std::vector> frames_from_callback_; +}; + +TEST_F(TestPacketBuffer, InsertOnePacket) { + VCMPacket packet; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); +} + +TEST_F(TestPacketBuffer, InsertMultiplePackets) { + VCMPacket packet; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); +} + +TEST_F(TestPacketBuffer, InsertDuplicatePacket) { + VCMPacket packet; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); +} + +TEST_F(TestPacketBuffer, ExpandBuffer) { + VCMPacket packet; + packet.seqNum = Rand(); + + for (int i = 0; i < kStartSize + 1; ++i) { + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + ++packet.seqNum; + } +} + +TEST_F(TestPacketBuffer, ExpandBufferOverflow) { + VCMPacket packet; + packet.seqNum = Rand(); + + for (int i = 0; i < kMaxSize; ++i) { + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + ++packet.seqNum; + } + + EXPECT_FALSE(packet_buffer_.InsertPacket(packet)); +} + +TEST_F(TestPacketBuffer, OnePacketOneFrame) { + VCMPacket packet; + packet.isFirstPacket = true; + packet.markerBit = true; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(1UL, frames_from_callback_.size()); +} + +TEST_F(TestPacketBuffer, TwoPacketsTwoFrames) { + VCMPacket packet; + packet.isFirstPacket = true; + packet.markerBit = true; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(2UL, frames_from_callback_.size()); +} + +TEST_F(TestPacketBuffer, TwoPacketsOneFrames) { + VCMPacket packet; + packet.isFirstPacket = true; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + packet.markerBit = true; + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(1UL, frames_from_callback_.size()); +} + +TEST_F(TestPacketBuffer, ThreePacketReorderingOneFrame) { + VCMPacket packet; + packet.isFirstPacket = true; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(0UL, frames_from_callback_.size()); + packet.isFirstPacket = false; + packet.markerBit = true; + packet.seqNum += 2; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(0UL, frames_from_callback_.size()); + packet.markerBit = false; + packet.seqNum -= 1; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(1UL, frames_from_callback_.size()); +} + +TEST_F(TestPacketBuffer, IndexWrapOneFrame) { + VCMPacket packet; + packet.isFirstPacket = true; + packet.seqNum = kStartSize - 1; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(0UL, frames_from_callback_.size()); + packet.isFirstPacket = false; + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(0UL, frames_from_callback_.size()); + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(0UL, frames_from_callback_.size()); + packet.markerBit = true; + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(1UL, frames_from_callback_.size()); +} + +TEST_F(TestPacketBuffer, DiscardOldPacket) { + uint16_t seq_num = Rand(); + VCMPacket packet; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + packet.seqNum += 2; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + + for (int i = 3; i < kMaxSize; ++i) { + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + } + + ++packet.seqNum; + EXPECT_FALSE(packet_buffer_.InsertPacket(packet)); + packet_buffer_.ClearTo(seq_num + 1); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); +} + +TEST_F(TestPacketBuffer, DiscardMultipleOldPackets) { + uint16_t seq_num = Rand(); + VCMPacket packet; + packet.seqNum = seq_num; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + packet.seqNum += 2; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + + for (int i = 3; i < kMaxSize; ++i) { + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + } + + packet_buffer_.ClearTo(seq_num + 15); + for (int i = 0; i < 15; ++i) { + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + } + for (int i = 15; i < kMaxSize; ++i) { + ++packet.seqNum; + EXPECT_FALSE(packet_buffer_.InsertPacket(packet)); + } +} + +TEST_F(TestPacketBuffer, GetBitstreamFromFrame) { + // "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)]; + + VCMPacket packet; + packet.isFirstPacket = true; + packet.seqNum = 0xfffe; + packet.dataPtr = many; + packet.sizeBytes = sizeof(many); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + packet.isFirstPacket = false; + ++packet.seqNum; + packet.dataPtr = bitstream; + packet.sizeBytes = sizeof(bitstream); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + ++packet.seqNum; + packet.dataPtr = such; + packet.sizeBytes = sizeof(such); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + packet.markerBit = true; + ++packet.seqNum; + packet.dataPtr = data; + packet.sizeBytes = sizeof(data); + EXPECT_EQ(0UL, frames_from_callback_.size()); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + ASSERT_EQ(1UL, frames_from_callback_.size()); + + EXPECT_TRUE(frames_from_callback_[0]->GetBitstream(result)); + EXPECT_EQ( + std::strcmp("many bitstream, such data", reinterpret_cast(result)), + 0); +} + +TEST_F(TestPacketBuffer, FreeSlotsOnFrameDestruction) { + VCMPacket packet; + packet.isFirstPacket = true; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(0UL, frames_from_callback_.size()); + packet.isFirstPacket = false; + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(0UL, frames_from_callback_.size()); + ++packet.seqNum; + packet.markerBit = true; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(1UL, frames_from_callback_.size()); + + frames_from_callback_.clear(); + + packet.isFirstPacket = true; + packet.markerBit = false; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(0UL, frames_from_callback_.size()); + packet.isFirstPacket = false; + ++packet.seqNum; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(0UL, frames_from_callback_.size()); + ++packet.seqNum; + packet.markerBit = true; + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(1UL, frames_from_callback_.size()); +} + +TEST_F(TestPacketBuffer, Flush) { + VCMPacket packet; + packet.isFirstPacket = true; + packet.markerBit = true; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + packet_buffer_.Flush(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_EQ(2UL, frames_from_callback_.size()); +} + +TEST_F(TestPacketBuffer, InvalidateFrameByFlushing) { + VCMPacket packet; + packet.isFirstPacket = true; + packet.markerBit = true; + packet.seqNum = Rand(); + EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + ASSERT_EQ(1UL, frames_from_callback_.size()); + + packet_buffer_.Flush(); + EXPECT_FALSE(frames_from_callback_[0]->GetBitstream(nullptr)); +} + +} // namespace video_coding +} // namespace webrtc diff --git a/webrtc/modules/video_coding/video_coding.gypi b/webrtc/modules/video_coding/video_coding.gypi index 82c272654c..7cfefed3ee 100644 --- a/webrtc/modules/video_coding/video_coding.gypi +++ b/webrtc/modules/video_coding/video_coding.gypi @@ -34,6 +34,7 @@ 'encoded_frame.h', 'fec_tables_xor.h', 'frame_buffer.h', + 'frame_object.h', 'generic_decoder.h', 'generic_encoder.h', 'histogram.h', @@ -47,6 +48,7 @@ 'nack_fec_tables.h', 'nack_module.h', 'packet.h', + 'packet_buffer.h', 'percentile_filter.h', 'qm_select_data.h', 'qm_select.h', @@ -65,6 +67,7 @@ 'decoding_state.cc', 'encoded_frame.cc', 'frame_buffer.cc', + 'frame_object.cc', 'generic_decoder.cc', 'generic_encoder.cc', 'inter_frame_delay.cc', @@ -75,6 +78,7 @@ 'media_optimization.cc', 'nack_module.cc', 'packet.cc', + 'packet_buffer.cc', 'percentile_filter.cc', 'qm_select.cc', 'receiver.cc',