diff --git a/webrtc/modules/video_coding/frame_object.cc b/webrtc/modules/video_coding/frame_object.cc index 363c8a7035..7a05d28d56 100644 --- a/webrtc/modules/video_coding/frame_object.cc +++ b/webrtc/modules/video_coding/frame_object.cc @@ -16,7 +16,6 @@ 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), @@ -27,18 +26,14 @@ RtpFrameObject::~RtpFrameObject() { packet_buffer_->ReturnFrame(this); } -uint16_t RtpFrameObject::first_packet() const { +uint16_t RtpFrameObject::first_seq_num() const { return first_packet_; } -uint16_t RtpFrameObject::last_packet() const { +uint16_t RtpFrameObject::last_seq_num() 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); } diff --git a/webrtc/modules/video_coding/frame_object.h b/webrtc/modules/video_coding/frame_object.h index 2a68293d63..c5cdd6eb11 100644 --- a/webrtc/modules/video_coding/frame_object.h +++ b/webrtc/modules/video_coding/frame_object.h @@ -11,16 +11,25 @@ #ifndef WEBRTC_MODULES_VIDEO_CODING_FRAME_OBJECT_H_ #define WEBRTC_MODULES_VIDEO_CODING_FRAME_OBJECT_H_ -#include "webrtc/modules/video_coding/packet.h" +#include +#include + +#include namespace webrtc { namespace video_coding { class FrameObject { public: - virtual uint16_t picture_id() const = 0; + static const uint8_t kMaxFrameReferences = 5; + virtual bool GetBitstream(uint8_t* destination) const = 0; virtual ~FrameObject() {} + + uint16_t picture_id; + size_t num_references; + std::array referencesr; + uint16_t references[kMaxFrameReferences]; }; class PacketBuffer; @@ -28,18 +37,16 @@ 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; + uint16_t first_seq_num() const; + uint16_t last_seq_num() const; bool GetBitstream(uint8_t* destination) const override; private: PacketBuffer* packet_buffer_; - uint16_t picture_id_; uint16_t first_packet_; uint16_t last_packet_; }; diff --git a/webrtc/modules/video_coding/packet_buffer.cc b/webrtc/modules/video_coding/packet_buffer.cc index 0a05baa16a..8cbfc0c66f 100644 --- a/webrtc/modules/video_coding/packet_buffer.cc +++ b/webrtc/modules/video_coding/packet_buffer.cc @@ -15,7 +15,6 @@ #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 { @@ -25,12 +24,14 @@ PacketBuffer::PacketBuffer(size_t start_buffer_size, OnCompleteFrameCallback* frame_callback) : size_(start_buffer_size), max_size_(max_buffer_size), - last_seq_num_(0), first_seq_num_(0), - initialized_(false), + last_seq_num_(0), + first_packet_received_(false), data_buffer_(start_buffer_size), sequence_buffer_(start_buffer_size), - frame_callback_(frame_callback) { + frame_callback_(frame_callback), + last_picture_id_(-1), + last_unwrap_(-1) { 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); @@ -40,12 +41,12 @@ PacketBuffer::PacketBuffer(size_t start_buffer_size, bool PacketBuffer::InsertPacket(const VCMPacket& packet) { rtc::CritScope lock(&crit_); uint16_t seq_num = packet.seqNum; - int index = seq_num % size_; + size_t index = seq_num % size_; - if (!initialized_) { + if (!first_packet_received_) { first_seq_num_ = seq_num - 1; last_seq_num_ = seq_num; - initialized_ = true; + first_packet_received_ = true; } if (sequence_buffer_[index].used) { @@ -70,16 +71,17 @@ bool PacketBuffer::InsertPacket(const VCMPacket& packet) { sequence_buffer_[index].frame_end = packet.markerBit; sequence_buffer_[index].seq_num = packet.seqNum; sequence_buffer_[index].continuous = false; + sequence_buffer_[index].frame_created = false; sequence_buffer_[index].used = true; data_buffer_[index] = packet; - FindCompleteFrames(seq_num); + FindFrames(seq_num); return true; } void PacketBuffer::ClearTo(uint16_t seq_num) { rtc::CritScope lock(&crit_); - int index = first_seq_num_ % size_; + size_t 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); @@ -96,7 +98,7 @@ bool PacketBuffer::ExpandBufferSize() { 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; + size_t index = sequence_buffer_[i].seq_num % new_size; new_sequence_buffer[index] = sequence_buffer_[i]; new_data_buffer[index] = data_buffer_[i]; } @@ -108,38 +110,47 @@ bool PacketBuffer::ExpandBufferSize() { } bool PacketBuffer::IsContinuous(uint16_t seq_num) const { - int index = seq_num % size_; + size_t 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_created) + return false; if (sequence_buffer_[index].frame_begin) return true; if (!sequence_buffer_[prev_index].used) return false; + if (sequence_buffer_[prev_index].seq_num != + static_cast(seq_num - 1)) + return false; if (sequence_buffer_[prev_index].continuous) return true; return false; } -void PacketBuffer::FindCompleteFrames(uint16_t seq_num) { - int index = seq_num % size_; +void PacketBuffer::FindFrames(uint16_t seq_num) { + size_t 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 all packets of the frame is continuous, find the first packet of the + // frame and create an RtpFrameObject. if (sequence_buffer_[index].frame_end) { - int rindex = index; + int start_index = index; uint16_t start_seq_num = seq_num; - while (!sequence_buffer_[rindex].frame_begin) { - rindex = rindex > 0 ? rindex - 1 : size_ - 1; + + while (!sequence_buffer_[start_index].frame_begin) { + sequence_buffer_[start_index].frame_created = true; + start_index = start_index > 0 ? start_index - 1 : size_ - 1; start_seq_num--; } + sequence_buffer_[start_index].frame_created = true; - std::unique_ptr frame( - new RtpFrameObject(this, 1, start_seq_num, seq_num)); - frame_callback_->OnCompleteFrame(std::move(frame)); + std::unique_ptr frame( + new RtpFrameObject(this, start_seq_num, seq_num)); + ManageFrame(std::move(frame)); } index = (index + 1) % size_; @@ -149,14 +160,13 @@ void PacketBuffer::FindCompleteFrames(uint16_t 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(); + size_t index = frame->first_seq_num() % size_; + size_t end = (frame->last_seq_num() + 1) % size_; + uint16_t seq_num = frame->first_seq_num(); while (index != end) { - if (sequence_buffer_[index].seq_num == seq_num) { + if (sequence_buffer_[index].seq_num == seq_num) sequence_buffer_[index].used = false; - sequence_buffer_[index].continuous = false; - } + index = (index + 1) % size_; ++seq_num; } @@ -173,9 +183,9 @@ 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(); + size_t index = frame.first_seq_num() % size_; + size_t end = (frame.last_seq_num() + 1) % size_; + uint16_t seq_num = frame.first_seq_num(); while (index != end) { if (!sequence_buffer_[index].used || sequence_buffer_[index].seq_num != seq_num) { @@ -192,12 +202,266 @@ bool PacketBuffer::GetBitstream(const RtpFrameObject& frame, return true; } +void PacketBuffer::ManageFrame(std::unique_ptr frame) { + size_t start_index = frame->first_seq_num() % size_; + VideoCodecType codec_type = data_buffer_[start_index].codec; + + switch (codec_type) { + case kVideoCodecULPFEC : + case kVideoCodecRED : + case kVideoCodecUnknown : { + RTC_NOTREACHED(); + } + case kVideoCodecVP8 : { + ManageFrameVp8(std::move(frame)); + break; + } + case kVideoCodecVP9 : { + // TODO(philipel): ManageFrameVp9(std::move(frame)); + break; + } + case kVideoCodecH264 : + case kVideoCodecI420 : + case kVideoCodecGeneric : + default : { + ManageFrameGeneric(std::move(frame)); + } + } +} + +void PacketBuffer::RetryStashedFrames() { + size_t num_stashed_frames = stashed_frames_.size(); + + // Clean up stashed frames if there are too many. + while (stashed_frames_.size() > kMaxStashedFrames) + stashed_frames_.pop(); + + // Since frames are stashed if there is not enough data to determine their + // frame references we should at most check |stashed_frames_.size()| in + // order to not pop and push frames in and endless loop. + for (size_t i = 0; i < num_stashed_frames && !stashed_frames_.empty(); ++i) { + std::unique_ptr frame = std::move(stashed_frames_.front()); + stashed_frames_.pop(); + ManageFrame(std::move(frame)); + } +} + +void PacketBuffer::ManageFrameGeneric( + std::unique_ptr frame) { + size_t index = frame->first_seq_num() % size_; + const VCMPacket& packet = data_buffer_[index]; + + if (packet.frameType == kVideoFrameKey) + last_seq_num_gop_[frame->last_seq_num()] = frame->last_seq_num(); + + // We have received a frame but not yet a keyframe, stash this frame. + if (last_seq_num_gop_.empty()) { + stashed_frames_.emplace(std::move(frame)); + return; + } + + // Clean up info for old keyframes but make sure to keep info + // for the last keyframe. + auto clean_to = last_seq_num_gop_.lower_bound(frame->last_seq_num() - 100); + if (clean_to != last_seq_num_gop_.end()) + last_seq_num_gop_.erase(last_seq_num_gop_.begin(), clean_to); + + // Find the last sequence number of the last frame for the keyframe + // that this frame indirectly references. + auto seq_num_it = last_seq_num_gop_.upper_bound(frame->last_seq_num()); + seq_num_it--; + + // Make sure the packet sequence numbers are continuous, otherwise stash + // this frame. + if (packet.frameType == kVideoFrameDelta) { + if (seq_num_it->second != + static_cast(frame->first_seq_num() - 1)) { + stashed_frames_.emplace(std::move(frame)); + return; + } + } + + RTC_DCHECK(AheadOrAt(frame->last_seq_num(), seq_num_it->first)); + + // Since keyframes can cause reordering of the frames delivered from + // FindFrames() we can't simply assign the picture id according to some + // incrementing counter. + frame->picture_id = frame->last_seq_num(); + frame->num_references = packet.frameType == kVideoFrameDelta; + frame->references[0] = seq_num_it->second; + seq_num_it->second = frame->picture_id; + + last_picture_id_ = frame->picture_id; + frame_callback_->OnCompleteFrame(std::move(frame)); + RetryStashedFrames(); +} + +void PacketBuffer::ManageFrameVp8(std::unique_ptr frame) { + size_t index = frame->first_seq_num() % size_; + const VCMPacket& packet = data_buffer_[index]; + const RTPVideoHeaderVP8& codec_header = + packet.codecSpecificHeader.codecHeader.VP8; + + if (codec_header.pictureId == kNoPictureId || + codec_header.temporalIdx == kNoTemporalIdx || + codec_header.tl0PicIdx == kNoTl0PicIdx) { + ManageFrameGeneric(std::move(frame)); + return; + } + + frame->picture_id = codec_header.pictureId % kPicIdLength; + + if (last_unwrap_ == -1) + last_unwrap_ = codec_header.pictureId; + + if (last_picture_id_ == -1) + last_picture_id_ = frame->picture_id; + + // Find if there has been a gap in fully received frames and save the picture + // id of those frames in |not_yet_received_frames_|. + if (AheadOf(frame->picture_id, last_picture_id_)) { + last_picture_id_ = Add(last_picture_id_, 1); + while (last_picture_id_ != frame->picture_id) { + not_yet_received_frames_.insert(last_picture_id_); + last_picture_id_ = Add(last_picture_id_, 1); + } + } + + // Clean up info for base layers that are too old. + uint8_t old_tl0_pic_idx = codec_header.tl0PicIdx - kMaxLayerInfo; + auto clean_layer_info_to = layer_info_.lower_bound(old_tl0_pic_idx); + layer_info_.erase(layer_info_.begin(), clean_layer_info_to); + + // Clean up info about not yet received frames that are too old. + uint16_t old_picture_id = Subtract(frame->picture_id, + kMaxNotYetReceivedFrames); + auto clean_frames_to = not_yet_received_frames_.lower_bound(old_picture_id); + not_yet_received_frames_.erase(not_yet_received_frames_.begin(), + clean_frames_to); + + if (packet.frameType == kVideoFrameKey) { + frame->num_references = 0; + layer_info_[codec_header.tl0PicIdx].fill(-1); + CompletedFrameVp8(std::move(frame)); + return; + } + + auto layer_info_it = layer_info_.find(codec_header.temporalIdx == 0 + ? codec_header.tl0PicIdx - 1 + : codec_header.tl0PicIdx); + + // If we don't have the base layer frame yet, stash this frame. + if (layer_info_it == layer_info_.end()) { + stashed_frames_.emplace(std::move(frame)); + return; + } + + // A non keyframe base layer frame has been received, copy the layer info + // from the previous base layer frame and set a reference to the previous + // base layer frame. + if (codec_header.temporalIdx == 0) { + layer_info_it = + layer_info_ + .insert(make_pair(codec_header.tl0PicIdx, layer_info_it->second)) + .first; + frame->num_references = 1; + frame->references[0] = layer_info_it->second[0]; + CompletedFrameVp8(std::move(frame)); + return; + } + + // Layer sync frame, this frame only references its base layer frame. + if (codec_header.layerSync) { + frame->num_references = 1; + frame->references[0] = layer_info_it->second[0]; + + CompletedFrameVp8(std::move(frame)); + return; + } + + // Find all references for this frame. + frame->num_references = 0; + for (uint8_t layer = 0; layer <= codec_header.temporalIdx; ++layer) { + RTC_DCHECK_NE(-1, layer_info_it->second[layer]); + + // If we have not yet received a frame between this frame and the referenced + // frame then we have to wait for that frame to be completed first. + auto not_received_frame_it = + not_yet_received_frames_.upper_bound(layer_info_it->second[layer]); + if (not_received_frame_it != not_yet_received_frames_.end() && + AheadOf(frame->picture_id, + *not_received_frame_it)) { + stashed_frames_.emplace(std::move(frame)); + return; + } + + ++frame->num_references; + frame->references[layer] = layer_info_it->second[layer]; + } + + CompletedFrameVp8(std::move(frame)); +} + +void PacketBuffer::CompletedFrameVp8(std::unique_ptr frame) { + size_t index = frame->first_seq_num() % size_; + const VCMPacket& packet = data_buffer_[index]; + const RTPVideoHeaderVP8& codec_header = + packet.codecSpecificHeader.codecHeader.VP8; + + uint8_t tl0_pic_idx = codec_header.tl0PicIdx; + uint8_t temporal_index = codec_header.temporalIdx; + auto layer_info_it = layer_info_.find(tl0_pic_idx); + + // Update this layer info and newer. + while (layer_info_it != layer_info_.end()) { + if (layer_info_it->second[temporal_index] != -1 && + AheadOf(layer_info_it->second[temporal_index], + frame->picture_id)) { + // The frame was not newer, then no subsequent layer info have to be + // update. + break; + } + + layer_info_it->second[codec_header.temporalIdx] = frame->picture_id; + ++tl0_pic_idx; + layer_info_it = layer_info_.find(tl0_pic_idx); + } + not_yet_received_frames_.erase(frame->picture_id); + + for (size_t r = 0; r < frame->num_references; ++r) + frame->references[r] = UnwrapPictureId(frame->references[r]); + frame->picture_id = UnwrapPictureId(frame->picture_id); + + frame_callback_->OnCompleteFrame(std::move(frame)); + RetryStashedFrames(); +} + +uint16_t PacketBuffer::UnwrapPictureId(uint16_t picture_id) { + if (last_unwrap_ == -1) + last_unwrap_ = picture_id; + + uint16_t unwrap_truncated = last_unwrap_ % kPicIdLength; + uint16_t diff = MinDiff(unwrap_truncated, picture_id); + + if (AheadOf(picture_id, unwrap_truncated)) + last_unwrap_ = Add<1 << 16>(last_unwrap_, diff); + else + last_unwrap_ = Subtract<1 << 16>(last_unwrap_, diff); + + return last_unwrap_; +} + void PacketBuffer::Flush() { rtc::CritScope lock(&crit_); - for (size_t i = 0; i < size_; ++i) { + for (size_t i = 0; i < size_; ++i) sequence_buffer_[i].used = false; - sequence_buffer_[i].continuous = false; - } + + last_seq_num_gop_.clear(); + while (!stashed_frames_.empty()) + stashed_frames_.pop(); + not_yet_received_frames_.clear(); + + first_packet_received_ = false; } } // namespace video_coding diff --git a/webrtc/modules/video_coding/packet_buffer.h b/webrtc/modules/video_coding/packet_buffer.h index 6ca514536e..caa81f6b99 100644 --- a/webrtc/modules/video_coding/packet_buffer.h +++ b/webrtc/modules/video_coding/packet_buffer.h @@ -11,12 +11,17 @@ #ifndef WEBRTC_MODULES_VIDEO_CODING_PACKET_BUFFER_H_ #define WEBRTC_MODULES_VIDEO_CODING_PACKET_BUFFER_H_ +#include #include +#include +#include +#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" +#include "webrtc/modules/video_coding/sequence_number_util.h" namespace webrtc { namespace video_coding { @@ -42,37 +47,131 @@ class PacketBuffer { void Flush(); private: + static const uint16_t kPicIdLength = 1 << 7; + static const uint8_t kMaxTemporalLayer = 5; + static const int kMaxStashedFrames = 10; + static const int kMaxLayerInfo = 10; + static const int kMaxNotYetReceivedFrames = 20; + 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. + // to determine whether a sequence of packets is continuous or not. struct ContinuityInfo { + // The sequence number of the packet. uint16_t seq_num = 0; + + // If this is the first packet of the frame. bool frame_begin = false; + + // If this is the last packet of the frame. bool frame_end = false; + + // If this slot is currently used. bool used = false; + + // If all its previous packets have been inserted into the packet buffer. bool continuous = false; + + // If this packet has been used to create a frame already. + bool frame_created = false; }; + // Expand the buffer. bool ExpandBufferSize() EXCLUSIVE_LOCKS_REQUIRED(crit_); + + // Test if all previous packets has arrived for the given sequence number. bool IsContinuous(uint16_t seq_num) const EXCLUSIVE_LOCKS_REQUIRED(crit_); - void FindCompleteFrames(uint16_t seq_num) EXCLUSIVE_LOCKS_REQUIRED(crit_); + + // Test if all packets of a frame has arrived, and if so, creates a frame. + // May create multiple frames per invocation. + void FindFrames(uint16_t seq_num) EXCLUSIVE_LOCKS_REQUIRED(crit_); + + // Copy the bitstream for |frame| to |destination|. bool GetBitstream(const RtpFrameObject& frame, uint8_t* destination); + + // Mark all slots used by |frame| as not used. void ReturnFrame(RtpFrameObject* frame); + // Find the references for this frame. + void ManageFrame(std::unique_ptr frame) + EXCLUSIVE_LOCKS_REQUIRED(crit_); + + // Retry finding references for all frames that previously didn't have + // all information needed. + void RetryStashedFrames() EXCLUSIVE_LOCKS_REQUIRED(crit_); + + // Find references for generic frames. + void ManageFrameGeneric(std::unique_ptr frame) + EXCLUSIVE_LOCKS_REQUIRED(crit_); + + // Find references for Vp8 frames + void ManageFrameVp8(std::unique_ptr frame) + EXCLUSIVE_LOCKS_REQUIRED(crit_); + + // Updates all necessary state used to determine frame references + // for Vp8 and then calls the |frame_callback| callback with the + // completed frame. + void CompletedFrameVp8(std::unique_ptr frame) + EXCLUSIVE_LOCKS_REQUIRED(crit_); + + // All picture ids are unwrapped to 16 bits. + uint16_t UnwrapPictureId(uint16_t picture_id) + EXCLUSIVE_LOCKS_REQUIRED(crit_); + 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_); + // The fist sequence number currently in the buffer. uint16_t first_seq_num_ GUARDED_BY(crit_); - bool initialized_ GUARDED_BY(crit_); + + // The last sequence number currently in the buffer. + uint16_t last_seq_num_ GUARDED_BY(crit_); + + // If the packet buffer has received its first packet. + bool first_packet_received_ GUARDED_BY(crit_); + + // Buffer that holds the inserted packets. std::vector data_buffer_ GUARDED_BY(crit_); + + // Buffer that holds the information about which slot that is currently in use + // and information needed to determine the continuity between packets. std::vector sequence_buffer_ GUARDED_BY(crit_); + // The callback that is called when a frame has been created and all its + // references has been found. OnCompleteFrameCallback* const frame_callback_; + + // Holds the last sequence number of the last frame that has been created + // given the last sequence number of a given keyframe. + std::map> + last_seq_num_gop_ GUARDED_BY(crit_); + + // Save the last picture id in order to detect when there is a gap in frames + // that have not yet been fully received. + int last_picture_id_ GUARDED_BY(crit_); + + // The last unwrapped picture id. Used to unwrap the picture id from a length + // of |kPicIdLength| to 16 bits. + int last_unwrap_ GUARDED_BY(crit_); + + // Frames earlier than the last received frame that have not yet been + // fully received. + std::set> + not_yet_received_frames_ GUARDED_BY(crit_); + + // Frames that have been fully received but didn't have all the information + // needed to determine their references. + std::queue> stashed_frames_ GUARDED_BY(crit_); + + // Holds the information about the last completed frame for a given temporal + // layer given a Tl0 picture index. + std::map, + DescendingSeqNumComp> layer_info_ GUARDED_BY(crit_); }; } // namespace video_coding diff --git a/webrtc/modules/video_coding/packet_buffer_unittest.cc b/webrtc/modules/video_coding/packet_buffer_unittest.cc index bc06940391..28c62c296f 100644 --- a/webrtc/modules/video_coding/packet_buffer_unittest.cc +++ b/webrtc/modules/video_coding/packet_buffer_unittest.cc @@ -24,142 +24,195 @@ class TestPacketBuffer : public ::testing::Test, public OnCompleteFrameCallback { protected: TestPacketBuffer() - : rand_(0x8739211), packet_buffer_(kStartSize, kMaxSize, this) {} + : rand_(0x8739211), + packet_buffer_(new PacketBuffer(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)); + uint16_t pid = frame->picture_id; + auto frame_it = frames_from_callback_.find(pid); + if (frame_it != frames_from_callback_.end()) { + ADD_FAILURE() << "Already received frame with picture id: " << pid; + return; + } + + frames_from_callback_.insert( + make_pair(frame->picture_id, 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 + // All frame objects must be destroyed before the packet buffer since + // a frame object will try to remove itself from the packet buffer // upon destruction. frames_from_callback_.clear(); } + // Insert a generic packet into the packet buffer. + void InsertGeneric(uint16_t seq_num, // packet sequence number + bool keyframe, // is keyframe + bool first, // is first packet of frame + bool last, // is last packet of frame + size_t 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; + packet.markerBit = last; + packet.sizeBytes = data_size; + packet.dataPtr = data; + + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); + } + + // Insert a Vp8 packet into the packet buffer. + void InsertVp8(uint16_t seq_num, // packet sequence number + bool keyframe, // is keyframe + bool first, // is first packet of frame + bool last, // is last packet of frame + bool sync = false, // is sync frame + int32_t pid = kNoPictureId, // picture id + uint8_t tid = kNoTemporalIdx, // temporal id + int32_t tl0 = kNoTl0PicIdx, // tl0 pic index + size_t data_size = 0, // size of data + uint8_t* data = nullptr) { // data pointer + VCMPacket packet; + packet.codec = kVideoCodecVP8; + packet.seqNum = seq_num; + packet.frameType = keyframe ? kVideoFrameKey : kVideoFrameDelta; + packet.isFirstPacket = first; + packet.markerBit = last; + packet.sizeBytes = data_size; + packet.dataPtr = data; + packet.codecSpecificHeader.codecHeader.VP8.pictureId = pid % (1 << 15); + packet.codecSpecificHeader.codecHeader.VP8.temporalIdx = tid; + packet.codecSpecificHeader.codecHeader.VP8.tl0PicIdx = tl0; + packet.codecSpecificHeader.codecHeader.VP8.layerSync = sync; + + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); + } + + // Check if a frame with picture id |pid| has been delivered from the packet + // buffer, and if so, if it has the references specified by |refs|. + template + void CheckReferences(uint16_t pid, T... refs) const { + auto frame_it = frames_from_callback_.find(pid); + if (frame_it == frames_from_callback_.end()) { + ADD_FAILURE() << "Could not find frame with picture id " << pid; + return; + } + + std::set actual_refs; + for (uint8_t r = 0; r < frame_it->second->num_references; ++r) { + actual_refs.insert(frame_it->second->references[r]); + } + + std::set expected_refs; + RefsToSet(&expected_refs, refs...); + + ASSERT_EQ(expected_refs, actual_refs); + } + + template + void RefsToSet(std::set* m, uint16_t ref, T... refs) const { + m->insert(ref); + RefsToSet(m, refs...); + } + + void RefsToSet(std::set* m) const {} + const int kStartSize = 16; const int kMaxSize = 64; Random rand_; - PacketBuffer packet_buffer_; - std::vector> frames_from_callback_; + std::unique_ptr packet_buffer_; + std::map> frames_from_callback_; }; TEST_F(TestPacketBuffer, InsertOnePacket) { VCMPacket packet; packet.seqNum = Rand(); - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); } TEST_F(TestPacketBuffer, InsertMultiplePackets) { VCMPacket packet; packet.seqNum = Rand(); - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + 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)); + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); } TEST_F(TestPacketBuffer, InsertDuplicatePacket) { VCMPacket packet; packet.seqNum = Rand(); - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + 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)); + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); } TEST_F(TestPacketBuffer, ExpandBuffer) { - VCMPacket packet; - packet.seqNum = Rand(); + uint16_t seq_num = Rand(); for (int i = 0; i < kStartSize + 1; ++i) { - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); - ++packet.seqNum; + // seq_num , keyframe, first, last + InsertGeneric(seq_num + i, true , true , true); } } TEST_F(TestPacketBuffer, ExpandBufferOverflow) { - VCMPacket packet; - packet.seqNum = Rand(); + uint16_t seq_num = Rand(); for (int i = 0; i < kMaxSize; ++i) { - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); - ++packet.seqNum; + // seq_num , keyframe, first, last + InsertGeneric(seq_num + i, true , true , true); } - EXPECT_FALSE(packet_buffer_.InsertPacket(packet)); + VCMPacket packet; + packet.seqNum = seq_num + kMaxSize + 1; + packet.sizeBytes = 1; + 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, GenericOnePacketOneFrame) { + // seq_num, keyframe, first, last + InsertGeneric(Rand() , true , true , true); + ASSERT_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)); +TEST_F(TestPacketBuffer, GenericTwoPacketsTwoFrames) { + uint16_t seq_num = Rand(); + + // seq_num , keyframe, first, last + InsertGeneric(seq_num , true , true , true); + InsertGeneric(seq_num + 1, true , true , true); + 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)); +TEST_F(TestPacketBuffer, GenericTwoPacketsOneFrames) { + uint16_t seq_num = Rand(); + + // seq_num , keyframe, first, last + InsertGeneric(seq_num , true , true , false); + InsertGeneric(seq_num + 1, true , false, true); + 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, GenericThreePacketReorderingOneFrame) { + uint16_t seq_num = Rand(); + + // seq_num , keyframe, first, last + InsertGeneric(seq_num , true , true , false); + InsertGeneric(seq_num + 2, true , false, true); + InsertGeneric(seq_num + 1, true , false, false); -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()); } @@ -167,45 +220,77 @@ TEST_F(TestPacketBuffer, DiscardOldPacket) { uint16_t seq_num = Rand(); VCMPacket packet; packet.seqNum = Rand(); - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); packet.seqNum += 2; - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); for (int i = 3; i < kMaxSize; ++i) { ++packet.seqNum; - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + 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)); + 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)); + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); packet.seqNum += 2; - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); for (int i = 3; i < kMaxSize; ++i) { ++packet.seqNum; - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); } - packet_buffer_.ClearTo(seq_num + 15); + packet_buffer_->ClearTo(seq_num + 15); for (int i = 0; i < 15; ++i) { ++packet.seqNum; - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); } for (int i = 15; i < kMaxSize; ++i) { ++packet.seqNum; - EXPECT_FALSE(packet_buffer_.InsertPacket(packet)); + EXPECT_FALSE(packet_buffer_->InsertPacket(packet)); } } +TEST_F(TestPacketBuffer, GenericFrames) { + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last + InsertGeneric(seq_num , true , true , true); + InsertGeneric(seq_num + 1, false, true , true); + InsertGeneric(seq_num + 2, false, true , true); + InsertGeneric(seq_num + 3, false, true , true); + + ASSERT_EQ(4UL, frames_from_callback_.size()); + CheckReferences(seq_num); + CheckReferences(seq_num + 1, seq_num); + CheckReferences(seq_num + 2, seq_num + 1); + CheckReferences(seq_num + 3, seq_num + 2); +} + +TEST_F(TestPacketBuffer, GenericFramesReordered) { + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last + InsertGeneric(seq_num + 1, false, true , true); + InsertGeneric(seq_num , true , true , true); + InsertGeneric(seq_num + 3, false, true , true); + InsertGeneric(seq_num + 2, false, true , true); + + ASSERT_EQ(4UL, frames_from_callback_.size()); + CheckReferences(seq_num); + CheckReferences(seq_num + 1, seq_num); + CheckReferences(seq_num + 2, seq_num + 1); + CheckReferences(seq_num + 3, seq_num + 2); +} + TEST_F(TestPacketBuffer, GetBitstreamFromFrame) { // "many bitstream, such data" with null termination. uint8_t many[] = {0x6d, 0x61, 0x6e, 0x79, 0x20}; @@ -216,89 +301,431 @@ TEST_F(TestPacketBuffer, GetBitstreamFromFrame) { 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()); + uint16_t seq_num = Rand(); - EXPECT_TRUE(frames_from_callback_[0]->GetBitstream(result)); - EXPECT_EQ( - std::strcmp("many bitstream, such data", reinterpret_cast(result)), - 0); + // seq_num , keyf , first, last , data_size , data + InsertGeneric(seq_num , true , true , false, sizeof(many) , many); + InsertGeneric(seq_num + 1, false, false, false, sizeof(bitstream), bitstream); + InsertGeneric(seq_num + 2, false, false, false, sizeof(such) , such); + InsertGeneric(seq_num + 3, false, false, true , sizeof(data) , data); + + ASSERT_EQ(1UL, frames_from_callback_.size()); + CheckReferences(seq_num + 3); + EXPECT_TRUE(frames_from_callback_[seq_num + 3]->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)); + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last + InsertGeneric(seq_num , true , true , false); + InsertGeneric(seq_num + 1, false, false, false); + InsertGeneric(seq_num + 2, false, false, true); 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)); + // seq_num , keyf , first, last + InsertGeneric(seq_num , true , true , false); + InsertGeneric(seq_num + 1, false, false, false); + InsertGeneric(seq_num + 2, false, false, true); 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)); + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last + InsertGeneric(seq_num , true , true , false); + InsertGeneric(seq_num + 1, false, false, false); + InsertGeneric(seq_num + 2, false, false, true); + EXPECT_EQ(1UL, frames_from_callback_.size()); + + packet_buffer_->Flush(); + + // seq_num , keyf , first, last + InsertGeneric(seq_num + kStartSize , true , true , false); + InsertGeneric(seq_num + kStartSize + 1, false, false, false); + InsertGeneric(seq_num + kStartSize + 2, false, false, true); EXPECT_EQ(2UL, frames_from_callback_.size()); } TEST_F(TestPacketBuffer, InvalidateFrameByFlushing) { VCMPacket packet; + packet.codec = kVideoCodecGeneric; + packet.frameType = kVideoFrameKey; packet.isFirstPacket = true; packet.markerBit = true; packet.seqNum = Rand(); - EXPECT_TRUE(packet_buffer_.InsertPacket(packet)); + EXPECT_TRUE(packet_buffer_->InsertPacket(packet)); ASSERT_EQ(1UL, frames_from_callback_.size()); - packet_buffer_.Flush(); - EXPECT_FALSE(frames_from_callback_[0]->GetBitstream(nullptr)); + packet_buffer_->Flush(); + EXPECT_FALSE(frames_from_callback_.begin()->second->GetBitstream(nullptr)); +} + +TEST_F(TestPacketBuffer, Vp8NoPictureId) { + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last + InsertVp8(seq_num , true , true , false); + InsertVp8(seq_num + 1 , false, false, false); + InsertVp8(seq_num + 2 , false, false, true); + ASSERT_EQ(1UL, frames_from_callback_.size()); + + InsertVp8(seq_num + 3 , false, true , false); + InsertVp8(seq_num + 4 , false, false, true); + ASSERT_EQ(2UL, frames_from_callback_.size()); + + InsertVp8(seq_num + 5 , false, true , false); + InsertVp8(seq_num + 6 , false, false, false); + InsertVp8(seq_num + 7 , false, false, false); + InsertVp8(seq_num + 8 , false, false, true); + ASSERT_EQ(3UL, frames_from_callback_.size()); + + InsertVp8(seq_num + 9 , false, true , true); + ASSERT_EQ(4UL, frames_from_callback_.size()); + + InsertVp8(seq_num + 10, false, true , false); + InsertVp8(seq_num + 11, false, false, true); + ASSERT_EQ(5UL, frames_from_callback_.size()); + + InsertVp8(seq_num + 12, true , true , true); + ASSERT_EQ(6UL, frames_from_callback_.size()); + + InsertVp8(seq_num + 13, false, true , false); + InsertVp8(seq_num + 14, false, false, false); + InsertVp8(seq_num + 15, false, false, false); + InsertVp8(seq_num + 16, false, false, false); + InsertVp8(seq_num + 17, false, false, true); + ASSERT_EQ(7UL, frames_from_callback_.size()); + + InsertVp8(seq_num + 18, false, true , true); + ASSERT_EQ(8UL, frames_from_callback_.size()); + + InsertVp8(seq_num + 19, false, true , false); + InsertVp8(seq_num + 20, false, false, true); + ASSERT_EQ(9UL, frames_from_callback_.size()); + + InsertVp8(seq_num + 21, false, true , true); + + ASSERT_EQ(10UL, frames_from_callback_.size()); + CheckReferences(seq_num + 2); + CheckReferences(seq_num + 4, seq_num + 2); + CheckReferences(seq_num + 8, seq_num + 4); + CheckReferences(seq_num + 9, seq_num + 8); + CheckReferences(seq_num + 11, seq_num + 9); + CheckReferences(seq_num + 12); + CheckReferences(seq_num + 17, seq_num + 12); + CheckReferences(seq_num + 18, seq_num + 17); + CheckReferences(seq_num + 20, seq_num + 18); + CheckReferences(seq_num + 21, seq_num + 20); +} + +TEST_F(TestPacketBuffer, Vp8NoPictureIdReordered) { + uint16_t seq_num = 0xfffa; + + // seq_num , keyf , first, last + InsertVp8(seq_num + 1 , false, false, false); + InsertVp8(seq_num , true , true , false); + InsertVp8(seq_num + 2 , false, false, true); + InsertVp8(seq_num + 4 , false, false, true); + InsertVp8(seq_num + 6 , false, false, false); + InsertVp8(seq_num + 3 , false, true , false); + InsertVp8(seq_num + 7 , false, false, false); + InsertVp8(seq_num + 5 , false, true , false); + InsertVp8(seq_num + 9 , false, true , true); + InsertVp8(seq_num + 10, false, true , false); + InsertVp8(seq_num + 8 , false, false, true); + InsertVp8(seq_num + 13, false, true , false); + InsertVp8(seq_num + 14, false, false, false); + InsertVp8(seq_num + 12, true , true , true); + InsertVp8(seq_num + 11, false, false, true); + InsertVp8(seq_num + 16, false, false, false); + InsertVp8(seq_num + 19, false, true , false); + InsertVp8(seq_num + 15, false, false, false); + InsertVp8(seq_num + 17, false, false, true); + InsertVp8(seq_num + 20, false, false, true); + InsertVp8(seq_num + 21, false, true , true); + InsertVp8(seq_num + 18, false, true , true); + + ASSERT_EQ(10UL, frames_from_callback_.size()); + CheckReferences(seq_num + 2); + CheckReferences(seq_num + 4, seq_num + 2); + CheckReferences(seq_num + 8, seq_num + 4); + CheckReferences(seq_num + 9, seq_num + 8); + CheckReferences(seq_num + 11, seq_num + 9); + CheckReferences(seq_num + 12); + CheckReferences(seq_num + 17, seq_num + 12); + CheckReferences(seq_num + 18, seq_num + 17); + CheckReferences(seq_num + 20, seq_num + 18); + CheckReferences(seq_num + 21, seq_num + 20); +} + + +TEST_F(TestPacketBuffer, Vp8KeyFrameReferences) { + uint16_t pid = Rand(); + // seq_num, keyf, first, last, sync , pid, tid, tl0 + InsertVp8(Rand() , true, true , true, false, pid, 0 , 0); + + ASSERT_EQ(1UL, frames_from_callback_.size()); + CheckReferences(pid); +} + +// Test with 1 temporal layer. +TEST_F(TestPacketBuffer, Vp8TemporalLayers_0) { + uint16_t pid = Rand(); + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last, sync , pid , tid, tl0 + InsertVp8(seq_num , true , true , true, false, pid , 0 , 1); + InsertVp8(seq_num + 1, false, true , true, false, pid + 1, 0 , 2); + InsertVp8(seq_num + 2, false, true , true, false, pid + 2, 0 , 3); + InsertVp8(seq_num + 3, false, true , true, false, pid + 3, 0 , 4); + + ASSERT_EQ(4UL, frames_from_callback_.size()); + CheckReferences(pid); + CheckReferences(pid + 1, pid); + CheckReferences(pid + 2, pid + 1); + CheckReferences(pid + 3, pid + 2); +} + +// Test with 1 temporal layer. +TEST_F(TestPacketBuffer, Vp8TemporalLayersReordering_0) { + uint16_t pid = Rand(); + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last, sync , pid , tid, tl0 + InsertVp8(seq_num , true , true , true, false, pid , 0 , 1); + InsertVp8(seq_num + 1, false, true , true, false, pid + 1, 0 , 2); + InsertVp8(seq_num + 3, false, true , true, false, pid + 3, 0 , 4); + InsertVp8(seq_num + 2, false, true , true, false, pid + 2, 0 , 3); + InsertVp8(seq_num + 5, false, true , true, false, pid + 5, 0 , 6); + InsertVp8(seq_num + 6, false, true , true, false, pid + 6, 0 , 7); + InsertVp8(seq_num + 4, false, true , true, false, pid + 4, 0 , 5); + + ASSERT_EQ(7UL, frames_from_callback_.size()); + CheckReferences(pid); + CheckReferences(pid + 1, pid); + CheckReferences(pid + 2, pid + 1); + CheckReferences(pid + 3, pid + 2); + CheckReferences(pid + 4, pid + 3); + CheckReferences(pid + 5, pid + 4); + CheckReferences(pid + 6, pid + 5); +} + +// Test with 2 temporal layers in a 01 pattern. +TEST_F(TestPacketBuffer, Vp8TemporalLayers_01) { + uint16_t pid = Rand(); + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last, sync , pid , tid, tl0 + InsertVp8(seq_num , true , true , true, false, pid , 0, 255); + InsertVp8(seq_num + 1, false, true , true, true , pid + 1, 1, 255); + InsertVp8(seq_num + 2, false, true , true, false, pid + 2, 0, 0); + InsertVp8(seq_num + 3, false, true , true, false, pid + 3, 1, 0); + + ASSERT_EQ(4UL, frames_from_callback_.size()); + CheckReferences(pid); + CheckReferences(pid + 1, pid); + CheckReferences(pid + 2, pid); + CheckReferences(pid + 3, pid + 1, pid + 2); +} + +// Test with 2 temporal layers in a 01 pattern. +TEST_F(TestPacketBuffer, Vp8TemporalLayersReordering_01) { + uint16_t pid = Rand(); + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last, sync , pid , tid, tl0 + InsertVp8(seq_num + 1, false, true , true, true , pid + 1, 1 , 255); + InsertVp8(seq_num , true , true , true, false, pid , 0 , 255); + InsertVp8(seq_num + 3, false, true , true, false, pid + 3, 1 , 0); + InsertVp8(seq_num + 5, false, true , true, false, pid + 5, 1 , 1); + InsertVp8(seq_num + 2, false, true , true, false, pid + 2, 0 , 0); + InsertVp8(seq_num + 4, false, true , true, false, pid + 4, 0 , 1); + InsertVp8(seq_num + 6, false, true , true, false, pid + 6, 0 , 2); + InsertVp8(seq_num + 7, false, true , true, false, pid + 7, 1 , 2); + + ASSERT_EQ(8UL, frames_from_callback_.size()); + CheckReferences(pid); + CheckReferences(pid + 1, pid); + CheckReferences(pid + 2, pid); + CheckReferences(pid + 3, pid + 1, pid + 2); + CheckReferences(pid + 4, pid + 2); + CheckReferences(pid + 5, pid + 3, pid + 4); + CheckReferences(pid + 6, pid + 4); + CheckReferences(pid + 7, pid + 5, pid + 6); +} + +// Test with 3 temporal layers in a 0212 pattern. +TEST_F(TestPacketBuffer, Vp8TemporalLayers_0212) { + uint16_t pid = Rand(); + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last, sync , pid , tid, tl0 + InsertVp8(seq_num , true , true , true , false, pid , 0 , 55); + InsertVp8(seq_num + 1 , false, true , true , true , pid + 1 , 2 , 55); + InsertVp8(seq_num + 2 , false, true , true , true , pid + 2 , 1 , 55); + InsertVp8(seq_num + 3 , false, true , true , false, pid + 3 , 2 , 55); + InsertVp8(seq_num + 4 , false, true , true , false, pid + 4 , 0 , 56); + InsertVp8(seq_num + 5 , false, true , true , false, pid + 5 , 2 , 56); + InsertVp8(seq_num + 6 , false, true , true , false, pid + 6 , 1 , 56); + InsertVp8(seq_num + 7 , false, true , true , false, pid + 7 , 2 , 56); + InsertVp8(seq_num + 8 , false, true , true , false, pid + 8 , 0 , 57); + InsertVp8(seq_num + 9 , false, true , true , true , pid + 9 , 2 , 57); + InsertVp8(seq_num + 10, false, true , true , true , pid + 10, 1 , 57); + InsertVp8(seq_num + 11, false, true , true , false, pid + 11, 2 , 57); + + ASSERT_EQ(12UL, frames_from_callback_.size()); + CheckReferences(pid); + CheckReferences(pid + 1 , pid); + CheckReferences(pid + 2 , pid); + CheckReferences(pid + 3 , pid, pid + 1, pid + 2); + CheckReferences(pid + 4 , pid); + CheckReferences(pid + 5 , pid + 2, pid + 3, pid + 4); + CheckReferences(pid + 6 , pid + 2, pid + 4); + CheckReferences(pid + 7 , pid + 4, pid + 5, pid + 6); + CheckReferences(pid + 8 , pid + 4); + CheckReferences(pid + 9 , pid + 8); + CheckReferences(pid + 10, pid + 8); + CheckReferences(pid + 11, pid + 8, pid + 9, pid + 10); +} + +// Test with 3 temporal layers in a 0212 pattern. +TEST_F(TestPacketBuffer, Vp8TemporalLayersReordering_0212) { + uint16_t pid = 126; + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last, sync , pid , tid, tl0 + InsertVp8(seq_num + 1 , false, true , true, true , pid + 1 , 2 , 55); + InsertVp8(seq_num , true , true , true, false, pid , 0 , 55); + InsertVp8(seq_num + 2 , false, true , true, true , pid + 2 , 1 , 55); + InsertVp8(seq_num + 4 , false, true , true, false, pid + 4 , 0 , 56); + InsertVp8(seq_num + 5 , false, true , true, false, pid + 5 , 2 , 56); + InsertVp8(seq_num + 3 , false, true , true, false, pid + 3 , 2 , 55); + InsertVp8(seq_num + 7 , false, true , true, false, pid + 7 , 2 , 56); + InsertVp8(seq_num + 9 , false, true , true, true , pid + 9 , 2 , 57); + InsertVp8(seq_num + 6 , false, true , true, false, pid + 6 , 1 , 56); + InsertVp8(seq_num + 8 , false, true , true, false, pid + 8 , 0 , 57); + InsertVp8(seq_num + 11, false, true , true, false, pid + 11, 2 , 57); + InsertVp8(seq_num + 10, false, true , true, true , pid + 10, 1 , 57); + + ASSERT_EQ(12UL, frames_from_callback_.size()); + CheckReferences(pid); + CheckReferences(pid + 1 , pid); + CheckReferences(pid + 2 , pid); + CheckReferences(pid + 3 , pid, pid + 1, pid + 2); + CheckReferences(pid + 4 , pid); + CheckReferences(pid + 5 , pid + 2, pid + 3, pid + 4); + CheckReferences(pid + 6 , pid + 2, pid + 4); + CheckReferences(pid + 7 , pid + 4, pid + 5, pid + 6); + CheckReferences(pid + 8 , pid + 4); + CheckReferences(pid + 9 , pid + 8); + CheckReferences(pid + 10, pid + 8); + CheckReferences(pid + 11, pid + 8, pid + 9, pid + 10); +} + +TEST_F(TestPacketBuffer, Vp8InsertManyFrames_0212) { + uint16_t pid = Rand(); + uint16_t seq_num = Rand(); + + const int keyframes_to_insert = 50; + const int frames_per_keyframe = 120; // Should be a multiple of 4. + uint8_t tl0 = 128; + + for (int k = 0; k < keyframes_to_insert; ++k) { + // seq_num , keyf , first, last , sync , pid , tid, tl0 + InsertVp8(seq_num , true , true , true , false, pid , 0 , tl0); + InsertVp8(seq_num + 1, false, true , true , true , pid + 1, 2 , tl0); + InsertVp8(seq_num + 2, false, true , true , true , pid + 2, 1 , tl0); + InsertVp8(seq_num + 3, false, true , true , false, pid + 3, 2 , tl0); + CheckReferences(pid); + CheckReferences(pid + 1, pid); + CheckReferences(pid + 2, pid); + CheckReferences(pid + 3, pid, pid + 1, pid + 2); + frames_from_callback_.clear(); + ++tl0; + + for (int f = 4; f < frames_per_keyframe; f += 4) { + uint16_t sf = seq_num + f; + uint16_t pidf = pid + f; + + // seq_num, keyf , first, last, sync , pid , tid, tl0 + InsertVp8(sf , false, true , true, false, pidf , 0 , tl0); + InsertVp8(sf + 1 , false, true , true, false, pidf + 1, 2 , tl0); + InsertVp8(sf + 2 , false, true , true, false, pidf + 2, 1 , tl0); + InsertVp8(sf + 3 , false, true , true, false, pidf + 3, 2 , tl0); + CheckReferences(pidf, pidf - 4); + CheckReferences(pidf + 1, pidf, pidf - 1, pidf - 2); + CheckReferences(pidf + 2, pidf, pidf - 2); + CheckReferences(pidf + 3, pidf, pidf + 1, pidf + 2); + frames_from_callback_.clear(); + ++tl0; + } + + pid += frames_per_keyframe; + seq_num += frames_per_keyframe; + } +} + +TEST_F(TestPacketBuffer, Vp8LayerSync) { + uint16_t pid = Rand(); + uint16_t seq_num = Rand(); + + // seq_num , keyf , first, last, sync , pid , tid, tl0 + InsertVp8(seq_num , true , true , true, false, pid , 0 , 0); + InsertVp8(seq_num + 1 , false, true , true, true , pid + 1 , 1 , 0); + InsertVp8(seq_num + 2 , false, true , true, false, pid + 2 , 0 , 1); + ASSERT_EQ(3UL, frames_from_callback_.size()); + + InsertVp8(seq_num + 4 , false, true , true, false, pid + 4 , 0 , 2); + InsertVp8(seq_num + 5 , false, true , true, true , pid + 5 , 1 , 2); + InsertVp8(seq_num + 6 , false, true , true, false, pid + 6 , 0 , 3); + InsertVp8(seq_num + 7 , false, true , true, false, pid + 7 , 1 , 3); + + ASSERT_EQ(7UL, frames_from_callback_.size()); + CheckReferences(pid); + CheckReferences(pid + 1, pid); + CheckReferences(pid + 2, pid); + CheckReferences(pid + 4, pid + 2); + CheckReferences(pid + 5, pid + 4); + CheckReferences(pid + 6, pid + 4); + CheckReferences(pid + 7, pid + 6, pid + 5); +} + +TEST_F(TestPacketBuffer, Vp8InsertLargeFrames) { + packet_buffer_.reset(new PacketBuffer(1 << 3, 1 << 12, this)); + uint16_t pid = Rand(); + uint16_t seq_num = Rand(); + + const uint16_t packets_per_frame = 1000; + uint16_t current = seq_num; + uint16_t end = current + packets_per_frame; + + // seq_num , keyf , first, last , sync , pid, tid, tl0 + InsertVp8(current++, true , true , false, false, pid, 0 , 0); + while (current != end) + InsertVp8(current++, false, false, false, false, pid, 0 , 0); + InsertVp8(current++, false, false, true , false, pid, 0 , 0); + end = current + packets_per_frame; + + for (int f = 1; f < 4; ++f) { + InsertVp8(current++, false, true , false, false, pid + f, 0, f); + while (current != end) + InsertVp8(current++, false, false, false, false, pid + f, 0, f); + InsertVp8(current++, false, false, true , false, pid + f, 0, f); + end = current + packets_per_frame; + } + + ASSERT_EQ(4UL, frames_from_callback_.size()); + CheckReferences(pid); + CheckReferences(pid + 1, pid); + CheckReferences(pid + 2, pid + 1); + CheckReferences(pid + 3, pid + 2); } } // namespace video_coding