git-svn-id: http://webrtc.googlecode.com/svn/trunk@1431 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
asapersson@webrtc.org 2012-01-16 11:06:31 +00:00
parent 67cdc22e7e
commit 0b3c35a258
21 changed files with 1352 additions and 389 deletions

View File

@ -437,6 +437,13 @@ public:
virtual WebRtc_Word32 DeregisterSendRtpHeaderExtension(
const RTPExtensionType type) = 0;
/*
* Enable/disable traffic smoothing of sending stream.
*/
virtual void SetTransmissionSmoothingStatus(const bool enable) = 0;
virtual bool TransmissionSmoothingStatus() const = 0;
/*
* get start timestamp
*/

View File

@ -38,6 +38,12 @@ enum RTPAliveType
kRtpAlive = 2
};
enum StorageType {
kDontStore,
kDontRetransmit,
kAllowRetransmission
};
enum RTPExtensionType
{
kRtpExtensionNone,

View File

@ -96,6 +96,10 @@ class MockRtpRtcp : public RtpRtcp {
WebRtc_Word32(const RTPExtensionType type, const WebRtc_UWord8 id));
MOCK_METHOD1(DeregisterSendRtpHeaderExtension,
WebRtc_Word32(const RTPExtensionType type));
MOCK_METHOD1(SetTransmissionSmoothingStatus,
void(const bool enable));
MOCK_CONST_METHOD0(TransmissionSmoothingStatus,
bool());
MOCK_CONST_METHOD0(StartTimestamp,
WebRtc_UWord32());
MOCK_METHOD1(SetStartTimestamp,

View File

@ -32,8 +32,8 @@ void RtpHeaderExtensionMap::Erase() {
}
}
WebRtc_Word32 RtpHeaderExtensionMap::Register(const RTPExtensionType type,
const WebRtc_UWord8 id) {
int32_t RtpHeaderExtensionMap::Register(const RTPExtensionType type,
const uint8_t id) {
if (id < 1 || id > 14) {
return -1;
}
@ -46,8 +46,8 @@ WebRtc_Word32 RtpHeaderExtensionMap::Register(const RTPExtensionType type,
return 0;
}
WebRtc_Word32 RtpHeaderExtensionMap::Deregister(const RTPExtensionType type) {
WebRtc_UWord8 id;
int32_t RtpHeaderExtensionMap::Deregister(const RTPExtensionType type) {
uint8_t id;
if (GetId(type, &id) != 0) {
return -1;
}
@ -61,8 +61,8 @@ WebRtc_Word32 RtpHeaderExtensionMap::Deregister(const RTPExtensionType type) {
return 0;
}
WebRtc_Word32 RtpHeaderExtensionMap::GetType(const WebRtc_UWord8 id,
RTPExtensionType* type) const {
int32_t RtpHeaderExtensionMap::GetType(const uint8_t id,
RTPExtensionType* type) const {
assert(type);
MapItem* item = extensionMap_.Find(id);
if (item == NULL) {
@ -73,8 +73,8 @@ WebRtc_Word32 RtpHeaderExtensionMap::GetType(const WebRtc_UWord8 id,
return 0;
}
WebRtc_Word32 RtpHeaderExtensionMap::GetId(const RTPExtensionType type,
WebRtc_UWord8* id) const {
int32_t RtpHeaderExtensionMap::GetId(const RTPExtensionType type,
uint8_t* id) const {
assert(id);
MapItem* item = extensionMap_.First();
while (item != NULL) {
@ -88,10 +88,9 @@ WebRtc_Word32 RtpHeaderExtensionMap::GetId(const RTPExtensionType type,
return -1;
}
WebRtc_UWord16 RtpHeaderExtensionMap::GetTotalLengthInBytes() const
{
uint16_t RtpHeaderExtensionMap::GetTotalLengthInBytes() const {
// Get length for each extension block.
WebRtc_UWord16 length = 0;
uint16_t length = 0;
MapItem* item = extensionMap_.First();
while (item != NULL) {
HeaderExtension* extension = (HeaderExtension*)item->GetItem();
@ -105,7 +104,29 @@ WebRtc_UWord16 RtpHeaderExtensionMap::GetTotalLengthInBytes() const
return length;
}
WebRtc_Word32 RtpHeaderExtensionMap::Size() const {
int32_t RtpHeaderExtensionMap::GetLengthUntilBlockStartInBytes(
const RTPExtensionType type) const {
uint8_t id;
if (GetId(type, &id) != 0) {
// Not registered.
return -1;
}
// Get length until start of extension block type.
uint16_t length = RTP_ONE_BYTE_HEADER_LENGTH_IN_BYTES;
MapItem* item = extensionMap_.First();
while (item != NULL) {
HeaderExtension* extension = (HeaderExtension*)item->GetItem();
if (extension->type == type) {
break;
} else {
length += extension->length;
}
item = extensionMap_.Next(item);
}
return length;
}
int32_t RtpHeaderExtensionMap::Size() const {
return extensionMap_.Size();
}
@ -118,9 +139,8 @@ RTPExtensionType RtpHeaderExtensionMap::First() const {
return extension->type;
}
RTPExtensionType RtpHeaderExtensionMap::Next(RTPExtensionType type) const
{
WebRtc_UWord8 id;
RTPExtensionType RtpHeaderExtensionMap::Next(RTPExtensionType type) const {
uint8_t id;
if (GetId(type, &id) != 0) {
return kRtpExtensionNone;
}

View File

@ -11,6 +11,8 @@
#ifndef WEBRTC_MODULES_RTP_RTCP_RTP_HEADER_EXTENSION_H_
#define WEBRTC_MODULES_RTP_RTCP_RTP_HEADER_EXTENSION_H_
#include <stdint.h>
#include "map_wrapper.h"
#include "rtp_rtcp_defines.h"
#include "typedefs.h"
@ -34,7 +36,7 @@ struct HeaderExtension {
}
const RTPExtensionType type;
WebRtc_UWord8 length;
uint8_t length;
};
class RtpHeaderExtensionMap {
@ -44,19 +46,21 @@ class RtpHeaderExtensionMap {
void Erase();
WebRtc_Word32 Register(const RTPExtensionType type, const WebRtc_UWord8 id);
int32_t Register(const RTPExtensionType type, const uint8_t id);
WebRtc_Word32 Deregister(const RTPExtensionType type);
int32_t Deregister(const RTPExtensionType type);
WebRtc_Word32 GetType(const WebRtc_UWord8 id, RTPExtensionType* type) const;
int32_t GetType(const uint8_t id, RTPExtensionType* type) const;
WebRtc_Word32 GetId(const RTPExtensionType type, WebRtc_UWord8* id) const;
int32_t GetId(const RTPExtensionType type, uint8_t* id) const;
WebRtc_UWord16 GetTotalLengthInBytes() const;
uint16_t GetTotalLengthInBytes() const;
int32_t GetLengthUntilBlockStartInBytes(const RTPExtensionType type) const;
void GetCopy(RtpHeaderExtensionMap* map) const;
WebRtc_Word32 Size() const;
int32_t Size() const;
RTPExtensionType First() const;

View File

@ -54,7 +54,7 @@ TEST_F(RtpHeaderExtensionTest, NonUniqueId) {
EXPECT_EQ(-1, map_.Register(kRtpExtensionTransmissionTimeOffset, kId));
}
TEST_F(RtpHeaderExtensionTest, GetLength) {
TEST_F(RtpHeaderExtensionTest, GetTotalLength) {
EXPECT_EQ(0, map_.GetTotalLengthInBytes());
EXPECT_EQ(0, map_.Register(kRtpExtensionTransmissionTimeOffset, kId));
EXPECT_EQ(RTP_ONE_BYTE_HEADER_LENGTH_IN_BYTES +
@ -62,6 +62,15 @@ TEST_F(RtpHeaderExtensionTest, GetLength) {
map_.GetTotalLengthInBytes());
}
TEST_F(RtpHeaderExtensionTest, GetLengthUntilBlockStart) {
EXPECT_EQ(-1, map_.GetLengthUntilBlockStartInBytes(
kRtpExtensionTransmissionTimeOffset));
EXPECT_EQ(0, map_.Register(kRtpExtensionTransmissionTimeOffset, kId));
EXPECT_EQ(RTP_ONE_BYTE_HEADER_LENGTH_IN_BYTES,
map_.GetLengthUntilBlockStartInBytes(
kRtpExtensionTransmissionTimeOffset));
}
TEST_F(RtpHeaderExtensionTest, GetType) {
RTPExtensionType typeOut;
EXPECT_EQ(-1, map_.GetType(kId, &typeOut));
@ -72,7 +81,7 @@ TEST_F(RtpHeaderExtensionTest, GetType) {
}
TEST_F(RtpHeaderExtensionTest, GetId) {
WebRtc_UWord8 idOut;
uint8_t idOut;
EXPECT_EQ(-1, map_.GetId(kRtpExtensionTransmissionTimeOffset, &idOut));
EXPECT_EQ(0, map_.Register(kRtpExtensionTransmissionTimeOffset, kId));

View File

@ -0,0 +1,278 @@
/*
* Copyright (c) 2011 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.
*/
/*
* Class for storing RTP packets.
*/
#include "rtp_packet_history.h"
#include <assert.h>
#include <cstring> // memset
#include "critical_section_wrapper.h"
#include "rtp_utility.h"
#include "trace.h"
namespace webrtc {
RTPPacketHistory::RTPPacketHistory(RtpRtcpClock* clock)
: clock_(*clock),
critsect_(CriticalSectionWrapper::CreateCriticalSection()),
store_(false),
prev_index_(0),
max_packet_length_(0) {
}
RTPPacketHistory::~RTPPacketHistory() {
Free();
delete critsect_;
}
void RTPPacketHistory::SetStorePacketsStatus(bool enable,
uint16_t number_to_store) {
if (enable) {
Allocate(number_to_store);
} else {
Free();
}
}
void RTPPacketHistory::Allocate(uint16_t number_to_store) {
assert(number_to_store > 0);
webrtc::CriticalSectionScoped cs(*critsect_);
if (store_) {
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
"SetStorePacketsStatus already set, number: %d", number_to_store);
return;
}
store_ = true;
stored_packets_.resize(number_to_store);
stored_seq_nums_.resize(number_to_store);
stored_lengths_.resize(number_to_store);
stored_times_.resize(number_to_store);
stored_resend_times_.resize(number_to_store);
stored_types_.resize(number_to_store);
}
void RTPPacketHistory::Free() {
webrtc::CriticalSectionScoped cs(*critsect_);
if (!store_) {
return;
}
std::vector<std::vector<uint8_t> >::iterator it;
for (it = stored_packets_.begin(); it != stored_packets_.end(); ++it) {
it->clear();
}
stored_packets_.clear();
stored_seq_nums_.clear();
stored_lengths_.clear();
stored_times_.clear();
stored_resend_times_.clear();
stored_types_.clear();
store_ = false;
prev_index_ = 0;
max_packet_length_ = 0;
}
bool RTPPacketHistory::StorePackets() const {
webrtc::CriticalSectionScoped cs(*critsect_);
return store_;
}
// private, lock should already be taken
void RTPPacketHistory::VerifyAndAllocatePacketLength(uint16_t packet_length) {
assert(packet_length > 0);
if (!store_) {
return;
}
if (packet_length <= max_packet_length_) {
return;
}
std::vector<std::vector<uint8_t> >::iterator it;
for (it = stored_packets_.begin(); it != stored_packets_.end(); ++it) {
it->resize(packet_length);
}
max_packet_length_ = packet_length;
}
int32_t RTPPacketHistory::PutRTPPacket(const uint8_t* packet,
uint16_t packet_length,
uint16_t max_packet_length,
StorageType type) {
if (type == kDontStore) {
return 0;
}
webrtc::CriticalSectionScoped cs(*critsect_);
if (!store_) {
return 0;
}
assert(packet);
assert(packet_length > 3);
VerifyAndAllocatePacketLength(max_packet_length);
if (packet_length > max_packet_length_) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, -1,
"Failed to store RTP packet, length: %d", packet_length);
return -1;
}
const uint16_t seq_num = (packet[2] << 8) + packet[3];
// Store packet
std::vector<std::vector<uint8_t> >::iterator it =
stored_packets_.begin() + prev_index_;
copy(packet, packet + packet_length, it->begin());
stored_seq_nums_[prev_index_] = seq_num;
stored_lengths_[prev_index_] = packet_length;
stored_times_[prev_index_] = clock_.GetTimeInMS();
stored_resend_times_[prev_index_] = 0; // packet not resent
stored_types_[prev_index_] = type;
++prev_index_;
if (prev_index_ >= stored_seq_nums_.size()) {
prev_index_ = 0;
}
return 0;
}
bool RTPPacketHistory::HasRTPPacket(uint16_t sequence_number) const {
webrtc::CriticalSectionScoped cs(*critsect_);
if (!store_) {
return false;
}
int32_t index = 0;
bool found = FindSeqNum(sequence_number, &index);
if (!found) {
return false;
}
uint16_t length = stored_lengths_.at(index);
if (length == 0 || length > max_packet_length_) {
// Invalid length.
return false;
}
return true;
}
bool RTPPacketHistory::GetRTPPacket(uint16_t sequence_number,
uint32_t min_elapsed_time_ms,
uint8_t* packet,
uint16_t* packet_length,
uint32_t* stored_time_ms,
StorageType* type) const {
webrtc::CriticalSectionScoped cs(*critsect_);
if (!store_) {
return false;
}
int32_t index = 0;
bool found = FindSeqNum(sequence_number, &index);
if (!found) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"No match for getting seqNum %u", sequence_number);
return false;
}
uint16_t length = stored_lengths_.at(index);
if (length == 0 || length > max_packet_length_) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"No match for getting seqNum %u, len %d", sequence_number, length);
return false;
}
if (length > *packet_length) {
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
"Input buffer too short for packet %u", sequence_number);
return false;
}
// Verify elapsed time since last retrieve.
uint32_t now = clock_.GetTimeInMS();
if (min_elapsed_time_ms > 0 &&
((now - stored_resend_times_.at(index)) < min_elapsed_time_ms)) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"Skip getting packet %u, packet recently resent.", sequence_number);
*packet_length = 0;
return true;
}
// Get packet.
std::vector<std::vector<uint8_t> >::const_iterator it_found_packet =
stored_packets_.begin() + index;
copy(it_found_packet->begin(), it_found_packet->begin() + length, packet);
*packet_length = stored_lengths_.at(index);
*stored_time_ms = stored_times_.at(index);
*type = stored_types_.at(index);
return true;
}
void RTPPacketHistory::UpdateResendTime(uint16_t sequence_number) {
webrtc::CriticalSectionScoped cs(*critsect_);
if (!store_) {
return;
}
int32_t index = 0;
bool found = FindSeqNum(sequence_number, &index);
if (!found) {
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
"Failed to update resend time, seq num: %u.", sequence_number);
return;
}
stored_resend_times_[index] = clock_.GetTimeInMS();
}
// private, lock should already be taken
bool RTPPacketHistory::FindSeqNum(uint16_t sequence_number,
int32_t* index) const {
uint16_t temp_sequence_number = 0;
if (prev_index_ > 0) {
*index = prev_index_ - 1;
temp_sequence_number = stored_seq_nums_[*index];
} else {
*index = stored_seq_nums_.size() - 1;
temp_sequence_number = stored_seq_nums_[*index]; // wrap
}
int32_t idx = (prev_index_ - 1) - (temp_sequence_number - sequence_number);
if (idx >= 0 && idx < static_cast<int>(stored_seq_nums_.size())) {
*index = idx;
temp_sequence_number = stored_seq_nums_[*index];
}
if (temp_sequence_number != sequence_number) {
// We did not found a match, search all.
for (uint16_t m = 0; m < stored_seq_nums_.size(); m++) {
if (stored_seq_nums_[m] == sequence_number) {
*index = m;
temp_sequence_number = stored_seq_nums_[*index];
break;
}
}
}
if (temp_sequence_number == sequence_number) {
// We found a match.
return true;
}
return false;
}
} // namespace webrtc

View File

@ -0,0 +1,87 @@
/*
* Copyright (c) 2011 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.
*/
/*
* Class for storing RTP packets.
*/
#ifndef WEBRTC_MODULES_RTP_RTCP_RTP_PACKET_HISTORY_H_
#define WEBRTC_MODULES_RTP_RTCP_RTP_PACKET_HISTORY_H_
#include <stdint.h>
#include <vector>
#include "module_common_types.h"
#include "rtp_rtcp_defines.h"
namespace webrtc {
class RtpRtcpClock;
class CriticalSectionWrapper;
class RTPPacketHistory {
public:
RTPPacketHistory(RtpRtcpClock* clock);
~RTPPacketHistory();
void SetStorePacketsStatus(bool enable, uint16_t number_to_store);
bool StorePackets() const;
// Stores RTP packet.
int32_t PutRTPPacket(const uint8_t* packet,
uint16_t packet_length,
uint16_t max_packet_length,
StorageType type);
// Gets stored RTP packet corresponding to the input sequence number.
// The packet is copied to the buffer pointed to by ptr_rtp_packet.
// The rtp_packet_length should show the available buffer size.
// Returns true if packet is found.
// rtp_packet_length: returns the copied packet length on success.
// min_elapsed_time_ms: the minimum time that must have elapsed since the last
// time the packet was resent (parameter is ignored if set to zero).
// If the packet is found but the minimum time has not elaped, no bytes are
// copied.
// stored_time_ms: returns the time when the packet was stored.
// type: returns the storage type set in PutRTPPacket.
bool GetRTPPacket(uint16_t sequence_number,
uint32_t min_elapsed_time_ms,
uint8_t* packet,
uint16_t* packet_length,
uint32_t* stored_time_ms,
StorageType* type) const;
bool HasRTPPacket(uint16_t sequence_number) const;
void UpdateResendTime(uint16_t sequence_number);
private:
void Allocate(uint16_t number_to_store);
void Free();
void VerifyAndAllocatePacketLength(uint16_t packet_length);
bool FindSeqNum(uint16_t sequence_number, int32_t* index) const;
private:
RtpRtcpClock& clock_;
CriticalSectionWrapper* critsect_;
bool store_;
uint32_t prev_index_;
uint16_t max_packet_length_;
std::vector<std::vector<uint8_t> > stored_packets_;
std::vector<uint16_t> stored_seq_nums_;
std::vector<uint16_t> stored_lengths_;
std::vector<uint32_t> stored_times_;
std::vector<uint32_t> stored_resend_times_;
std::vector<StorageType> stored_types_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_RTP_RTCP_RTP_PACKET_HISTORY_H_

View File

@ -0,0 +1,216 @@
/*
* Copyright (c) 2011 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.
*/
/*
* This file includes unit tests for the RTPPacketHistory.
*/
#include <gtest/gtest.h>
#include <stdint.h>
#include "rtp_packet_history.h"
#include "rtp_rtcp_defines.h"
#include "rtp_utility.h"
#include "typedefs.h"
namespace webrtc {
class FakeClock : public RtpRtcpClock {
public:
FakeClock() {
time_in_ms_ = 123456;
}
// Return a timestamp in milliseconds relative to some arbitrary
// source; the source is fixed for this clock.
virtual WebRtc_UWord32 GetTimeInMS() {
return time_in_ms_;
}
// Retrieve an NTP absolute timestamp.
virtual void CurrentNTP(WebRtc_UWord32& secs, WebRtc_UWord32& frac) {
secs = time_in_ms_ / 1000;
frac = (time_in_ms_ % 1000) * 4294967;
}
void IncrementTime(WebRtc_UWord32 time_increment_ms) {
time_in_ms_ += time_increment_ms;
}
private:
WebRtc_UWord32 time_in_ms_;
};
class RtpPacketHistoryTest : public ::testing::Test {
protected:
RtpPacketHistoryTest()
: hist_(new RTPPacketHistory(&fake_clock_)) {
}
~RtpPacketHistoryTest() {
delete hist_;
}
FakeClock fake_clock_;
RTPPacketHistory* hist_;
enum {kPayload = 127};
enum {kSsrc = 12345678};
enum {kSeqNum = 88};
enum {kTimestamp = 127};
enum {kMaxPacketLength = 1500};
uint8_t packet_[kMaxPacketLength];
uint8_t packet_out_[kMaxPacketLength];
void CreateRtpPacket(uint16_t seq_num, uint32_t ssrc, uint8_t payload,
uint32_t timestamp, uint8_t* array, uint16_t* cur_pos) {
array[(*cur_pos)++] = 0x80;
array[(*cur_pos)++] = payload;
array[(*cur_pos)++] = seq_num >> 8;
array[(*cur_pos)++] = seq_num;
array[(*cur_pos)++] = timestamp >> 24;
array[(*cur_pos)++] = timestamp >> 16;
array[(*cur_pos)++] = timestamp >> 8;
array[(*cur_pos)++] = timestamp;
array[(*cur_pos)++] = ssrc >> 24;
array[(*cur_pos)++] = ssrc >> 16;
array[(*cur_pos)++] = ssrc >> 8;
array[(*cur_pos)++] = ssrc;
}
};
TEST_F(RtpPacketHistoryTest, SetStoreStatus) {
EXPECT_FALSE(hist_->StorePackets());
hist_->SetStorePacketsStatus(true, 10);
EXPECT_TRUE(hist_->StorePackets());
hist_->SetStorePacketsStatus(false, 0);
EXPECT_FALSE(hist_->StorePackets());
}
TEST_F(RtpPacketHistoryTest, NoStoreStatus) {
EXPECT_FALSE(hist_->StorePackets());
uint16_t len = 0;
CreateRtpPacket(kSeqNum, kSsrc, kPayload, kTimestamp, packet_, &len);
EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength,
kAllowRetransmission));
// Packet should not be stored.
len = kMaxPacketLength;
uint32_t time;
StorageType type;
EXPECT_FALSE(hist_->GetRTPPacket(kSeqNum, 0, packet_, &len, &time, &type));
}
TEST_F(RtpPacketHistoryTest, DontStore) {
hist_->SetStorePacketsStatus(true, 10);
uint16_t len = 0;
CreateRtpPacket(kSeqNum, kSsrc, kPayload, kTimestamp, packet_, &len);
EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength, kDontStore));
// Packet should not be stored.
len = kMaxPacketLength;
uint32_t time;
StorageType type;
EXPECT_FALSE(hist_->GetRTPPacket(kSeqNum, 0, packet_, &len, &time, &type));
}
TEST_F(RtpPacketHistoryTest, PutRtpPacket_TooLargePacketLength) {
hist_->SetStorePacketsStatus(true, 10);
EXPECT_EQ(-1, hist_->PutRTPPacket(packet_,
kMaxPacketLength + 1,
kMaxPacketLength,
kAllowRetransmission));
}
TEST_F(RtpPacketHistoryTest, GetRtpPacket_TooSmallBuffer) {
hist_->SetStorePacketsStatus(true, 10);
uint16_t len = 0;
CreateRtpPacket(kSeqNum, kSsrc, kPayload, kTimestamp, packet_, &len);
EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength,
kAllowRetransmission));
uint16_t len_out = len - 1;
uint32_t time;
StorageType type;
EXPECT_FALSE(hist_->GetRTPPacket(kSeqNum, 0, packet_, &len_out, &time,
&type));
}
TEST_F(RtpPacketHistoryTest, GetRtpPacket_NotStored) {
hist_->SetStorePacketsStatus(true, 10);
uint16_t len = kMaxPacketLength;
uint32_t time;
StorageType type;
EXPECT_FALSE(hist_->GetRTPPacket(0, 0, packet_, &len, &time, &type));
}
TEST_F(RtpPacketHistoryTest, PutRtpPacket) {
hist_->SetStorePacketsStatus(true, 10);
uint16_t len = 0;
CreateRtpPacket(kSeqNum, kSsrc, kPayload, kTimestamp, packet_, &len);
EXPECT_FALSE(hist_->HasRTPPacket(kSeqNum));
EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength,
kAllowRetransmission));
EXPECT_TRUE(hist_->HasRTPPacket(kSeqNum));
}
TEST_F(RtpPacketHistoryTest, GetRtpPacket) {
hist_->SetStorePacketsStatus(true, 10);
uint16_t len = 0;
CreateRtpPacket(kSeqNum, kSsrc, kPayload, kTimestamp, packet_, &len);
EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength,
kAllowRetransmission));
uint16_t len_out = kMaxPacketLength;
uint32_t time;
StorageType type;
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 0, packet_out_, &len_out, &time,
&type));
EXPECT_EQ(len, len_out);
EXPECT_EQ(kAllowRetransmission, type);
for (int i = 0; i < len; i++) {
EXPECT_EQ(packet_[i], packet_out_[i]);
}
}
TEST_F(RtpPacketHistoryTest, DontRetransmit) {
hist_->SetStorePacketsStatus(true, 10);
uint16_t len = 0;
CreateRtpPacket(kSeqNum, kSsrc, kPayload, kTimestamp, packet_, &len);
EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength,
kDontRetransmit));
uint16_t len_out = kMaxPacketLength;
uint32_t time;
StorageType type;
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 0, packet_out_, &len_out, &time,
&type));
EXPECT_EQ(len, len_out);
EXPECT_EQ(kDontRetransmit, type);
}
TEST_F(RtpPacketHistoryTest, MinResendTime) {
hist_->SetStorePacketsStatus(true, 10);
WebRtc_UWord32 store_time = fake_clock_.GetTimeInMS();
uint16_t len = 0;
CreateRtpPacket(kSeqNum, kSsrc, kPayload, kTimestamp, packet_, &len);
EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength,
kAllowRetransmission));
hist_->UpdateResendTime(kSeqNum);
fake_clock_.IncrementTime(100);
// Time has elapsed.
len = kMaxPacketLength;
StorageType type;
uint32_t time;
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 100, packet_, &len, &time, &type));
EXPECT_GT(len, 0);
EXPECT_EQ(store_time, time);
// Time has not elapsed. Packet should be found, but no bytes copied.
len = kMaxPacketLength;
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 101, packet_, &len, &time, &type));
EXPECT_EQ(0, len);
}
} // namespace webrtc

