diff --git a/webrtc/modules/video_coding/main/interface/video_coding.h b/webrtc/modules/video_coding/main/interface/video_coding.h index 41a63d1f6..9f5ddefb6 100644 --- a/webrtc/modules/video_coding/main/interface/video_coding.h +++ b/webrtc/modules/video_coding/main/interface/video_coding.h @@ -412,6 +412,10 @@ public: // < 0, on error. virtual int32_t Decode(uint16_t maxWaitTimeMs = 200) = 0; + // Registers a callback which conveys the size of the render buffer. + virtual int RegisterRenderBufferSizeCallback( + VCMRenderBufferSizeCallback* callback) = 0; + // Waits for the next frame in the dual jitter buffer to become complete // (waits no longer than maxWaitTimeMs), then passes it to the dual decoder // for decoding. This will never trigger a render callback. Should be diff --git a/webrtc/modules/video_coding/main/interface/video_coding_defines.h b/webrtc/modules/video_coding/main/interface/video_coding_defines.h index fcee34b58..b1b52bd8e 100644 --- a/webrtc/modules/video_coding/main/interface/video_coding_defines.h +++ b/webrtc/modules/video_coding/main/interface/video_coding_defines.h @@ -182,6 +182,17 @@ class VCMQMSettingsCallback { } }; +// Callback class used for telling the user about the size (in time) of the +// render buffer, that is the size in time of the complete continuous frames. +class VCMRenderBufferSizeCallback { + public: + virtual void RenderBufferSizeMs(int buffer_size_ms) = 0; + + protected: + virtual ~VCMRenderBufferSizeCallback() { + } +}; + } // namespace webrtc #endif // WEBRTC_MODULES_INTERFACE_VIDEO_CODING_DEFINES_H_ diff --git a/webrtc/modules/video_coding/main/source/decoding_state.cc b/webrtc/modules/video_coding/main/source/decoding_state.cc index eb03705ec..0974584d7 100644 --- a/webrtc/modules/video_coding/main/source/decoding_state.cc +++ b/webrtc/modules/video_coding/main/source/decoding_state.cc @@ -72,6 +72,16 @@ void VCMDecodingState::SetState(const VCMFrameBuffer* frame) { in_initial_state_ = false; } +void VCMDecodingState::CopyFrom(const VCMDecodingState& state) { + sequence_num_ = state.sequence_num_; + time_stamp_ = state.time_stamp_; + picture_id_ = state.picture_id_; + temporal_id_ = state.temporal_id_; + tl0_pic_id_ = state.tl0_pic_id_; + full_sync_ = state.full_sync_; + in_initial_state_ = state.in_initial_state_; +} + void VCMDecodingState::SetStateOneBack(const VCMFrameBuffer* frame) { assert(frame != NULL && frame->GetHighSeqNum() >= 0); sequence_num_ = static_cast(frame->GetHighSeqNum()) - 1u; diff --git a/webrtc/modules/video_coding/main/source/decoding_state.h b/webrtc/modules/video_coding/main/source/decoding_state.h index 2fd143b7a..f0019660e 100644 --- a/webrtc/modules/video_coding/main/source/decoding_state.h +++ b/webrtc/modules/video_coding/main/source/decoding_state.h @@ -31,6 +31,7 @@ class VCMDecodingState { // possible, i.e. temporal info, picture ID or sequence number. bool ContinuousFrame(const VCMFrameBuffer* frame) const; void SetState(const VCMFrameBuffer* frame); + void CopyFrom(const VCMDecodingState& state); // Set the decoding state one frame back. void SetStateOneBack(const VCMFrameBuffer* frame); void UpdateEmptyFrame(const VCMFrameBuffer* frame); diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.cc b/webrtc/modules/video_coding/main/source/jitter_buffer.cc index 098824269..6f133b3ca 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer.cc +++ b/webrtc/modules/video_coding/main/source/jitter_buffer.cc @@ -982,6 +982,46 @@ int64_t VCMJitterBuffer::LastDecodedTimestamp() const { return last_decoded_state_.time_stamp(); } +int VCMJitterBuffer::RenderBufferSizeMs() { + CriticalSectionScoped cs(crit_sect_); + CleanUpOldOrEmptyFrames(); + if (frame_list_.empty()) { + return 0; + } + FrameList::iterator frame_it = frame_list_.begin(); + VCMFrameBuffer* current_frame = *frame_it; + // Search for a complete and continuous sequence (starting from the last + // decoded state or current frame if in initial state). + VCMDecodingState previous_state; + if (last_decoded_state_.in_initial_state()) { + // Start with a key frame. + frame_it = find_if(frame_list_.begin(), frame_list_.end(), + CompleteKeyFrameCriteria()); + if (frame_it == frame_list_.end()) { + return 0; + } + current_frame = *frame_it; + previous_state.SetState(current_frame); + } else { + previous_state.CopyFrom(last_decoded_state_); + } + bool continuous_complete = true; + int64_t start_render = current_frame->RenderTimeMs(); + ++frame_it; + while (frame_it != frame_list_.end() && continuous_complete) { + current_frame = *frame_it; + continuous_complete = current_frame->IsSessionComplete() && + previous_state.ContinuousFrame(current_frame); + previous_state.SetState(current_frame); + ++frame_it; + } + // Desired frame is the previous one. + --frame_it; + current_frame = *frame_it; + // Got the frame, now compute the time delta. + return static_cast(current_frame->RenderTimeMs() - start_render); +} + // Set the frame state to free and remove it from the sorted // frame list. Must be called from inside the critical section crit_sect_. void VCMJitterBuffer::ReleaseFrameIfNotDecoding(VCMFrameBuffer* frame) { diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer.h b/webrtc/modules/video_coding/main/source/jitter_buffer.h index ec35d1f97..81a637a10 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer.h +++ b/webrtc/modules/video_coding/main/source/jitter_buffer.h @@ -166,6 +166,9 @@ class VCMJitterBuffer { int64_t LastDecodedTimestamp() const; bool decode_with_errors() const {return decode_with_errors_;} + // Returns size in time (milliseconds) of complete continuous frames. + int RenderBufferSizeMs(); + private: class SequenceNumberLessThan { public: @@ -211,6 +214,8 @@ class VCMJitterBuffer { // Can return a decodable, incomplete frame when enabled. FrameList::iterator FindOldestCompleteContinuousFrame(); + // Cleans the frame list in the JB from old/empty frames. + // Should only be called prior to actual use. void CleanUpOldOrEmptyFrames(); // Sets the "decodable" and "frame loss" flags of a frame depending on which diff --git a/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc b/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc index bf819735b..7971fae34 100644 --- a/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc +++ b/webrtc/modules/video_coding/main/source/jitter_buffer_unittest.cc @@ -16,146 +16,12 @@ #include "webrtc/modules/video_coding/main/source/jitter_buffer.h" #include "webrtc/modules/video_coding/main/source/media_opt_util.h" #include "webrtc/modules/video_coding/main/source/packet.h" +#include "webrtc/modules/video_coding/main/source/stream_generator.h" #include "webrtc/modules/video_coding/main/test/test_util.h" #include "webrtc/system_wrappers/interface/clock.h" namespace webrtc { -enum { kDefaultFrameRate = 25u }; -enum { kDefaultFramePeriodMs = 1000u / kDefaultFrameRate }; -const unsigned int kDefaultBitrateKbps = 1000; -const unsigned int kFrameSize = (kDefaultBitrateKbps + kDefaultFrameRate * 4) / - (kDefaultFrameRate * 8); -const unsigned int kMaxPacketSize = 1500; - -class StreamGenerator { - public: - StreamGenerator(uint16_t start_seq_num, uint32_t start_timestamp, - int64_t current_time) - : packets_(), - sequence_number_(start_seq_num), - timestamp_(start_timestamp), - start_time_(current_time) {} - - void Init(uint16_t start_seq_num, uint32_t start_timestamp, - int64_t current_time) { - packets_.clear(); - sequence_number_ = start_seq_num; - timestamp_ = start_timestamp; - start_time_ = current_time; - memset(&packet_buffer, 0, sizeof(packet_buffer)); - } - - void GenerateFrame(FrameType type, int num_media_packets, - int num_empty_packets, int64_t current_time) { - timestamp_ += 90 * (current_time - start_time_); - // Move the sequence number counter if all packets from the previous frame - // wasn't collected. - sequence_number_ += packets_.size(); - packets_.clear(); - for (int i = 0; i < num_media_packets; ++i) { - const int packet_size = (kFrameSize + num_media_packets / 2) / - num_media_packets; - packets_.push_back(GeneratePacket(sequence_number_, - timestamp_, - packet_size, - (i == 0), - (i == num_media_packets - 1), - type)); - ++sequence_number_; - } - for (int i = 0; i < num_empty_packets; ++i) { - packets_.push_back(GeneratePacket(sequence_number_, - timestamp_, - 0, - false, - false, - kFrameEmpty)); - ++sequence_number_; - } - } - - VCMPacket GeneratePacket(uint16_t sequence_number, - uint32_t timestamp, - unsigned int size, - bool first_packet, - bool marker_bit, - FrameType type) { - EXPECT_LT(size, kMaxPacketSize); - VCMPacket packet; - packet.seqNum = sequence_number; - packet.timestamp = timestamp; - packet.frameType = type; - packet.isFirstPacket = first_packet; - packet.markerBit = marker_bit; - packet.sizeBytes = size; - packet.dataPtr = packet_buffer; - if (packet.isFirstPacket) - packet.completeNALU = kNaluStart; - else if (packet.markerBit) - packet.completeNALU = kNaluEnd; - else - packet.completeNALU = kNaluIncomplete; - return packet; - } - - bool PopPacket(VCMPacket* packet, int index) { - std::list::iterator it = GetPacketIterator(index); - if (it == packets_.end()) - return false; - if (packet) - *packet = (*it); - packets_.erase(it); - return true; - } - - bool GetPacket(VCMPacket* packet, int index) { - std::list::iterator it = GetPacketIterator(index); - if (it == packets_.end()) - return false; - if (packet) - *packet = (*it); - return true; - } - - bool NextPacket(VCMPacket* packet) { - if (packets_.empty()) - return false; - if (packet != NULL) - *packet = packets_.front(); - packets_.pop_front(); - return true; - } - - uint16_t NextSequenceNumber() const { - if (packets_.empty()) - return sequence_number_; - return packets_.front().seqNum; - } - - int PacketsRemaining() const { - return packets_.size(); - } - - private: - std::list::iterator GetPacketIterator(int index) { - std::list::iterator it = packets_.begin(); - for (int i = 0; i < index; ++i) { - ++it; - if (it == packets_.end()) break; - } - return it; - } - - std::list packets_; - uint16_t sequence_number_; - uint32_t timestamp_; - int64_t start_time_; - uint8_t packet_buffer[kMaxPacketSize]; - - DISALLOW_COPY_AND_ASSIGN(StreamGenerator); -}; - class TestRunningJitterBuffer : public ::testing::Test { protected: enum { kDataBufferSize = 10 }; @@ -166,7 +32,7 @@ class TestRunningJitterBuffer : public ::testing::Test { oldest_packet_to_nack_ = 250; jitter_buffer_ = new VCMJitterBuffer(clock_.get(), &event_factory_, -1, -1, true); - stream_generator = new StreamGenerator(0, 0, clock_->TimeInMilliseconds()); + stream_generator_ = new StreamGenerator(0, 0, clock_->TimeInMilliseconds()); jitter_buffer_->Start(); jitter_buffer_->SetNackSettings(max_nack_list_size_, oldest_packet_to_nack_); @@ -175,7 +41,7 @@ class TestRunningJitterBuffer : public ::testing::Test { virtual void TearDown() { jitter_buffer_->Stop(); - delete stream_generator; + delete stream_generator_; delete jitter_buffer_; } @@ -184,7 +50,7 @@ class TestRunningJitterBuffer : public ::testing::Test { VCMEncodedFrame* frame; packet.dataPtr = data_buffer_; - bool packet_available = stream_generator->PopPacket(&packet, index); + bool packet_available = stream_generator_->PopPacket(&packet, index); EXPECT_TRUE(packet_available); if (!packet_available) return kStateError; // Return here to avoid crashes below. @@ -197,7 +63,7 @@ class TestRunningJitterBuffer : public ::testing::Test { VCMEncodedFrame* frame; packet.dataPtr = data_buffer_; - bool packet_available = stream_generator->GetPacket(&packet, index); + bool packet_available = stream_generator_->GetPacket(&packet, index); EXPECT_TRUE(packet_available); if (!packet_available) return kStateError; // Return here to avoid crashes below. @@ -206,7 +72,7 @@ class TestRunningJitterBuffer : public ::testing::Test { } VCMFrameBufferEnum InsertFrame(FrameType frame_type) { - stream_generator->GenerateFrame(frame_type, + stream_generator_->GenerateFrame(frame_type, (frame_type != kFrameEmpty) ? 1 : 0, (frame_type == kFrameEmpty) ? 1 : 0, clock_->TimeInMilliseconds()); @@ -229,7 +95,7 @@ class TestRunningJitterBuffer : public ::testing::Test { } void DropFrame(int num_packets) { - stream_generator->GenerateFrame(kVideoFrameDelta, num_packets, 0, + stream_generator_->GenerateFrame(kVideoFrameDelta, num_packets, 0, clock_->TimeInMilliseconds()); clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); } @@ -250,7 +116,7 @@ class TestRunningJitterBuffer : public ::testing::Test { } VCMJitterBuffer* jitter_buffer_; - StreamGenerator* stream_generator; + StreamGenerator* stream_generator_; scoped_ptr clock_; NullEventFactory event_factory_; size_t max_nack_list_size_; @@ -288,7 +154,7 @@ TEST_F(TestRunningJitterBuffer, Full) { TEST_F(TestRunningJitterBuffer, EmptyPackets) { // Make sure a frame can get complete even though empty packets are missing. - stream_generator->GenerateFrame(kVideoFrameKey, 3, 3, + stream_generator_->GenerateFrame(kVideoFrameKey, 3, 3, clock_->TimeInMilliseconds()); bool request_key_frame = false; EXPECT_EQ(kFirstPacket, InsertPacketAndPop(4)); @@ -488,11 +354,11 @@ TEST_F(TestJitterBufferNack, NoNackListReturnedBeforeFirstDecode) { } TEST_F(TestJitterBufferNack, NackListBuiltBeforeFirstDecode) { - stream_generator->Init(0, 0, clock_->TimeInMilliseconds()); + stream_generator_->Init(0, 0, clock_->TimeInMilliseconds()); InsertFrame(kVideoFrameKey); - stream_generator->GenerateFrame(kVideoFrameDelta, 2, 0, + stream_generator_->GenerateFrame(kVideoFrameDelta, 2, 0, clock_->TimeInMilliseconds()); - stream_generator->NextPacket(NULL); // Drop packet. + stream_generator_->NextPacket(NULL); // Drop packet. EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0)); EXPECT_TRUE(DecodeCompleteFrame()); uint16_t nack_list_size = 0; @@ -503,8 +369,8 @@ TEST_F(TestJitterBufferNack, NackListBuiltBeforeFirstDecode) { } TEST_F(TestJitterBufferNack, UseNackToRecoverFirstKeyFrame) { - stream_generator->Init(0, 0, clock_->TimeInMilliseconds()); - stream_generator->GenerateFrame(kVideoFrameKey, 3, 0, + stream_generator_->Init(0, 0, clock_->TimeInMilliseconds()); + stream_generator_->GenerateFrame(kVideoFrameKey, 3, 0, clock_->TimeInMilliseconds()); EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0)); // Drop second packet. @@ -516,7 +382,7 @@ TEST_F(TestJitterBufferNack, UseNackToRecoverFirstKeyFrame) { EXPECT_EQ(1, nack_list_size); ASSERT_TRUE(list != NULL); VCMPacket packet; - stream_generator->GetPacket(&packet, 0); + stream_generator_->GetPacket(&packet, 0); EXPECT_EQ(packet.seqNum, list[0]); } @@ -530,21 +396,21 @@ TEST_F(TestJitterBufferNack, NormalOperation) { // ---------------------------------------------------------------- // | 1 | 2 | .. | 8 | 9 | x | 11 | 12 | .. | 19 | x | 21 | .. | 100 | // ---------------------------------------------------------------- - stream_generator->GenerateFrame(kVideoFrameKey, 100, 0, + stream_generator_->GenerateFrame(kVideoFrameKey, 100, 0, clock_->TimeInMilliseconds()); clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0)); // Verify that the frame is incomplete. EXPECT_FALSE(DecodeCompleteFrame()); - while (stream_generator->PacketsRemaining() > 1) { - if (stream_generator->NextSequenceNumber() % 10 != 0) { + while (stream_generator_->PacketsRemaining() > 1) { + if (stream_generator_->NextSequenceNumber() % 10 != 0) { EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); } else { - stream_generator->NextPacket(NULL); // Drop packet + stream_generator_->NextPacket(NULL); // Drop packet } } EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); - EXPECT_EQ(0, stream_generator->PacketsRemaining()); + EXPECT_EQ(0, stream_generator_->PacketsRemaining()); EXPECT_FALSE(DecodeCompleteFrame()); EXPECT_FALSE(DecodeIncompleteFrame()); uint16_t nack_list_size = 0; @@ -563,24 +429,24 @@ TEST_F(TestJitterBufferNack, NormalOperationWrap) { // ------- ------------------------------------------------------------ // | 65532 | | 65533 | 65534 | 65535 | x | 1 | .. | 9 | x | 11 |.....| 96 | // ------- ------------------------------------------------------------ - stream_generator->Init(65532, 0, clock_->TimeInMilliseconds()); + stream_generator_->Init(65532, 0, clock_->TimeInMilliseconds()); InsertFrame(kVideoFrameKey); EXPECT_FALSE(request_key_frame); EXPECT_TRUE(DecodeCompleteFrame()); - stream_generator->GenerateFrame(kVideoFrameDelta, 100, 0, + stream_generator_->GenerateFrame(kVideoFrameDelta, 100, 0, clock_->TimeInMilliseconds()); EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0)); - while (stream_generator->PacketsRemaining() > 1) { - if (stream_generator->NextSequenceNumber() % 10 != 0) { + while (stream_generator_->PacketsRemaining() > 1) { + if (stream_generator_->NextSequenceNumber() % 10 != 0) { EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); EXPECT_FALSE(request_key_frame); } else { - stream_generator->NextPacket(NULL); // Drop packet. + stream_generator_->NextPacket(NULL); // Drop packet } } EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); EXPECT_FALSE(request_key_frame); - EXPECT_EQ(0, stream_generator->PacketsRemaining()); + EXPECT_EQ(0, stream_generator_->PacketsRemaining()); EXPECT_FALSE(DecodeCompleteFrame()); EXPECT_FALSE(DecodeCompleteFrame()); uint16_t nack_list_size = 0; diff --git a/webrtc/modules/video_coding/main/source/receiver.cc b/webrtc/modules/video_coding/main/source/receiver.cc index fffe6094d..65d7a6839 100644 --- a/webrtc/modules/video_coding/main/source/receiver.cc +++ b/webrtc/modules/video_coding/main/source/receiver.cc @@ -432,6 +432,10 @@ int VCMReceiver::SetMinReceiverDelay(int desired_delay_ms) { return 0; } +int VCMReceiver::RenderBufferSizeMs() { + return jitter_buffer_.RenderBufferSizeMs(); +} + void VCMReceiver::UpdateState(VCMReceiverState new_state) { CriticalSectionScoped cs(crit_sect_); assert(!(state_ == kPassive && new_state == kWaitForPrimaryDecode)); diff --git a/webrtc/modules/video_coding/main/source/receiver.h b/webrtc/modules/video_coding/main/source/receiver.h index f0cf86599..a62ae2f15 100644 --- a/webrtc/modules/video_coding/main/source/receiver.h +++ b/webrtc/modules/video_coding/main/source/receiver.h @@ -80,6 +80,10 @@ class VCMReceiver { void SetDecodeWithErrors(bool enable); bool DecodeWithErrors() const; + // Returns size in time (milliseconds) of complete continuous frames in the + // jitter buffer. + int RenderBufferSizeMs(); + private: VCMEncodedFrame* FrameForDecoding(uint16_t max_wait_time_ms, int64_t nextrender_time_ms, diff --git a/webrtc/modules/video_coding/main/source/receiver_unittest.cc b/webrtc/modules/video_coding/main/source/receiver_unittest.cc new file mode 100644 index 000000000..885433539 --- /dev/null +++ b/webrtc/modules/video_coding/main/source/receiver_unittest.cc @@ -0,0 +1,125 @@ +/* Copyright (c) 2013 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 "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/video_coding/main/source/receiver.h" +#include "webrtc/modules/video_coding/main/source/packet.h" +#include "webrtc/modules/video_coding/main/source/timing.h" +#include "webrtc/modules/video_coding/main/test/test_util.h" +#include "webrtc/modules/video_coding/main/source/stream_generator.h" +#include "webrtc/system_wrappers/interface/clock.h" +#include "webrtc/system_wrappers/interface/critical_section_wrapper.h" + +namespace webrtc { + +class TestVCMReceiver : public ::testing::Test { + protected: + enum { kDataBufferSize = 10 }; + enum { kWidth = 640 }; + enum { kHeight = 480 }; + + TestVCMReceiver() + : clock_(new SimulatedClock(0)), + timing_(clock_.get()), + receiver_(&timing_, clock_.get(), &event_factory_, 1, 1, true) { + stream_generator_.reset(new + StreamGenerator(0, 0, clock_->TimeInMilliseconds())); + memset(data_buffer_, 0, kDataBufferSize); + } + + virtual void SetUp() { + receiver_.Reset(); + } + + int32_t InsertPacket(int index) { + VCMPacket packet; + packet.dataPtr = data_buffer_; + bool packet_available = stream_generator_->GetPacket(&packet, index); + EXPECT_TRUE(packet_available); + if (!packet_available) + return kStateError; // Return here to avoid crashes below. + // Arbitrary width and height. + return receiver_.InsertPacket(packet, 640, 480); + } + + int32_t InsertPacketAndPop(int index) { + VCMPacket packet; + packet.dataPtr = data_buffer_; + bool packet_available = stream_generator_->PopPacket(&packet, index); + EXPECT_TRUE(packet_available); + if (!packet_available) + return kStateError; // Return here to avoid crashes below. + return receiver_.InsertPacket(packet, kWidth, kHeight); + } + + int32_t InsertFrame(FrameType frame_type, bool complete) { + int num_of_packets = complete ? 1 : 2; + stream_generator_->GenerateFrame( + frame_type, + (frame_type != kFrameEmpty) ? num_of_packets : 0, + (frame_type == kFrameEmpty) ? 1 : 0, + clock_->TimeInMilliseconds()); + int32_t ret = InsertPacketAndPop(0); + clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); + return ret; + } + + scoped_ptr clock_; + VCMTiming timing_; + NullEventFactory event_factory_; + VCMReceiver receiver_; + scoped_ptr stream_generator_; + uint8_t data_buffer_[kDataBufferSize]; +}; + +TEST_F(TestVCMReceiver, RenderBufferSize_AllComplete) { + EXPECT_EQ(0, receiver_.RenderBufferSizeMs()); + EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError); + int num_of_frames = 10; + for (int i = 0; i < num_of_frames; ++i) { + EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError); + } + EXPECT_EQ(num_of_frames * kDefaultFramePeriodMs, + receiver_.RenderBufferSizeMs()); +} + +TEST_F(TestVCMReceiver, RenderBufferSize_NotAllComplete) { + EXPECT_EQ(0, receiver_.RenderBufferSizeMs()); + EXPECT_GE(InsertFrame(kVideoFrameKey, true), kNoError); + int num_of_frames = 10; + for (int i = 0; i < num_of_frames; ++i) { + EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError); + } + num_of_frames++; + EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError); + for (int i = 0; i < num_of_frames; ++i) { + EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError); + } + EXPECT_EQ(num_of_frames * kDefaultFramePeriodMs, + receiver_.RenderBufferSizeMs()); +} + +TEST_F(TestVCMReceiver, RenderBufferSize_NoKeyFrame) { + EXPECT_EQ(0, receiver_.RenderBufferSizeMs()); + int num_of_frames = 10; + for (int i = 0; i < num_of_frames; ++i) { + EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError); + } + EXPECT_GE(InsertFrame(kVideoFrameDelta, false), kNoError); + for (int i = 0; i < num_of_frames; ++i) { + EXPECT_GE(InsertFrame(kVideoFrameDelta, true), kNoError); + } + EXPECT_EQ(0, receiver_.RenderBufferSizeMs()); +} + +} // namespace webrtc diff --git a/webrtc/modules/video_coding/main/source/stream_generator.cc b/webrtc/modules/video_coding/main/source/stream_generator.cc new file mode 100644 index 000000000..b4be4ab46 --- /dev/null +++ b/webrtc/modules/video_coding/main/source/stream_generator.cc @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2013 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/main/source/stream_generator.h" + +#include + +#include + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/video_coding/main/source/packet.h" +#include "webrtc/modules/video_coding/main/test/test_util.h" +#include "webrtc/system_wrappers/interface/clock.h" + + +namespace webrtc { + +StreamGenerator::StreamGenerator(uint16_t start_seq_num, + uint32_t start_timestamp, + int64_t current_time) + : packets_(), + sequence_number_(start_seq_num), + timestamp_(start_timestamp), + start_time_(current_time) {} + +void StreamGenerator::Init(uint16_t start_seq_num, uint32_t start_timestamp, + int64_t current_time) { + packets_.clear(); + sequence_number_ = start_seq_num; + timestamp_ = start_timestamp; + start_time_ = current_time; + memset(&packet_buffer, 0, sizeof(packet_buffer)); +} + +void StreamGenerator::GenerateFrame(FrameType type, + int num_media_packets, + int num_empty_packets, + int64_t current_time) { + timestamp_ += 90 * (current_time - start_time_); + // Move the sequence number counter if all packets from the previous frame + // wasn't collected. + sequence_number_ += packets_.size(); + packets_.clear(); + for (int i = 0; i < num_media_packets; ++i) { + const int packet_size = (kFrameSize + num_media_packets / 2) / + num_media_packets; + bool marker_bit = (i == num_media_packets - 1); + packets_.push_back(GeneratePacket(sequence_number_, + timestamp_, + packet_size, + (i == 0), + marker_bit, + type)); + ++sequence_number_; + } + for (int i = 0; i < num_empty_packets; ++i) { + packets_.push_back(GeneratePacket(sequence_number_, + timestamp_, + 0, + false, + false, + kFrameEmpty)); + ++sequence_number_; + } +} + +VCMPacket StreamGenerator::GeneratePacket(uint16_t sequence_number, + uint32_t timestamp, + unsigned int size, + bool first_packet, + bool marker_bit, + FrameType type) { + EXPECT_LT(size, kMaxPacketSize); + VCMPacket packet; + packet.seqNum = sequence_number; + packet.timestamp = timestamp; + packet.frameType = type; + packet.isFirstPacket = first_packet; + packet.markerBit = marker_bit; + packet.sizeBytes = size; + packet.dataPtr = packet_buffer; + if (packet.isFirstPacket) + packet.completeNALU = kNaluStart; + else if (packet.markerBit) + packet.completeNALU = kNaluEnd; + else + packet.completeNALU = kNaluIncomplete; + return packet; +} + +bool StreamGenerator::PopPacket(VCMPacket* packet, int index) { + std::list::iterator it = GetPacketIterator(index); + if (it == packets_.end()) + return false; + if (packet) + *packet = (*it); + packets_.erase(it); + return true; +} + +bool StreamGenerator::GetPacket(VCMPacket* packet, int index) { + std::list::iterator it = GetPacketIterator(index); + if (it == packets_.end()) + return false; + if (packet) + *packet = (*it); + return true; +} + +bool StreamGenerator::NextPacket(VCMPacket* packet) { + if (packets_.empty()) + return false; + if (packet != NULL) + *packet = packets_.front(); + packets_.pop_front(); + return true; +} + +uint16_t StreamGenerator::NextSequenceNumber() const { + if (packets_.empty()) + return sequence_number_; + return packets_.front().seqNum; +} + +int StreamGenerator::PacketsRemaining() const { + return packets_.size(); +} + +std::list::iterator StreamGenerator::GetPacketIterator(int index) { + std::list::iterator it = packets_.begin(); + for (int i = 0; i < index; ++i) { + ++it; + if (it == packets_.end()) break; + } + return it; +} + +} // namespace webrtc diff --git a/webrtc/modules/video_coding/main/source/stream_generator.h b/webrtc/modules/video_coding/main/source/stream_generator.h new file mode 100644 index 000000000..563050412 --- /dev/null +++ b/webrtc/modules/video_coding/main/source/stream_generator.h @@ -0,0 +1,72 @@ +/* + * Copyright (c) 2013 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_MAIN_SOURCE_STREAM_GENERATOR_H_ +#define WEBRTC_MODULES_VIDEO_CODING_MAIN_SOURCE_STREAM_GENERATOR_H_ + +#include + +#include + +#include "webrtc/modules/video_coding/main/source/packet.h" +#include "webrtc/modules/video_coding/main/test/test_util.h" + +namespace webrtc { + +const unsigned int kDefaultBitrateKbps = 1000; +enum { kDefaultFrameRate = 25u }; +const unsigned int kMaxPacketSize = 1500; +const unsigned int kFrameSize = (kDefaultBitrateKbps + kDefaultFrameRate * 4) / + (kDefaultFrameRate * 8); +enum { kDefaultFramePeriodMs = 1000u / kDefaultFrameRate }; + + + +class StreamGenerator { + public: + StreamGenerator(uint16_t start_seq_num, uint32_t start_timestamp, + int64_t current_time); + void Init(uint16_t start_seq_num, uint32_t start_timestamp, + int64_t current_time); + + void GenerateFrame(FrameType type, int num_media_packets, + int num_empty_packets, int64_t current_time); + + VCMPacket GeneratePacket(uint16_t sequence_number, + uint32_t timestamp, + unsigned int size, + bool first_packet, + bool marker_bit, + FrameType type); + + bool PopPacket(VCMPacket* packet, int index); + + bool GetPacket(VCMPacket* packet, int index); + + bool NextPacket(VCMPacket* packet); + + uint16_t NextSequenceNumber() const; + + int PacketsRemaining() const; + + private: + std::list::iterator GetPacketIterator(int index); + + std::list packets_; + uint16_t sequence_number_; + uint32_t timestamp_; + int64_t start_time_; + uint8_t packet_buffer[kMaxPacketSize]; + + DISALLOW_COPY_AND_ASSIGN(StreamGenerator); +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_VIDEO_CODING_MAIN_SOURCE_STREAM_GENERATOR_H_ diff --git a/webrtc/modules/video_coding/main/source/video_coding_impl.cc b/webrtc/modules/video_coding/main/source/video_coding_impl.cc index 74730ecb9..aa7cd49b6 100644 --- a/webrtc/modules/video_coding/main/source/video_coding_impl.cc +++ b/webrtc/modules/video_coding/main/source/video_coding_impl.cc @@ -62,6 +62,7 @@ VideoCodingModuleImpl::VideoCodingModuleImpl(const int32_t id, _frameStorageCallback(NULL), _receiveStatsCallback(NULL), _packetRequestCallback(NULL), + render_buffer_callback_(NULL), _decoder(NULL), _dualDecoder(NULL), #ifdef DEBUG_DECODER_BIT_STREAM @@ -154,6 +155,12 @@ VideoCodingModuleImpl::Process() _receiver.ReceiveStatistics(&bitRate, &frameRate); _receiveStatsCallback->ReceiveStatistics(bitRate, frameRate); } + + // Size of render buffer. + if (render_buffer_callback_) { + int buffer_size_ms = _receiver.RenderBufferSizeMs(); + render_buffer_callback_->RenderBufferSizeMs(buffer_size_ms); + } } // Send-side statistics @@ -870,6 +877,13 @@ VideoCodingModuleImpl::RegisterPacketRequestCallback( return VCM_OK; } +int VideoCodingModuleImpl::RegisterRenderBufferSizeCallback( + VCMRenderBufferSizeCallback* callback) { + CriticalSectionScoped cs(_receiveCritSect); + render_buffer_callback_ = callback; + return VCM_OK; +} + // Decode next frame, blocking. // Should be called as often as possible to get the most out of the decoder. int32_t diff --git a/webrtc/modules/video_coding/main/source/video_coding_impl.h b/webrtc/modules/video_coding/main/source/video_coding_impl.h index 91f151cc5..84972737e 100644 --- a/webrtc/modules/video_coding/main/source/video_coding_impl.h +++ b/webrtc/modules/video_coding/main/source/video_coding_impl.h @@ -196,6 +196,10 @@ public: virtual int32_t RegisterPacketRequestCallback( VCMPacketRequestCallback* callback); + // Render buffer size callback. + virtual int RegisterRenderBufferSizeCallback( + VCMRenderBufferSizeCallback* callback); + // Decode next frame, blocks for a maximum of maxWaitTimeMs milliseconds. // Should be called as often as possible to get the most out of the decoder. virtual int32_t Decode(uint16_t maxWaitTimeMs = 200); @@ -294,6 +298,7 @@ private: VCMFrameStorageCallback* _frameStorageCallback; VCMReceiveStatisticsCallback* _receiveStatsCallback; VCMPacketRequestCallback* _packetRequestCallback; + VCMRenderBufferSizeCallback* render_buffer_callback_; VCMGenericDecoder* _decoder; VCMGenericDecoder* _dualDecoder; #ifdef DEBUG_DECODER_BIT_STREAM diff --git a/webrtc/modules/video_coding/main/source/video_coding_test.gypi b/webrtc/modules/video_coding/main/source/video_coding_test.gypi index f18048ea5..c3eed238e 100644 --- a/webrtc/modules/video_coding/main/source/video_coding_test.gypi +++ b/webrtc/modules/video_coding/main/source/video_coding_test.gypi @@ -105,7 +105,10 @@ '../interface/mock/mock_vcm_callbacks.h', 'decoding_state_unittest.cc', 'jitter_buffer_unittest.cc', + 'receiver_unittest.cc', 'session_info_unittest.cc', + 'stream_generator.cc', + 'stream_generator.h', 'video_coding_robustness_unittest.cc', 'video_coding_impl_unittest.cc', 'qm_select_unittest.cc',