Review URL: http://webrtc-codereview.appspot.com/321011
git-svn-id: http://webrtc.googlecode.com/svn/trunk@1431 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
67cdc22e7e
commit
0b3c35a258
@ -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
|
||||
*/
|
||||
|
||||
@ -38,6 +38,12 @@ enum RTPAliveType
|
||||
kRtpAlive = 2
|
||||
};
|
||||
|
||||
enum StorageType {
|
||||
kDontStore,
|
||||
kDontRetransmit,
|
||||
kAllowRetransmission
|
||||
};
|
||||
|
||||
enum RTPExtensionType
|
||||
{
|
||||
kRtpExtensionNone,
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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;
|
||||
|
||||
|
||||
@ -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));
|
||||
|
||||
278
src/modules/rtp_rtcp/source/rtp_packet_history.cc
Normal file
278
src/modules/rtp_rtcp/source/rtp_packet_history.cc
Normal 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
|
||||
87
src/modules/rtp_rtcp/source/rtp_packet_history.h
Normal file
87
src/modules/rtp_rtcp/source/rtp_packet_history.h
Normal 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_
|
||||
216
src/modules/rtp_rtcp/source/rtp_packet_history_test.cc
Normal file
216
src/modules/rtp_rtcp/source/rtp_packet_history_test.cc
Normal 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
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
*/
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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',
|
||||
],
|
||||
},
|
||||
],
|
||||
|
||||
@ -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)
|
||||
{
|
||||
|
||||
@ -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;
|
||||
|
||||
@ -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
|
||||
|
||||
117
src/modules/rtp_rtcp/source/transmission_bucket.cc
Normal file
117
src/modules/rtp_rtcp/source/transmission_bucket.cc
Normal 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
|
||||
64
src/modules/rtp_rtcp/source/transmission_bucket.h
Normal file
64
src/modules/rtp_rtcp/source/transmission_bucket.h
Normal 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_
|
||||
64
src/modules/rtp_rtcp/source/transmission_bucket_test.cc
Normal file
64
src/modules/rtp_rtcp/source/transmission_bucket_test.cc
Normal 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
|
||||
@ -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));
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user