View File

@ -75,6 +75,8 @@
'h263_information.h',
'remote_rate_control.cc',
'remote_rate_control.h',
'rtp_packet_history.cc',
'rtp_packet_history.h',
'rtp_receiver_video.cc',
'rtp_receiver_video.h',
'rtp_sender_video.cc',
@ -84,6 +86,8 @@
'video_codec_information.h',
'rtp_format_vp8.cc',
'rtp_format_vp8.h',
'transmission_bucket.cc',
'transmission_bucket.h',
# Mocks
'../mocks/mock_rtp_rtcp.h',
], # source

View File

@ -13,7 +13,9 @@
// Configuration file for RTP utilities (RTPSender, RTPReceiver ...)
namespace webrtc {
enum { kRtpRtcpMaxIdleTimeProcess = 10 };
enum { kRtpRtcpMaxIdleTimeProcess = 5,
kRtpRtcpBitrateProcessTimeMs = 10,
kRtpRtcpPacketTimeoutProcessTimeMs = 100 };
enum { NACK_PACKETS_MAX_SIZE = 256 }; // in packets
enum { NACK_BYTECOUNT_SIZE = 60}; // size of our NACK history

View File

@ -87,6 +87,8 @@ ModuleRtpRtcpImpl::ModuleRtpRtcpImpl(const WebRtc_Word32 id,
_audio(audio),
_collisionDetected(false),
_lastProcessTime(clock->GetTimeInMS()),
_lastBitrateProcessTime(clock->GetTimeInMS()),
_lastPacketTimeoutProcessTime(clock->GetTimeInMS()),
_packetOverHead(28), // IPV4 UDP
_criticalSectionModulePtrs(CriticalSectionWrapper::CreateCriticalSection()),
@ -398,47 +400,55 @@ WebRtc_Word32 ModuleRtpRtcpImpl::TimeUntilNextProcess()
// Process any pending tasks such as timeouts
// non time critical events
WebRtc_Word32 ModuleRtpRtcpImpl::Process()
{
_lastProcessTime = _clock.GetTimeInMS();
WebRtc_Word32 ModuleRtpRtcpImpl::Process() {
const WebRtc_UWord32 now = _clock.GetTimeInMS();
_lastProcessTime = now;
_rtpSender.ProcessSendToNetwork();
if (now >= _lastPacketTimeoutProcessTime +
kRtpRtcpPacketTimeoutProcessTimeMs) {
_rtpReceiver.PacketTimeout();
_rtcpReceiver.PacketTimeout();
_lastPacketTimeoutProcessTime = now;
}
if (now >= _lastBitrateProcessTime + kRtpRtcpBitrateProcessTimeMs) {
_rtpSender.ProcessBitrate();
_rtpReceiver.ProcessBitrate();
_lastBitrateProcessTime = now;
}
ProcessDeadOrAliveTimer();
ProcessDeadOrAliveTimer();
const bool defaultInstance(_childModules.empty() ? false : true);
if(!defaultInstance &&_rtcpSender.TimeToSendRTCPReport())
{
WebRtc_UWord16 RTT = 0;
_rtcpReceiver.RTT(_rtpReceiver.SSRC(), &RTT, NULL, NULL, NULL);
if (REMB() && _rtcpSender.ValidBitrateEstimate())
{
unsigned int target_bitrate =
_rtcpSender.CalculateNewTargetBitrate(RTT);
_rtcpSender.UpdateRemoteBitrateEstimate(target_bitrate);
} else if (TMMBR()) {
const bool defaultInstance(_childModules.empty() ? false : true);
if(!defaultInstance &&_rtcpSender.TimeToSendRTCPReport())
{
WebRtc_UWord16 RTT = 0;
_rtcpReceiver.RTT(_rtpReceiver.SSRC(), &RTT, NULL, NULL, NULL);
if (REMB() && _rtcpSender.ValidBitrateEstimate())
{
unsigned int target_bitrate =
_rtcpSender.CalculateNewTargetBitrate(RTT);
}
_rtcpSender.SendRTCP(kRtcpReport, 0, 0, RTT);
_rtcpSender.UpdateRemoteBitrateEstimate(target_bitrate);
} else if (TMMBR()) {
_rtcpSender.CalculateNewTargetBitrate(RTT);
}
_rtcpSender.SendRTCP(kRtcpReport, 0, 0, RTT);
}
if (_rtpSender.RTPKeepalive()) {
// check time to send RTP keep alive
if (_rtpSender.TimeToSendRTPKeepalive()) {
_rtpSender.SendRTPKeepalivePacket();
}
if(_rtpSender.RTPKeepalive())
{
// check time to send RTP keep alive
if( _rtpSender.TimeToSendRTPKeepalive())
{
_rtpSender.SendRTPKeepalivePacket();
}
}
if(UpdateRTCPReceiveInformationTimers())
{
// a receiver has timed out
UpdateTMMBR();
}
return 0;
}
if (UpdateRTCPReceiveInformationTimers()) {
// a receiver has timed out
UpdateTMMBR();
}
return 0;
}
/**
@ -1849,6 +1859,14 @@ WebRtc_Word32 ModuleRtpRtcpImpl::DeregisterReceiveRtpHeaderExtension(
return _rtpReceiver.DeregisterRtpHeaderExtension(type);
}
void ModuleRtpRtcpImpl::SetTransmissionSmoothingStatus(const bool enable) {
_rtpSender.SetTransmissionSmoothingStatus(enable);
}
bool ModuleRtpRtcpImpl::TransmissionSmoothingStatus() const {
return _rtpSender.TransmissionSmoothingStatus();
}
/*
* (TMMBR) Temporary Max Media Bit Rate
*/

View File

@ -181,6 +181,10 @@ public:
virtual WebRtc_Word32 DeregisterSendRtpHeaderExtension(
const RTPExtensionType type);
virtual void SetTransmissionSmoothingStatus(const bool enable);
virtual bool TransmissionSmoothingStatus() const;
// get start timestamp
virtual WebRtc_UWord32 StartTimestamp() const;
@ -572,6 +576,8 @@ private:
const bool _audio;
bool _collisionDetected;
WebRtc_UWord32 _lastProcessTime;
WebRtc_UWord32 _lastBitrateProcessTime;
WebRtc_UWord32 _lastPacketTimeoutProcessTime;
WebRtc_UWord16 _packetOverHead;
CriticalSectionWrapper* _criticalSectionModulePtrs;

View File

@ -25,10 +25,12 @@
'rtp_format_vp8_test_helper.cc',
'rtp_format_vp8_test_helper.h',
'rtcp_format_remb_unittest.cc',
'rtp_packet_history_test.cc',
'rtp_utility_test.cc',
'rtp_header_extension_test.cc',
'rtp_sender_test.cc',
'rtcp_sender_test.cc',
'transmission_bucket_test.cc',
],
},
],

View File

@ -15,6 +15,7 @@
#include "critical_section_wrapper.h"
#include "trace.h"
#include "rtp_packet_history.h"
#include "rtp_sender_audio.h"
#include "rtp_sender_video.h"
@ -49,20 +50,16 @@ RTPSender::RTPSender(const WebRtc_Word32 id,
_keepAliveLastSent(0),
_keepAliveDeltaTimeSend(0),
_storeSentPackets(false),
_storeSentPacketsNumber(0),
_prevSentPacketsCritsect(CriticalSectionWrapper::CreateCriticalSection()),
_prevSentPacketsIndex(0),
_ptrPrevSentPackets(NULL),
_prevSentPacketsSeqNum(NULL),
_prevSentPacketsLength(NULL),
_prevSentPacketsResendTime(NULL),
// NACK
_nackByteCountTimes(),
_nackByteCount(),
_nackBitrate(clock),
_packetHistory(new RTPPacketHistory(clock)),
_sendBucket(),
_timeLastSendToNetworkUpdate(clock->GetTimeInMS()),
_transmissionSmoothing(false),
// statistics
_packetsSent(0),
_payloadBytesSent(0),
@ -113,7 +110,7 @@ RTPSender::~RTPSender()
_ssrcDB.ReturnSSRC(_ssrc);
SSRCDatabase::ReturnSSRCDatabase();
delete _prevSentPacketsCritsect;
delete _sendCritsect;
delete _transportCritsect;
@ -136,18 +133,7 @@ RTPSender::~RTPSender()
}
} while (loop);
for(WebRtc_Word32 i=0; i< _storeSentPacketsNumber; i++)
{
if(_ptrPrevSentPackets[i])
{
delete [] _ptrPrevSentPackets[i];
_ptrPrevSentPackets[i] = 0;
}
}
delete [] _ptrPrevSentPackets;
delete [] _prevSentPacketsSeqNum;
delete [] _prevSentPacketsLength;
delete [] _prevSentPacketsResendTime;
delete _packetHistory;
delete _audio;
delete _video;
@ -207,6 +193,7 @@ RTPSender::Init(const WebRtc_UWord32 remoteSSRC)
_nackBitrate.Init();
SetStorePacketsStatus(false, 0);
_sendBucket.Reset();
Bitrate::Init();
@ -570,23 +557,6 @@ RTPSender::SetMaxPayloadLength(const WebRtc_UWord16 maxPayloadLength, const WebR
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__);
return -1;
}
if(maxPayloadLength > _maxPayloadLength)
{
CriticalSectionScoped lock(_prevSentPacketsCritsect);
if(_storeSentPackets)
{
// we need to free the memmory allocated for storing sent packets
// will be allocated in SendToNetwork
for(WebRtc_Word32 i=0; i< _storeSentPacketsNumber; i++)
{
if(_ptrPrevSentPackets[i])
{
delete [] _ptrPrevSentPackets[i];
_ptrPrevSentPackets[i] = NULL;
}
}
}
}
CriticalSectionScoped cs(_sendCritsect);
_maxPayloadLength = maxPayloadLength;
@ -618,6 +588,16 @@ RTPSender::PacketOverHead() const
return _packetOverHead;
}
void RTPSender::SetTransmissionSmoothingStatus(const bool enable) {
CriticalSectionScoped cs(_sendCritsect);
_transmissionSmoothing = enable;
}
bool RTPSender::TransmissionSmoothingStatus() const {
CriticalSectionScoped cs(_sendCritsect);
return _transmissionSmoothing;
}
void RTPSender::SetRTXStatus(const bool enable,
const bool setSSRC,
const WebRtc_UWord32 SSRC) {
@ -839,216 +819,110 @@ WebRtc_Word32 RTPSender::SendPadData(WebRtc_Word8 payload_type,
return 0;
}
WebRtc_Word32
RTPSender::SetStorePacketsStatus(const bool enable, const WebRtc_UWord16 numberToStore)
{
CriticalSectionScoped lock(_prevSentPacketsCritsect);
if(enable)
{
if(_storeSentPackets)
{
// already enabled
return -1;
}
if(numberToStore > 0)
{
_storeSentPackets = enable;
_storeSentPacketsNumber = numberToStore;
_ptrPrevSentPackets = new WebRtc_Word8*[numberToStore],
_prevSentPacketsSeqNum = new WebRtc_UWord16[numberToStore];
_prevSentPacketsLength = new WebRtc_UWord16[numberToStore];
_prevSentPacketsResendTime = new WebRtc_UWord32[numberToStore];
memset(_ptrPrevSentPackets,0, sizeof(WebRtc_Word8*)*numberToStore);
memset(_prevSentPacketsSeqNum,0, sizeof(WebRtc_UWord16)*numberToStore);
memset(_prevSentPacketsLength,0, sizeof(WebRtc_UWord16)*numberToStore);
memset(_prevSentPacketsResendTime,0,sizeof(WebRtc_UWord32)*numberToStore);
} else
{
// storing 0 packets does not make sence
return -1;
}
} else
{
_storeSentPackets = enable;
if(_storeSentPacketsNumber > 0)
{
for(WebRtc_Word32 i=0; i< _storeSentPacketsNumber; i++)
{
if(_ptrPrevSentPackets[i])
{
delete [] _ptrPrevSentPackets[i];
_ptrPrevSentPackets[i] = 0;
}
}
delete [] _ptrPrevSentPackets;
delete [] _prevSentPacketsSeqNum;
delete [] _prevSentPacketsLength;
delete [] _prevSentPacketsResendTime;
_ptrPrevSentPackets = NULL;
_prevSentPacketsSeqNum = NULL;
_prevSentPacketsLength = NULL;
_prevSentPacketsResendTime = NULL;
_storeSentPacketsNumber = 0;
}
}
return 0;
WebRtc_Word32 RTPSender::SetStorePacketsStatus(
const bool enable,
const WebRtc_UWord16 numberToStore) {
_packetHistory->SetStorePacketsStatus(enable, numberToStore);
return 0;
}
bool RTPSender::StorePackets() const {
return _storeSentPackets;
return _packetHistory->StorePackets();
}
WebRtc_Word32 RTPSender::ReSendPacket(WebRtc_UWord16 packetID,
WebRtc_UWord32 minResendTime) {
WebRtc_Word32 length = 0;
WebRtc_Word32 index = 0;
WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE];
{
CriticalSectionScoped lock(_prevSentPacketsCritsect);
WebRtc_Word32 RTPSender::ReSendPacket(WebRtc_UWord16 packet_id,
WebRtc_UWord32 min_resend_time) {
WebRtc_UWord16 seqNum = 0;
if (!_storeSentPackets) {
WEBRTC_TRACE(kTraceWarning,
kTraceRtpRtcp,
_id,
"Ignoring request to ReSendPacket:%u we're not storing.",
seqNum);
return -1;
}
if (_prevSentPacketsIndex) {
seqNum = _prevSentPacketsSeqNum[_prevSentPacketsIndex-1];
} else {
seqNum = _prevSentPacketsSeqNum[_storeSentPacketsNumber-1];
}
index = (_prevSentPacketsIndex-1) - (seqNum - packetID);
if (index >= 0 && index < _storeSentPacketsNumber) {
seqNum = _prevSentPacketsSeqNum[index];
}
if (seqNum != packetID) {
// we did not found a match, search all
for (WebRtc_Word32 m = 0; m < _storeSentPacketsNumber; m++) {
if(_prevSentPacketsSeqNum[m] == packetID) {
index = m;
seqNum = _prevSentPacketsSeqNum[index];
break;
}
}
}
if (seqNum != packetID) {
WEBRTC_TRACE(kTraceWarning,
kTraceRtpRtcp,
_id,
"No match for resending seqNum %u and packetId %u",
seqNum, packetID);
return -1;
}
WebRtc_UWord32 timeNow = _clock.GetTimeInMS();
if (minResendTime > 0 &&
(timeNow-_prevSentPacketsResendTime[index] < minResendTime)) {
// No point in sending the packet again yet. Get out of here
WEBRTC_TRACE(kTraceStream,
kTraceRtpRtcp,
_id,
"Skipping to resend RTP packet %d, it was just resent",
seqNum);
return 0;
}
length = _prevSentPacketsLength[index];
if (length > _maxPayloadLength || _ptrPrevSentPackets[index] == 0) {
WEBRTC_TRACE(kTraceWarning,
kTraceRtpRtcp,
_id,
"Failed to resend seqNum %u: length = %d index = %d",
seqNum, length, index);
return -1;
}
if (length == 0) {
// This is a valid case since packets which we decide not to retransmit
// are stored but with length zero.
return 0;
}
if (_RTX) {
CriticalSectionScoped cs(_sendCritsect);
// Copy to local buffer for callback and add RTX header.
ModuleRTPUtility::RTPHeaderParser rtpParser(
reinterpret_cast<const WebRtc_UWord8*>(_ptrPrevSentPackets[index]),
length);
WebRtc_UWord16 length = IP_PACKET_SIZE;
WebRtc_UWord8 data_buffer[IP_PACKET_SIZE];
WebRtc_UWord8* buffer_to_send_ptr = data_buffer;
WebRtcRTPHeader rtp_header;
rtpParser.Parse(rtp_header);
// Add original RTP header.
memcpy(dataBuffer, _ptrPrevSentPackets[index],
rtp_header.header.headerLength);
// Replace sequence number.
WebRtc_UWord8* ptr = dataBuffer + 2;
ModuleRTPUtility::AssignUWord16ToBuffer(ptr, _sequenceNumberRTX++);
// Replace SSRC.
ptr += 6;
ModuleRTPUtility::AssignUWord32ToBuffer(ptr, _ssrcRTX);
// Add OSN (original sequence number).
ptr = dataBuffer + rtp_header.header.headerLength;
ModuleRTPUtility::AssignUWord16ToBuffer(
ptr, rtp_header.header.sequenceNumber);
ptr += 2;
// Add original payload data.
memcpy(ptr,
_ptrPrevSentPackets[index] + rtp_header.header.headerLength,
length - rtp_header.header.headerLength);
length += 2;
} else {
// copy to local buffer for callback
memcpy(dataBuffer, _ptrPrevSentPackets[index], length);
}
} // End of scope lock(_prevSentPacketsCritsect).
WebRtc_Word32 i = ReSendToNetwork(dataBuffer, length);
if (_storeSentPackets && i > 0) {
CriticalSectionScoped lock(_prevSentPacketsCritsect);
// Make sure the packet is still in the array
if(_prevSentPacketsSeqNum[index] == packetID) {
// Store the time when the frame was last resent.
_prevSentPacketsResendTime[index]= _clock.GetTimeInMS();
}
return i; //bytes sent over network
WebRtc_UWord32 stored_time_in_ms;
StorageType type;
bool found = _packetHistory->GetRTPPacket(packet_id,
min_resend_time, data_buffer, &length, &stored_time_in_ms, &type);
if (!found) {
// Packet not found.
return -1;
}
WEBRTC_TRACE(kTraceWarning,
kTraceRtpRtcp,
_id,
"Transport failed to resend packetID %u",
packetID);
return -1;
if (length == 0 || type == kDontRetransmit) {
// No bytes copied (packet recently resent, skip resending) or
// packet should not be retransmitted.
return 0;
}
if (_RTX) {
WebRtc_UWord8 data_buffer_rtx[IP_PACKET_SIZE];
buffer_to_send_ptr = data_buffer_rtx;
CriticalSectionScoped cs(_sendCritsect);
// Add RTX header.
ModuleRTPUtility::RTPHeaderParser rtpParser(
reinterpret_cast<const WebRtc_UWord8*>(data_buffer),
length);
WebRtcRTPHeader rtp_header;
rtpParser.Parse(rtp_header);
// Add original RTP header.
memcpy(data_buffer_rtx, data_buffer, rtp_header.header.headerLength);
// Replace sequence number.
WebRtc_UWord8* ptr = data_buffer_rtx + 2;
ModuleRTPUtility::AssignUWord16ToBuffer(ptr, _sequenceNumberRTX++);
// Replace SSRC.
ptr += 6;
ModuleRTPUtility::AssignUWord32ToBuffer(ptr, _ssrcRTX);
// Add OSN (original sequence number).
ptr = data_buffer_rtx + rtp_header.header.headerLength;
ModuleRTPUtility::AssignUWord16ToBuffer(
ptr, rtp_header.header.sequenceNumber);
ptr += 2;
// Add original payload data.
memcpy(ptr,
data_buffer + rtp_header.header.headerLength,
length - rtp_header.header.headerLength);
length += 2;
}
WebRtc_Word32 bytes_sent = ReSendToNetwork(buffer_to_send_ptr, length);
if (bytes_sent <= 0) {
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
"Transport failed to resend packet_id %u", packet_id);
return -1;
}
// Store the time when the packet was last resent.
_packetHistory->UpdateResendTime(packet_id);
return bytes_sent;
}
WebRtc_Word32 RTPSender::ReSendToNetwork(const WebRtc_UWord8* packet,
const WebRtc_UWord32 size) {
WebRtc_Word32 i = -1;
WebRtc_Word32 bytes_sent = -1;
{
CriticalSectionScoped lock(_transportCritsect);
if(_transport) {
i = _transport->SendPacket(_id, packet, size);
if (_transport) {
bytes_sent = _transport->SendPacket(_id, packet, size);
}
}
if(i > 0) {
CriticalSectionScoped cs(_sendCritsect);
Bitrate::Update(i);
_packetsSent++;
// We on purpose don't add to _payloadBytesSent since this is a re-transmit
// and not new payload data
if (bytes_sent <= 0) {
return -1;
}
return i;
// Update send statistics
CriticalSectionScoped cs(_sendCritsect);
Bitrate::Update(bytes_sent);
_packetsSent++;
// We on purpose don't add to _payloadBytesSent since this is a
// re-transmit and not new payload data.
return bytes_sent;
}
int RTPSender::SelectiveRetransmissions() const {
@ -1068,12 +942,12 @@ RTPSender::OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength,
const WebRtc_UWord32 now = _clock.GetTimeInMS();
WebRtc_UWord32 bytesReSent = 0;
// Enough bandwith to send NACK?
// Enough bandwidth to send NACK?
if (!ProcessNACKBitRate(now)) {
WEBRTC_TRACE(kTraceStream,
kTraceRtpRtcp,
_id,
"NACK bitrate reached. Skipp sending NACK response. Target %d",
"NACK bitrate reached. Skip sending NACK response. Target %d",
TargetSendBitrateKbit());
return;
}
@ -1171,86 +1045,108 @@ void RTPSender::UpdateNACKBitRate(const WebRtc_UWord32 bytes,
}
}
void RTPSender::ProcessSendToNetwork() {
// triggered by timer
WebRtc_UWord32 delta_time_ms;
{
CriticalSectionScoped cs(_sendCritsect);
if (!_transmissionSmoothing) {
return;
}
WebRtc_UWord32 now = _clock.GetTimeInMS();
delta_time_ms = now - _timeLastSendToNetworkUpdate;
_timeLastSendToNetworkUpdate = now;
}
_sendBucket.UpdateBytesPerInterval(delta_time_ms, _targetSendBitrate);
while (!_sendBucket.Empty()) {
WebRtc_Word32 seq_num = _sendBucket.GetNextPacket();
if (seq_num < 0) {
break;
}
WebRtc_UWord8 data_buffer[IP_PACKET_SIZE];
WebRtc_UWord16 length = IP_PACKET_SIZE;
WebRtc_UWord32 stored_time_ms;
StorageType type;
assert(_packetHistory->GetRTPPacket(seq_num, 0, data_buffer, &length,
&stored_time_ms, &type));
assert(length > 0);
WebRtc_UWord32 diff_ms = _clock.GetTimeInMS() - stored_time_ms;
ModuleRTPUtility::RTPHeaderParser rtpParser(data_buffer, length);
WebRtcRTPHeader rtp_header;
assert(rtpParser.Parse(rtp_header));
UpdateTransmissionTimeOffset(data_buffer, length, rtp_header, diff_ms);
// Send packet
WebRtc_Word32 bytes_sent = -1;
{
CriticalSectionScoped cs(_transportCritsect);
if (_transport) {
bytes_sent = _transport->SendPacket(_id, data_buffer, length);
}
}
// Update send statistics
if (bytes_sent > 0) {
CriticalSectionScoped cs(_sendCritsect);
Bitrate::Update(bytes_sent);
_packetsSent++;
if (bytes_sent > rtp_header.header.headerLength) {
_payloadBytesSent += bytes_sent - rtp_header.header.headerLength;
}
}
}
}
WebRtc_Word32
RTPSender::SendToNetwork(const WebRtc_UWord8* buffer,
const WebRtc_UWord16 length,
const WebRtc_UWord16 rtpLength,
const StorageType storage)
{
WebRtc_Word32 retVal = -1;
// sanity
if(length + rtpLength > _maxPayloadLength)
{
return -1;
}
// Make sure the packet is big enough for us to parse the sequence number.
assert(length + rtpLength > 3);
// Parse the sequence number from the RTP header.
WebRtc_UWord16 sequenceNumber = (buffer[2] << 8) + buffer[3];
switch (storage) {
case kAllowRetransmission:
StorePacket(buffer, length + rtpLength, sequenceNumber);
break;
case kDontRetransmit:
// Store an empty packet. Won't be retransmitted if NACKed.
StorePacket(NULL, 0, sequenceNumber);
break;
case kDontStore:
break;
default:
assert(false);
}
// Send packet
{
CriticalSectionScoped cs(_transportCritsect);
if(_transport)
{
retVal = _transport->SendPacket(_id, buffer, length + rtpLength);
}
}
// success?
if(retVal > 0)
{
CriticalSectionScoped cs(_sendCritsect);
Bitrate::Update(retVal);
_packetsSent++;
if(retVal > rtpLength)
{
_payloadBytesSent += retVal-rtpLength;
}
return 0;
}
// Used for NACK or to spead out the transmission of packets.
if (_packetHistory->PutRTPPacket(
buffer, rtpLength + length, _maxPayloadLength, storage) != 0) {
return -1;
}
}
void RTPSender::StorePacket(const uint8_t* buffer, uint16_t length,
uint16_t sequence_number) {
// Store packet to be used for NACK.
CriticalSectionScoped lock(_prevSentPacketsCritsect);
if(_storeSentPackets) {
if(_ptrPrevSentPackets[0] == NULL) {
for(WebRtc_Word32 i = 0; i < _storeSentPacketsNumber; i++) {
_ptrPrevSentPackets[i] = new char[_maxPayloadLength];
memset(_ptrPrevSentPackets[i], 0, _maxPayloadLength);
}
}
if (_transmissionSmoothing) {
const WebRtc_UWord16 sequenceNumber = (buffer[2] << 8) + buffer[3];
_sendBucket.Fill(sequenceNumber, rtpLength + length);
// Packet will be sent at a later time.
return 0;
}
if (buffer != NULL && length > 0) {
memcpy(_ptrPrevSentPackets[_prevSentPacketsIndex], buffer, length);
}
_prevSentPacketsSeqNum[_prevSentPacketsIndex] = sequence_number;
_prevSentPacketsLength[_prevSentPacketsIndex] = length;
// Packet has not been re-sent.
_prevSentPacketsResendTime[_prevSentPacketsIndex] = 0;
_prevSentPacketsIndex++;
if(_prevSentPacketsIndex >= _storeSentPacketsNumber) {
_prevSentPacketsIndex = 0;
// Send packet
WebRtc_Word32 bytes_sent = -1;
{
CriticalSectionScoped cs(_transportCritsect);
if (_transport) {
bytes_sent = _transport->SendPacket(_id, buffer, length + rtpLength);
}
}
if (bytes_sent <= 0) {
return -1;
}
// Update send statistics
CriticalSectionScoped cs(_sendCritsect);
Bitrate::Update(bytes_sent);
_packetsSent++;
if (bytes_sent > rtpLength) {
_payloadBytesSent += bytes_sent - rtpLength;
}
return 0;
}
void
@ -1453,7 +1349,7 @@ RTPSender::BuildTransmissionTimeOffsetExtension(WebRtc_UWord8* dataBuffer) const
//
// 0 1 2 3
// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// | ID | len=2 | transmission offset |
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@ -1475,6 +1371,60 @@ RTPSender::BuildTransmissionTimeOffsetExtension(WebRtc_UWord8* dataBuffer) const
return TRANSMISSION_TIME_OFFSET_LENGTH_IN_BYTES;
}
void RTPSender::UpdateTransmissionTimeOffset(
WebRtc_UWord8* rtp_packet,
const WebRtc_UWord16 rtp_packet_length,
const WebRtcRTPHeader& rtp_header,
const WebRtc_UWord32 time_ms) const {
CriticalSectionScoped cs(_sendCritsect);
// Get length until start of transmission block.
int transmission_block_pos =
_rtpHeaderExtensionMap.GetLengthUntilBlockStartInBytes(
kRtpExtensionTransmissionTimeOffset);
if (transmission_block_pos < 0) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
"Failed to update transmission time offset, not registered.");
return;
}
int block_pos = 12 + rtp_header.header.numCSRCs + transmission_block_pos;
if ((rtp_packet_length < block_pos + 4)) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
"Failed to update transmission time offset, invalid length.");
return;
}
// Verify that header contains extension.
if (!((rtp_packet[12 + rtp_header.header.numCSRCs] == 0xBE) &&
(rtp_packet[12 + rtp_header.header.numCSRCs + 1] == 0xDE))) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
"Failed to update transmission time offset, hdr extension not found.");
return;
}
// Get id.
WebRtc_UWord8 id = 0;
if (_rtpHeaderExtensionMap.GetId(kRtpExtensionTransmissionTimeOffset,
&id) != 0) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
"Failed to update transmission time offset, no id.");
return;
}
// Verify first byte in block.
const WebRtc_UWord8 first_block_byte = (id << 4) + 2;
if (rtp_packet[block_pos] != first_block_byte) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
"Failed to update transmission time offset.");
return;
}
// Update transmission offset field.
ModuleRTPUtility::AssignUWord24ToBuffer(rtp_packet + block_pos + 1,
time_ms * 90); // RTP timestamp
}
WebRtc_Word32
RTPSender::RegisterSendTransport(Transport* transport)
{

View File

@ -11,7 +11,7 @@
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_H_
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_SENDER_H_
#include "rtp_rtcp_config.h" // misc. defines (e.g. MAX_PACKET_LENGTH)
#include "rtp_rtcp_config.h" // misc. defines (e.g. MAX_PACKET_LENGTH)
#include "rtp_rtcp_defines.h"
#include "common_types.h" // Encryption
#include "ssrc_database.h"
@ -20,6 +20,7 @@
#include "Bitrate.h"
#include "rtp_header_extension.h"
#include "video_codec_information.h"
#include "transmission_bucket.h"
#include <cassert>
#include <cmath>
@ -28,15 +29,10 @@
namespace webrtc {
class CriticalSectionWrapper;
class RTPPacketHistory;
class RTPSenderAudio;
class RTPSenderVideo;
enum StorageType {
kDontStore,
kDontRetransmit,
kAllowRetransmission
};
class RTPSenderInterface
{
public:
@ -78,6 +74,7 @@ public:
void ChangeUniqueId(const WebRtc_Word32 id);
void ProcessBitrate();
void ProcessSendToNetwork();
WebRtc_UWord16 TargetSendBitrateKbit() const;
WebRtc_UWord16 ActualSendBitrateKbit() const;
@ -169,6 +166,15 @@ public:
WebRtc_UWord8 BuildTransmissionTimeOffsetExtension(
WebRtc_UWord8* dataBuffer) const;
void UpdateTransmissionTimeOffset(WebRtc_UWord8* rtp_packet,
const WebRtc_UWord16 rtp_packet_length,
const WebRtcRTPHeader& rtp_header,
const WebRtc_UWord32 time_ms) const;
void SetTransmissionSmoothingStatus(const bool enable);
bool TransmissionSmoothingStatus() const;
/*
* NACK
*/
@ -183,8 +189,8 @@ public:
bool StorePackets() const;
WebRtc_Word32 ReSendPacket(WebRtc_UWord16 packetID,
WebRtc_UWord32 minResendTime=0);
WebRtc_Word32 ReSendPacket(WebRtc_UWord16 packet_id,
WebRtc_UWord32 min_resend_time = 0);
WebRtc_Word32 ReSendToNetwork(const WebRtc_UWord8* packet,
const WebRtc_UWord32 size);
@ -301,20 +307,18 @@ public:
const bool deltaUseUepProtection);
protected:
WebRtc_Word32 CheckPayloadType(const WebRtc_Word8 payloadType, RtpVideoCodecTypes& videoType);
WebRtc_Word32 CheckPayloadType(const WebRtc_Word8 payloadType,
RtpVideoCodecTypes& videoType);
private:
void UpdateNACKBitRate(const WebRtc_UWord32 bytes,
const WebRtc_UWord32 now);
void StorePacket(const uint8_t* buffer, uint16_t length,
uint16_t sequence_number);
WebRtc_Word32 _id;
const bool _audioConfigured;
RTPSenderAudio* _audio;
RTPSenderVideo* _video;
WebRtc_Word32 _id;
const bool _audioConfigured;
RTPSenderAudio* _audio;
RTPSenderVideo* _video;
CriticalSectionWrapper* _sendCritsect;
CriticalSectionWrapper* _transportCritsect;
@ -337,21 +341,16 @@ private:
WebRtc_UWord32 _keepAliveLastSent;
WebRtc_UWord16 _keepAliveDeltaTimeSend;
bool _storeSentPackets;
WebRtc_UWord16 _storeSentPacketsNumber;
CriticalSectionWrapper* _prevSentPacketsCritsect;
WebRtc_Word32 _prevSentPacketsIndex;
WebRtc_Word8** _ptrPrevSentPackets;
WebRtc_UWord16* _prevSentPacketsSeqNum;
WebRtc_UWord16* _prevSentPacketsLength;
WebRtc_UWord32* _prevSentPacketsResendTime;
// NACK
WebRtc_UWord32 _nackByteCountTimes[NACK_BYTECOUNT_SIZE];
WebRtc_Word32 _nackByteCount[NACK_BYTECOUNT_SIZE];
Bitrate _nackBitrate;
RTPPacketHistory* _packetHistory;
TransmissionBucket _sendBucket;
WebRtc_UWord32 _timeLastSendToNetworkUpdate;
bool _transmissionSmoothing;
// statistics
WebRtc_UWord32 _packetsSent;
WebRtc_UWord32 _payloadBytesSent;

View File

@ -32,19 +32,66 @@ const int kTimeOffset = 22222;
const int kMaxPacketLength = 1500;
} // namespace
class FakeClockTest : public RtpRtcpClock {
public:
FakeClockTest() {
time_in_ms_ = 123456;
}
// Return a timestamp in milliseconds relative to some arbitrary
// source; the source is fixed for this clock.
virtual WebRtc_UWord32 GetTimeInMS() {
return time_in_ms_;
}
// Retrieve an NTP absolute timestamp.
virtual void CurrentNTP(WebRtc_UWord32& secs, WebRtc_UWord32& frac) {
secs = time_in_ms_ / 1000;
frac = (time_in_ms_ % 1000) * 4294967;
}
void IncrementTime(WebRtc_UWord32 time_increment_ms) {
time_in_ms_ += time_increment_ms;
}
private:
WebRtc_UWord32 time_in_ms_;
};
class LoopbackTransportTest : public webrtc::Transport {
public:
LoopbackTransportTest()
: packets_sent_(0),
last_sent_packet_len_(0) {
}
virtual int SendPacket(int channel, const void *data, int len) {
packets_sent_++;
memcpy(last_sent_packet_, data, len);
last_sent_packet_len_ = len;
return len;
}
virtual int SendRTCPPacket(int channel, const void *data, int len) {
return -1;
}
int packets_sent_;
int last_sent_packet_len_;
uint8_t last_sent_packet_[kMaxPacketLength];
};
class RtpSenderTest : public ::testing::Test {
protected:
RtpSenderTest()
: rtp_sender_(new RTPSender(0, false, ModuleRTPUtility::GetSystemClock())),
: fake_clock_(),
rtp_sender_(new RTPSender(0, false, &fake_clock_)),
transport_(),
kMarkerBit(true),
kType(kRtpExtensionTransmissionTimeOffset) {
kType(kRtpExtensionTransmissionTimeOffset),
packet_() {
EXPECT_EQ(0, rtp_sender_->SetSequenceNumber(kSeqNum));
}
~RtpSenderTest() {
delete rtp_sender_;
}
FakeClockTest fake_clock_;
RTPSender* rtp_sender_;
LoopbackTransportTest transport_;
const bool kMarkerBit;
RTPExtensionType kType;
uint8_t packet_[kMaxPacketLength];
@ -124,4 +171,57 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithExtension) {
EXPECT_EQ(length, rtp_header2.header.headerLength);
EXPECT_EQ(0, rtp_header2.extension.transmissionTimeOffset);
}
TEST_F(RtpSenderTest, NoTrafficSmoothing) {
EXPECT_EQ(0, rtp_sender_->RegisterSendTransport(&transport_));
WebRtc_Word32 rtp_length = rtp_sender_->BuildRTPheader(packet_,
kPayload,
kMarkerBit,
kTimestamp);
// Packet should be sent immediately.
EXPECT_EQ(0, rtp_sender_->SendToNetwork(packet_, 0, rtp_length,
kAllowRetransmission));
EXPECT_EQ(1, transport_.packets_sent_);
EXPECT_EQ(rtp_length, transport_.last_sent_packet_len_);
}
TEST_F(RtpSenderTest, TrafficSmoothing) {
rtp_sender_->SetTransmissionSmoothingStatus(true);
EXPECT_EQ(0, rtp_sender_->SetStorePacketsStatus(true, 10));
EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(kType, kId));
EXPECT_EQ(0, rtp_sender_->RegisterSendTransport(&transport_));
WebRtc_Word32 rtp_length = rtp_sender_->BuildRTPheader(packet_,
kPayload,
kMarkerBit,
kTimestamp);
// Packet should be stored in a send bucket.
EXPECT_EQ(0, rtp_sender_->SendToNetwork(packet_, 0, rtp_length,
kAllowRetransmission));
EXPECT_EQ(0, transport_.packets_sent_);
const int kStoredTimeInMs = 100;
fake_clock_.IncrementTime(kStoredTimeInMs);
// Process send bucket. Packet should now be sent.
rtp_sender_->ProcessSendToNetwork();
EXPECT_EQ(1, transport_.packets_sent_);
EXPECT_EQ(rtp_length, transport_.last_sent_packet_len_);
// Parse sent packet.
webrtc::ModuleRTPUtility::RTPHeaderParser rtpParser(
transport_.last_sent_packet_, rtp_length);
webrtc::WebRtcRTPHeader rtp_header;
RtpHeaderExtensionMap map;
map.Register(kType, kId);
const bool valid_rtp_header = rtpParser.Parse(rtp_header, &map);
ASSERT_TRUE(valid_rtp_header);
// Verify transmission time offset.
EXPECT_EQ(kStoredTimeInMs * 90, rtp_header.extension.transmissionTimeOffset);
}
} // namespace webrtc

