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:
parent
8392cd9edd
commit
381da4be9c
@ -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
|
||||||
|
@ -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_
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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) {
|
||||||
|
@ -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
|
||||||
|
@ -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;
|
||||||
|
@ -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));
|
||||||
|
@ -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,
|
||||||
|
125
webrtc/modules/video_coding/main/source/receiver_unittest.cc
Normal file
125
webrtc/modules/video_coding/main/source/receiver_unittest.cc
Normal 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
|
145
webrtc/modules/video_coding/main/source/stream_generator.cc
Normal file
145
webrtc/modules/video_coding/main/source/stream_generator.cc
Normal 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
|
72
webrtc/modules/video_coding/main/source/stream_generator.h
Normal file
72
webrtc/modules/video_coding/main/source/stream_generator.h
Normal 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_
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user