Adds support for sending redundant payloads over RTX.
TEST=trybots BUG=1812 R=mflodman@webrtc.org, pbos@webrtc.org Review URL: https://webrtc-codereview.appspot.com/4169004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5209 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
9523b55826
commit
7e9315b42e
@ -1,3 +1,3 @@
|
|||||||
# Tests that are too slow.
|
# Tests that are too slow.
|
||||||
RampUpTest/*
|
RampUpTest.*
|
||||||
CallTest.PlaysOutAudioAndVideoInSync
|
CallTest.PlaysOutAudioAndVideoInSync
|
@ -15,6 +15,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "webrtc/call.h"
|
#include "webrtc/call.h"
|
||||||
|
#include "webrtc/common.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
|
||||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||||
#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h"
|
#include "webrtc/system_wrappers/interface/rw_lock_wrapper.h"
|
||||||
@ -148,7 +149,7 @@ namespace internal {
|
|||||||
TraceDispatcher* global_trace_dispatcher = NULL;
|
TraceDispatcher* global_trace_dispatcher = NULL;
|
||||||
} // internal
|
} // internal
|
||||||
|
|
||||||
Call* Call::Create(const Call::Config& config) {
|
void CreateTraceDispatcher() {
|
||||||
if (internal::global_trace_dispatcher == NULL) {
|
if (internal::global_trace_dispatcher == NULL) {
|
||||||
TraceDispatcher* dispatcher = new TraceDispatcher();
|
TraceDispatcher* dispatcher = new TraceDispatcher();
|
||||||
// TODO(pbos): Atomic compare and exchange.
|
// TODO(pbos): Atomic compare and exchange.
|
||||||
@ -158,8 +159,13 @@ Call* Call::Create(const Call::Config& config) {
|
|||||||
delete dispatcher;
|
delete dispatcher;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
VideoEngine* video_engine = VideoEngine::Create();
|
Call* Call::Create(const Call::Config& config) {
|
||||||
|
CreateTraceDispatcher();
|
||||||
|
|
||||||
|
VideoEngine* video_engine = config.webrtc_config != NULL ?
|
||||||
|
VideoEngine::Create(*config.webrtc_config) : VideoEngine::Create();
|
||||||
assert(video_engine != NULL);
|
assert(video_engine != NULL);
|
||||||
|
|
||||||
return new internal::Call(video_engine, config);
|
return new internal::Call(video_engine, config);
|
||||||
|
@ -38,12 +38,15 @@ class Call {
|
|||||||
public:
|
public:
|
||||||
struct Config {
|
struct Config {
|
||||||
explicit Config(newapi::Transport* send_transport)
|
explicit Config(newapi::Transport* send_transport)
|
||||||
: send_transport(send_transport),
|
: webrtc_config(NULL),
|
||||||
|
send_transport(send_transport),
|
||||||
overuse_detection(false),
|
overuse_detection(false),
|
||||||
voice_engine(NULL),
|
voice_engine(NULL),
|
||||||
trace_callback(NULL),
|
trace_callback(NULL),
|
||||||
trace_filter(kTraceDefault) {}
|
trace_filter(kTraceDefault) {}
|
||||||
|
|
||||||
|
webrtc::Config* webrtc_config;
|
||||||
|
|
||||||
newapi::Transport* send_transport;
|
newapi::Transport* send_transport;
|
||||||
bool overuse_detection;
|
bool overuse_detection;
|
||||||
|
|
||||||
@ -56,6 +59,9 @@ class Call {
|
|||||||
|
|
||||||
static Call* Create(const Call::Config& config);
|
static Call* Create(const Call::Config& config);
|
||||||
|
|
||||||
|
static Call* Create(const Call::Config& config,
|
||||||
|
const webrtc::Config& webrtc_config);
|
||||||
|
|
||||||
virtual std::vector<VideoCodec> GetVideoCodecs() = 0;
|
virtual std::vector<VideoCodec> GetVideoCodecs() = 0;
|
||||||
|
|
||||||
virtual VideoSendStream::Config GetDefaultSendConfig() = 0;
|
virtual VideoSendStream::Config GetDefaultSendConfig() = 0;
|
||||||
|
25
webrtc/experiments.h
Normal file
25
webrtc/experiments.h
Normal file
@ -0,0 +1,25 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_EXPERIMENTS_H_
|
||||||
|
#define WEBRTC_EXPERIMENTS_H_
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
struct PaddingStrategy {
|
||||||
|
PaddingStrategy()
|
||||||
|
: redundant_payloads(false) {}
|
||||||
|
explicit PaddingStrategy(bool redundant_payloads)
|
||||||
|
: redundant_payloads(redundant_payloads) {}
|
||||||
|
virtual ~PaddingStrategy() {}
|
||||||
|
|
||||||
|
const bool redundant_payloads;
|
||||||
|
};
|
||||||
|
} // namespace webrtc
|
||||||
|
#endif // WEBRTC_EXPERIMENTS_H_
|
@ -251,7 +251,7 @@ class RtpRtcp : public Module {
|
|||||||
/*
|
/*
|
||||||
* Turn on/off sending RTX (RFC 4588) on a specific SSRC.
|
* Turn on/off sending RTX (RFC 4588) on a specific SSRC.
|
||||||
*/
|
*/
|
||||||
virtual int32_t SetRTXSendStatus(RtxMode mode, bool set_ssrc,
|
virtual int32_t SetRTXSendStatus(int modes, bool set_ssrc,
|
||||||
uint32_t ssrc) = 0;
|
uint32_t ssrc) = 0;
|
||||||
|
|
||||||
// Sets the payload type to use when sending RTX packets. Note that this
|
// Sets the payload type to use when sending RTX packets. Note that this
|
||||||
@ -261,7 +261,7 @@ class RtpRtcp : public Module {
|
|||||||
/*
|
/*
|
||||||
* Get status of sending RTX (RFC 4588) on a specific SSRC.
|
* Get status of sending RTX (RFC 4588) on a specific SSRC.
|
||||||
*/
|
*/
|
||||||
virtual int32_t RTXSendStatus(RtxMode* mode, uint32_t* ssrc,
|
virtual int32_t RTXSendStatus(int* modes, uint32_t* ssrc,
|
||||||
int* payloadType) const = 0;
|
int* payloadType) const = 0;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
@ -129,9 +129,10 @@ enum RetransmissionMode {
|
|||||||
};
|
};
|
||||||
|
|
||||||
enum RtxMode {
|
enum RtxMode {
|
||||||
kRtxOff = 0,
|
kRtxOff = 0x0,
|
||||||
kRtxRetransmitted = 1, // Apply RTX only to retransmitted packets.
|
kRtxRetransmitted = 0x1, // Only send retransmissions over RTX.
|
||||||
kRtxAll = 2 // Apply RTX to all packets (source + retransmissions).
|
kRtxRedundantPayloads = 0x2 // Preventively send redundant payloads
|
||||||
|
// instead of padding.
|
||||||
};
|
};
|
||||||
|
|
||||||
const int kRtxHeaderSize = 2;
|
const int kRtxHeaderSize = 2;
|
||||||
|
@ -85,9 +85,9 @@ class MockRtpRtcp : public RtpRtcp {
|
|||||||
MOCK_METHOD1(SetCSRCStatus,
|
MOCK_METHOD1(SetCSRCStatus,
|
||||||
int32_t(const bool include));
|
int32_t(const bool include));
|
||||||
MOCK_METHOD3(SetRTXSendStatus,
|
MOCK_METHOD3(SetRTXSendStatus,
|
||||||
int32_t(RtxMode mode, bool setSSRC, uint32_t ssrc));
|
int32_t(int modes, bool setSSRC, uint32_t ssrc));
|
||||||
MOCK_CONST_METHOD3(RTXSendStatus,
|
MOCK_CONST_METHOD3(RTXSendStatus,
|
||||||
int32_t(RtxMode* mode, uint32_t* ssrc, int* payload_type));
|
int32_t(int* modes, uint32_t* ssrc, int* payload_type));
|
||||||
MOCK_METHOD1(SetRtxSendPayloadType,
|
MOCK_METHOD1(SetRtxSendPayloadType,
|
||||||
void(int));
|
void(int));
|
||||||
MOCK_METHOD1(SetSendingStatus,
|
MOCK_METHOD1(SetSendingStatus,
|
||||||
|
@ -343,27 +343,3 @@ TEST_F(RtpRtcpRtxNackTest, RtxNack) {
|
|||||||
EXPECT_EQ(kTestNumberOfRtxPackets, transport_.count_rtx_ssrc_);
|
EXPECT_EQ(kTestNumberOfRtxPackets, transport_.count_rtx_ssrc_);
|
||||||
EXPECT_TRUE(ExpectedPacketsReceived());
|
EXPECT_TRUE(ExpectedPacketsReceived());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpRtcpRtxNackTest, RTXAllNoLoss) {
|
|
||||||
RunRtxTest(kRtxAll, 0);
|
|
||||||
EXPECT_EQ(kTestSequenceNumber, *(receiver_.sequence_numbers_.begin()));
|
|
||||||
EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
|
|
||||||
*(receiver_.sequence_numbers_.rbegin()));
|
|
||||||
// We have transmitted all packets twice, and loss was set to 0.
|
|
||||||
EXPECT_EQ(kTestNumberOfPackets * 2u, receiver_.sequence_numbers_.size());
|
|
||||||
// Half of the packets should be via RTX.
|
|
||||||
EXPECT_EQ(static_cast<int>(kTestNumberOfPackets),
|
|
||||||
transport_.count_rtx_ssrc_);
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_F(RtpRtcpRtxNackTest, RTXAllWithLoss) {
|
|
||||||
int loss = 10;
|
|
||||||
RunRtxTest(kRtxAll, loss);
|
|
||||||
EXPECT_EQ(kTestSequenceNumber, *(receiver_.sequence_numbers_.begin()));
|
|
||||||
EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
|
|
||||||
*(receiver_.sequence_numbers_.rbegin()));
|
|
||||||
// Got everything but lost packets.
|
|
||||||
EXPECT_EQ(2u * (kTestNumberOfPackets - kTestNumberOfPackets / loss),
|
|
||||||
receiver_.sequence_numbers_.size());
|
|
||||||
EXPECT_EQ(static_cast<int>(kTestNumberOfPackets), transport_.count_rtx_ssrc_);
|
|
||||||
}
|
|
||||||
|
@ -11,7 +11,10 @@
|
|||||||
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_history.h"
|
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_history.h"
|
||||||
|
|
||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
#include <string.h> // memset
|
#include <string.h> // memset
|
||||||
|
#include <limits>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
||||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||||
@ -19,6 +22,8 @@
|
|||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
|
enum { kMinPacketRequestBytes = 50 };
|
||||||
|
|
||||||
RTPPacketHistory::RTPPacketHistory(Clock* clock)
|
RTPPacketHistory::RTPPacketHistory(Clock* clock)
|
||||||
: clock_(clock),
|
: clock_(clock),
|
||||||
critsect_(CriticalSectionWrapper::CreateCriticalSection()),
|
critsect_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||||
@ -55,7 +60,7 @@ void RTPPacketHistory::Allocate(uint16_t number_to_store) {
|
|||||||
stored_seq_nums_.resize(number_to_store);
|
stored_seq_nums_.resize(number_to_store);
|
||||||
stored_lengths_.resize(number_to_store);
|
stored_lengths_.resize(number_to_store);
|
||||||
stored_times_.resize(number_to_store);
|
stored_times_.resize(number_to_store);
|
||||||
stored_resend_times_.resize(number_to_store);
|
stored_send_times_.resize(number_to_store);
|
||||||
stored_types_.resize(number_to_store);
|
stored_types_.resize(number_to_store);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -74,7 +79,7 @@ void RTPPacketHistory::Free() {
|
|||||||
stored_seq_nums_.clear();
|
stored_seq_nums_.clear();
|
||||||
stored_lengths_.clear();
|
stored_lengths_.clear();
|
||||||
stored_times_.clear();
|
stored_times_.clear();
|
||||||
stored_resend_times_.clear();
|
stored_send_times_.clear();
|
||||||
stored_types_.clear();
|
stored_types_.clear();
|
||||||
|
|
||||||
store_ = false;
|
store_ = false;
|
||||||
@ -139,9 +144,9 @@ int32_t RTPPacketHistory::PutRTPPacket(const uint8_t* packet,
|
|||||||
|
|
||||||
stored_seq_nums_[prev_index_] = seq_num;
|
stored_seq_nums_[prev_index_] = seq_num;
|
||||||
stored_lengths_[prev_index_] = packet_length;
|
stored_lengths_[prev_index_] = packet_length;
|
||||||
stored_times_[prev_index_] =
|
stored_times_[prev_index_] = (capture_time_ms > 0) ? capture_time_ms :
|
||||||
(capture_time_ms > 0) ? capture_time_ms : clock_->TimeInMilliseconds();
|
clock_->TimeInMilliseconds();
|
||||||
stored_resend_times_[prev_index_] = 0; // packet not resent
|
stored_send_times_[prev_index_] = 0; // Packet not sent.
|
||||||
stored_types_[prev_index_] = type;
|
stored_types_[prev_index_] = type;
|
||||||
|
|
||||||
++prev_index_;
|
++prev_index_;
|
||||||
@ -211,12 +216,12 @@ bool RTPPacketHistory::HasRTPPacket(uint16_t sequence_number) const {
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RTPPacketHistory::GetRTPPacket(uint16_t sequence_number,
|
bool RTPPacketHistory::GetPacketAndSetSendTime(uint16_t sequence_number,
|
||||||
uint32_t min_elapsed_time_ms,
|
uint32_t min_elapsed_time_ms,
|
||||||
uint8_t* packet,
|
bool retransmit,
|
||||||
uint16_t* packet_length,
|
uint8_t* packet,
|
||||||
int64_t* stored_time_ms,
|
uint16_t* packet_length,
|
||||||
StorageType* type) const {
|
int64_t* stored_time_ms) {
|
||||||
CriticalSectionScoped cs(critsect_);
|
CriticalSectionScoped cs(critsect_);
|
||||||
if (!store_) {
|
if (!store_) {
|
||||||
return false;
|
return false;
|
||||||
@ -237,46 +242,58 @@ bool RTPPacketHistory::GetRTPPacket(uint16_t sequence_number,
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (length > *packet_length) {
|
if (length > *packet_length) {
|
||||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
|
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
|
||||||
"Input buffer too short for packet %u", sequence_number);
|
"Input buffer too short for packet %u", sequence_number);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Verify elapsed time since last retrieve.
|
// Verify elapsed time since last retrieve.
|
||||||
int64_t now = clock_->TimeInMilliseconds();
|
int64_t now = clock_->TimeInMilliseconds();
|
||||||
if (min_elapsed_time_ms > 0 &&
|
if (min_elapsed_time_ms > 0 &&
|
||||||
((now - stored_resend_times_.at(index)) < min_elapsed_time_ms)) {
|
((now - stored_send_times_.at(index)) < min_elapsed_time_ms)) {
|
||||||
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
|
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
|
||||||
"Skip getting packet %u, packet recently resent.", sequence_number);
|
"Skip getting packet %u, packet recently resent.", sequence_number);
|
||||||
*packet_length = 0;
|
*packet_length = 0;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get packet.
|
if (length == 0 ||
|
||||||
std::vector<std::vector<uint8_t> >::const_iterator it_found_packet =
|
(retransmit && stored_types_.at(index) == kDontRetransmit)) {
|
||||||
stored_packets_.begin() + index;
|
// No bytes copied since this packet shouldn't be retransmitted or is
|
||||||
std::copy(it_found_packet->begin(), it_found_packet->begin() + length, packet);
|
// of zero size.
|
||||||
*packet_length = stored_lengths_.at(index);
|
return false;
|
||||||
*stored_time_ms = stored_times_.at(index);
|
}
|
||||||
*type = stored_types_.at(index);
|
stored_send_times_[index] = clock_->TimeInMilliseconds();
|
||||||
|
GetPacket(index, packet, packet_length, stored_time_ms);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTPPacketHistory::UpdateResendTime(uint16_t sequence_number) {
|
void RTPPacketHistory::GetPacket(int index,
|
||||||
CriticalSectionScoped cs(critsect_);
|
uint8_t* packet,
|
||||||
if (!store_) {
|
uint16_t* packet_length,
|
||||||
return;
|
int64_t* stored_time_ms) const {
|
||||||
}
|
// Get packet.
|
||||||
|
uint16_t length = stored_lengths_.at(index);
|
||||||
|
std::vector<std::vector<uint8_t> >::const_iterator it_found_packet =
|
||||||
|
stored_packets_.begin() + index;
|
||||||
|
std::copy(it_found_packet->begin(), it_found_packet->begin() + length,
|
||||||
|
packet);
|
||||||
|
*packet_length = length;
|
||||||
|
*stored_time_ms = stored_times_.at(index);
|
||||||
|
}
|
||||||
|
|
||||||
int32_t index = 0;
|
bool RTPPacketHistory::GetBestFittingPacket(uint8_t* packet,
|
||||||
bool found = FindSeqNum(sequence_number, &index);
|
uint16_t* packet_length,
|
||||||
if (!found) {
|
int64_t* stored_time_ms) {
|
||||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
|
CriticalSectionScoped cs(critsect_);
|
||||||
"Failed to update resend time, seq num: %u.", sequence_number);
|
if (!store_)
|
||||||
return;
|
return false;
|
||||||
}
|
int index = FindBestFittingPacket(*packet_length);
|
||||||
stored_resend_times_[index] = clock_->TimeInMilliseconds();
|
if (index < 0)
|
||||||
|
return false;
|
||||||
|
GetPacket(index, packet, packet_length, stored_time_ms);
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// private, lock should already be taken
|
// private, lock should already be taken
|
||||||
@ -313,4 +330,23 @@ bool RTPPacketHistory::FindSeqNum(uint16_t sequence_number,
|
|||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int RTPPacketHistory::FindBestFittingPacket(uint16_t size) const {
|
||||||
|
if (size < kMinPacketRequestBytes || stored_lengths_.empty())
|
||||||
|
return -1;
|
||||||
|
int min_diff = -1;
|
||||||
|
size_t best_index = 0;
|
||||||
|
for (size_t i = 0; i < stored_lengths_.size(); ++i) {
|
||||||
|
if (stored_lengths_[i] == 0)
|
||||||
|
continue;
|
||||||
|
int diff = abs(stored_lengths_[i] - size);
|
||||||
|
if (min_diff < 0 || diff < min_diff) {
|
||||||
|
min_diff = diff;
|
||||||
|
best_index = i;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (min_diff < 0)
|
||||||
|
return -1;
|
||||||
|
return best_index;
|
||||||
|
}
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -59,22 +59,26 @@ class RTPPacketHistory {
|
|||||||
// copied.
|
// copied.
|
||||||
// stored_time_ms: returns the time when the packet was stored.
|
// stored_time_ms: returns the time when the packet was stored.
|
||||||
// type: returns the storage type set in PutRTPPacket.
|
// type: returns the storage type set in PutRTPPacket.
|
||||||
bool GetRTPPacket(uint16_t sequence_number,
|
bool GetPacketAndSetSendTime(uint16_t sequence_number,
|
||||||
uint32_t min_elapsed_time_ms,
|
uint32_t min_elapsed_time_ms,
|
||||||
uint8_t* packet,
|
bool retransmit,
|
||||||
uint16_t* packet_length,
|
uint8_t* packet,
|
||||||
int64_t* stored_time_ms,
|
uint16_t* packet_length,
|
||||||
StorageType* type) const;
|
int64_t* stored_time_ms);
|
||||||
|
|
||||||
|
bool GetBestFittingPacket(uint8_t* packet, uint16_t* packet_length,
|
||||||
|
int64_t* stored_time_ms);
|
||||||
|
|
||||||
bool HasRTPPacket(uint16_t sequence_number) const;
|
bool HasRTPPacket(uint16_t sequence_number) const;
|
||||||
|
|
||||||
void UpdateResendTime(uint16_t sequence_number);
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
void GetPacket(int index, uint8_t* packet, uint16_t* packet_length,
|
||||||
|
int64_t* stored_time_ms) const;
|
||||||
void Allocate(uint16_t number_to_store);
|
void Allocate(uint16_t number_to_store);
|
||||||
void Free();
|
void Free();
|
||||||
void VerifyAndAllocatePacketLength(uint16_t packet_length);
|
void VerifyAndAllocatePacketLength(uint16_t packet_length);
|
||||||
bool FindSeqNum(uint16_t sequence_number, int32_t* index) const;
|
bool FindSeqNum(uint16_t sequence_number, int32_t* index) const;
|
||||||
|
int FindBestFittingPacket(uint16_t size) const;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Clock* clock_;
|
Clock* clock_;
|
||||||
@ -87,7 +91,7 @@ class RTPPacketHistory {
|
|||||||
std::vector<uint16_t> stored_seq_nums_;
|
std::vector<uint16_t> stored_seq_nums_;
|
||||||
std::vector<uint16_t> stored_lengths_;
|
std::vector<uint16_t> stored_lengths_;
|
||||||
std::vector<int64_t> stored_times_;
|
std::vector<int64_t> stored_times_;
|
||||||
std::vector<int64_t> stored_resend_times_;
|
std::vector<int64_t> stored_send_times_;
|
||||||
std::vector<StorageType> stored_types_;
|
std::vector<StorageType> stored_types_;
|
||||||
};
|
};
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -74,8 +74,8 @@ TEST_F(RtpPacketHistoryTest, NoStoreStatus) {
|
|||||||
// Packet should not be stored.
|
// Packet should not be stored.
|
||||||
len = kMaxPacketLength;
|
len = kMaxPacketLength;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
StorageType type;
|
EXPECT_FALSE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_, &len,
|
||||||
EXPECT_FALSE(hist_->GetRTPPacket(kSeqNum, 0, packet_, &len, &time, &type));
|
&time));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpPacketHistoryTest, DontStore) {
|
TEST_F(RtpPacketHistoryTest, DontStore) {
|
||||||
@ -89,8 +89,8 @@ TEST_F(RtpPacketHistoryTest, DontStore) {
|
|||||||
// Packet should not be stored.
|
// Packet should not be stored.
|
||||||
len = kMaxPacketLength;
|
len = kMaxPacketLength;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
StorageType type;
|
EXPECT_FALSE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_, &len,
|
||||||
EXPECT_FALSE(hist_->GetRTPPacket(kSeqNum, 0, packet_, &len, &time, &type));
|
&time));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpPacketHistoryTest, PutRtpPacket_TooLargePacketLength) {
|
TEST_F(RtpPacketHistoryTest, PutRtpPacket_TooLargePacketLength) {
|
||||||
@ -112,17 +112,16 @@ TEST_F(RtpPacketHistoryTest, GetRtpPacket_TooSmallBuffer) {
|
|||||||
capture_time_ms, kAllowRetransmission));
|
capture_time_ms, kAllowRetransmission));
|
||||||
uint16_t len_out = len - 1;
|
uint16_t len_out = len - 1;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
StorageType type;
|
EXPECT_FALSE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_,
|
||||||
EXPECT_FALSE(hist_->GetRTPPacket(kSeqNum, 0, packet_, &len_out, &time,
|
&len_out, &time));
|
||||||
&type));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpPacketHistoryTest, GetRtpPacket_NotStored) {
|
TEST_F(RtpPacketHistoryTest, GetRtpPacket_NotStored) {
|
||||||
hist_->SetStorePacketsStatus(true, 10);
|
hist_->SetStorePacketsStatus(true, 10);
|
||||||
uint16_t len = kMaxPacketLength;
|
uint16_t len = kMaxPacketLength;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
StorageType type;
|
EXPECT_FALSE(hist_->GetPacketAndSetSendTime(0, 0, false, packet_, &len,
|
||||||
EXPECT_FALSE(hist_->GetRTPPacket(0, 0, packet_, &len, &time, &type));
|
&time));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(RtpPacketHistoryTest, PutRtpPacket) {
|
TEST_F(RtpPacketHistoryTest, PutRtpPacket) {
|
||||||
@ -147,11 +146,9 @@ TEST_F(RtpPacketHistoryTest, GetRtpPacket) {
|
|||||||
|
|
||||||
uint16_t len_out = kMaxPacketLength;
|
uint16_t len_out = kMaxPacketLength;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
StorageType type;
|
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_out_,
|
||||||
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 0, packet_out_, &len_out, &time,
|
&len_out, &time));
|
||||||
&type));
|
|
||||||
EXPECT_EQ(len, len_out);
|
EXPECT_EQ(len, len_out);
|
||||||
EXPECT_EQ(kAllowRetransmission, type);
|
|
||||||
EXPECT_EQ(capture_time_ms, time);
|
EXPECT_EQ(capture_time_ms, time);
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
EXPECT_EQ(packet_[i], packet_out_[i]);
|
EXPECT_EQ(packet_[i], packet_out_[i]);
|
||||||
@ -176,11 +173,9 @@ TEST_F(RtpPacketHistoryTest, ReplaceRtpHeader) {
|
|||||||
|
|
||||||
uint16_t len_out = kMaxPacketLength;
|
uint16_t len_out = kMaxPacketLength;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
StorageType type;
|
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_out_,
|
||||||
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 0, packet_out_, &len_out, &time,
|
&len_out, &time));
|
||||||
&type));
|
|
||||||
EXPECT_EQ(len, len_out);
|
EXPECT_EQ(len, len_out);
|
||||||
EXPECT_EQ(kAllowRetransmission, type);
|
|
||||||
EXPECT_EQ(capture_time_ms, time);
|
EXPECT_EQ(capture_time_ms, time);
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
EXPECT_EQ(packet_[i], packet_out_[i]);
|
EXPECT_EQ(packet_[i], packet_out_[i]);
|
||||||
@ -207,11 +202,9 @@ TEST_F(RtpPacketHistoryTest, NoCaptureTime) {
|
|||||||
|
|
||||||
uint16_t len_out = kMaxPacketLength;
|
uint16_t len_out = kMaxPacketLength;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
StorageType type;
|
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_out_,
|
||||||
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 0, packet_out_, &len_out, &time,
|
&len_out, &time));
|
||||||
&type));
|
|
||||||
EXPECT_EQ(len, len_out);
|
EXPECT_EQ(len, len_out);
|
||||||
EXPECT_EQ(kAllowRetransmission, type);
|
|
||||||
EXPECT_EQ(capture_time_ms, time);
|
EXPECT_EQ(capture_time_ms, time);
|
||||||
for (int i = 0; i < len; i++) {
|
for (int i = 0; i < len; i++) {
|
||||||
EXPECT_EQ(packet_[i], packet_out_[i]);
|
EXPECT_EQ(packet_[i], packet_out_[i]);
|
||||||
@ -228,11 +221,9 @@ TEST_F(RtpPacketHistoryTest, DontRetransmit) {
|
|||||||
|
|
||||||
uint16_t len_out = kMaxPacketLength;
|
uint16_t len_out = kMaxPacketLength;
|
||||||
int64_t time;
|
int64_t time;
|
||||||
StorageType type;
|
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 0, false, packet_out_,
|
||||||
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 0, packet_out_, &len_out, &time,
|
&len_out, &time));
|
||||||
&type));
|
|
||||||
EXPECT_EQ(len, len_out);
|
EXPECT_EQ(len, len_out);
|
||||||
EXPECT_EQ(kDontRetransmit, type);
|
|
||||||
EXPECT_EQ(capture_time_ms, time);
|
EXPECT_EQ(capture_time_ms, time);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -244,20 +235,22 @@ TEST_F(RtpPacketHistoryTest, MinResendTime) {
|
|||||||
EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength,
|
EXPECT_EQ(0, hist_->PutRTPPacket(packet_, len, kMaxPacketLength,
|
||||||
capture_time_ms, kAllowRetransmission));
|
capture_time_ms, kAllowRetransmission));
|
||||||
|
|
||||||
hist_->UpdateResendTime(kSeqNum);
|
int64_t time;
|
||||||
|
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 100, false, packet_, &len,
|
||||||
|
&time));
|
||||||
fake_clock_.AdvanceTimeMilliseconds(100);
|
fake_clock_.AdvanceTimeMilliseconds(100);
|
||||||
|
|
||||||
// Time has elapsed.
|
// Time has elapsed.
|
||||||
len = kMaxPacketLength;
|
len = kMaxPacketLength;
|
||||||
StorageType type;
|
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 100, false, packet_, &len,
|
||||||
int64_t time;
|
&time));
|
||||||
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 100, packet_, &len, &time, &type));
|
|
||||||
EXPECT_GT(len, 0);
|
EXPECT_GT(len, 0);
|
||||||
EXPECT_EQ(capture_time_ms, time);
|
EXPECT_EQ(capture_time_ms, time);
|
||||||
|
|
||||||
// Time has not elapsed. Packet should be found, but no bytes copied.
|
// Time has not elapsed. Packet should be found, but no bytes copied.
|
||||||
len = kMaxPacketLength;
|
len = kMaxPacketLength;
|
||||||
EXPECT_TRUE(hist_->GetRTPPacket(kSeqNum, 101, packet_, &len, &time, &type));
|
EXPECT_TRUE(hist_->GetPacketAndSetSendTime(kSeqNum, 101, false, packet_, &len,
|
||||||
|
&time));
|
||||||
EXPECT_EQ(0, len);
|
EXPECT_EQ(0, len);
|
||||||
}
|
}
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -269,15 +269,13 @@ int32_t ModuleRtpRtcpImpl::Process() {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t ModuleRtpRtcpImpl::SetRTXSendStatus(RtxMode mode, bool set_ssrc,
|
int32_t ModuleRtpRtcpImpl::SetRTXSendStatus(int mode, bool set_ssrc,
|
||||||
uint32_t ssrc) {
|
uint32_t ssrc) {
|
||||||
rtp_sender_.SetRTXStatus(mode, set_ssrc, ssrc);
|
rtp_sender_.SetRTXStatus(mode, set_ssrc, ssrc);
|
||||||
|
|
||||||
|
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t ModuleRtpRtcpImpl::RTXSendStatus(RtxMode* mode, uint32_t* ssrc,
|
int32_t ModuleRtpRtcpImpl::RTXSendStatus(int* mode, uint32_t* ssrc,
|
||||||
int* payload_type) const {
|
int* payload_type) const {
|
||||||
rtp_sender_.RTXStatus(mode, ssrc, payload_type);
|
rtp_sender_.RTXStatus(mode, ssrc, payload_type);
|
||||||
return 0;
|
return 0;
|
||||||
@ -1628,7 +1626,7 @@ int64_t ModuleRtpRtcpImpl::RtcpReportInterval() {
|
|||||||
void ModuleRtpRtcpImpl::SetRtcpReceiverSsrcs(uint32_t main_ssrc) {
|
void ModuleRtpRtcpImpl::SetRtcpReceiverSsrcs(uint32_t main_ssrc) {
|
||||||
std::set<uint32_t> ssrcs;
|
std::set<uint32_t> ssrcs;
|
||||||
ssrcs.insert(main_ssrc);
|
ssrcs.insert(main_ssrc);
|
||||||
RtxMode rtx_mode = kRtxOff;
|
int rtx_mode = kRtxOff;
|
||||||
uint32_t rtx_ssrc = 0;
|
uint32_t rtx_ssrc = 0;
|
||||||
int rtx_payload_type = 0;
|
int rtx_payload_type = 0;
|
||||||
rtp_sender_.RTXStatus(&rtx_mode, &rtx_ssrc, &rtx_payload_type);
|
rtp_sender_.RTXStatus(&rtx_mode, &rtx_ssrc, &rtx_payload_type);
|
||||||
|
@ -95,11 +95,11 @@ class ModuleRtpRtcpImpl : public RtpRtcp {
|
|||||||
|
|
||||||
virtual uint32_t ByteCountSent() const;
|
virtual uint32_t ByteCountSent() const;
|
||||||
|
|
||||||
virtual int32_t SetRTXSendStatus(const RtxMode mode,
|
virtual int32_t SetRTXSendStatus(const int mode,
|
||||||
const bool set_ssrc,
|
const bool set_ssrc,
|
||||||
const uint32_t ssrc) OVERRIDE;
|
const uint32_t ssrc) OVERRIDE;
|
||||||
|
|
||||||
virtual int32_t RTXSendStatus(RtxMode* mode, uint32_t* ssrc,
|
virtual int32_t RTXSendStatus(int* mode, uint32_t* ssrc,
|
||||||
int* payloadType) const OVERRIDE;
|
int* payloadType) const OVERRIDE;
|
||||||
|
|
||||||
|
|
||||||
|
@ -12,7 +12,6 @@
|
|||||||
|
|
||||||
#include <stdlib.h> // srand
|
#include <stdlib.h> // srand
|
||||||
|
|
||||||
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_history.h"
|
|
||||||
#include "webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h"
|
#include "webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/source/rtp_sender_video.h"
|
#include "webrtc/modules/rtp_rtcp/source/rtp_sender_video.h"
|
||||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||||
@ -54,7 +53,7 @@ RTPSender::RTPSender(const int32_t id, const bool audio, Clock *clock,
|
|||||||
transmission_time_offset_(0), absolute_send_time_(0),
|
transmission_time_offset_(0), absolute_send_time_(0),
|
||||||
// NACK.
|
// NACK.
|
||||||
nack_byte_count_times_(), nack_byte_count_(), nack_bitrate_(clock),
|
nack_byte_count_times_(), nack_byte_count_(), nack_bitrate_(clock),
|
||||||
packet_history_(new RTPPacketHistory(clock)),
|
packet_history_(clock),
|
||||||
// Statistics
|
// Statistics
|
||||||
statistics_crit_(CriticalSectionWrapper::CreateCriticalSection()),
|
statistics_crit_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||||
packets_sent_(0), payload_bytes_sent_(0), start_time_stamp_forced_(false),
|
packets_sent_(0), payload_bytes_sent_(0), start_time_stamp_forced_(false),
|
||||||
@ -97,7 +96,6 @@ RTPSender::~RTPSender() {
|
|||||||
delete it->second;
|
delete it->second;
|
||||||
payload_type_map_.erase(it);
|
payload_type_map_.erase(it);
|
||||||
}
|
}
|
||||||
delete packet_history_;
|
|
||||||
delete audio_;
|
delete audio_;
|
||||||
delete video_;
|
delete video_;
|
||||||
|
|
||||||
@ -271,7 +269,7 @@ uint16_t RTPSender::MaxPayloadLength() const {
|
|||||||
|
|
||||||
uint16_t RTPSender::PacketOverHead() const { return packet_over_head_; }
|
uint16_t RTPSender::PacketOverHead() const { return packet_over_head_; }
|
||||||
|
|
||||||
void RTPSender::SetRTXStatus(RtxMode mode, bool set_ssrc, uint32_t ssrc) {
|
void RTPSender::SetRTXStatus(int mode, bool set_ssrc, uint32_t ssrc) {
|
||||||
CriticalSectionScoped cs(send_critsect_);
|
CriticalSectionScoped cs(send_critsect_);
|
||||||
rtx_ = mode;
|
rtx_ = mode;
|
||||||
if (rtx_ != kRtxOff) {
|
if (rtx_ != kRtxOff) {
|
||||||
@ -283,7 +281,7 @@ void RTPSender::SetRTXStatus(RtxMode mode, bool set_ssrc, uint32_t ssrc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void RTPSender::RTXStatus(RtxMode* mode, uint32_t* ssrc,
|
void RTPSender::RTXStatus(int* mode, uint32_t* ssrc,
|
||||||
int* payload_type) const {
|
int* payload_type) const {
|
||||||
CriticalSectionScoped cs(send_critsect_);
|
CriticalSectionScoped cs(send_critsect_);
|
||||||
*mode = rtx_;
|
*mode = rtx_;
|
||||||
@ -389,6 +387,28 @@ int32_t RTPSender::SendOutgoingData(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int RTPSender::SendRedundantPayloads(int payload_type, int bytes_to_send) {
|
||||||
|
if (!(rtx_ & kRtxRedundantPayloads))
|
||||||
|
return 0;
|
||||||
|
uint8_t buffer[IP_PACKET_SIZE];
|
||||||
|
int bytes_left = bytes_to_send;
|
||||||
|
while (bytes_left > 0) {
|
||||||
|
uint16_t length = bytes_left;
|
||||||
|
int64_t capture_time_ms;
|
||||||
|
if (!packet_history_.GetBestFittingPacket(buffer, &length,
|
||||||
|
&capture_time_ms)) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!PrepareAndSendPacket(buffer, length, capture_time_ms, true))
|
||||||
|
return -1;
|
||||||
|
ModuleRTPUtility::RTPHeaderParser rtp_parser(buffer, length);
|
||||||
|
RTPHeader rtp_header;
|
||||||
|
rtp_parser.Parse(rtp_header);
|
||||||
|
bytes_left -= length - rtp_header.headerLength;
|
||||||
|
}
|
||||||
|
return bytes_to_send - bytes_left;
|
||||||
|
}
|
||||||
|
|
||||||
bool RTPSender::SendPaddingAccordingToBitrate(
|
bool RTPSender::SendPaddingAccordingToBitrate(
|
||||||
int8_t payload_type, uint32_t capture_timestamp,
|
int8_t payload_type, uint32_t capture_timestamp,
|
||||||
int64_t capture_time_ms) {
|
int64_t capture_time_ms) {
|
||||||
@ -509,11 +529,11 @@ int RTPSender::SendPadData(int payload_type, uint32_t timestamp,
|
|||||||
|
|
||||||
void RTPSender::SetStorePacketsStatus(const bool enable,
|
void RTPSender::SetStorePacketsStatus(const bool enable,
|
||||||
const uint16_t number_to_store) {
|
const uint16_t number_to_store) {
|
||||||
packet_history_->SetStorePacketsStatus(enable, number_to_store);
|
packet_history_.SetStorePacketsStatus(enable, number_to_store);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool RTPSender::StorePackets() const {
|
bool RTPSender::StorePackets() const {
|
||||||
return packet_history_->StorePackets();
|
return packet_history_.StorePackets();
|
||||||
}
|
}
|
||||||
|
|
||||||
int32_t RTPSender::ReSendPacket(uint16_t packet_id, uint32_t min_resend_time) {
|
int32_t RTPSender::ReSendPacket(uint16_t packet_id, uint32_t min_resend_time) {
|
||||||
@ -521,20 +541,12 @@ int32_t RTPSender::ReSendPacket(uint16_t packet_id, uint32_t min_resend_time) {
|
|||||||
uint8_t data_buffer[IP_PACKET_SIZE];
|
uint8_t data_buffer[IP_PACKET_SIZE];
|
||||||
uint8_t *buffer_to_send_ptr = data_buffer;
|
uint8_t *buffer_to_send_ptr = data_buffer;
|
||||||
int64_t capture_time_ms;
|
int64_t capture_time_ms;
|
||||||
StorageType type;
|
if (!packet_history_.GetPacketAndSetSendTime(packet_id, min_resend_time, true,
|
||||||
if (!packet_history_->GetRTPPacket(packet_id, min_resend_time, data_buffer,
|
data_buffer, &length,
|
||||||
&length, &capture_time_ms, &type)) {
|
&capture_time_ms)) {
|
||||||
// Packet not found.
|
// Packet not found.
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
if (length == 0 || type == kDontRetransmit) {
|
|
||||||
// No bytes copied (packet recently resent, skip resending) or
|
|
||||||
// packet should not be retransmitted.
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Store the time when the packet was last sent or added to pacer.
|
|
||||||
packet_history_->UpdateResendTime(packet_id);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
// Update send statistics prior to pacer.
|
// Update send statistics prior to pacer.
|
||||||
@ -567,7 +579,7 @@ int32_t RTPSender::ReSendPacket(uint16_t packet_id, uint32_t min_resend_time) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
uint8_t data_buffer_rtx[IP_PACKET_SIZE];
|
uint8_t data_buffer_rtx[IP_PACKET_SIZE];
|
||||||
if (rtx_ != kRtxOff) {
|
if ((rtx_ & kRtxRetransmitted) > 0) {
|
||||||
BuildRtxPacket(data_buffer, &length, data_buffer_rtx);
|
BuildRtxPacket(data_buffer, &length, data_buffer_rtx);
|
||||||
buffer_to_send_ptr = data_buffer_rtx;
|
buffer_to_send_ptr = data_buffer_rtx;
|
||||||
}
|
}
|
||||||
@ -714,33 +726,39 @@ void RTPSender::UpdateNACKBitRate(const uint32_t bytes,
|
|||||||
bool RTPSender::TimeToSendPacket(uint16_t sequence_number,
|
bool RTPSender::TimeToSendPacket(uint16_t sequence_number,
|
||||||
int64_t capture_time_ms,
|
int64_t capture_time_ms,
|
||||||
bool retransmission) {
|
bool retransmission) {
|
||||||
StorageType type;
|
|
||||||
uint16_t length = IP_PACKET_SIZE;
|
uint16_t length = IP_PACKET_SIZE;
|
||||||
uint8_t data_buffer[IP_PACKET_SIZE];
|
uint8_t data_buffer[IP_PACKET_SIZE];
|
||||||
int64_t stored_time_ms;
|
int64_t stored_time_ms;
|
||||||
uint8_t *buffer_to_send_ptr = data_buffer;
|
|
||||||
|
|
||||||
if (packet_history_ == NULL) {
|
if (!packet_history_.GetPacketAndSetSendTime(sequence_number,
|
||||||
|
0,
|
||||||
|
retransmission,
|
||||||
|
data_buffer,
|
||||||
|
&length,
|
||||||
|
&stored_time_ms)) {
|
||||||
// Packet cannot be found. Allow sending to continue.
|
// Packet cannot be found. Allow sending to continue.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
if (!packet_history_->GetRTPPacket(sequence_number, 0, data_buffer, &length,
|
return PrepareAndSendPacket(data_buffer, length, capture_time_ms,
|
||||||
&stored_time_ms, &type)) {
|
retransmission && (rtx_ & kRtxRetransmitted) > 0);
|
||||||
// Packet cannot be found. Allow sending to continue.
|
}
|
||||||
return true;
|
|
||||||
}
|
|
||||||
assert(length > 0);
|
|
||||||
|
|
||||||
ModuleRTPUtility::RTPHeaderParser rtp_parser(data_buffer, length);
|
bool RTPSender::PrepareAndSendPacket(uint8_t* buffer,
|
||||||
|
uint16_t length,
|
||||||
|
int64_t capture_time_ms,
|
||||||
|
bool send_over_rtx) {
|
||||||
|
uint8_t *buffer_to_send_ptr = buffer;
|
||||||
|
|
||||||
|
ModuleRTPUtility::RTPHeaderParser rtp_parser(buffer, length);
|
||||||
RTPHeader rtp_header;
|
RTPHeader rtp_header;
|
||||||
rtp_parser.Parse(rtp_header);
|
rtp_parser.Parse(rtp_header);
|
||||||
TRACE_EVENT_INSTANT2("webrtc_rtp", "RTPSender::TimeToSendPacket",
|
TRACE_EVENT_INSTANT2("webrtc_rtp", "RTPSender::TimeToSendPacket",
|
||||||
"timestamp", rtp_header.timestamp,
|
"timestamp", rtp_header.timestamp,
|
||||||
"seqnum", sequence_number);
|
"seqnum", rtp_header.sequenceNumber);
|
||||||
|
|
||||||
uint8_t data_buffer_rtx[IP_PACKET_SIZE];
|
uint8_t data_buffer_rtx[IP_PACKET_SIZE];
|
||||||
if (retransmission && rtx_ != kRtxOff) {
|
if (send_over_rtx) {
|
||||||
BuildRtxPacket(data_buffer, &length, data_buffer_rtx);
|
BuildRtxPacket(buffer, &length, data_buffer_rtx);
|
||||||
buffer_to_send_ptr = data_buffer_rtx;
|
buffer_to_send_ptr = data_buffer_rtx;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -753,9 +771,9 @@ bool RTPSender::TimeToSendPacket(uint16_t sequence_number,
|
|||||||
UpdateAbsoluteSendTime(buffer_to_send_ptr, length, rtp_header, now_ms);
|
UpdateAbsoluteSendTime(buffer_to_send_ptr, length, rtp_header, now_ms);
|
||||||
if (updated_transmission_time_offset || updated_abs_send_time) {
|
if (updated_transmission_time_offset || updated_abs_send_time) {
|
||||||
// Update stored packet in case of receiving a re-transmission request.
|
// Update stored packet in case of receiving a re-transmission request.
|
||||||
packet_history_->ReplaceRTPHeader(buffer_to_send_ptr,
|
packet_history_.ReplaceRTPHeader(buffer_to_send_ptr,
|
||||||
rtp_header.sequenceNumber,
|
rtp_header.sequenceNumber,
|
||||||
rtp_header.headerLength);
|
rtp_header.headerLength);
|
||||||
}
|
}
|
||||||
return SendPacketToNetwork(buffer_to_send_ptr, length);
|
return SendPacketToNetwork(buffer_to_send_ptr, length);
|
||||||
}
|
}
|
||||||
@ -769,7 +787,8 @@ int RTPSender::TimeToSendPadding(int bytes) {
|
|||||||
uint32_t timestamp;
|
uint32_t timestamp;
|
||||||
{
|
{
|
||||||
CriticalSectionScoped cs(send_critsect_);
|
CriticalSectionScoped cs(send_critsect_);
|
||||||
payload_type = (rtx_ == kRtxOff) ? payload_type_ : payload_type_rtx_;
|
payload_type = ((rtx_ & kRtxRedundantPayloads) > 0) ? payload_type_rtx_ :
|
||||||
|
payload_type_;
|
||||||
timestamp = timestamp_;
|
timestamp = timestamp_;
|
||||||
capture_time_ms = capture_time_ms_;
|
capture_time_ms = capture_time_ms_;
|
||||||
if (last_timestamp_time_ms_ > 0) {
|
if (last_timestamp_time_ms_ > 0) {
|
||||||
@ -779,8 +798,14 @@ int RTPSender::TimeToSendPadding(int bytes) {
|
|||||||
(clock_->TimeInMilliseconds() - last_timestamp_time_ms_);
|
(clock_->TimeInMilliseconds() - last_timestamp_time_ms_);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return SendPadData(payload_type, timestamp, capture_time_ms, bytes,
|
int bytes_sent = SendRedundantPayloads(payload_type, bytes);
|
||||||
kDontStore, true, true);
|
bytes -= bytes_sent;
|
||||||
|
if (bytes > 0) {
|
||||||
|
int padding_sent = SendPadData(payload_type, timestamp, capture_time_ms,
|
||||||
|
bytes, kDontStore, true, true);
|
||||||
|
bytes_sent += padding_sent;
|
||||||
|
}
|
||||||
|
return bytes_sent;
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO(pwestin): send in the RTPHeaderParser to avoid parsing it again.
|
// TODO(pwestin): send in the RTPHeaderParser to avoid parsing it again.
|
||||||
@ -807,33 +832,17 @@ int32_t RTPSender::SendToNetwork(
|
|||||||
rtp_header, now_ms);
|
rtp_header, now_ms);
|
||||||
|
|
||||||
// Used for NACK and to spread out the transmission of packets.
|
// Used for NACK and to spread out the transmission of packets.
|
||||||
if (packet_history_->PutRTPPacket(buffer, rtp_header_length + payload_length,
|
if (packet_history_.PutRTPPacket(buffer, rtp_header_length + payload_length,
|
||||||
max_payload_length_, capture_time_ms,
|
max_payload_length_, capture_time_ms,
|
||||||
storage) != 0) {
|
storage) != 0) {
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create and send RTX Packet.
|
|
||||||
// TODO(pwesin): This should be moved to its own code path triggered by pacer.
|
|
||||||
bool rtx_sent = false;
|
|
||||||
if (rtx_ == kRtxAll && storage == kAllowRetransmission) {
|
|
||||||
uint16_t length_rtx = payload_length + rtp_header_length;
|
|
||||||
uint8_t data_buffer_rtx[IP_PACKET_SIZE];
|
|
||||||
BuildRtxPacket(buffer, &length_rtx, data_buffer_rtx);
|
|
||||||
if (!SendPacketToNetwork(data_buffer_rtx, length_rtx)) return -1;
|
|
||||||
rtx_sent = true;
|
|
||||||
}
|
|
||||||
{
|
{
|
||||||
// Update send statistics prior to pacer.
|
// Update send statistics prior to pacer.
|
||||||
CriticalSectionScoped lock(statistics_crit_.get());
|
CriticalSectionScoped lock(statistics_crit_.get());
|
||||||
Bitrate::Update(payload_length + rtp_header_length);
|
Bitrate::Update(payload_length + rtp_header_length);
|
||||||
++packets_sent_;
|
++packets_sent_;
|
||||||
payload_bytes_sent_ += payload_length;
|
payload_bytes_sent_ += payload_length;
|
||||||
if (rtx_sent) {
|
|
||||||
// The RTX packet.
|
|
||||||
++packets_sent_;
|
|
||||||
payload_bytes_sent_ += payload_length;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (paced_sender_ && storage != kDontStore) {
|
if (paced_sender_ && storage != kDontStore) {
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/source/bitrate.h"
|
#include "webrtc/modules/rtp_rtcp/source/bitrate.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
|
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
|
||||||
|
#include "webrtc/modules/rtp_rtcp/source/rtp_packet_history.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_config.h"
|
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_config.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/source/ssrc_database.h"
|
#include "webrtc/modules/rtp_rtcp/source/ssrc_database.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/source/video_codec_information.h"
|
#include "webrtc/modules/rtp_rtcp/source/video_codec_information.h"
|
||||||
@ -30,7 +31,6 @@
|
|||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
class CriticalSectionWrapper;
|
class CriticalSectionWrapper;
|
||||||
class RTPPacketHistory;
|
|
||||||
class RTPSenderAudio;
|
class RTPSenderAudio;
|
||||||
class RTPSenderVideo;
|
class RTPSenderVideo;
|
||||||
|
|
||||||
@ -134,10 +134,6 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
|
|||||||
VideoCodecInformation *codec_info = NULL,
|
VideoCodecInformation *codec_info = NULL,
|
||||||
const RTPVideoTypeHeader * rtp_type_hdr = NULL);
|
const RTPVideoTypeHeader * rtp_type_hdr = NULL);
|
||||||
|
|
||||||
int BuildPaddingPacket(uint8_t* packet, int header_length, int32_t bytes);
|
|
||||||
int SendPadData(int payload_type, uint32_t timestamp, int64_t capture_time_ms,
|
|
||||||
int32_t bytes, StorageType store,
|
|
||||||
bool force_full_size_packets, bool only_pad_after_markerbit);
|
|
||||||
// RTP header extension
|
// RTP header extension
|
||||||
int32_t SetTransmissionTimeOffset(
|
int32_t SetTransmissionTimeOffset(
|
||||||
const int32_t transmission_time_offset);
|
const int32_t transmission_time_offset);
|
||||||
@ -187,9 +183,9 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
|
|||||||
bool ProcessNACKBitRate(const uint32_t now);
|
bool ProcessNACKBitRate(const uint32_t now);
|
||||||
|
|
||||||
// RTX.
|
// RTX.
|
||||||
void SetRTXStatus(RtxMode mode, bool set_ssrc, uint32_t ssrc);
|
void SetRTXStatus(int mode, bool set_ssrc, uint32_t ssrc);
|
||||||
|
|
||||||
void RTXStatus(RtxMode* mode, uint32_t* ssrc, int* payload_type) const;
|
void RTXStatus(int* mode, uint32_t* ssrc, int* payload_type) const;
|
||||||
|
|
||||||
void SetRtxPayloadType(int payloadType);
|
void SetRtxPayloadType(int payloadType);
|
||||||
|
|
||||||
@ -276,9 +272,20 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
|
|||||||
|
|
||||||
void UpdateNACKBitRate(const uint32_t bytes, const uint32_t now);
|
void UpdateNACKBitRate(const uint32_t bytes, const uint32_t now);
|
||||||
|
|
||||||
|
bool PrepareAndSendPacket(uint8_t* buffer,
|
||||||
|
uint16_t length,
|
||||||
|
int64_t capture_time_ms,
|
||||||
|
bool send_over_rtx);
|
||||||
|
|
||||||
|
int SendRedundantPayloads(int payload_type, int bytes);
|
||||||
|
|
||||||
bool SendPaddingAccordingToBitrate(int8_t payload_type,
|
bool SendPaddingAccordingToBitrate(int8_t payload_type,
|
||||||
uint32_t capture_timestamp,
|
uint32_t capture_timestamp,
|
||||||
int64_t capture_time_ms);
|
int64_t capture_time_ms);
|
||||||
|
int BuildPaddingPacket(uint8_t* packet, int header_length, int32_t bytes);
|
||||||
|
int SendPadData(int payload_type, uint32_t timestamp, int64_t capture_time_ms,
|
||||||
|
int32_t bytes, StorageType store,
|
||||||
|
bool force_full_size_packets, bool only_pad_after_markerbit);
|
||||||
|
|
||||||
void BuildRtxPacket(uint8_t* buffer, uint16_t* length,
|
void BuildRtxPacket(uint8_t* buffer, uint16_t* length,
|
||||||
uint8_t* buffer_rtx);
|
uint8_t* buffer_rtx);
|
||||||
@ -312,7 +319,7 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
|
|||||||
int32_t nack_byte_count_[NACK_BYTECOUNT_SIZE];
|
int32_t nack_byte_count_[NACK_BYTECOUNT_SIZE];
|
||||||
Bitrate nack_bitrate_;
|
Bitrate nack_bitrate_;
|
||||||
|
|
||||||
RTPPacketHistory *packet_history_;
|
RTPPacketHistory packet_history_;
|
||||||
|
|
||||||
// Statistics
|
// Statistics
|
||||||
scoped_ptr<CriticalSectionWrapper> statistics_crit_;
|
scoped_ptr<CriticalSectionWrapper> statistics_crit_;
|
||||||
@ -336,7 +343,7 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
|
|||||||
uint8_t num_csrcs_;
|
uint8_t num_csrcs_;
|
||||||
uint32_t csrcs_[kRtpCsrcSize];
|
uint32_t csrcs_[kRtpCsrcSize];
|
||||||
bool include_csrcs_;
|
bool include_csrcs_;
|
||||||
RtxMode rtx_;
|
int rtx_;
|
||||||
uint32_t ssrc_rtx_;
|
uint32_t ssrc_rtx_;
|
||||||
int payload_type_rtx_;
|
int payload_type_rtx_;
|
||||||
};
|
};
|
||||||
|
@ -21,6 +21,7 @@
|
|||||||
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
|
#include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
|
#include "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
|
||||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||||
|
#include "webrtc/test/mock_transport.h"
|
||||||
#include "webrtc/typedefs.h"
|
#include "webrtc/typedefs.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
@ -113,6 +114,23 @@ class RtpSenderTest : public ::testing::Test {
|
|||||||
EXPECT_EQ(0, rtp_header.numCSRCs);
|
EXPECT_EQ(0, rtp_header.numCSRCs);
|
||||||
EXPECT_EQ(0, rtp_header.paddingLength);
|
EXPECT_EQ(0, rtp_header.paddingLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void SendPacket(int64_t capture_time_ms, int payload_length) {
|
||||||
|
uint32_t timestamp = capture_time_ms * 90;
|
||||||
|
int32_t rtp_length = rtp_sender_->BuildRTPheader(packet_,
|
||||||
|
kPayload,
|
||||||
|
kMarkerBit,
|
||||||
|
timestamp,
|
||||||
|
capture_time_ms);
|
||||||
|
|
||||||
|
// Packet should be stored in a send bucket.
|
||||||
|
EXPECT_EQ(0, rtp_sender_->SendToNetwork(packet_,
|
||||||
|
payload_length,
|
||||||
|
rtp_length,
|
||||||
|
capture_time_ms,
|
||||||
|
kAllowRetransmission,
|
||||||
|
PacedSender::kNormalPriority));
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(RtpSenderTest, RegisterRtpTransmissionTimeOffsetHeaderExtension) {
|
TEST_F(RtpSenderTest, RegisterRtpTransmissionTimeOffsetHeaderExtension) {
|
||||||
@ -582,6 +600,69 @@ TEST_F(RtpSenderTest, SendPadding) {
|
|||||||
EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
|
EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(RtpSenderTest, SendRedundantPayloads) {
|
||||||
|
MockTransport transport;
|
||||||
|
rtp_sender_.reset(new RTPSender(0, false, &fake_clock_, &transport, NULL,
|
||||||
|
&mock_paced_sender_));
|
||||||
|
rtp_sender_->SetSequenceNumber(kSeqNum);
|
||||||
|
// Make all packets go through the pacer.
|
||||||
|
EXPECT_CALL(mock_paced_sender_,
|
||||||
|
SendPacket(PacedSender::kNormalPriority, _, _, _, _, _)).
|
||||||
|
WillRepeatedly(testing::Return(false));
|
||||||
|
|
||||||
|
uint16_t seq_num = kSeqNum;
|
||||||
|
rtp_sender_->SetStorePacketsStatus(true, 10);
|
||||||
|
int rtp_header_len = 12;
|
||||||
|
EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
|
||||||
|
kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
|
||||||
|
rtp_header_len += 4; // 4 bytes extension.
|
||||||
|
rtp_header_len += 4; // 4 extra bytes common to all extension headers.
|
||||||
|
|
||||||
|
rtp_sender_->SetRTXStatus(kRtxRetransmitted | kRtxRedundantPayloads, true,
|
||||||
|
1234);
|
||||||
|
|
||||||
|
// Create and set up parser.
|
||||||
|
scoped_ptr<webrtc::RtpHeaderParser> rtp_parser(
|
||||||
|
webrtc::RtpHeaderParser::Create());
|
||||||
|
ASSERT_TRUE(rtp_parser.get() != NULL);
|
||||||
|
rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionTransmissionTimeOffset,
|
||||||
|
kTransmissionTimeOffsetExtensionId);
|
||||||
|
rtp_parser->RegisterRtpHeaderExtension(kRtpExtensionAbsoluteSendTime,
|
||||||
|
kAbsoluteSendTimeExtensionId);
|
||||||
|
rtp_sender_->SetTargetSendBitrate(300000);
|
||||||
|
const size_t kNumPayloadSizes = 10;
|
||||||
|
const int kPayloadSizes[kNumPayloadSizes] = {500, 550, 600, 650, 700, 750,
|
||||||
|
800, 850, 900, 950};
|
||||||
|
// Send 10 packets of increasing size.
|
||||||
|
for (size_t i = 0; i < kNumPayloadSizes; ++i) {
|
||||||
|
int64_t capture_time_ms = fake_clock_.TimeInMilliseconds();
|
||||||
|
EXPECT_CALL(transport, SendPacket(_, _, _))
|
||||||
|
.WillOnce(testing::ReturnArg<2>());
|
||||||
|
SendPacket(capture_time_ms, kPayloadSizes[i]);
|
||||||
|
rtp_sender_->TimeToSendPacket(seq_num++, capture_time_ms, false);
|
||||||
|
fake_clock_.AdvanceTimeMilliseconds(33);
|
||||||
|
}
|
||||||
|
const int kPaddingPayloadSize = 224;
|
||||||
|
// The amount of padding to send it too small to send a payload packet.
|
||||||
|
EXPECT_CALL(transport, SendPacket(_, _, kPaddingPayloadSize + rtp_header_len))
|
||||||
|
.WillOnce(testing::ReturnArg<2>());
|
||||||
|
EXPECT_EQ(kPaddingPayloadSize, rtp_sender_->TimeToSendPadding(49));
|
||||||
|
|
||||||
|
const int kRtxHeaderSize = 2;
|
||||||
|
EXPECT_CALL(transport, SendPacket(_, _, kPayloadSizes[0] +
|
||||||
|
rtp_header_len + kRtxHeaderSize))
|
||||||
|
.WillOnce(testing::ReturnArg<2>());
|
||||||
|
EXPECT_EQ(kPayloadSizes[0], rtp_sender_->TimeToSendPadding(500));
|
||||||
|
|
||||||
|
EXPECT_CALL(transport, SendPacket(_, _, kPayloadSizes[kNumPayloadSizes - 1] +
|
||||||
|
rtp_header_len + kRtxHeaderSize))
|
||||||
|
.WillOnce(testing::ReturnArg<2>());
|
||||||
|
EXPECT_CALL(transport, SendPacket(_, _, kPaddingPayloadSize + rtp_header_len))
|
||||||
|
.WillOnce(testing::ReturnArg<2>());
|
||||||
|
EXPECT_EQ(kPayloadSizes[kNumPayloadSizes - 1] + kPaddingPayloadSize,
|
||||||
|
rtp_sender_->TimeToSendPadding(999));
|
||||||
|
}
|
||||||
|
|
||||||
TEST_F(RtpSenderTest, SendGenericVideo) {
|
TEST_F(RtpSenderTest, SendGenericVideo) {
|
||||||
char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC";
|
char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC";
|
||||||
const uint8_t payload_type = 127;
|
const uint8_t payload_type = 127;
|
||||||
|
@ -116,7 +116,7 @@ TEST_F(RtpRtcpAPITest, RTCP) {
|
|||||||
|
|
||||||
TEST_F(RtpRtcpAPITest, RtxSender) {
|
TEST_F(RtpRtcpAPITest, RtxSender) {
|
||||||
unsigned int ssrc = 0;
|
unsigned int ssrc = 0;
|
||||||
RtxMode rtx_mode = kRtxOff;
|
int rtx_mode = kRtxOff;
|
||||||
const int kRtxPayloadType = 119;
|
const int kRtxPayloadType = 119;
|
||||||
int payload_type = -1;
|
int payload_type = -1;
|
||||||
EXPECT_EQ(0, module->SetRTXSendStatus(kRtxRetransmitted, true, 1));
|
EXPECT_EQ(0, module->SetRTXSendStatus(kRtxRetransmitted, true, 1));
|
||||||
|
27
webrtc/test/mock_transport.h
Normal file
27
webrtc/test/mock_transport.h
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||||
|
*
|
||||||
|
* Use of this source code is governed by a BSD-style license
|
||||||
|
* that can be found in the LICENSE file in the root of the source
|
||||||
|
* tree. An additional intellectual property rights grant can be found
|
||||||
|
* in the file PATENTS. All contributing project authors may
|
||||||
|
* be found in the AUTHORS file in the root of the source tree.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WEBRTC_TEST_MOCK_TRANSPORT_H_
|
||||||
|
#define WEBRTC_TEST_MOCK_TRANSPORT_H_
|
||||||
|
|
||||||
|
#include "testing/gmock/include/gmock/gmock.h"
|
||||||
|
#include "webrtc/transport.h"
|
||||||
|
|
||||||
|
namespace webrtc {
|
||||||
|
|
||||||
|
class MockTransport : public webrtc::Transport {
|
||||||
|
public:
|
||||||
|
MOCK_METHOD3(SendPacket,
|
||||||
|
int(int channel, const void* data, int len));
|
||||||
|
MOCK_METHOD3(SendRTCPPacket,
|
||||||
|
int(int channel, const void* data, int len));
|
||||||
|
};
|
||||||
|
} // namespace webrtc
|
||||||
|
#endif // WEBRTC_TEST_MOCK_TRANSPORT_H_
|
@ -34,6 +34,7 @@
|
|||||||
'mac/run_tests.mm',
|
'mac/run_tests.mm',
|
||||||
'mac/video_renderer_mac.h',
|
'mac/video_renderer_mac.h',
|
||||||
'mac/video_renderer_mac.mm',
|
'mac/video_renderer_mac.mm',
|
||||||
|
'mock_transport.h',
|
||||||
'null_platform_renderer.cc',
|
'null_platform_renderer.cc',
|
||||||
'null_transport.cc',
|
'null_transport.cc',
|
||||||
'null_transport.h',
|
'null_transport.h',
|
||||||
|
@ -10,13 +10,17 @@
|
|||||||
#include <assert.h>
|
#include <assert.h>
|
||||||
|
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "testing/gtest/include/gtest/gtest.h"
|
#include "testing/gtest/include/gtest/gtest.h"
|
||||||
|
|
||||||
#include "webrtc/call.h"
|
#include "webrtc/call.h"
|
||||||
|
#include "webrtc/common.h"
|
||||||
|
#include "webrtc/experiments.h"
|
||||||
#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
|
#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h"
|
#include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
|
||||||
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
|
#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h"
|
||||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||||
@ -26,18 +30,22 @@
|
|||||||
#include "webrtc/test/fake_decoder.h"
|
#include "webrtc/test/fake_decoder.h"
|
||||||
#include "webrtc/test/fake_encoder.h"
|
#include "webrtc/test/fake_encoder.h"
|
||||||
#include "webrtc/test/frame_generator_capturer.h"
|
#include "webrtc/test/frame_generator_capturer.h"
|
||||||
|
#include "webrtc/test/testsupport/perf_test.h"
|
||||||
#include "webrtc/video/transport_adapter.h"
|
#include "webrtc/video/transport_adapter.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
static const int kTOffsetExtensionId = 7;
|
static const int kTOffsetExtensionId = 7;
|
||||||
|
static const int kMaxPacketSize = 1500;
|
||||||
}
|
}
|
||||||
|
|
||||||
class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
|
class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
|
||||||
public:
|
public:
|
||||||
typedef std::map<uint32_t, int> BytesSentMap;
|
typedef std::map<uint32_t, int> BytesSentMap;
|
||||||
|
typedef std::map<uint32_t, uint32_t> SsrcMap;
|
||||||
StreamObserver(int num_expected_ssrcs,
|
StreamObserver(int num_expected_ssrcs,
|
||||||
|
const SsrcMap& rtx_media_ssrcs,
|
||||||
newapi::Transport* feedback_transport,
|
newapi::Transport* feedback_transport,
|
||||||
Clock* clock)
|
Clock* clock)
|
||||||
: critical_section_(CriticalSectionWrapper::CreateCriticalSection()),
|
: critical_section_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||||
@ -45,8 +53,17 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
|
|||||||
rtp_parser_(RtpHeaderParser::Create()),
|
rtp_parser_(RtpHeaderParser::Create()),
|
||||||
feedback_transport_(feedback_transport),
|
feedback_transport_(feedback_transport),
|
||||||
receive_stats_(ReceiveStatistics::Create(clock)),
|
receive_stats_(ReceiveStatistics::Create(clock)),
|
||||||
|
payload_registry_(new RTPPayloadRegistry(
|
||||||
|
-1, RTPPayloadStrategy::CreateStrategy(false))),
|
||||||
clock_(clock),
|
clock_(clock),
|
||||||
num_expected_ssrcs_(num_expected_ssrcs) {
|
num_expected_ssrcs_(num_expected_ssrcs),
|
||||||
|
rtx_media_ssrcs_(rtx_media_ssrcs),
|
||||||
|
total_sent_(0),
|
||||||
|
padding_sent_(0),
|
||||||
|
rtx_media_sent_(0),
|
||||||
|
total_packets_sent_(0),
|
||||||
|
padding_packets_sent_(0),
|
||||||
|
rtx_media_packets_sent_(0) {
|
||||||
// Ideally we would only have to instantiate an RtcpSender, an
|
// Ideally we would only have to instantiate an RtcpSender, an
|
||||||
// RtpHeaderParser and a RemoteBitrateEstimator here, but due to the current
|
// RtpHeaderParser and a RemoteBitrateEstimator here, but due to the current
|
||||||
// state of the RTP module we need a full module and receive statistics to
|
// state of the RTP module we need a full module and receive statistics to
|
||||||
@ -66,8 +83,25 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
|
|||||||
virtual void OnReceiveBitrateChanged(const std::vector<unsigned int>& ssrcs,
|
virtual void OnReceiveBitrateChanged(const std::vector<unsigned int>& ssrcs,
|
||||||
unsigned int bitrate) {
|
unsigned int bitrate) {
|
||||||
CriticalSectionScoped lock(critical_section_.get());
|
CriticalSectionScoped lock(critical_section_.get());
|
||||||
if (ssrcs.size() == num_expected_ssrcs_ && bitrate >= kExpectedBitrateBps)
|
if (ssrcs.size() == num_expected_ssrcs_ && bitrate >= kExpectedBitrateBps) {
|
||||||
all_ssrcs_sent_->Set();
|
if (rtx_media_ssrcs_.empty() || rtx_media_sent_ > 0) {
|
||||||
|
const ::testing::TestInfo* const test_info =
|
||||||
|
::testing::UnitTest::GetInstance()->current_test_info();
|
||||||
|
webrtc::test::PrintResult("total-sent", "", test_info->name(),
|
||||||
|
total_sent_, "bytes", false);
|
||||||
|
webrtc::test::PrintResult("padding-sent", "", test_info->name(),
|
||||||
|
padding_sent_, "bytes", false);
|
||||||
|
webrtc::test::PrintResult("rtx-media-sent", "", test_info->name(),
|
||||||
|
rtx_media_sent_, "bytes", false);
|
||||||
|
webrtc::test::PrintResult("total-packets-sent", "", test_info->name(),
|
||||||
|
total_packets_sent_, "packets", false);
|
||||||
|
webrtc::test::PrintResult("padding-packets-sent", "", test_info->name(),
|
||||||
|
padding_packets_sent_, "packets", false);
|
||||||
|
webrtc::test::PrintResult("rtx-packets-sent", "", test_info->name(),
|
||||||
|
rtx_media_packets_sent_, "packets", false);
|
||||||
|
all_ssrcs_sent_->Set();
|
||||||
|
}
|
||||||
|
}
|
||||||
rtp_rtcp_->SetREMBData(
|
rtp_rtcp_->SetREMBData(
|
||||||
bitrate, static_cast<uint8_t>(ssrcs.size()), &ssrcs[0]);
|
bitrate, static_cast<uint8_t>(ssrcs.size()), &ssrcs[0]);
|
||||||
rtp_rtcp_->Process();
|
rtp_rtcp_->Process();
|
||||||
@ -78,12 +112,34 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
|
|||||||
RTPHeader header;
|
RTPHeader header;
|
||||||
EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length), &header));
|
EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length), &header));
|
||||||
receive_stats_->IncomingPacket(header, length, false);
|
receive_stats_->IncomingPacket(header, length, false);
|
||||||
rtp_rtcp_->SetRemoteSSRC(header.ssrc);
|
payload_registry_->SetIncomingPayloadType(header);
|
||||||
remote_bitrate_estimator_->IncomingPacket(
|
remote_bitrate_estimator_->IncomingPacket(
|
||||||
clock_->TimeInMilliseconds(), static_cast<int>(length - 12), header);
|
clock_->TimeInMilliseconds(), static_cast<int>(length - 12), header);
|
||||||
if (remote_bitrate_estimator_->TimeUntilNextProcess() <= 0) {
|
if (remote_bitrate_estimator_->TimeUntilNextProcess() <= 0) {
|
||||||
remote_bitrate_estimator_->Process();
|
remote_bitrate_estimator_->Process();
|
||||||
}
|
}
|
||||||
|
total_sent_ += length;
|
||||||
|
padding_sent_ += header.paddingLength;
|
||||||
|
++total_packets_sent_;
|
||||||
|
if (header.paddingLength > 0)
|
||||||
|
++padding_packets_sent_;
|
||||||
|
if (rtx_media_ssrcs_.find(header.ssrc) != rtx_media_ssrcs_.end()) {
|
||||||
|
rtx_media_sent_ += length - header.headerLength - header.paddingLength;
|
||||||
|
if (header.paddingLength == 0)
|
||||||
|
++rtx_media_packets_sent_;
|
||||||
|
uint8_t restored_packet[kMaxPacketSize];
|
||||||
|
uint8_t* restored_packet_ptr = restored_packet;
|
||||||
|
int restored_length = static_cast<int>(length);
|
||||||
|
payload_registry_->RestoreOriginalPacket(
|
||||||
|
&restored_packet_ptr, packet, &restored_length,
|
||||||
|
rtx_media_ssrcs_[header.ssrc],
|
||||||
|
header);
|
||||||
|
length = restored_length;
|
||||||
|
EXPECT_TRUE(rtp_parser_->Parse(restored_packet, static_cast<int>(length),
|
||||||
|
&header));
|
||||||
|
} else {
|
||||||
|
rtp_rtcp_->SetRemoteSSRC(header.ssrc);
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -102,9 +158,17 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
|
|||||||
scoped_ptr<RtpRtcp> rtp_rtcp_;
|
scoped_ptr<RtpRtcp> rtp_rtcp_;
|
||||||
internal::TransportAdapter feedback_transport_;
|
internal::TransportAdapter feedback_transport_;
|
||||||
scoped_ptr<ReceiveStatistics> receive_stats_;
|
scoped_ptr<ReceiveStatistics> receive_stats_;
|
||||||
|
scoped_ptr<RTPPayloadRegistry> payload_registry_;
|
||||||
scoped_ptr<RemoteBitrateEstimator> remote_bitrate_estimator_;
|
scoped_ptr<RemoteBitrateEstimator> remote_bitrate_estimator_;
|
||||||
Clock* clock_;
|
Clock* clock_;
|
||||||
const size_t num_expected_ssrcs_;
|
const size_t num_expected_ssrcs_;
|
||||||
|
SsrcMap rtx_media_ssrcs_;
|
||||||
|
size_t total_sent_;
|
||||||
|
size_t padding_sent_;
|
||||||
|
size_t rtx_media_sent_;
|
||||||
|
int total_packets_sent_;
|
||||||
|
int padding_packets_sent_;
|
||||||
|
int rtx_media_packets_sent_;
|
||||||
};
|
};
|
||||||
|
|
||||||
class RampUpTest : public ::testing::TestWithParam<bool> {
|
class RampUpTest : public ::testing::TestWithParam<bool> {
|
||||||
@ -112,52 +176,82 @@ class RampUpTest : public ::testing::TestWithParam<bool> {
|
|||||||
virtual void SetUp() { reserved_ssrcs_.clear(); }
|
virtual void SetUp() { reserved_ssrcs_.clear(); }
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
void RunRampUpTest(bool pacing, bool rtx) {
|
||||||
|
const size_t kNumberOfStreams = 3;
|
||||||
|
std::vector<uint32_t> ssrcs;
|
||||||
|
for (size_t i = 0; i < kNumberOfStreams; ++i)
|
||||||
|
ssrcs.push_back(static_cast<uint32_t>(i + 1));
|
||||||
|
uint32_t kRtxSsrcs[kNumberOfStreams] = {111, 112, 113};
|
||||||
|
StreamObserver::SsrcMap rtx_ssrc_map;
|
||||||
|
if (rtx) {
|
||||||
|
for (size_t i = 0; i < ssrcs.size(); ++i)
|
||||||
|
rtx_ssrc_map[kRtxSsrcs[i]] = ssrcs[i];
|
||||||
|
}
|
||||||
|
test::DirectTransport receiver_transport;
|
||||||
|
int num_expected_ssrcs = kNumberOfStreams + (rtx ? 1 : 0);
|
||||||
|
StreamObserver stream_observer(
|
||||||
|
num_expected_ssrcs, rtx_ssrc_map, &receiver_transport,
|
||||||
|
Clock::GetRealTimeClock());
|
||||||
|
|
||||||
|
Call::Config call_config(&stream_observer);
|
||||||
|
webrtc::Config webrtc_config;
|
||||||
|
call_config.webrtc_config = &webrtc_config;
|
||||||
|
webrtc_config.Set<PaddingStrategy>(new PaddingStrategy(rtx));
|
||||||
|
scoped_ptr<Call> call(Call::Create(call_config));
|
||||||
|
VideoSendStream::Config send_config = call->GetDefaultSendConfig();
|
||||||
|
|
||||||
|
receiver_transport.SetReceiver(call->Receiver());
|
||||||
|
|
||||||
|
test::FakeEncoder encoder(Clock::GetRealTimeClock());
|
||||||
|
send_config.encoder = &encoder;
|
||||||
|
send_config.internal_source = false;
|
||||||
|
test::FakeEncoder::SetCodecSettings(&send_config.codec, kNumberOfStreams);
|
||||||
|
send_config.codec.plType = 125;
|
||||||
|
send_config.pacing = pacing;
|
||||||
|
send_config.rtp.nack.rtp_history_ms = 1000;
|
||||||
|
send_config.rtp.ssrcs.insert(send_config.rtp.ssrcs.begin(), ssrcs.begin(),
|
||||||
|
ssrcs.end());
|
||||||
|
if (rtx) {
|
||||||
|
send_config.rtp.rtx.rtx_payload_type = 96;
|
||||||
|
send_config.rtp.rtx.ssrcs.insert(send_config.rtp.rtx.ssrcs.begin(),
|
||||||
|
kRtxSsrcs,
|
||||||
|
kRtxSsrcs + kNumberOfStreams);
|
||||||
|
}
|
||||||
|
send_config.rtp.extensions.push_back(
|
||||||
|
RtpExtension(RtpExtension::kTOffset, kTOffsetExtensionId));
|
||||||
|
|
||||||
|
VideoSendStream* send_stream = call->CreateVideoSendStream(send_config);
|
||||||
|
|
||||||
|
scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
|
||||||
|
test::FrameGeneratorCapturer::Create(send_stream->Input(),
|
||||||
|
send_config.codec.width,
|
||||||
|
send_config.codec.height,
|
||||||
|
30,
|
||||||
|
Clock::GetRealTimeClock()));
|
||||||
|
|
||||||
|
send_stream->StartSending();
|
||||||
|
frame_generator_capturer->Start();
|
||||||
|
|
||||||
|
EXPECT_EQ(kEventSignaled, stream_observer.Wait());
|
||||||
|
|
||||||
|
frame_generator_capturer->Stop();
|
||||||
|
send_stream->StopSending();
|
||||||
|
|
||||||
|
call->DestroyVideoSendStream(send_stream);
|
||||||
|
}
|
||||||
std::map<uint32_t, bool> reserved_ssrcs_;
|
std::map<uint32_t, bool> reserved_ssrcs_;
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_P(RampUpTest, RampUpWithPadding) {
|
TEST_F(RampUpTest, WithoutPacing) {
|
||||||
static const size_t kNumStreams = 3;
|
RunRampUpTest(false, false);
|
||||||
test::DirectTransport receiver_transport;
|
|
||||||
StreamObserver stream_observer(
|
|
||||||
kNumStreams, &receiver_transport, Clock::GetRealTimeClock());
|
|
||||||
Call::Config call_config(&stream_observer);
|
|
||||||
scoped_ptr<Call> call(Call::Create(call_config));
|
|
||||||
VideoSendStream::Config send_config = call->GetDefaultSendConfig();
|
|
||||||
|
|
||||||
receiver_transport.SetReceiver(call->Receiver());
|
|
||||||
|
|
||||||
test::FakeEncoder encoder(Clock::GetRealTimeClock());
|
|
||||||
send_config.encoder = &encoder;
|
|
||||||
send_config.internal_source = false;
|
|
||||||
test::FakeEncoder::SetCodecSettings(&send_config.codec, kNumStreams);
|
|
||||||
send_config.codec.plType = 125;
|
|
||||||
send_config.pacing = GetParam();
|
|
||||||
send_config.rtp.extensions.push_back(
|
|
||||||
RtpExtension(RtpExtension::kTOffset, kTOffsetExtensionId));
|
|
||||||
|
|
||||||
for (size_t i = 0; i < kNumStreams; ++i)
|
|
||||||
send_config.rtp.ssrcs.push_back(static_cast<uint32_t>(i + 1));
|
|
||||||
|
|
||||||
VideoSendStream* send_stream = call->CreateVideoSendStream(send_config);
|
|
||||||
|
|
||||||
scoped_ptr<test::FrameGeneratorCapturer> frame_generator_capturer(
|
|
||||||
test::FrameGeneratorCapturer::Create(send_stream->Input(),
|
|
||||||
send_config.codec.width,
|
|
||||||
send_config.codec.height,
|
|
||||||
30,
|
|
||||||
Clock::GetRealTimeClock()));
|
|
||||||
|
|
||||||
send_stream->StartSending();
|
|
||||||
frame_generator_capturer->Start();
|
|
||||||
|
|
||||||
EXPECT_EQ(kEventSignaled, stream_observer.Wait());
|
|
||||||
|
|
||||||
frame_generator_capturer->Stop();
|
|
||||||
send_stream->StopSending();
|
|
||||||
|
|
||||||
call->DestroyVideoSendStream(send_stream);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
INSTANTIATE_TEST_CASE_P(RampUpTest, RampUpTest, ::testing::Bool());
|
TEST_F(RampUpTest, WithPacing) {
|
||||||
|
RunRampUpTest(true, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(RampUpTest, WithPacingAndRtx) {
|
||||||
|
RunRampUpTest(true, true);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -13,7 +13,9 @@
|
|||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "webrtc/common.h"
|
||||||
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
|
||||||
|
#include "webrtc/experiments.h"
|
||||||
#include "webrtc/modules/pacing/include/paced_sender.h"
|
#include "webrtc/modules/pacing/include/paced_sender.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h"
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h"
|
||||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
|
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h"
|
||||||
@ -102,7 +104,8 @@ ViEChannel::ViEChannel(int32_t channel_id,
|
|||||||
sender_(sender),
|
sender_(sender),
|
||||||
nack_history_size_sender_(kSendSidePacketHistorySize),
|
nack_history_size_sender_(kSendSidePacketHistorySize),
|
||||||
max_nack_reordering_threshold_(kMaxPacketAgeToNack),
|
max_nack_reordering_threshold_(kMaxPacketAgeToNack),
|
||||||
pre_render_callback_(NULL) {
|
pre_render_callback_(NULL),
|
||||||
|
config_(config) {
|
||||||
WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id, channel_id),
|
WEBRTC_TRACE(kTraceMemory, kTraceVideo, ViEId(engine_id, channel_id),
|
||||||
"ViEChannel::ViEChannel(channel_id: %d, engine_id: %d)",
|
"ViEChannel::ViEChannel(channel_id: %d, engine_id: %d)",
|
||||||
channel_id, engine_id);
|
channel_id, engine_id);
|
||||||
@ -965,9 +968,12 @@ int32_t ViEChannel::SetSSRC(const uint32_t SSRC,
|
|||||||
ViEId(engine_id_, channel_id_),
|
ViEId(engine_id_, channel_id_),
|
||||||
"%s(usage:%d, SSRC: 0x%x, idx:%u)",
|
"%s(usage:%d, SSRC: 0x%x, idx:%u)",
|
||||||
__FUNCTION__, usage, SSRC, simulcast_idx);
|
__FUNCTION__, usage, SSRC, simulcast_idx);
|
||||||
|
int rtx_settings = kRtxRetransmitted;
|
||||||
|
if (config_.Get<PaddingStrategy>().redundant_payloads)
|
||||||
|
rtx_settings |= kRtxRedundantPayloads;
|
||||||
if (simulcast_idx == 0) {
|
if (simulcast_idx == 0) {
|
||||||
if (usage == kViEStreamTypeRtx) {
|
if (usage == kViEStreamTypeRtx) {
|
||||||
return rtp_rtcp_->SetRTXSendStatus(kRtxRetransmitted, true, SSRC);
|
return rtp_rtcp_->SetRTXSendStatus(rtx_settings, true, SSRC);
|
||||||
}
|
}
|
||||||
return rtp_rtcp_->SetSSRC(SSRC);
|
return rtp_rtcp_->SetSSRC(SSRC);
|
||||||
}
|
}
|
||||||
@ -983,7 +989,7 @@ int32_t ViEChannel::SetSSRC(const uint32_t SSRC,
|
|||||||
}
|
}
|
||||||
RtpRtcp* rtp_rtcp_module = *it;
|
RtpRtcp* rtp_rtcp_module = *it;
|
||||||
if (usage == kViEStreamTypeRtx) {
|
if (usage == kViEStreamTypeRtx) {
|
||||||
return rtp_rtcp_module->SetRTXSendStatus(kRtxRetransmitted, true, SSRC);
|
return rtp_rtcp_module->SetRTXSendStatus(rtx_settings, true, SSRC);
|
||||||
}
|
}
|
||||||
return rtp_rtcp_module->SetSSRC(SSRC);
|
return rtp_rtcp_module->SetSSRC(SSRC);
|
||||||
}
|
}
|
||||||
|
@ -403,6 +403,7 @@ class ViEChannel
|
|||||||
int nack_history_size_sender_;
|
int nack_history_size_sender_;
|
||||||
int max_nack_reordering_threshold_;
|
int max_nack_reordering_threshold_;
|
||||||
I420FrameCallback* pre_render_callback_;
|
I420FrameCallback* pre_render_callback_;
|
||||||
|
const Config& config_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
@ -63,6 +63,7 @@
|
|||||||
'call.h',
|
'call.h',
|
||||||
'config.cc',
|
'config.cc',
|
||||||
'config.h',
|
'config.h',
|
||||||
|
'experiments.h',
|
||||||
'frame_callback.h',
|
'frame_callback.h',
|
||||||
'transport.h',
|
'transport.h',
|
||||||
'video_receive_stream.h',
|
'video_receive_stream.h',
|
||||||
|
Loading…
x
Reference in New Issue
Block a user