View File

@ -0,0 +1,117 @@
/*
* Copyright (c) 2011 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 "transmission_bucket.h"
#include <assert.h>
#include "critical_section_wrapper.h"
namespace webrtc {
TransmissionBucket::TransmissionBucket()
: critsect_(CriticalSectionWrapper::CreateCriticalSection()),
accumulator_(0),
bytes_rem_total_(0),
bytes_rem_interval_(0),
packets_(),
first_(true) {
}
TransmissionBucket::~TransmissionBucket() {
packets_.clear();
delete critsect_;
}
void TransmissionBucket::Reset() {
webrtc::CriticalSectionScoped cs(*critsect_);
accumulator_ = 0;
bytes_rem_total_ = 0;
bytes_rem_interval_ = 0;
packets_.clear();
first_ = true;
}
void TransmissionBucket::Fill(const uint16_t seq_num,
const uint32_t num_bytes) {
webrtc::CriticalSectionScoped cs(*critsect_);
accumulator_ += num_bytes;
Packet p(seq_num, num_bytes);
packets_.push_back(p);
}
bool TransmissionBucket::Empty() {
webrtc::CriticalSectionScoped cs(*critsect_);
return packets_.empty();
}
void TransmissionBucket::UpdateBytesPerInterval(
const uint32_t delta_time_ms,
const uint16_t target_bitrate_kbps) {
webrtc::CriticalSectionScoped cs(*critsect_);
const float kMargin = 1.05;
uint32_t bytes_per_interval =
kMargin * (target_bitrate_kbps * delta_time_ms / 8);
if (bytes_rem_interval_ < 0) {
bytes_rem_interval_ += bytes_per_interval;
} else {
bytes_rem_interval_ = bytes_per_interval;
}
if (accumulator_) {
bytes_rem_total_ += bytes_per_interval;
return;
}
bytes_rem_total_ = bytes_per_interval;
}
int32_t TransmissionBucket::GetNextPacket() {
webrtc::CriticalSectionScoped cs(*critsect_);
if (accumulator_ == 0) {
// Empty.
return -1;
}
std::vector<Packet>::const_iterator it_begin = packets_.begin();
const uint16_t num_bytes = (*it_begin).length_;
const uint16_t seq_num = (*it_begin).sequence_number_;
if (first_) {
// Ok to transmit first packet.
first_ = false;
packets_.erase(packets_.begin());
return seq_num;
}
const float kFrameComplete = 0.80;
if (num_bytes * kFrameComplete > bytes_rem_total_) {
// Packet does not fit.
return -1;
}
if (bytes_rem_interval_ <= 0) {
// All bytes consumed for this interval.
return -1;
}
// Ok to transmit packet.
bytes_rem_total_ -= num_bytes;
bytes_rem_interval_ -= num_bytes;
assert(accumulator_ - num_bytes >= 0);
accumulator_ -= num_bytes;
packets_.erase(packets_.begin());
return seq_num;
}
} // namespace webrtc

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2011 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_RTP_RTCP_TRANSMISSION_BUCKET_H_
#define WEBRTC_MODULES_RTP_RTCP_TRANSMISSION_BUCKET_H_
#include <stdint.h>
#include <vector>
#include "typedefs.h"
namespace webrtc
{
class CriticalSectionWrapper;
class TransmissionBucket {
public:
TransmissionBucket();
~TransmissionBucket();
// Resets members to initial state.
void Reset();
// Adds packet to be sent.
void Fill(const uint16_t seq_num, const uint32_t num_bytes);
// Returns true if there is no packet to be sent.
bool Empty();
// Updates the number of bytes that can be sent for the next time interval.
void UpdateBytesPerInterval(const uint32_t delta_time_in_ms,
const uint16_t target_bitrate_kbps);
// Checks if next packet in line can be transmitted. Returns the sequence
// number of the packet on success, -1 otherwise. The packet is removed from
// the vector on success.
int32_t GetNextPacket();
private:
struct Packet {
Packet(uint16_t sequence_number, uint16_t length_in_bytes)
: sequence_number_(sequence_number),
length_(length_in_bytes) {
}
uint16_t sequence_number_;
uint16_t length_;
};
CriticalSectionWrapper* critsect_;
uint32_t accumulator_;
int32_t bytes_rem_total_;
int32_t bytes_rem_interval_;
std::vector<Packet> packets_;
bool first_;
};
} // namespace webrtc
#endif // WEBRTC_MODULES_RTP_RTCP_TRANSMISSION_BUCKET_H_

View File

@ -0,0 +1,64 @@
/*
* Copyright (c) 2011 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.
*/
/*
* This file includes unit tests for the TransmissionBucket.
*/
#include <gtest/gtest.h>
#include "transmission_bucket.h"
namespace webrtc {
class TransmissionBucketTest : public ::testing::Test {
protected:
TransmissionBucket send_bucket_;
};
TEST_F(TransmissionBucketTest, Fill) {
EXPECT_TRUE(send_bucket_.Empty());
send_bucket_.Fill(1, 100);
EXPECT_FALSE(send_bucket_.Empty());
}
TEST_F(TransmissionBucketTest, Reset) {
send_bucket_.Fill(1, 100);
EXPECT_FALSE(send_bucket_.Empty());
send_bucket_.Reset();
EXPECT_TRUE(send_bucket_.Empty());
}
TEST_F(TransmissionBucketTest, GetNextPacket) {
EXPECT_EQ(-1, send_bucket_.GetNextPacket()); // empty
send_bucket_.Fill(1234, 100);
EXPECT_EQ(1234, send_bucket_.GetNextPacket()); // first packet ok
send_bucket_.Fill(1235, 100);
EXPECT_EQ(-1, send_bucket_.GetNextPacket()); // packet does not fit
}
TEST_F(TransmissionBucketTest, UpdateBytesPerInterval) {
const int delta_time_ms = 1;
const int target_bitrate_kbps = 800;
send_bucket_.UpdateBytesPerInterval(delta_time_ms, target_bitrate_kbps);
send_bucket_.Fill(1234, 50);
send_bucket_.Fill(1235, 50);
send_bucket_.Fill(1236, 50);
EXPECT_EQ(1234, send_bucket_.GetNextPacket()); // first packet ok
EXPECT_EQ(1235, send_bucket_.GetNextPacket()); // ok
EXPECT_EQ(1236, send_bucket_.GetNextPacket()); // ok
EXPECT_TRUE(send_bucket_.Empty());
send_bucket_.Fill(1237, 50);
EXPECT_EQ(-1, send_bucket_.GetNextPacket()); // packet does not fit
}
} // namespace webrtc

View File

@ -89,6 +89,12 @@ TEST_F(RtpRtcpAPITest, CSRC) {
EXPECT_EQ(test_CSRC[1], testOfCSRC[1]);
}
TEST_F(RtpRtcpAPITest, TrafficSmoothing) {
EXPECT_FALSE(module->TransmissionSmoothingStatus());
module->SetTransmissionSmoothingStatus(true);
EXPECT_TRUE(module->TransmissionSmoothingStatus());
}
TEST_F(RtpRtcpAPITest, RTCP) {
EXPECT_EQ(kRtcpOff, module->RTCP());
EXPECT_EQ(0, module->SetRTCPStatus(kRtcpCompound));