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
This commit is contained in:
mikhal@webrtc.org 2013-04-25 21:45:29 +00:00
parent 8392cd9edd
commit 381da4be9c
15 changed files with 469 additions and 160 deletions

View File

@ -412,6 +412,10 @@ public:
// < 0, on error. // < 0, on error.
virtual int32_t Decode(uint16_t maxWaitTimeMs = 200) = 0; 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 for the next frame in the dual jitter buffer to become complete
// (waits no longer than maxWaitTimeMs), then passes it to the dual decoder // (waits no longer than maxWaitTimeMs), then passes it to the dual decoder
// for decoding. This will never trigger a render callback. Should be // for decoding. This will never trigger a render callback. Should be

View File

@ -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 } // namespace webrtc
#endif // WEBRTC_MODULES_INTERFACE_VIDEO_CODING_DEFINES_H_ #endif // WEBRTC_MODULES_INTERFACE_VIDEO_CODING_DEFINES_H_

View File

@ -72,6 +72,16 @@ void VCMDecodingState::SetState(const VCMFrameBuffer* frame) {
in_initial_state_ = false; 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) { void VCMDecodingState::SetStateOneBack(const VCMFrameBuffer* frame) {
assert(frame != NULL && frame->GetHighSeqNum() >= 0); assert(frame != NULL && frame->GetHighSeqNum() >= 0);
sequence_num_ = static_cast<uint16_t>(frame->GetHighSeqNum()) - 1u; sequence_num_ = static_cast<uint16_t>(frame->GetHighSeqNum()) - 1u;

View File

@ -31,6 +31,7 @@ class VCMDecodingState {
// possible, i.e. temporal info, picture ID or sequence number. // possible, i.e. temporal info, picture ID or sequence number.
bool ContinuousFrame(const VCMFrameBuffer* frame) const; bool ContinuousFrame(const VCMFrameBuffer* frame) const;
void SetState(const VCMFrameBuffer* frame); void SetState(const VCMFrameBuffer* frame);
void CopyFrom(const VCMDecodingState& state);
// Set the decoding state one frame back. // Set the decoding state one frame back.
void SetStateOneBack(const VCMFrameBuffer* frame); void SetStateOneBack(const VCMFrameBuffer* frame);
void UpdateEmptyFrame(const VCMFrameBuffer* frame); void UpdateEmptyFrame(const VCMFrameBuffer* frame);

View File

@ -982,6 +982,46 @@ int64_t VCMJitterBuffer::LastDecodedTimestamp() const {
return last_decoded_state_.time_stamp(); 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 // Set the frame state to free and remove it from the sorted
// frame list. Must be called from inside the critical section crit_sect_. // frame list. Must be called from inside the critical section crit_sect_.
void VCMJitterBuffer::ReleaseFrameIfNotDecoding(VCMFrameBuffer* frame) { void VCMJitterBuffer::ReleaseFrameIfNotDecoding(VCMFrameBuffer* frame) {

View File

@ -166,6 +166,9 @@ class VCMJitterBuffer {
int64_t LastDecodedTimestamp() const; int64_t LastDecodedTimestamp() const;
bool decode_with_errors() const {return decode_with_errors_;} bool decode_with_errors() const {return decode_with_errors_;}
// Returns size in time (milliseconds) of complete continuous frames.
int RenderBufferSizeMs();
private: private:
class SequenceNumberLessThan { class SequenceNumberLessThan {
public: public:
@ -211,6 +214,8 @@ class VCMJitterBuffer {
// Can return a decodable, incomplete frame when enabled. // Can return a decodable, incomplete frame when enabled.
FrameList::iterator FindOldestCompleteContinuousFrame(); FrameList::iterator FindOldestCompleteContinuousFrame();
// Cleans the frame list in the JB from old/empty frames.
// Should only be called prior to actual use.
void CleanUpOldOrEmptyFrames(); void CleanUpOldOrEmptyFrames();
// Sets the "decodable" and "frame loss" flags of a frame depending on which // Sets the "decodable" and "frame loss" flags of a frame depending on which

View File

@ -16,146 +16,12 @@
#include "webrtc/modules/video_coding/main/source/jitter_buffer.h" #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/media_opt_util.h"
#include "webrtc/modules/video_coding/main/source/packet.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/modules/video_coding/main/test/test_util.h"
#include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/clock.h"
namespace webrtc { 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 { class TestRunningJitterBuffer : public ::testing::Test {
protected: protected:
enum { kDataBufferSize = 10 }; enum { kDataBufferSize = 10 };
@ -166,7 +32,7 @@ class TestRunningJitterBuffer : public ::testing::Test {
oldest_packet_to_nack_ = 250; oldest_packet_to_nack_ = 250;
jitter_buffer_ = new VCMJitterBuffer(clock_.get(), &event_factory_, -1, -1, jitter_buffer_ = new VCMJitterBuffer(clock_.get(), &event_factory_, -1, -1,
true); true);
stream_generator = new StreamGenerator(0, 0, clock_->TimeInMilliseconds()); stream_generator_ = new StreamGenerator(0, 0, clock_->TimeInMilliseconds());
jitter_buffer_->Start(); jitter_buffer_->Start();
jitter_buffer_->SetNackSettings(max_nack_list_size_, jitter_buffer_->SetNackSettings(max_nack_list_size_,
oldest_packet_to_nack_); oldest_packet_to_nack_);
@ -175,7 +41,7 @@ class TestRunningJitterBuffer : public ::testing::Test {
virtual void TearDown() { virtual void TearDown() {
jitter_buffer_->Stop(); jitter_buffer_->Stop();
delete stream_generator; delete stream_generator_;
delete jitter_buffer_; delete jitter_buffer_;
} }
@ -184,7 +50,7 @@ class TestRunningJitterBuffer : public ::testing::Test {
VCMEncodedFrame* frame; VCMEncodedFrame* frame;
packet.dataPtr = data_buffer_; packet.dataPtr = data_buffer_;
bool packet_available = stream_generator->PopPacket(&packet, index); bool packet_available = stream_generator_->PopPacket(&packet, index);
EXPECT_TRUE(packet_available); EXPECT_TRUE(packet_available);
if (!packet_available) if (!packet_available)
return kStateError; // Return here to avoid crashes below. return kStateError; // Return here to avoid crashes below.
@ -197,7 +63,7 @@ class TestRunningJitterBuffer : public ::testing::Test {
VCMEncodedFrame* frame; VCMEncodedFrame* frame;
packet.dataPtr = data_buffer_; packet.dataPtr = data_buffer_;
bool packet_available = stream_generator->GetPacket(&packet, index); bool packet_available = stream_generator_->GetPacket(&packet, index);
EXPECT_TRUE(packet_available); EXPECT_TRUE(packet_available);
if (!packet_available) if (!packet_available)
return kStateError; // Return here to avoid crashes below. return kStateError; // Return here to avoid crashes below.
@ -206,7 +72,7 @@ class TestRunningJitterBuffer : public ::testing::Test {
} }
VCMFrameBufferEnum InsertFrame(FrameType frame_type) { 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,
(frame_type == kFrameEmpty) ? 1 : 0, (frame_type == kFrameEmpty) ? 1 : 0,
clock_->TimeInMilliseconds()); clock_->TimeInMilliseconds());
@ -229,7 +95,7 @@ class TestRunningJitterBuffer : public ::testing::Test {
} }
void DropFrame(int num_packets) { void DropFrame(int num_packets) {
stream_generator->GenerateFrame(kVideoFrameDelta, num_packets, 0, stream_generator_->GenerateFrame(kVideoFrameDelta, num_packets, 0,
clock_->TimeInMilliseconds()); clock_->TimeInMilliseconds());
clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs);
} }
@ -250,7 +116,7 @@ class TestRunningJitterBuffer : public ::testing::Test {
} }
VCMJitterBuffer* jitter_buffer_; VCMJitterBuffer* jitter_buffer_;
StreamGenerator* stream_generator; StreamGenerator* stream_generator_;
scoped_ptr<SimulatedClock> clock_; scoped_ptr<SimulatedClock> clock_;
NullEventFactory event_factory_; NullEventFactory event_factory_;
size_t max_nack_list_size_; size_t max_nack_list_size_;
@ -288,7 +154,7 @@ TEST_F(TestRunningJitterBuffer, Full) {
TEST_F(TestRunningJitterBuffer, EmptyPackets) { TEST_F(TestRunningJitterBuffer, EmptyPackets) {
// Make sure a frame can get complete even though empty packets are missing. // 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()); clock_->TimeInMilliseconds());
bool request_key_frame = false; bool request_key_frame = false;
EXPECT_EQ(kFirstPacket, InsertPacketAndPop(4)); EXPECT_EQ(kFirstPacket, InsertPacketAndPop(4));
@ -488,11 +354,11 @@ TEST_F(TestJitterBufferNack, NoNackListReturnedBeforeFirstDecode) {
} }
TEST_F(TestJitterBufferNack, NackListBuiltBeforeFirstDecode) { TEST_F(TestJitterBufferNack, NackListBuiltBeforeFirstDecode) {
stream_generator->Init(0, 0, clock_->TimeInMilliseconds()); stream_generator_->Init(0, 0, clock_->TimeInMilliseconds());
InsertFrame(kVideoFrameKey); InsertFrame(kVideoFrameKey);
stream_generator->GenerateFrame(kVideoFrameDelta, 2, 0, stream_generator_->GenerateFrame(kVideoFrameDelta, 2, 0,
clock_->TimeInMilliseconds()); clock_->TimeInMilliseconds());
stream_generator->NextPacket(NULL); // Drop packet. stream_generator_->NextPacket(NULL); // Drop packet.
EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0)); EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0));
EXPECT_TRUE(DecodeCompleteFrame()); EXPECT_TRUE(DecodeCompleteFrame());
uint16_t nack_list_size = 0; uint16_t nack_list_size = 0;
@ -503,8 +369,8 @@ TEST_F(TestJitterBufferNack, NackListBuiltBeforeFirstDecode) {
} }
TEST_F(TestJitterBufferNack, UseNackToRecoverFirstKeyFrame) { TEST_F(TestJitterBufferNack, UseNackToRecoverFirstKeyFrame) {
stream_generator->Init(0, 0, clock_->TimeInMilliseconds()); stream_generator_->Init(0, 0, clock_->TimeInMilliseconds());
stream_generator->GenerateFrame(kVideoFrameKey, 3, 0, stream_generator_->GenerateFrame(kVideoFrameKey, 3, 0,
clock_->TimeInMilliseconds()); clock_->TimeInMilliseconds());
EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0)); EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0));
// Drop second packet. // Drop second packet.
@ -516,7 +382,7 @@ TEST_F(TestJitterBufferNack, UseNackToRecoverFirstKeyFrame) {
EXPECT_EQ(1, nack_list_size); EXPECT_EQ(1, nack_list_size);
ASSERT_TRUE(list != NULL); ASSERT_TRUE(list != NULL);
VCMPacket packet; VCMPacket packet;
stream_generator->GetPacket(&packet, 0); stream_generator_->GetPacket(&packet, 0);
EXPECT_EQ(packet.seqNum, list[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 | // | 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_->TimeInMilliseconds());
clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs); clock_->AdvanceTimeMilliseconds(kDefaultFramePeriodMs);
EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0)); EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0));
// Verify that the frame is incomplete. // Verify that the frame is incomplete.
EXPECT_FALSE(DecodeCompleteFrame()); EXPECT_FALSE(DecodeCompleteFrame());
while (stream_generator->PacketsRemaining() > 1) { while (stream_generator_->PacketsRemaining() > 1) {
if (stream_generator->NextSequenceNumber() % 10 != 0) { if (stream_generator_->NextSequenceNumber() % 10 != 0) {
EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); EXPECT_EQ(kIncomplete, InsertPacketAndPop(0));
} else { } else {
stream_generator->NextPacket(NULL); // Drop packet stream_generator_->NextPacket(NULL); // Drop packet
} }
} }
EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); EXPECT_EQ(kIncomplete, InsertPacketAndPop(0));
EXPECT_EQ(0, stream_generator->PacketsRemaining()); EXPECT_EQ(0, stream_generator_->PacketsRemaining());
EXPECT_FALSE(DecodeCompleteFrame()); EXPECT_FALSE(DecodeCompleteFrame());
EXPECT_FALSE(DecodeIncompleteFrame()); EXPECT_FALSE(DecodeIncompleteFrame());
uint16_t nack_list_size = 0; uint16_t nack_list_size = 0;
@ -563,24 +429,24 @@ TEST_F(TestJitterBufferNack, NormalOperationWrap) {
// ------- ------------------------------------------------------------ // ------- ------------------------------------------------------------
// | 65532 | | 65533 | 65534 | 65535 | x | 1 | .. | 9 | x | 11 |.....| 96 | // | 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); InsertFrame(kVideoFrameKey);
EXPECT_FALSE(request_key_frame); EXPECT_FALSE(request_key_frame);
EXPECT_TRUE(DecodeCompleteFrame()); EXPECT_TRUE(DecodeCompleteFrame());
stream_generator->GenerateFrame(kVideoFrameDelta, 100, 0, stream_generator_->GenerateFrame(kVideoFrameDelta, 100, 0,
clock_->TimeInMilliseconds()); clock_->TimeInMilliseconds());
EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0)); EXPECT_EQ(kFirstPacket, InsertPacketAndPop(0));
while (stream_generator->PacketsRemaining() > 1) { while (stream_generator_->PacketsRemaining() > 1) {
if (stream_generator->NextSequenceNumber() % 10 != 0) { if (stream_generator_->NextSequenceNumber() % 10 != 0) {
EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); EXPECT_EQ(kIncomplete, InsertPacketAndPop(0));
EXPECT_FALSE(request_key_frame); EXPECT_FALSE(request_key_frame);
} else { } else {
stream_generator->NextPacket(NULL); // Drop packet. stream_generator_->NextPacket(NULL); // Drop packet
} }
} }
EXPECT_EQ(kIncomplete, InsertPacketAndPop(0)); EXPECT_EQ(kIncomplete, InsertPacketAndPop(0));
EXPECT_FALSE(request_key_frame); EXPECT_FALSE(request_key_frame);
EXPECT_EQ(0, stream_generator->PacketsRemaining()); EXPECT_EQ(0, stream_generator_->PacketsRemaining());
EXPECT_FALSE(DecodeCompleteFrame()); EXPECT_FALSE(DecodeCompleteFrame());
EXPECT_FALSE(DecodeCompleteFrame()); EXPECT_FALSE(DecodeCompleteFrame());
uint16_t nack_list_size = 0; uint16_t nack_list_size = 0;

View File

@ -432,6 +432,10 @@ int VCMReceiver::SetMinReceiverDelay(int desired_delay_ms) {
return 0; return 0;
} }
int VCMReceiver::RenderBufferSizeMs() {
return jitter_buffer_.RenderBufferSizeMs();
}
void VCMReceiver::UpdateState(VCMReceiverState new_state) { void VCMReceiver::UpdateState(VCMReceiverState new_state) {
CriticalSectionScoped cs(crit_sect_); CriticalSectionScoped cs(crit_sect_);
assert(!(state_ == kPassive && new_state == kWaitForPrimaryDecode)); assert(!(state_ == kPassive && new_state == kWaitForPrimaryDecode));

View File

@ -80,6 +80,10 @@ class VCMReceiver {
void SetDecodeWithErrors(bool enable); void SetDecodeWithErrors(bool enable);
bool DecodeWithErrors() const; bool DecodeWithErrors() const;
// Returns size in time (milliseconds) of complete continuous frames in the
// jitter buffer.
int RenderBufferSizeMs();
private: private:
VCMEncodedFrame* FrameForDecoding(uint16_t max_wait_time_ms, VCMEncodedFrame* FrameForDecoding(uint16_t max_wait_time_ms,
int64_t nextrender_time_ms, int64_t nextrender_time_ms,

View File

@ -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

View File

@ -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

View File

@ -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_

View File

@ -62,6 +62,7 @@ VideoCodingModuleImpl::VideoCodingModuleImpl(const int32_t id,
_frameStorageCallback(NULL), _frameStorageCallback(NULL),
_receiveStatsCallback(NULL), _receiveStatsCallback(NULL),
_packetRequestCallback(NULL), _packetRequestCallback(NULL),
render_buffer_callback_(NULL),
_decoder(NULL), _decoder(NULL),
_dualDecoder(NULL), _dualDecoder(NULL),
#ifdef DEBUG_DECODER_BIT_STREAM #ifdef DEBUG_DECODER_BIT_STREAM
@ -154,6 +155,12 @@ VideoCodingModuleImpl::Process()
_receiver.ReceiveStatistics(&bitRate, &frameRate); _receiver.ReceiveStatistics(&bitRate, &frameRate);
_receiveStatsCallback->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 // Send-side statistics
@ -870,6 +877,13 @@ VideoCodingModuleImpl::RegisterPacketRequestCallback(
return VCM_OK; return VCM_OK;
} }
int VideoCodingModuleImpl::RegisterRenderBufferSizeCallback(
VCMRenderBufferSizeCallback* callback) {
CriticalSectionScoped cs(_receiveCritSect);
render_buffer_callback_ = callback;
return VCM_OK;
}
// Decode next frame, blocking. // Decode next frame, blocking.
// Should be called as often as possible to get the most out of the decoder. // Should be called as often as possible to get the most out of the decoder.
int32_t int32_t

View File

@ -196,6 +196,10 @@ public:
virtual int32_t RegisterPacketRequestCallback( virtual int32_t RegisterPacketRequestCallback(
VCMPacketRequestCallback* callback); VCMPacketRequestCallback* callback);
// Render buffer size callback.
virtual int RegisterRenderBufferSizeCallback(
VCMRenderBufferSizeCallback* callback);
// Decode next frame, blocks for a maximum of maxWaitTimeMs milliseconds. // 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. // Should be called as often as possible to get the most out of the decoder.
virtual int32_t Decode(uint16_t maxWaitTimeMs = 200); virtual int32_t Decode(uint16_t maxWaitTimeMs = 200);
@ -294,6 +298,7 @@ private:
VCMFrameStorageCallback* _frameStorageCallback; VCMFrameStorageCallback* _frameStorageCallback;
VCMReceiveStatisticsCallback* _receiveStatsCallback; VCMReceiveStatisticsCallback* _receiveStatsCallback;
VCMPacketRequestCallback* _packetRequestCallback; VCMPacketRequestCallback* _packetRequestCallback;
VCMRenderBufferSizeCallback* render_buffer_callback_;
VCMGenericDecoder* _decoder; VCMGenericDecoder* _decoder;
VCMGenericDecoder* _dualDecoder; VCMGenericDecoder* _dualDecoder;
#ifdef DEBUG_DECODER_BIT_STREAM #ifdef DEBUG_DECODER_BIT_STREAM

View File

@ -105,7 +105,10 @@
'../interface/mock/mock_vcm_callbacks.h', '../interface/mock/mock_vcm_callbacks.h',
'decoding_state_unittest.cc', 'decoding_state_unittest.cc',
'jitter_buffer_unittest.cc', 'jitter_buffer_unittest.cc',
'receiver_unittest.cc',
'session_info_unittest.cc', 'session_info_unittest.cc',
'stream_generator.cc',
'stream_generator.h',
'video_coding_robustness_unittest.cc', 'video_coding_robustness_unittest.cc',
'video_coding_impl_unittest.cc', 'video_coding_impl_unittest.cc',
'qm_select_unittest.cc', 'qm_select_unittest.cc',