VCM: Adding API for the size(duration) of the jitter buffer.
Refers to the duration in time of the frames which are ready to be sent to the decoder.
Review URL: https://webrtc-codereview.appspot.com/1319004
git-svn-id: http://webrtc.googlecode.com/svn/trunk@3903 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/video_coding/main/interface/video_coding.h b/webrtc/modules/video_coding/main/interface/video_coding.h
index 41a63d1..9f5ddef 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 @@
// < 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 fcee34b..b1b52bd 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 @@
}
};
+// 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 eb03705..0974584 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 @@
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<uint16_t>(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 2fd143b..f001966 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 @@
// 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 0988242..6f133b3 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 @@
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<int>(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 ec35d1f..81a637a 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 @@
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 @@
// 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 bf81973..7971fae 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<VCMPacket>::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<VCMPacket>::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<VCMPacket>::iterator GetPacketIterator(int index) {
- std::list<VCMPacket>::iterator it = packets_.begin();
- for (int i = 0; i < index; ++i) {
- ++it;
- if (it == packets_.end()) break;
- }
- return it;
- }
-
- std::list<VCMPacket> 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 @@
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 @@
virtual void TearDown() {
jitter_buffer_->Stop();
- delete stream_generator;
+ delete stream_generator_;
delete jitter_buffer_;
}
@@ -184,7 +50,7 @@
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 @@
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 @@
}
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 @@
}
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 @@
}
VCMJitterBuffer* jitter_buffer_;
- StreamGenerator* stream_generator;
+ StreamGenerator* stream_generator_;
scoped_ptr<SimulatedClock> clock_;
NullEventFactory event_factory_;
size_t max_nack_list_size_;
@@ -288,7 +154,7 @@
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, 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, 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 @@
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 @@
// ----------------------------------------------------------------
// | 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 @@
// ------- ------------------------------------------------------------
// | 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 fffe609..65d7a68 100644
--- a/webrtc/modules/video_coding/main/source/receiver.cc
+++ b/webrtc/modules/video_coding/main/source/receiver.cc
@@ -432,6 +432,10 @@
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 f0cf865..a62ae2f 100644
--- a/webrtc/modules/video_coding/main/source/receiver.h
+++ b/webrtc/modules/video_coding/main/source/receiver.h
@@ -80,6 +80,10 @@
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 0000000..8854335
--- /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 <string.h>
+
+#include <list>
+
+#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<SimulatedClock> clock_;
+ VCMTiming timing_;
+ NullEventFactory event_factory_;
+ VCMReceiver receiver_;
+ scoped_ptr<StreamGenerator> 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 0000000..b4be4ab
--- /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 <string.h>
+
+#include <list>
+
+#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<VCMPacket>::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<VCMPacket>::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<VCMPacket>::iterator StreamGenerator::GetPacketIterator(int index) {
+ std::list<VCMPacket>::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 0000000..5630504
--- /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 <string.h>
+
+#include <list>
+
+#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<VCMPacket>::iterator GetPacketIterator(int index);
+
+ std::list<VCMPacket> 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 74730ec..aa7cd49 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 @@
_frameStorageCallback(NULL),
_receiveStatsCallback(NULL),
_packetRequestCallback(NULL),
+ render_buffer_callback_(NULL),
_decoder(NULL),
_dualDecoder(NULL),
#ifdef DEBUG_DECODER_BIT_STREAM
@@ -154,6 +155,12 @@
_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 @@
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 91f151c..8497273 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 @@
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 @@
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 f18048e..c3eed23 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',