Adds support for combining RTX and FEC/RED.
This is accomplished by breaking out RTX and FEC/RED functionality from the RTP module and keeping track of the base payload type, that is the payload type received when not receiving RTX. Enables retransmissions over RTX by default in the loopback test. BUG=1811 TESTS=voe/vie_auto_test --automated and trybots. R=mflodman@webrtc.org, pbos@webrtc.org, xians@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2154004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4692 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
5500d93fe5
commit
7bb8f02274
@ -98,9 +98,7 @@ enum RtpVideoCodecTypes
|
||||
{
|
||||
kRtpVideoNone,
|
||||
kRtpVideoGeneric,
|
||||
kRtpVideoVp8,
|
||||
kRtpVideoFec,
|
||||
kRtpVideoI420
|
||||
kRtpVideoVp8
|
||||
};
|
||||
struct RTPVideoHeader
|
||||
{
|
||||
|
||||
@ -166,11 +166,11 @@
|
||||
'remote_bitrate_estimator/bitrate_estimator_unittest.cc',
|
||||
'remote_bitrate_estimator/rtp_to_ntp_unittest.cc',
|
||||
'rtp_rtcp/source/mock/mock_rtp_payload_strategy.h',
|
||||
'rtp_rtcp/source/fec_receiver_unittest.cc',
|
||||
'rtp_rtcp/source/fec_test_helper.cc',
|
||||
'rtp_rtcp/source/fec_test_helper.h',
|
||||
'rtp_rtcp/source/nack_rtx_unittest.cc',
|
||||
'rtp_rtcp/source/producer_fec_unittest.cc',
|
||||
'rtp_rtcp/source/receiver_fec_unittest.cc',
|
||||
'rtp_rtcp/source/receive_statistics_unittest.cc',
|
||||
'rtp_rtcp/source/rtcp_format_remb_unittest.cc',
|
||||
'rtp_rtcp/source/rtcp_sender_unittest.cc',
|
||||
|
||||
33
webrtc/modules/rtp_rtcp/interface/fec_receiver.h
Normal file
33
webrtc/modules/rtp_rtcp/interface/fec_receiver.h
Normal file
@ -0,0 +1,33 @@
|
||||
/*
|
||||
* Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_RTP_RTCP_INTERFACE_FEC_RECEIVER_H_
|
||||
#define WEBRTC_MODULES_RTP_RTCP_INTERFACE_FEC_RECEIVER_H_
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class FecReceiver {
|
||||
public:
|
||||
static FecReceiver* Create(int32_t id, RtpData* callback);
|
||||
|
||||
virtual ~FecReceiver() {}
|
||||
|
||||
virtual int32_t AddReceivedRedPacket(const RTPHeader& rtp_header,
|
||||
const uint8_t* incoming_rtp_packet,
|
||||
int packet_length,
|
||||
uint8_t ulpfec_payload_type) = 0;
|
||||
|
||||
virtual int32_t ProcessReceivedFec() = 0;
|
||||
};
|
||||
} // namespace webrtc
|
||||
#endif // WEBRTC_MODULES_RTP_RTCP_INTERFACE_FEC_RECEIVER_H_
|
||||
@ -44,8 +44,17 @@ class StreamStatistician {
|
||||
virtual void GetDataCounters(uint32_t* bytes_received,
|
||||
uint32_t* packets_received) const = 0;
|
||||
virtual uint32_t BitrateReceived() const = 0;
|
||||
|
||||
// Resets all statistics.
|
||||
virtual void ResetStatistics() = 0;
|
||||
|
||||
// Returns true if the packet with RTP header |header| is likely to be a
|
||||
// retransmitted packet, false otherwise.
|
||||
virtual bool IsRetransmitOfOldPacket(const RTPHeader& header,
|
||||
int min_rtt) const = 0;
|
||||
|
||||
// Returns true if |sequence_number| is received in order, false otherwise.
|
||||
virtual bool IsPacketInOrder(uint16_t sequence_number) const = 0;
|
||||
};
|
||||
|
||||
typedef std::map<uint32_t, StreamStatistician*> StatisticianMap;
|
||||
@ -58,7 +67,7 @@ class ReceiveStatistics : public Module {
|
||||
|
||||
// Updates the receive statistics with this packet.
|
||||
virtual void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
|
||||
bool retransmitted, bool in_order) = 0;
|
||||
bool retransmitted) = 0;
|
||||
|
||||
// Returns a map of all statisticians which have seen an incoming packet
|
||||
// during the last two seconds.
|
||||
@ -66,16 +75,20 @@ class ReceiveStatistics : public Module {
|
||||
|
||||
// Returns a pointer to the statistician of an ssrc.
|
||||
virtual StreamStatistician* GetStatistician(uint32_t ssrc) const = 0;
|
||||
|
||||
// Sets the max reordering threshold in number of packets.
|
||||
virtual void SetMaxReorderingThreshold(int max_reordering_threshold) = 0;
|
||||
};
|
||||
|
||||
class NullReceiveStatistics : public ReceiveStatistics {
|
||||
public:
|
||||
virtual void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
|
||||
bool retransmitted, bool in_order) OVERRIDE;
|
||||
bool retransmitted) OVERRIDE;
|
||||
virtual StatisticianMap GetActiveStatisticians() const OVERRIDE;
|
||||
virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE;
|
||||
virtual int32_t TimeUntilNextProcess() OVERRIDE;
|
||||
virtual int32_t Process() OVERRIDE;
|
||||
virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -76,6 +76,26 @@ class RTPPayloadRegistry {
|
||||
const uint32_t rate,
|
||||
int8_t* payload_type) const;
|
||||
|
||||
void SetRtxStatus(bool enable, uint32_t ssrc);
|
||||
|
||||
bool RtxEnabled() const;
|
||||
|
||||
void SetRtxPayloadType(int payload_type);
|
||||
|
||||
bool IsRtx(const RTPHeader& header) const;
|
||||
|
||||
bool RestoreOriginalPacket(uint8_t** restored_packet,
|
||||
const uint8_t* packet,
|
||||
int* packet_length,
|
||||
uint32_t original_ssrc,
|
||||
const RTPHeader& header) const;
|
||||
|
||||
bool IsRed(const RTPHeader& header) const;
|
||||
|
||||
// Returns true if the media of this RTP packet is encapsulated within an
|
||||
// extra header, such as RTX or RED.
|
||||
bool IsEncapsulated(const RTPHeader& header) const;
|
||||
|
||||
bool GetPayloadSpecifics(uint8_t payload_type, PayloadUnion* payload) const;
|
||||
|
||||
int GetPayloadTypeFrequency(uint8_t payload_type) const;
|
||||
@ -85,22 +105,38 @@ class RTPPayloadRegistry {
|
||||
ModuleRTPUtility::Payload*& payload) const;
|
||||
|
||||
void ResetLastReceivedPayloadTypes() {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
last_received_payload_type_ = -1;
|
||||
last_received_media_payload_type_ = -1;
|
||||
}
|
||||
|
||||
// This sets the payload type of the packets being received from the network
|
||||
// on the media SSRC. For instance if packets are encapsulated with RED, this
|
||||
// payload type will be the RED payload type.
|
||||
void SetIncomingPayloadType(const RTPHeader& header);
|
||||
|
||||
// Returns true if the new media payload type has not changed.
|
||||
bool ReportMediaPayloadType(uint8_t media_payload_type);
|
||||
|
||||
int8_t red_payload_type() const { return red_payload_type_; }
|
||||
int8_t red_payload_type() const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
return red_payload_type_;
|
||||
}
|
||||
int8_t ulpfec_payload_type() const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
return ulpfec_payload_type_;
|
||||
}
|
||||
int8_t last_received_payload_type() const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
return last_received_payload_type_;
|
||||
}
|
||||
void set_last_received_payload_type(int8_t last_received_payload_type) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
last_received_payload_type_ = last_received_payload_type;
|
||||
}
|
||||
|
||||
int8_t last_received_media_payload_type() const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
return last_received_media_payload_type_;
|
||||
};
|
||||
|
||||
@ -113,12 +149,20 @@ class RTPPayloadRegistry {
|
||||
const uint8_t channels,
|
||||
const uint32_t rate);
|
||||
|
||||
bool IsRtxInternal(const RTPHeader& header) const;
|
||||
|
||||
scoped_ptr<CriticalSectionWrapper> crit_sect_;
|
||||
ModuleRTPUtility::PayloadTypeMap payload_type_map_;
|
||||
int32_t id_;
|
||||
scoped_ptr<RTPPayloadStrategy> rtp_payload_strategy_;
|
||||
int8_t red_payload_type_;
|
||||
int8_t ulpfec_payload_type_;
|
||||
int8_t incoming_payload_type_;
|
||||
int8_t last_received_payload_type_;
|
||||
int8_t last_received_media_payload_type_;
|
||||
bool rtx_;
|
||||
int8_t payload_type_rtx_;
|
||||
uint32_t ssrc_rtx_;
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -70,9 +70,9 @@ class RtpReceiver {
|
||||
// Parses the media specific parts of an RTP packet and updates the receiver
|
||||
// state. This for instance means that any changes in SSRC and payload type is
|
||||
// detected and acted upon.
|
||||
virtual bool IncomingRtpPacket(RTPHeader* rtp_header,
|
||||
const uint8_t* incoming_rtp_packet,
|
||||
int incoming_rtp_packet_length,
|
||||
virtual bool IncomingRtpPacket(const RTPHeader& rtp_header,
|
||||
const uint8_t* payload,
|
||||
int payload_length,
|
||||
PayloadUnion payload_specific,
|
||||
bool in_order) = 0;
|
||||
|
||||
@ -80,8 +80,7 @@ class RtpReceiver {
|
||||
virtual NACKMethod NACK() const = 0;
|
||||
|
||||
// Turn negative acknowledgement (NACK) requests on/off.
|
||||
virtual int32_t SetNACKStatus(const NACKMethod method,
|
||||
int max_reordering_threshold) = 0;
|
||||
virtual void SetNACKStatus(const NACKMethod method) = 0;
|
||||
|
||||
// Returns the last received timestamp.
|
||||
virtual uint32_t Timestamp() const = 0;
|
||||
@ -96,24 +95,6 @@ class RtpReceiver {
|
||||
|
||||
// Returns the current energy of the RTP stream received.
|
||||
virtual int32_t Energy(uint8_t array_of_energy[kRtpCsrcSize]) const = 0;
|
||||
|
||||
// Enable/disable RTX and set the SSRC to be used.
|
||||
virtual void SetRTXStatus(bool enable, uint32_t ssrc) = 0;
|
||||
|
||||
// Returns the current RTX status and the SSRC and payload type used.
|
||||
virtual void RTXStatus(bool* enable, uint32_t* ssrc,
|
||||
int* payload_type) const = 0;
|
||||
|
||||
// Sets the RTX payload type.
|
||||
virtual void SetRtxPayloadType(int payload_type) = 0;
|
||||
|
||||
// Returns true if the packet with RTP header |header| is likely to be a
|
||||
// retransmitted packet, false otherwise.
|
||||
virtual bool RetransmitOfOldPacket(const RTPHeader& header, int jitter,
|
||||
int min_rtt) const = 0;
|
||||
|
||||
// Returns true if |sequence_number| is received in order, false otherwise.
|
||||
virtual bool InOrderPacket(const uint16_t sequence_number) const = 0;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
|
||||
@ -277,17 +277,17 @@ class NullRtpFeedback : public RtpFeedback {
|
||||
const int frequency,
|
||||
const uint8_t channels,
|
||||
const uint32_t rate) OVERRIDE {
|
||||
return 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
virtual void OnIncomingSSRCChanged(const int32_t id,
|
||||
const uint32_t ssrc) OVERRIDE {}
|
||||
|
||||
virtual void OnIncomingCSRCChanged(const int32_t id,
|
||||
const uint32_t CSRC,
|
||||
const bool added) OVERRIDE {}
|
||||
virtual void OnIncomingCSRCChanged(const int32_t id,
|
||||
const uint32_t CSRC,
|
||||
const bool added) OVERRIDE {}
|
||||
|
||||
virtual void ResetStatistics(uint32_t ssrc) OVERRIDE {}
|
||||
virtual void ResetStatistics(uint32_t ssrc) OVERRIDE {}
|
||||
};
|
||||
|
||||
// Null object version of RtpData.
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/source/receiver_fec.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h"
|
||||
|
||||
#include <assert.h>
|
||||
|
||||
@ -20,33 +20,28 @@
|
||||
|
||||
// RFC 5109
|
||||
namespace webrtc {
|
||||
ReceiverFEC::ReceiverFEC(const int32_t id, RtpData* callback)
|
||||
|
||||
FecReceiver* FecReceiver::Create(int32_t id, RtpData* callback) {
|
||||
return new FecReceiverImpl(id, callback);
|
||||
}
|
||||
|
||||
FecReceiverImpl::FecReceiverImpl(const int32_t id, RtpData* callback)
|
||||
: id_(id),
|
||||
crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||
recovered_packet_callback_(callback),
|
||||
fec_(new ForwardErrorCorrection(id)),
|
||||
payload_type_fec_(-1) {}
|
||||
fec_(new ForwardErrorCorrection(id)) {}
|
||||
|
||||
ReceiverFEC::~ReceiverFEC() {
|
||||
// Clean up DecodeFEC()
|
||||
FecReceiverImpl::~FecReceiverImpl() {
|
||||
while (!received_packet_list_.empty()) {
|
||||
ForwardErrorCorrection::ReceivedPacket* received_packet =
|
||||
received_packet_list_.front();
|
||||
delete received_packet;
|
||||
delete received_packet_list_.front();
|
||||
received_packet_list_.pop_front();
|
||||
}
|
||||
assert(received_packet_list_.empty());
|
||||
|
||||
if (fec_ != NULL) {
|
||||
fec_->ResetState(&recovered_packet_list_);
|
||||
delete fec_;
|
||||
}
|
||||
}
|
||||
|
||||
void ReceiverFEC::SetPayloadTypeFEC(const int8_t payload_type) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
payload_type_fec_ = payload_type;
|
||||
}
|
||||
// 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
|
||||
// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|
||||
@ -75,17 +70,12 @@ void ReceiverFEC::SetPayloadTypeFEC(const int8_t payload_type) {
|
||||
// block length: 10 bits Length in bytes of the corresponding data
|
||||
// block excluding header.
|
||||
|
||||
int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
|
||||
const uint8_t* incoming_rtp_packet,
|
||||
const uint16_t payload_data_length,
|
||||
bool& FECpacket) {
|
||||
int32_t FecReceiverImpl::AddReceivedRedPacket(
|
||||
const RTPHeader& header, const uint8_t* incoming_rtp_packet,
|
||||
int packet_length, uint8_t ulpfec_payload_type) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
|
||||
if (payload_type_fec_ == -1) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
uint8_t REDHeaderLength = 1;
|
||||
uint16_t payload_data_length = packet_length - header.headerLength;
|
||||
|
||||
// Add to list without RED header, aka a virtual RTP packet
|
||||
// we remove the RED header
|
||||
@ -96,26 +86,19 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
|
||||
|
||||
// get payload type from RED header
|
||||
uint8_t payload_type =
|
||||
incoming_rtp_packet[rtp_header->header.headerLength] & 0x7f;
|
||||
incoming_rtp_packet[header.headerLength] & 0x7f;
|
||||
|
||||
// use the payload_type to decide if it's FEC or coded data
|
||||
if (payload_type_fec_ == payload_type) {
|
||||
received_packet->is_fec = true;
|
||||
FECpacket = true;
|
||||
} else {
|
||||
received_packet->is_fec = false;
|
||||
FECpacket = false;
|
||||
}
|
||||
received_packet->seq_num = rtp_header->header.sequenceNumber;
|
||||
received_packet->is_fec = payload_type == ulpfec_payload_type;
|
||||
received_packet->seq_num = header.sequenceNumber;
|
||||
|
||||
uint16_t blockLength = 0;
|
||||
if (incoming_rtp_packet[rtp_header->header.headerLength] & 0x80) {
|
||||
if (incoming_rtp_packet[header.headerLength] & 0x80) {
|
||||
// f bit set in RED header
|
||||
REDHeaderLength = 4;
|
||||
uint16_t timestamp_offset =
|
||||
(incoming_rtp_packet[rtp_header->header.headerLength + 1]) << 8;
|
||||
(incoming_rtp_packet[header.headerLength + 1]) << 8;
|
||||
timestamp_offset +=
|
||||
incoming_rtp_packet[rtp_header->header.headerLength + 2];
|
||||
incoming_rtp_packet[header.headerLength + 2];
|
||||
timestamp_offset = timestamp_offset >> 2;
|
||||
if (timestamp_offset != 0) {
|
||||
// |timestampOffset| should be 0. However, it's possible this is the first
|
||||
@ -127,11 +110,11 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
|
||||
}
|
||||
|
||||
blockLength =
|
||||
(0x03 & incoming_rtp_packet[rtp_header->header.headerLength + 2]) << 8;
|
||||
blockLength += (incoming_rtp_packet[rtp_header->header.headerLength + 3]);
|
||||
(0x03 & incoming_rtp_packet[header.headerLength + 2]) << 8;
|
||||
blockLength += (incoming_rtp_packet[header.headerLength + 3]);
|
||||
|
||||
// check next RED header
|
||||
if (incoming_rtp_packet[rtp_header->header.headerLength + 4] & 0x80) {
|
||||
if (incoming_rtp_packet[header.headerLength + 4] & 0x80) {
|
||||
// more than 2 blocks in packet not supported
|
||||
delete received_packet;
|
||||
assert(false);
|
||||
@ -152,7 +135,7 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
|
||||
|
||||
// copy the RTP header
|
||||
memcpy(received_packet->pkt->data, incoming_rtp_packet,
|
||||
rtp_header->header.headerLength);
|
||||
header.headerLength);
|
||||
|
||||
// replace the RED payload type
|
||||
received_packet->pkt->data[1] &= 0x80; // reset the payload
|
||||
@ -161,8 +144,8 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
|
||||
|
||||
// copy the payload data
|
||||
memcpy(
|
||||
received_packet->pkt->data + rtp_header->header.headerLength,
|
||||
incoming_rtp_packet + rtp_header->header.headerLength + REDHeaderLength,
|
||||
received_packet->pkt->data + header.headerLength,
|
||||
incoming_rtp_packet + header.headerLength + REDHeaderLength,
|
||||
blockLength);
|
||||
|
||||
received_packet->pkt->length = blockLength;
|
||||
@ -171,11 +154,11 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
|
||||
second_received_packet->pkt = new ForwardErrorCorrection::Packet;
|
||||
|
||||
second_received_packet->is_fec = true;
|
||||
second_received_packet->seq_num = rtp_header->header.sequenceNumber;
|
||||
second_received_packet->seq_num = header.sequenceNumber;
|
||||
|
||||
// copy the FEC payload data
|
||||
memcpy(second_received_packet->pkt->data,
|
||||
incoming_rtp_packet + rtp_header->header.headerLength +
|
||||
incoming_rtp_packet + header.headerLength +
|
||||
REDHeaderLength + blockLength,
|
||||
payload_data_length - REDHeaderLength - blockLength);
|
||||
|
||||
@ -186,7 +169,7 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
|
||||
// everything behind the RED header
|
||||
memcpy(
|
||||
received_packet->pkt->data,
|
||||
incoming_rtp_packet + rtp_header->header.headerLength + REDHeaderLength,
|
||||
incoming_rtp_packet + header.headerLength + REDHeaderLength,
|
||||
payload_data_length - REDHeaderLength);
|
||||
received_packet->pkt->length = payload_data_length - REDHeaderLength;
|
||||
received_packet->ssrc =
|
||||
@ -195,7 +178,7 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
|
||||
} else {
|
||||
// copy the RTP header
|
||||
memcpy(received_packet->pkt->data, incoming_rtp_packet,
|
||||
rtp_header->header.headerLength);
|
||||
header.headerLength);
|
||||
|
||||
// replace the RED payload type
|
||||
received_packet->pkt->data[1] &= 0x80; // reset the payload
|
||||
@ -204,12 +187,12 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
|
||||
|
||||
// copy the media payload data
|
||||
memcpy(
|
||||
received_packet->pkt->data + rtp_header->header.headerLength,
|
||||
incoming_rtp_packet + rtp_header->header.headerLength + REDHeaderLength,
|
||||
received_packet->pkt->data + header.headerLength,
|
||||
incoming_rtp_packet + header.headerLength + REDHeaderLength,
|
||||
payload_data_length - REDHeaderLength);
|
||||
|
||||
received_packet->pkt->length =
|
||||
rtp_header->header.headerLength + payload_data_length - REDHeaderLength;
|
||||
header.headerLength + payload_data_length - REDHeaderLength;
|
||||
}
|
||||
|
||||
if (received_packet->pkt->length == 0) {
|
||||
@ -225,7 +208,7 @@ int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t ReceiverFEC::ProcessReceivedFEC() {
|
||||
int32_t FecReceiverImpl::ProcessReceivedFec() {
|
||||
crit_sect_->Enter();
|
||||
if (!received_packet_list_.empty()) {
|
||||
// Send received media packet to VCM.
|
||||
@ -8,11 +8,12 @@
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
|
||||
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
|
||||
#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_
|
||||
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_
|
||||
|
||||
// This header is included to get the nested declaration of Packet structure.
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h"
|
||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
@ -22,19 +23,17 @@ namespace webrtc {
|
||||
|
||||
class CriticalSectionWrapper;
|
||||
|
||||
class ReceiverFEC {
|
||||
class FecReceiverImpl : public FecReceiver {
|
||||
public:
|
||||
ReceiverFEC(const int32_t id, RtpData* callback);
|
||||
virtual ~ReceiverFEC();
|
||||
FecReceiverImpl(const int32_t id, RtpData* callback);
|
||||
virtual ~FecReceiverImpl();
|
||||
|
||||
int32_t AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
|
||||
const uint8_t* incoming_rtp_packet,
|
||||
const uint16_t payload_data_length,
|
||||
bool& FECpacket);
|
||||
virtual int32_t AddReceivedRedPacket(const RTPHeader& rtp_header,
|
||||
const uint8_t* incoming_rtp_packet,
|
||||
int packet_length,
|
||||
uint8_t ulpfec_payload_type) OVERRIDE;
|
||||
|
||||
int32_t ProcessReceivedFEC();
|
||||
|
||||
void SetPayloadTypeFEC(const int8_t payload_type);
|
||||
virtual int32_t ProcessReceivedFec() OVERRIDE;
|
||||
|
||||
private:
|
||||
int id_;
|
||||
@ -46,8 +45,7 @@ class ReceiverFEC {
|
||||
// arrives. We should remove the list.
|
||||
ForwardErrorCorrection::ReceivedPacketList received_packet_list_;
|
||||
ForwardErrorCorrection::RecoveredPacketList recovered_packet_list_;
|
||||
int8_t payload_type_fec_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
|
||||
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_
|
||||
@ -14,9 +14,9 @@
|
||||
|
||||
#include "testing/gmock/include/gmock/gmock.h"
|
||||
#include "testing/gtest/include/gtest/gtest.h"
|
||||
#include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/fec_test_helper.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/receiver_fec.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::Args;
|
||||
@ -40,9 +40,8 @@ class ReceiverFecTest : public ::testing::Test {
|
||||
protected:
|
||||
virtual void SetUp() {
|
||||
fec_ = new ForwardErrorCorrection(0);
|
||||
receiver_fec_ = new ReceiverFEC(0, &rtp_data_callback_);
|
||||
receiver_fec_ = FecReceiver::Create(0, &rtp_data_callback_);
|
||||
generator_ = new FrameGenerator();
|
||||
receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
@ -83,27 +82,23 @@ class ReceiverFecTest : public ::testing::Test {
|
||||
|
||||
void BuildAndAddRedMediaPacket(RtpPacket* packet) {
|
||||
RtpPacket* red_packet = generator_->BuildMediaRedPacket(packet);
|
||||
bool is_fec = false;
|
||||
EXPECT_EQ(0, receiver_fec_->AddReceivedFECPacket(
|
||||
&red_packet->header, red_packet->data,
|
||||
red_packet->length - kRtpHeaderSize, is_fec));
|
||||
EXPECT_EQ(0, receiver_fec_->AddReceivedRedPacket(
|
||||
red_packet->header.header, red_packet->data,
|
||||
red_packet->length, kFecPayloadType));
|
||||
delete red_packet;
|
||||
EXPECT_FALSE(is_fec);
|
||||
}
|
||||
|
||||
void BuildAndAddRedFecPacket(Packet* packet) {
|
||||
RtpPacket* red_packet = generator_->BuildFecRedPacket(packet);
|
||||
bool is_fec = false;
|
||||
EXPECT_EQ(0, receiver_fec_->AddReceivedFECPacket(
|
||||
&red_packet->header, red_packet->data,
|
||||
red_packet->length - kRtpHeaderSize, is_fec));
|
||||
EXPECT_EQ(0, receiver_fec_->AddReceivedRedPacket(
|
||||
red_packet->header.header, red_packet->data,
|
||||
red_packet->length, kFecPayloadType));
|
||||
delete red_packet;
|
||||
EXPECT_TRUE(is_fec);
|
||||
}
|
||||
|
||||
ForwardErrorCorrection* fec_;
|
||||
MockRtpData rtp_data_callback_;
|
||||
ReceiverFEC* receiver_fec_;
|
||||
FecReceiver* receiver_fec_;
|
||||
FrameGenerator* generator_;
|
||||
};
|
||||
|
||||
@ -127,13 +122,13 @@ TEST_F(ReceiverFecTest, TwoMediaOneFec) {
|
||||
std::list<RtpPacket*>::iterator media_it = media_rtp_packets.begin();
|
||||
BuildAndAddRedMediaPacket(*media_it);
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
// Drop one media packet.
|
||||
std::list<Packet*>::iterator fec_it = fec_packets.begin();
|
||||
BuildAndAddRedFecPacket(*fec_it);
|
||||
++it;
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
@ -152,12 +147,12 @@ TEST_F(ReceiverFecTest, TwoMediaTwoFec) {
|
||||
std::list<Packet*>::iterator fec_it = fec_packets.begin();
|
||||
BuildAndAddRedFecPacket(*fec_it);
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
++fec_it;
|
||||
BuildAndAddRedFecPacket(*fec_it);
|
||||
++it;
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
@ -175,12 +170,12 @@ TEST_F(ReceiverFecTest, TwoFramesOneFec) {
|
||||
std::list<RtpPacket*>::iterator it = media_rtp_packets.begin();
|
||||
BuildAndAddRedMediaPacket(media_rtp_packets.front());
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
// Drop one media packet.
|
||||
BuildAndAddRedFecPacket(fec_packets.front());
|
||||
++it;
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
@ -199,11 +194,11 @@ TEST_F(ReceiverFecTest, OneCompleteOneUnrecoverableFrame) {
|
||||
std::list<RtpPacket*>::iterator it = media_rtp_packets.begin();
|
||||
BuildAndAddRedMediaPacket(*it); // First frame: one packet.
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
++it;
|
||||
BuildAndAddRedMediaPacket(*it); // First packet of second frame.
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
@ -225,12 +220,12 @@ TEST_F(ReceiverFecTest, MaxFramesOneFec) {
|
||||
for (; it != media_rtp_packets.end(); ++it) {
|
||||
BuildAndAddRedMediaPacket(*it);
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
}
|
||||
BuildAndAddRedFecPacket(fec_packets.front());
|
||||
it = media_rtp_packets.begin();
|
||||
VerifyReconstructedMediaPacket(*it, 1);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
@ -267,7 +262,7 @@ TEST_F(ReceiverFecTest, PacketNotDroppedTooEarly) {
|
||||
BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front());
|
||||
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
|
||||
.Times(1).WillRepeatedly(Return(true));
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
delayed_fec = fec_packets.front();
|
||||
|
||||
// Fill the FEC decoder. No packets should be dropped.
|
||||
@ -282,14 +277,14 @@ TEST_F(ReceiverFecTest, PacketNotDroppedTooEarly) {
|
||||
BuildAndAddRedMediaPacket(*it);
|
||||
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
|
||||
.Times(1).WillRepeatedly(Return(true));
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
}
|
||||
|
||||
// Add the delayed FEC packet. One packet should be reconstructed.
|
||||
BuildAndAddRedFecPacket(delayed_fec);
|
||||
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
|
||||
.Times(1).WillRepeatedly(Return(true));
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
|
||||
DeletePackets(&media_packets_batch1);
|
||||
DeletePackets(&media_packets_batch2);
|
||||
@ -311,7 +306,7 @@ TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) {
|
||||
BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front());
|
||||
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
|
||||
.Times(1).WillRepeatedly(Return(true));
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
delayed_fec = fec_packets.front();
|
||||
|
||||
// Fill the FEC decoder and force the last packet to be dropped.
|
||||
@ -326,7 +321,7 @@ TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) {
|
||||
BuildAndAddRedMediaPacket(*it);
|
||||
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
|
||||
.Times(1).WillRepeatedly(Return(true));
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
}
|
||||
|
||||
// Add the delayed FEC packet. No packet should be reconstructed since the
|
||||
@ -334,7 +329,7 @@ TEST_F(ReceiverFecTest, PacketDroppedWhenTooOld) {
|
||||
BuildAndAddRedFecPacket(delayed_fec);
|
||||
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
|
||||
.Times(0);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
|
||||
DeletePackets(&media_packets_batch1);
|
||||
DeletePackets(&media_packets_batch2);
|
||||
@ -358,7 +353,7 @@ TEST_F(ReceiverFecTest, OldFecPacketDropped) {
|
||||
BuildAndAddRedFecPacket(*it);
|
||||
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
|
||||
.Times(0);
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
}
|
||||
media_packets.insert(media_packets.end(), frame_media_packets.begin(),
|
||||
frame_media_packets.end());
|
||||
@ -372,7 +367,7 @@ TEST_F(ReceiverFecTest, OldFecPacketDropped) {
|
||||
BuildAndAddRedMediaPacket(media_rtp_packets.front());
|
||||
EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
|
||||
.Times(1).WillRepeatedly(Return(true));
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
|
||||
EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
|
||||
|
||||
DeletePackets(&media_packets);
|
||||
}
|
||||
@ -75,8 +75,7 @@ class RtxLoopBackTransport : public webrtc::Transport {
|
||||
count_rtx_ssrc_(0),
|
||||
rtp_payload_registry_(NULL),
|
||||
rtp_receiver_(NULL),
|
||||
module_(NULL) {
|
||||
}
|
||||
module_(NULL) {}
|
||||
|
||||
void SetSendModule(RtpRtcp* rtpRtcpModule,
|
||||
RTPPayloadRegistry* rtp_payload_registry,
|
||||
@ -112,19 +111,33 @@ class RtxLoopBackTransport : public webrtc::Transport {
|
||||
count_ < consecutive_drop_end_) {
|
||||
return len;
|
||||
}
|
||||
int packet_length = len;
|
||||
uint8_t restored_packet[1500];
|
||||
uint8_t* restored_packet_ptr = restored_packet;
|
||||
RTPHeader header;
|
||||
scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
|
||||
if (!parser->Parse(static_cast<const uint8_t*>(data), len, &header)) {
|
||||
if (!parser->Parse(ptr, len, &header)) {
|
||||
return -1;
|
||||
}
|
||||
if (rtp_payload_registry_->IsRtx(header)) {
|
||||
// Remove the RTX header and parse the original RTP header.
|
||||
EXPECT_TRUE(rtp_payload_registry_->RestoreOriginalPacket(
|
||||
&restored_packet_ptr, ptr, &packet_length, rtp_receiver_->SSRC(),
|
||||
header));
|
||||
if (!parser->Parse(restored_packet_ptr, packet_length, &header)) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
restored_packet_ptr += header.headerLength;
|
||||
packet_length -= header.headerLength;
|
||||
PayloadUnion payload_specific;
|
||||
if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
|
||||
&payload_specific)) {
|
||||
&payload_specific)) {
|
||||
return -1;
|
||||
}
|
||||
if (!rtp_receiver_->IncomingRtpPacket(&header,
|
||||
static_cast<const uint8_t*>(data),
|
||||
len, payload_specific, true)) {
|
||||
if (!rtp_receiver_->IncomingRtpPacket(header, restored_packet_ptr,
|
||||
packet_length, payload_specific,
|
||||
true)) {
|
||||
return -1;
|
||||
}
|
||||
return len;
|
||||
@ -177,7 +190,7 @@ class RtpRtcpRtxNackTest : public ::testing::Test {
|
||||
|
||||
EXPECT_EQ(0, rtp_rtcp_module_->SetSSRC(kTestSsrc));
|
||||
EXPECT_EQ(0, rtp_rtcp_module_->SetRTCPStatus(kRtcpCompound));
|
||||
EXPECT_EQ(0, rtp_receiver_->SetNACKStatus(kNackRtcp, 450));
|
||||
rtp_receiver_->SetNACKStatus(kNackRtcp);
|
||||
EXPECT_EQ(0, rtp_rtcp_module_->SetStorePacketsStatus(true, 600));
|
||||
EXPECT_EQ(0, rtp_rtcp_module_->SetSendingStatus(true));
|
||||
EXPECT_EQ(0, rtp_rtcp_module_->SetSequenceNumber(kTestSequenceNumber));
|
||||
@ -240,7 +253,7 @@ class RtpRtcpRtxNackTest : public ::testing::Test {
|
||||
}
|
||||
|
||||
void RunRtxTest(RtxMode rtx_method, int loss) {
|
||||
rtp_receiver_->SetRTXStatus(true, kTestSsrc + 1);
|
||||
rtp_payload_registry_.SetRtxStatus(true, kTestSsrc + 1);
|
||||
EXPECT_EQ(0, rtp_rtcp_module_->SetRTXSendStatus(rtx_method, true,
|
||||
kTestSsrc + 1));
|
||||
transport_.DropEveryNthPacket(loss);
|
||||
|
||||
@ -10,6 +10,8 @@
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/source/bitrate.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||
@ -27,10 +29,12 @@ StreamStatisticianImpl::StreamStatisticianImpl(Clock* clock)
|
||||
crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||
incoming_bitrate_(clock),
|
||||
ssrc_(0),
|
||||
max_reordering_threshold_(kDefaultMaxReorderingThreshold),
|
||||
jitter_q4_(0),
|
||||
jitter_max_q4_(0),
|
||||
cumulative_loss_(0),
|
||||
jitter_q4_transmission_time_offset_(0),
|
||||
last_receive_time_ms_(0),
|
||||
last_receive_time_secs_(0),
|
||||
last_receive_time_frac_(0),
|
||||
last_received_timestamp_(0),
|
||||
@ -69,9 +73,9 @@ void StreamStatisticianImpl::ResetStatistics() {
|
||||
|
||||
void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header,
|
||||
size_t bytes,
|
||||
bool retransmitted,
|
||||
bool in_order) {
|
||||
bool retransmitted) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
bool in_order = InOrderPacketInternal(header.sequenceNumber);
|
||||
ssrc_ = header.ssrc;
|
||||
incoming_bitrate_.Update(bytes);
|
||||
received_byte_count_ += bytes;
|
||||
@ -83,6 +87,7 @@ void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header,
|
||||
received_seq_max_ = header.sequenceNumber;
|
||||
received_inorder_packet_count_ = 1;
|
||||
clock_->CurrentNtp(last_receive_time_secs_, last_receive_time_frac_);
|
||||
last_receive_time_ms_ = clock_->TimeInMilliseconds();
|
||||
return;
|
||||
}
|
||||
|
||||
@ -96,8 +101,7 @@ void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header,
|
||||
received_inorder_packet_count_++;
|
||||
|
||||
// Wrong if we use RetransmitOfOldPacket.
|
||||
int32_t seq_diff =
|
||||
header.sequenceNumber - received_seq_max_;
|
||||
int32_t seq_diff = header.sequenceNumber - received_seq_max_;
|
||||
if (seq_diff < 0) {
|
||||
// Wrap around detected.
|
||||
received_seq_wraps_++;
|
||||
@ -147,6 +151,7 @@ void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header,
|
||||
last_received_timestamp_ = header.timestamp;
|
||||
last_receive_time_secs_ = receive_time_secs;
|
||||
last_receive_time_frac_ = receive_time_frac;
|
||||
last_receive_time_ms_ = clock_->TimeInMilliseconds();
|
||||
} else {
|
||||
if (retransmitted) {
|
||||
received_retransmitted_packets_++;
|
||||
@ -162,6 +167,12 @@ void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header,
|
||||
received_packet_overhead_ = (15 * received_packet_overhead_ + packet_oh) >> 4;
|
||||
}
|
||||
|
||||
void StreamStatisticianImpl::SetMaxReorderingThreshold(
|
||||
int max_reordering_threshold) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
max_reordering_threshold_ = max_reordering_threshold;
|
||||
}
|
||||
|
||||
bool StreamStatisticianImpl::GetStatistics(Statistics* statistics, bool reset) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
if (received_seq_first_ == 0 && received_byte_count_ == 0) {
|
||||
@ -275,6 +286,62 @@ void StreamStatisticianImpl::LastReceiveTimeNtp(uint32_t* secs,
|
||||
*frac = last_receive_time_frac_;
|
||||
}
|
||||
|
||||
bool StreamStatisticianImpl::IsRetransmitOfOldPacket(
|
||||
const RTPHeader& header, int min_rtt) const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
if (InOrderPacketInternal(header.sequenceNumber)) {
|
||||
return false;
|
||||
}
|
||||
uint32_t frequency_khz = header.payload_type_frequency / 1000;
|
||||
assert(frequency_khz > 0);
|
||||
|
||||
int64_t time_diff_ms = clock_->TimeInMilliseconds() -
|
||||
last_receive_time_ms_;
|
||||
|
||||
// Diff in time stamp since last received in order.
|
||||
uint32_t timestamp_diff = header.timestamp - last_received_timestamp_;
|
||||
int32_t rtp_time_stamp_diff_ms = static_cast<int32_t>(timestamp_diff) /
|
||||
frequency_khz;
|
||||
|
||||
int32_t max_delay_ms = 0;
|
||||
if (min_rtt == 0) {
|
||||
// Jitter standard deviation in samples.
|
||||
float jitter_std = sqrt(static_cast<float>(jitter_q4_ >> 4));
|
||||
|
||||
// 2 times the standard deviation => 95% confidence.
|
||||
// And transform to milliseconds by dividing by the frequency in kHz.
|
||||
max_delay_ms = static_cast<int32_t>((2 * jitter_std) / frequency_khz);
|
||||
|
||||
// Min max_delay_ms is 1.
|
||||
if (max_delay_ms == 0) {
|
||||
max_delay_ms = 1;
|
||||
}
|
||||
} else {
|
||||
max_delay_ms = (min_rtt / 3) + 1;
|
||||
}
|
||||
return time_diff_ms > rtp_time_stamp_diff_ms + max_delay_ms;
|
||||
}
|
||||
|
||||
bool StreamStatisticianImpl::IsPacketInOrder(uint16_t sequence_number) const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
return InOrderPacketInternal(sequence_number);
|
||||
}
|
||||
|
||||
bool StreamStatisticianImpl::InOrderPacketInternal(
|
||||
uint16_t sequence_number) const {
|
||||
// First packet is always in order.
|
||||
if (last_receive_time_ms_ == 0)
|
||||
return true;
|
||||
|
||||
if (IsNewerSequenceNumber(sequence_number, received_seq_max_)) {
|
||||
return true;
|
||||
} else {
|
||||
// If we have a restart of the remote side this packet is still in order.
|
||||
return !IsNewerSequenceNumber(sequence_number, received_seq_max_ -
|
||||
max_reordering_threshold_);
|
||||
}
|
||||
}
|
||||
|
||||
ReceiveStatistics* ReceiveStatistics::Create(Clock* clock) {
|
||||
return new ReceiveStatisticsImpl(clock);
|
||||
}
|
||||
@ -292,8 +359,7 @@ ReceiveStatisticsImpl::~ReceiveStatisticsImpl() {
|
||||
}
|
||||
|
||||
void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header,
|
||||
size_t bytes, bool old_packet,
|
||||
bool in_order) {
|
||||
size_t bytes, bool old_packet) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
StatisticianImplMap::iterator it = statisticians_.find(header.ssrc);
|
||||
if (it == statisticians_.end()) {
|
||||
@ -302,8 +368,7 @@ void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header,
|
||||
header.ssrc, new StreamStatisticianImpl(clock_)));
|
||||
it = insert_result.first;
|
||||
}
|
||||
statisticians_[header.ssrc]->IncomingPacket(header, bytes, old_packet,
|
||||
in_order);
|
||||
statisticians_[header.ssrc]->IncomingPacket(header, bytes, old_packet);
|
||||
}
|
||||
|
||||
void ReceiveStatisticsImpl::ChangeSsrc(uint32_t from_ssrc, uint32_t to_ssrc) {
|
||||
@ -342,6 +407,15 @@ StreamStatistician* ReceiveStatisticsImpl::GetStatistician(
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void ReceiveStatisticsImpl::SetMaxReorderingThreshold(
|
||||
int max_reordering_threshold) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
for (StatisticianImplMap::iterator it = statisticians_.begin();
|
||||
it != statisticians_.end(); ++it) {
|
||||
it->second->SetMaxReorderingThreshold(max_reordering_threshold);
|
||||
}
|
||||
}
|
||||
|
||||
int32_t ReceiveStatisticsImpl::Process() {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
for (StatisticianImplMap::iterator it = statisticians_.begin();
|
||||
@ -362,8 +436,7 @@ int32_t ReceiveStatisticsImpl::TimeUntilNextProcess() {
|
||||
|
||||
void NullReceiveStatistics::IncomingPacket(const RTPHeader& rtp_header,
|
||||
size_t bytes,
|
||||
bool retransmitted,
|
||||
bool in_order) {}
|
||||
bool retransmitted) {}
|
||||
|
||||
StatisticianMap NullReceiveStatistics::GetActiveStatisticians() const {
|
||||
return StatisticianMap();
|
||||
@ -374,6 +447,9 @@ StreamStatistician* NullReceiveStatistics::GetStatistician(
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void NullReceiveStatistics::SetMaxReorderingThreshold(
|
||||
int max_reordering_threshold) {}
|
||||
|
||||
int32_t NullReceiveStatistics::TimeUntilNextProcess() { return 0; }
|
||||
|
||||
int32_t NullReceiveStatistics::Process() { return 0; }
|
||||
|
||||
@ -34,17 +34,24 @@ class StreamStatisticianImpl : public StreamStatistician {
|
||||
uint32_t* packets_received) const OVERRIDE;
|
||||
virtual uint32_t BitrateReceived() const OVERRIDE;
|
||||
virtual void ResetStatistics() OVERRIDE;
|
||||
virtual bool IsRetransmitOfOldPacket(const RTPHeader& header,
|
||||
int min_rtt) const OVERRIDE;
|
||||
virtual bool IsPacketInOrder(uint16_t sequence_number) const OVERRIDE;
|
||||
|
||||
void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
|
||||
bool retransmitted, bool in_order);
|
||||
bool retransmitted);
|
||||
void SetMaxReorderingThreshold(int max_reordering_threshold);
|
||||
void ProcessBitrate();
|
||||
virtual void LastReceiveTimeNtp(uint32_t* secs, uint32_t* frac) const;
|
||||
|
||||
private:
|
||||
bool InOrderPacketInternal(uint16_t sequence_number) const;
|
||||
|
||||
Clock* clock_;
|
||||
scoped_ptr<CriticalSectionWrapper> crit_sect_;
|
||||
Bitrate incoming_bitrate_;
|
||||
uint32_t ssrc_;
|
||||
int max_reordering_threshold_; // In number of packets or sequence numbers.
|
||||
|
||||
// Stats on received RTP packets.
|
||||
uint32_t jitter_q4_;
|
||||
@ -52,6 +59,7 @@ class StreamStatisticianImpl : public StreamStatistician {
|
||||
uint32_t cumulative_loss_;
|
||||
uint32_t jitter_q4_transmission_time_offset_;
|
||||
|
||||
int64_t last_receive_time_ms_;
|
||||
uint32_t last_receive_time_secs_;
|
||||
uint32_t last_receive_time_frac_;
|
||||
uint32_t last_received_timestamp_;
|
||||
@ -82,9 +90,10 @@ class ReceiveStatisticsImpl : public ReceiveStatistics {
|
||||
|
||||
// Implement ReceiveStatistics.
|
||||
virtual void IncomingPacket(const RTPHeader& header, size_t bytes,
|
||||
bool old_packet, bool in_order) OVERRIDE;
|
||||
bool old_packet) OVERRIDE;
|
||||
virtual StatisticianMap GetActiveStatisticians() const OVERRIDE;
|
||||
virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE;
|
||||
virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE;
|
||||
|
||||
// Implement Module.
|
||||
virtual int32_t Process() OVERRIDE;
|
||||
|
||||
@ -43,14 +43,14 @@ class ReceiveStatisticsTest : public ::testing::Test {
|
||||
};
|
||||
|
||||
TEST_F(ReceiveStatisticsTest, TwoIncomingSsrcs) {
|
||||
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true);
|
||||
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
|
||||
++header1_.sequenceNumber;
|
||||
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true);
|
||||
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
|
||||
++header2_.sequenceNumber;
|
||||
clock_.AdvanceTimeMilliseconds(100);
|
||||
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true);
|
||||
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
|
||||
++header1_.sequenceNumber;
|
||||
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true);
|
||||
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
|
||||
++header2_.sequenceNumber;
|
||||
|
||||
StreamStatistician* statistician =
|
||||
@ -75,9 +75,9 @@ TEST_F(ReceiveStatisticsTest, TwoIncomingSsrcs) {
|
||||
EXPECT_EQ(2u, statisticians.size());
|
||||
// Add more incoming packets and verify that they are registered in both
|
||||
// access methods.
|
||||
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true);
|
||||
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
|
||||
++header1_.sequenceNumber;
|
||||
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true);
|
||||
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
|
||||
++header2_.sequenceNumber;
|
||||
|
||||
statisticians[kSsrc1]->GetDataCounters(&bytes_received, &packets_received);
|
||||
@ -98,10 +98,10 @@ TEST_F(ReceiveStatisticsTest, TwoIncomingSsrcs) {
|
||||
}
|
||||
|
||||
TEST_F(ReceiveStatisticsTest, ActiveStatisticians) {
|
||||
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true);
|
||||
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
|
||||
++header1_.sequenceNumber;
|
||||
clock_.AdvanceTimeMilliseconds(1000);
|
||||
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true);
|
||||
receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
|
||||
++header2_.sequenceNumber;
|
||||
StatisticianMap statisticians = receive_statistics_->GetActiveStatisticians();
|
||||
// Nothing should time out since only 1000 ms has passed since the first
|
||||
@ -118,7 +118,7 @@ TEST_F(ReceiveStatisticsTest, ActiveStatisticians) {
|
||||
statisticians = receive_statistics_->GetActiveStatisticians();
|
||||
EXPECT_EQ(0u, statisticians.size());
|
||||
|
||||
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true);
|
||||
receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
|
||||
++header1_.sequenceNumber;
|
||||
// kSsrc1 should be active again and the data counters should have survived.
|
||||
statisticians = receive_statistics_->GetActiveStatisticians();
|
||||
|
||||
@ -375,8 +375,8 @@ TEST_F(RtcpSenderTest, TestCompound) {
|
||||
PayloadUnion payload_specific;
|
||||
EXPECT_TRUE(rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
|
||||
&payload_specific));
|
||||
receive_statistics_->IncomingPacket(header, packet_length, false, true);
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(&header, packet_, packet_length,
|
||||
receive_statistics_->IncomingPacket(header, packet_length, false);
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(header, packet_, packet_length,
|
||||
payload_specific, true));
|
||||
|
||||
EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true));
|
||||
|
||||
@ -17,11 +17,17 @@ namespace webrtc {
|
||||
RTPPayloadRegistry::RTPPayloadRegistry(
|
||||
const int32_t id,
|
||||
RTPPayloadStrategy* rtp_payload_strategy)
|
||||
: id_(id),
|
||||
: crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||
id_(id),
|
||||
rtp_payload_strategy_(rtp_payload_strategy),
|
||||
red_payload_type_(-1),
|
||||
ulpfec_payload_type_(-1),
|
||||
incoming_payload_type_(-1),
|
||||
last_received_payload_type_(-1),
|
||||
last_received_media_payload_type_(-1) {}
|
||||
last_received_media_payload_type_(-1),
|
||||
rtx_(false),
|
||||
payload_type_rtx_(-1),
|
||||
ssrc_rtx_(0) {}
|
||||
|
||||
RTPPayloadRegistry::~RTPPayloadRegistry() {
|
||||
while (!payload_type_map_.empty()) {
|
||||
@ -64,6 +70,8 @@ int32_t RTPPayloadRegistry::RegisterReceivePayload(
|
||||
|
||||
size_t payload_name_length = strlen(payload_name);
|
||||
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
|
||||
ModuleRTPUtility::PayloadTypeMap::iterator it =
|
||||
payload_type_map_.find(payload_type);
|
||||
|
||||
@ -105,7 +113,12 @@ int32_t RTPPayloadRegistry::RegisterReceivePayload(
|
||||
payload = new ModuleRTPUtility::Payload;
|
||||
memset(payload, 0, sizeof(*payload));
|
||||
payload->audio = false;
|
||||
payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
|
||||
strncpy(payload->name, payload_name, RTP_PAYLOAD_NAME_SIZE - 1);
|
||||
} else if (ModuleRTPUtility::StringCompare(payload_name, "ulpfec", 3)) {
|
||||
ulpfec_payload_type_ = payload_type;
|
||||
payload = new ModuleRTPUtility::Payload;
|
||||
memset(payload, 0, sizeof(*payload));
|
||||
payload->audio = false;
|
||||
strncpy(payload->name, payload_name, RTP_PAYLOAD_NAME_SIZE - 1);
|
||||
} else {
|
||||
*created_new_payload = true;
|
||||
@ -123,6 +136,7 @@ int32_t RTPPayloadRegistry::RegisterReceivePayload(
|
||||
|
||||
int32_t RTPPayloadRegistry::DeRegisterReceivePayload(
|
||||
const int8_t payload_type) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
ModuleRTPUtility::PayloadTypeMap::iterator it =
|
||||
payload_type_map_.find(payload_type);
|
||||
|
||||
@ -139,6 +153,7 @@ int32_t RTPPayloadRegistry::DeRegisterReceivePayload(
|
||||
|
||||
// There can't be several codecs with the same rate, frequency and channels
|
||||
// for audio codecs, but there can for video.
|
||||
// Always called from within a critical section.
|
||||
void RTPPayloadRegistry::DeregisterAudioCodecOrRedTypeRegardlessOfPayloadType(
|
||||
const char payload_name[RTP_PAYLOAD_NAME_SIZE],
|
||||
const size_t payload_name_length,
|
||||
@ -186,6 +201,8 @@ int32_t RTPPayloadRegistry::ReceivePayloadType(
|
||||
}
|
||||
size_t payload_name_length = strlen(payload_name);
|
||||
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
|
||||
ModuleRTPUtility::PayloadTypeMap::const_iterator it =
|
||||
payload_type_map_.begin();
|
||||
|
||||
@ -226,8 +243,84 @@ int32_t RTPPayloadRegistry::ReceivePayloadType(
|
||||
return -1;
|
||||
}
|
||||
|
||||
void RTPPayloadRegistry::SetRtxStatus(bool enable, uint32_t ssrc) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
rtx_ = enable;
|
||||
ssrc_rtx_ = ssrc;
|
||||
}
|
||||
|
||||
bool RTPPayloadRegistry::RtxEnabled() const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
return rtx_;
|
||||
}
|
||||
|
||||
bool RTPPayloadRegistry::IsRtx(const RTPHeader& header) const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
return IsRtxInternal(header);
|
||||
}
|
||||
|
||||
bool RTPPayloadRegistry::IsRtxInternal(const RTPHeader& header) const {
|
||||
return rtx_ && ssrc_rtx_ == header.ssrc;
|
||||
}
|
||||
|
||||
bool RTPPayloadRegistry::RestoreOriginalPacket(uint8_t** restored_packet,
|
||||
const uint8_t* packet,
|
||||
int* packet_length,
|
||||
uint32_t original_ssrc,
|
||||
const RTPHeader& header) const {
|
||||
if (kRtxHeaderSize + header.headerLength > *packet_length) {
|
||||
return false;
|
||||
}
|
||||
const uint8_t* rtx_header = packet + header.headerLength;
|
||||
uint16_t original_sequence_number = (rtx_header[0] << 8) + rtx_header[1];
|
||||
|
||||
// Copy the packet into the restored packet, except for the RTX header.
|
||||
memcpy(*restored_packet, packet, header.headerLength);
|
||||
memcpy(*restored_packet + header.headerLength,
|
||||
packet + header.headerLength + kRtxHeaderSize,
|
||||
*packet_length - header.headerLength - kRtxHeaderSize);
|
||||
*packet_length -= kRtxHeaderSize;
|
||||
|
||||
// Replace the SSRC and the sequence number with the originals.
|
||||
ModuleRTPUtility::AssignUWord16ToBuffer(*restored_packet + 2,
|
||||
original_sequence_number);
|
||||
ModuleRTPUtility::AssignUWord32ToBuffer(*restored_packet + 8, original_ssrc);
|
||||
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
|
||||
if (payload_type_rtx_ != -1) {
|
||||
if (header.payloadType == payload_type_rtx_ &&
|
||||
incoming_payload_type_ != -1) {
|
||||
(*restored_packet)[1] = static_cast<uint8_t>(incoming_payload_type_);
|
||||
if (header.markerBit) {
|
||||
(*restored_packet)[1] |= kRtpMarkerBitMask; // Marker bit is set.
|
||||
}
|
||||
} else {
|
||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_,
|
||||
"Incorrect RTX configuration, dropping packet.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RTPPayloadRegistry::SetRtxPayloadType(int payload_type) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
payload_type_rtx_ = payload_type;
|
||||
}
|
||||
|
||||
bool RTPPayloadRegistry::IsRed(const RTPHeader& header) const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
return red_payload_type_ == header.payloadType;
|
||||
}
|
||||
|
||||
bool RTPPayloadRegistry::IsEncapsulated(const RTPHeader& header) const {
|
||||
return IsRed(header) || IsRtx(header);
|
||||
}
|
||||
|
||||
bool RTPPayloadRegistry::GetPayloadSpecifics(uint8_t payload_type,
|
||||
PayloadUnion* payload) const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
ModuleRTPUtility::PayloadTypeMap::const_iterator it =
|
||||
payload_type_map_.find(payload_type);
|
||||
|
||||
@ -245,12 +338,14 @@ int RTPPayloadRegistry::GetPayloadTypeFrequency(
|
||||
if (!PayloadTypeToPayload(payload_type, payload)) {
|
||||
return -1;
|
||||
}
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
return rtp_payload_strategy_->GetPayloadTypeFrequency(*payload);
|
||||
}
|
||||
|
||||
bool RTPPayloadRegistry::PayloadTypeToPayload(
|
||||
const uint8_t payload_type,
|
||||
ModuleRTPUtility::Payload*& payload) const {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
|
||||
ModuleRTPUtility::PayloadTypeMap::const_iterator it =
|
||||
payload_type_map_.find(payload_type);
|
||||
@ -264,8 +359,14 @@ bool RTPPayloadRegistry::PayloadTypeToPayload(
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RTPPayloadRegistry::ReportMediaPayloadType(
|
||||
uint8_t media_payload_type) {
|
||||
void RTPPayloadRegistry::SetIncomingPayloadType(const RTPHeader& header) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
if (!IsRtxInternal(header))
|
||||
incoming_payload_type_ = header.payloadType;
|
||||
}
|
||||
|
||||
bool RTPPayloadRegistry::ReportMediaPayloadType(uint8_t media_payload_type) {
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
if (last_received_media_payload_type_ == media_payload_type) {
|
||||
// Media type unchanged.
|
||||
return true;
|
||||
@ -350,7 +451,7 @@ class RTPPayloadVideoStrategy : public RTPPayloadStrategy {
|
||||
} else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) {
|
||||
videoType = kRtpVideoGeneric;
|
||||
} else if (ModuleRTPUtility::StringCompare(payloadName, "ULPFEC", 6)) {
|
||||
videoType = kRtpVideoFec;
|
||||
videoType = kRtpVideoNone;
|
||||
} else {
|
||||
videoType = kRtpVideoGeneric;
|
||||
}
|
||||
|
||||
@ -183,8 +183,8 @@ int32_t RTPReceiverAudio::OnNewPayloadTypeCreated(
|
||||
int32_t RTPReceiverAudio::ParseRtpPacket(WebRtcRTPHeader* rtp_header,
|
||||
const PayloadUnion& specific_payload,
|
||||
bool is_red,
|
||||
const uint8_t* packet,
|
||||
uint16_t packet_length,
|
||||
const uint8_t* payload,
|
||||
uint16_t payload_length,
|
||||
int64_t timestamp_ms,
|
||||
bool is_first_packet) {
|
||||
TRACE_EVENT2("webrtc_rtp", "Audio::ParseRtp",
|
||||
@ -198,13 +198,11 @@ int32_t RTPReceiverAudio::ParseRtpPacket(WebRtcRTPHeader* rtp_header,
|
||||
rtp_header->type.Audio.arrOfEnergy,
|
||||
rtp_header->type.Audio.numEnergy);
|
||||
}
|
||||
const uint8_t* payload_data =
|
||||
ModuleRTPUtility::GetPayloadData(rtp_header->header, packet);
|
||||
const uint16_t payload_data_length =
|
||||
ModuleRTPUtility::GetPayloadDataLength(rtp_header->header, packet_length);
|
||||
const uint16_t payload_data_length = payload_length -
|
||||
rtp_header->header.paddingLength;
|
||||
|
||||
return ParseAudioCodecSpecific(rtp_header,
|
||||
payload_data,
|
||||
payload,
|
||||
payload_data_length,
|
||||
specific_payload.Audio,
|
||||
is_red);
|
||||
@ -384,6 +382,7 @@ int32_t RTPReceiverAudio::ParseAudioCodecSpecific(
|
||||
}
|
||||
}
|
||||
}
|
||||
// TODO(holmer): Break this out to have RED parsing handled generically.
|
||||
if (is_red && !(payload_data[0] & 0x80)) {
|
||||
// we recive only one frame packed in a RED packet remove the RED wrapper
|
||||
rtp_header->header.payloadType = payload_data[0];
|
||||
|
||||
@ -82,11 +82,7 @@ RtpReceiverImpl::RtpReceiverImpl(int32_t id,
|
||||
last_received_timestamp_(0),
|
||||
last_received_frame_time_ms_(0),
|
||||
last_received_sequence_number_(0),
|
||||
nack_method_(kNackOff),
|
||||
max_reordering_threshold_(kDefaultMaxReorderingThreshold),
|
||||
rtx_(false),
|
||||
ssrc_rtx_(0),
|
||||
payload_type_rtx_(-1) {
|
||||
nack_method_(kNackOff) {
|
||||
assert(incoming_audio_messages_callback);
|
||||
assert(incoming_messages_callback);
|
||||
|
||||
@ -152,37 +148,9 @@ NACKMethod RtpReceiverImpl::NACK() const {
|
||||
}
|
||||
|
||||
// Turn negative acknowledgment requests on/off.
|
||||
int32_t RtpReceiverImpl::SetNACKStatus(const NACKMethod method,
|
||||
int max_reordering_threshold) {
|
||||
void RtpReceiverImpl::SetNACKStatus(const NACKMethod method) {
|
||||
CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
|
||||
if (max_reordering_threshold < 0) {
|
||||
return -1;
|
||||
} else if (method == kNackRtcp) {
|
||||
max_reordering_threshold_ = max_reordering_threshold;
|
||||
} else {
|
||||
max_reordering_threshold_ = kDefaultMaxReorderingThreshold;
|
||||
}
|
||||
nack_method_ = method;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RtpReceiverImpl::SetRTXStatus(bool enable, uint32_t ssrc) {
|
||||
CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
|
||||
rtx_ = enable;
|
||||
ssrc_rtx_ = ssrc;
|
||||
}
|
||||
|
||||
void RtpReceiverImpl::RTXStatus(bool* enable, uint32_t* ssrc,
|
||||
int* payload_type) const {
|
||||
CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
|
||||
*enable = rtx_;
|
||||
*ssrc = ssrc_rtx_;
|
||||
*payload_type = payload_type_rtx_;
|
||||
}
|
||||
|
||||
void RtpReceiverImpl::SetRtxPayloadType(int payload_type) {
|
||||
CriticalSectionScoped cs(critical_section_rtp_receiver_.get());
|
||||
payload_type_rtx_ = payload_type;
|
||||
}
|
||||
|
||||
uint32_t RtpReceiverImpl::SSRC() const {
|
||||
@ -208,55 +176,21 @@ int32_t RtpReceiverImpl::Energy(
|
||||
}
|
||||
|
||||
bool RtpReceiverImpl::IncomingRtpPacket(
|
||||
RTPHeader* rtp_header,
|
||||
const uint8_t* packet,
|
||||
int packet_length,
|
||||
PayloadUnion payload_specific,
|
||||
bool in_order) {
|
||||
// The rtp_header argument contains the parsed RTP header.
|
||||
int length = packet_length - rtp_header->paddingLength;
|
||||
|
||||
const RTPHeader& rtp_header,
|
||||
const uint8_t* payload,
|
||||
int payload_length,
|
||||
PayloadUnion payload_specific,
|
||||
bool in_order) {
|
||||
// Sanity check.
|
||||
if ((length - rtp_header->headerLength) < 0) {
|
||||
if (payload_length < 0) {
|
||||
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_,
|
||||
"%s invalid argument",
|
||||
__FUNCTION__);
|
||||
return false;
|
||||
}
|
||||
{
|
||||
CriticalSectionScoped cs(critical_section_rtp_receiver_.get());
|
||||
// TODO(holmer): Make rtp_header const after RTX has been broken out.
|
||||
if (rtx_) {
|
||||
if (ssrc_rtx_ == rtp_header->ssrc) {
|
||||
// Sanity check, RTX packets has 2 extra header bytes.
|
||||
if (rtp_header->headerLength + kRtxHeaderSize > packet_length) {
|
||||
return false;
|
||||
}
|
||||
// If a specific RTX payload type is negotiated, set back to the media
|
||||
// payload type and treat it like a media packet from here.
|
||||
if (payload_type_rtx_ != -1) {
|
||||
if (payload_type_rtx_ == rtp_header->payloadType &&
|
||||
rtp_payload_registry_->last_received_media_payload_type() != -1) {
|
||||
rtp_header->payloadType =
|
||||
rtp_payload_registry_->last_received_media_payload_type();
|
||||
} else {
|
||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_,
|
||||
"Incorrect RTX configuration, dropping packet.");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
rtp_header->ssrc = ssrc_;
|
||||
rtp_header->sequenceNumber =
|
||||
(packet[rtp_header->headerLength] << 8) +
|
||||
packet[1 + rtp_header->headerLength];
|
||||
// Count the RTX header as part of the RTP
|
||||
rtp_header->headerLength += 2;
|
||||
}
|
||||
}
|
||||
}
|
||||
int8_t first_payload_byte = 0;
|
||||
if (length > 0) {
|
||||
first_payload_byte = packet[rtp_header->headerLength];
|
||||
if (payload_length > 0) {
|
||||
first_payload_byte = payload[0];
|
||||
}
|
||||
// Trigger our callbacks.
|
||||
CheckSSRCChanged(rtp_header);
|
||||
@ -269,7 +203,7 @@ bool RtpReceiverImpl::IncomingRtpPacket(
|
||||
is_red,
|
||||
&payload_specific,
|
||||
&should_reset_statistics) == -1) {
|
||||
if (length - rtp_header->headerLength == 0) {
|
||||
if (payload_length == 0) {
|
||||
// OK, keep-alive packet.
|
||||
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
|
||||
"%s received keepalive",
|
||||
@ -288,24 +222,23 @@ bool RtpReceiverImpl::IncomingRtpPacket(
|
||||
|
||||
WebRtcRTPHeader webrtc_rtp_header;
|
||||
memset(&webrtc_rtp_header, 0, sizeof(webrtc_rtp_header));
|
||||
webrtc_rtp_header.header = *rtp_header;
|
||||
CheckCSRC(&webrtc_rtp_header);
|
||||
webrtc_rtp_header.header = rtp_header;
|
||||
CheckCSRC(webrtc_rtp_header);
|
||||
|
||||
uint16_t payload_data_length =
|
||||
ModuleRTPUtility::GetPayloadDataLength(*rtp_header, packet_length);
|
||||
uint16_t payload_data_length = payload_length - rtp_header.paddingLength;
|
||||
|
||||
bool is_first_packet_in_frame = false;
|
||||
bool is_first_packet = false;
|
||||
{
|
||||
CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
|
||||
is_first_packet_in_frame =
|
||||
last_received_sequence_number_ + 1 == rtp_header->sequenceNumber &&
|
||||
Timestamp() != rtp_header->timestamp;
|
||||
last_received_sequence_number_ + 1 == rtp_header.sequenceNumber &&
|
||||
Timestamp() != rtp_header.timestamp;
|
||||
is_first_packet = is_first_packet_in_frame || last_receive_time_ == 0;
|
||||
}
|
||||
|
||||
int32_t ret_val = rtp_media_receiver_->ParseRtpPacket(
|
||||
&webrtc_rtp_header, payload_specific, is_red, packet, packet_length,
|
||||
&webrtc_rtp_header, payload_specific, is_red, payload, payload_length,
|
||||
clock_->TimeInMilliseconds(), is_first_packet);
|
||||
|
||||
if (ret_val < 0) {
|
||||
@ -319,73 +252,16 @@ bool RtpReceiverImpl::IncomingRtpPacket(
|
||||
last_received_payload_length_ = payload_data_length;
|
||||
|
||||
if (in_order) {
|
||||
if (last_received_timestamp_ != rtp_header->timestamp) {
|
||||
last_received_timestamp_ = rtp_header->timestamp;
|
||||
if (last_received_timestamp_ != rtp_header.timestamp) {
|
||||
last_received_timestamp_ = rtp_header.timestamp;
|
||||
last_received_frame_time_ms_ = clock_->TimeInMilliseconds();
|
||||
}
|
||||
last_received_sequence_number_ = rtp_header->sequenceNumber;
|
||||
last_received_sequence_number_ = rtp_header.sequenceNumber;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool RtpReceiverImpl::RetransmitOfOldPacket(const RTPHeader& header,
|
||||
int jitter, int min_rtt) const {
|
||||
if (InOrderPacket(header.sequenceNumber)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
CriticalSectionScoped cs(critical_section_rtp_receiver_.get());
|
||||
uint32_t frequency_khz = header.payload_type_frequency / 1000;
|
||||
assert(frequency_khz > 0);
|
||||
|
||||
int64_t time_diff_ms = clock_->TimeInMilliseconds() -
|
||||
last_receive_time_;
|
||||
|
||||
// Diff in time stamp since last received in order.
|
||||
uint32_t timestamp_diff = header.timestamp - last_received_timestamp_;
|
||||
int32_t rtp_time_stamp_diff_ms = static_cast<int32_t>(timestamp_diff) /
|
||||
frequency_khz;
|
||||
|
||||
int32_t max_delay_ms = 0;
|
||||
if (min_rtt == 0) {
|
||||
// Jitter standard deviation in samples.
|
||||
float jitter_std = sqrt(static_cast<float>(jitter));
|
||||
|
||||
// 2 times the standard deviation => 95% confidence.
|
||||
// And transform to milliseconds by dividing by the frequency in kHz.
|
||||
max_delay_ms = static_cast<int32_t>((2 * jitter_std) / frequency_khz);
|
||||
|
||||
// Min max_delay_ms is 1.
|
||||
if (max_delay_ms == 0) {
|
||||
max_delay_ms = 1;
|
||||
}
|
||||
} else {
|
||||
max_delay_ms = (min_rtt / 3) + 1;
|
||||
}
|
||||
if (time_diff_ms > rtp_time_stamp_diff_ms + max_delay_ms) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool RtpReceiverImpl::InOrderPacket(const uint16_t sequence_number) const {
|
||||
CriticalSectionScoped cs(critical_section_rtp_receiver_.get());
|
||||
|
||||
// First packet is always in order.
|
||||
if (last_receive_time_ == 0)
|
||||
return true;
|
||||
|
||||
if (IsNewerSequenceNumber(sequence_number, last_received_sequence_number_)) {
|
||||
return true;
|
||||
} else {
|
||||
// If we have a restart of the remote side this packet is still in order.
|
||||
return !IsNewerSequenceNumber(sequence_number,
|
||||
last_received_sequence_number_ -
|
||||
max_reordering_threshold_);
|
||||
}
|
||||
}
|
||||
|
||||
TelephoneEventHandler* RtpReceiverImpl::GetTelephoneEventHandler() {
|
||||
return rtp_media_receiver_->GetTelephoneEventHandler();
|
||||
}
|
||||
@ -401,7 +277,7 @@ int32_t RtpReceiverImpl::LastReceivedTimeMs() const {
|
||||
}
|
||||
|
||||
// Implementation note: must not hold critsect when called.
|
||||
void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader* rtp_header) {
|
||||
void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader& rtp_header) {
|
||||
bool new_ssrc = false;
|
||||
bool re_initialize_decoder = false;
|
||||
char payload_name[RTP_PAYLOAD_NAME_SIZE];
|
||||
@ -413,7 +289,7 @@ void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader* rtp_header) {
|
||||
|
||||
int8_t last_received_payload_type =
|
||||
rtp_payload_registry_->last_received_payload_type();
|
||||
if (ssrc_ != rtp_header->ssrc ||
|
||||
if (ssrc_ != rtp_header.ssrc ||
|
||||
(last_received_payload_type == -1 && ssrc_ == 0)) {
|
||||
// We need the payload_type_ to make the call if the remote SSRC is 0.
|
||||
new_ssrc = true;
|
||||
@ -427,12 +303,12 @@ void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader* rtp_header) {
|
||||
// Do we have a SSRC? Then the stream is restarted.
|
||||
if (ssrc_ != 0) {
|
||||
// Do we have the same codec? Then re-initialize coder.
|
||||
if (rtp_header->payloadType == last_received_payload_type) {
|
||||
if (rtp_header.payloadType == last_received_payload_type) {
|
||||
re_initialize_decoder = true;
|
||||
|
||||
Payload* payload;
|
||||
if (!rtp_payload_registry_->PayloadTypeToPayload(
|
||||
rtp_header->payloadType, payload)) {
|
||||
rtp_header.payloadType, payload)) {
|
||||
return;
|
||||
}
|
||||
assert(payload);
|
||||
@ -444,24 +320,24 @@ void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader* rtp_header) {
|
||||
}
|
||||
}
|
||||
}
|
||||
ssrc_ = rtp_header->ssrc;
|
||||
ssrc_ = rtp_header.ssrc;
|
||||
}
|
||||
}
|
||||
|
||||
if (new_ssrc) {
|
||||
// We need to get this to our RTCP sender and receiver.
|
||||
// We need to do this outside critical section.
|
||||
cb_rtp_feedback_->OnIncomingSSRCChanged(id_, rtp_header->ssrc);
|
||||
cb_rtp_feedback_->OnIncomingSSRCChanged(id_, rtp_header.ssrc);
|
||||
}
|
||||
|
||||
if (re_initialize_decoder) {
|
||||
if (-1 == cb_rtp_feedback_->OnInitializeDecoder(
|
||||
id_, rtp_header->payloadType, payload_name,
|
||||
rtp_header->payload_type_frequency, channels, rate)) {
|
||||
id_, rtp_header.payloadType, payload_name,
|
||||
rtp_header.payload_type_frequency, channels, rate)) {
|
||||
// New stream, same codec.
|
||||
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_,
|
||||
"Failed to create decoder for payload type:%d",
|
||||
rtp_header->payloadType);
|
||||
rtp_header.payloadType);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -474,7 +350,7 @@ void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader* rtp_header) {
|
||||
// media_specific interface (such as CheckPayloadChange, possibly get/set
|
||||
// last known payload).
|
||||
int32_t RtpReceiverImpl::CheckPayloadChanged(
|
||||
const RTPHeader* rtp_header,
|
||||
const RTPHeader& rtp_header,
|
||||
const int8_t first_payload_byte,
|
||||
bool& is_red,
|
||||
PayloadUnion* specific_payload,
|
||||
@ -482,13 +358,15 @@ int32_t RtpReceiverImpl::CheckPayloadChanged(
|
||||
bool re_initialize_decoder = false;
|
||||
|
||||
char payload_name[RTP_PAYLOAD_NAME_SIZE];
|
||||
int8_t payload_type = rtp_header->payloadType;
|
||||
int8_t payload_type = rtp_header.payloadType;
|
||||
|
||||
{
|
||||
CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
|
||||
|
||||
int8_t last_received_payload_type =
|
||||
rtp_payload_registry_->last_received_payload_type();
|
||||
// TODO(holmer): Remove this code when RED parsing has been broken out from
|
||||
// RtpReceiverAudio.
|
||||
if (payload_type != last_received_payload_type) {
|
||||
if (rtp_payload_registry_->red_payload_type() == payload_type) {
|
||||
// Get the real codec payload type.
|
||||
@ -537,16 +415,11 @@ int32_t RtpReceiverImpl::CheckPayloadChanged(
|
||||
rtp_media_receiver_->GetLastMediaSpecificPayload(specific_payload);
|
||||
|
||||
if (!payload->audio) {
|
||||
if (VideoCodecType() == kRtpVideoFec) {
|
||||
// Only reset the decoder on media packets.
|
||||
bool media_type_unchanged =
|
||||
rtp_payload_registry_->ReportMediaPayloadType(payload_type);
|
||||
if (media_type_unchanged) {
|
||||
// Only reset the decoder if the media codec type has changed.
|
||||
re_initialize_decoder = false;
|
||||
} else {
|
||||
bool media_type_unchanged =
|
||||
rtp_payload_registry_->ReportMediaPayloadType(payload_type);
|
||||
if (media_type_unchanged) {
|
||||
// Only reset the decoder if the media codec type has changed.
|
||||
re_initialize_decoder = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (re_initialize_decoder) {
|
||||
@ -569,7 +442,7 @@ int32_t RtpReceiverImpl::CheckPayloadChanged(
|
||||
}
|
||||
|
||||
// Implementation note: must not hold critsect when called.
|
||||
void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader* rtp_header) {
|
||||
void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader& rtp_header) {
|
||||
int32_t num_csrcs_diff = 0;
|
||||
uint32_t old_remote_csrc[kRtpCsrcSize];
|
||||
uint8_t old_num_csrcs = 0;
|
||||
@ -578,7 +451,7 @@ void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader* rtp_header) {
|
||||
CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
|
||||
|
||||
if (!rtp_media_receiver_->ShouldReportCsrcChanges(
|
||||
rtp_header->header.payloadType)) {
|
||||
rtp_header.header.payloadType)) {
|
||||
return;
|
||||
}
|
||||
old_num_csrcs = num_csrcs_;
|
||||
@ -587,11 +460,11 @@ void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader* rtp_header) {
|
||||
memcpy(old_remote_csrc, current_remote_csrc_,
|
||||
num_csrcs_ * sizeof(uint32_t));
|
||||
}
|
||||
const uint8_t num_csrcs = rtp_header->header.numCSRCs;
|
||||
const uint8_t num_csrcs = rtp_header.header.numCSRCs;
|
||||
if ((num_csrcs > 0) && (num_csrcs <= kRtpCsrcSize)) {
|
||||
// Copy new.
|
||||
memcpy(current_remote_csrc_,
|
||||
rtp_header->header.arrOfCSRCs,
|
||||
rtp_header.header.arrOfCSRCs,
|
||||
num_csrcs * sizeof(uint32_t));
|
||||
}
|
||||
if (num_csrcs > 0 || old_num_csrcs > 0) {
|
||||
@ -605,8 +478,8 @@ void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader* rtp_header) {
|
||||
|
||||
bool have_called_callback = false;
|
||||
// Search for new CSRC in old array.
|
||||
for (uint8_t i = 0; i < rtp_header->header.numCSRCs; ++i) {
|
||||
const uint32_t csrc = rtp_header->header.arrOfCSRCs[i];
|
||||
for (uint8_t i = 0; i < rtp_header.header.numCSRCs; ++i) {
|
||||
const uint32_t csrc = rtp_header.header.arrOfCSRCs[i];
|
||||
|
||||
bool found_match = false;
|
||||
for (uint8_t j = 0; j < old_num_csrcs; ++j) {
|
||||
@ -626,8 +499,8 @@ void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader* rtp_header) {
|
||||
const uint32_t csrc = old_remote_csrc[i];
|
||||
|
||||
bool found_match = false;
|
||||
for (uint8_t j = 0; j < rtp_header->header.numCSRCs; ++j) {
|
||||
if (csrc == rtp_header->header.arrOfCSRCs[j]) {
|
||||
for (uint8_t j = 0; j < rtp_header.header.numCSRCs; ++j) {
|
||||
if (csrc == rtp_header.header.arrOfCSRCs[j]) {
|
||||
found_match = true;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -46,16 +46,16 @@ class RtpReceiverImpl : public RtpReceiver {
|
||||
int32_t DeRegisterReceivePayload(const int8_t payload_type);
|
||||
|
||||
bool IncomingRtpPacket(
|
||||
RTPHeader* rtp_header,
|
||||
const uint8_t* incoming_rtp_packet,
|
||||
int incoming_rtp_packet_length,
|
||||
const RTPHeader& rtp_header,
|
||||
const uint8_t* payload,
|
||||
int payload_length,
|
||||
PayloadUnion payload_specific,
|
||||
bool in_order);
|
||||
|
||||
NACKMethod NACK() const;
|
||||
|
||||
// Turn negative acknowledgement requests on/off.
|
||||
int32_t SetNACKStatus(const NACKMethod method, int max_reordering_threshold);
|
||||
void SetNACKStatus(const NACKMethod method);
|
||||
|
||||
// Returns the last received timestamp.
|
||||
virtual uint32_t Timestamp() const;
|
||||
@ -74,19 +74,16 @@ class RtpReceiverImpl : public RtpReceiver {
|
||||
|
||||
void SetRtxPayloadType(int payload_type);
|
||||
|
||||
virtual bool RetransmitOfOldPacket(const RTPHeader& header,
|
||||
int jitter, int min_rtt) const;
|
||||
bool InOrderPacket(const uint16_t sequence_number) const;
|
||||
TelephoneEventHandler* GetTelephoneEventHandler();
|
||||
|
||||
private:
|
||||
RtpVideoCodecTypes VideoCodecType() const;
|
||||
|
||||
void CheckSSRCChanged(const RTPHeader* rtp_header);
|
||||
void CheckCSRC(const WebRtcRTPHeader* rtp_header);
|
||||
int32_t CheckPayloadChanged(const RTPHeader* rtp_header,
|
||||
void CheckSSRCChanged(const RTPHeader& rtp_header);
|
||||
void CheckCSRC(const WebRtcRTPHeader& rtp_header);
|
||||
int32_t CheckPayloadChanged(const RTPHeader& rtp_header,
|
||||
const int8_t first_payload_byte,
|
||||
bool& isRED,
|
||||
bool& is_red,
|
||||
PayloadUnion* payload,
|
||||
bool* should_reset_statistics);
|
||||
|
||||
@ -112,11 +109,6 @@ class RtpReceiverImpl : public RtpReceiver {
|
||||
uint16_t last_received_sequence_number_;
|
||||
|
||||
NACKMethod nack_method_;
|
||||
int max_reordering_threshold_;
|
||||
|
||||
bool rtx_;
|
||||
uint32_t ssrc_rtx_;
|
||||
int payload_type_rtx_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_IMPL_H_
|
||||
|
||||
@ -43,8 +43,8 @@ class RTPReceiverStrategy {
|
||||
virtual int32_t ParseRtpPacket(WebRtcRTPHeader* rtp_header,
|
||||
const PayloadUnion& specific_payload,
|
||||
bool is_red,
|
||||
const uint8_t* packet,
|
||||
uint16_t packet_length,
|
||||
const uint8_t* payload,
|
||||
uint16_t payload_length,
|
||||
int64_t timestamp_ms,
|
||||
bool is_first_packet) = 0;
|
||||
|
||||
|
||||
@ -10,24 +10,17 @@
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h"
|
||||
|
||||
#include <math.h>
|
||||
|
||||
#include <assert.h> // assert
|
||||
#include <string.h> // memcpy()
|
||||
#include <assert.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/receiver_fec.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h"
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
|
||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||
#include "webrtc/system_wrappers/interface/trace.h"
|
||||
#include "webrtc/system_wrappers/interface/trace_event.h"
|
||||
|
||||
namespace webrtc {
|
||||
uint32_t BitRateBPS(uint16_t x) {
|
||||
return (x & 0x3fff) * uint32_t(pow(10.0f, (2 + (x >> 14))));
|
||||
}
|
||||
|
||||
RTPReceiverStrategy* RTPReceiverStrategy::CreateVideoStrategy(
|
||||
int32_t id, RtpData* data_callback) {
|
||||
@ -36,12 +29,9 @@ RTPReceiverStrategy* RTPReceiverStrategy::CreateVideoStrategy(
|
||||
|
||||
RTPReceiverVideo::RTPReceiverVideo(int32_t id, RtpData* data_callback)
|
||||
: RTPReceiverStrategy(data_callback),
|
||||
id_(id),
|
||||
receive_fec_(NULL) {
|
||||
}
|
||||
id_(id) {}
|
||||
|
||||
RTPReceiverVideo::~RTPReceiverVideo() {
|
||||
delete receive_fec_;
|
||||
}
|
||||
|
||||
bool RTPReceiverVideo::ShouldReportCsrcChanges(
|
||||
@ -54,13 +44,6 @@ int32_t RTPReceiverVideo::OnNewPayloadTypeCreated(
|
||||
const char payload_name[RTP_PAYLOAD_NAME_SIZE],
|
||||
int8_t payload_type,
|
||||
uint32_t frequency) {
|
||||
if (ModuleRTPUtility::StringCompare(payload_name, "ULPFEC", 6)) {
|
||||
// Enable FEC if not enabled.
|
||||
if (receive_fec_ == NULL) {
|
||||
receive_fec_ = new ReceiverFEC(id_, data_callback_);
|
||||
}
|
||||
receive_fec_->SetPayloadTypeFEC(payload_type);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -68,25 +51,20 @@ int32_t RTPReceiverVideo::ParseRtpPacket(
|
||||
WebRtcRTPHeader* rtp_header,
|
||||
const PayloadUnion& specific_payload,
|
||||
bool is_red,
|
||||
const uint8_t* packet,
|
||||
uint16_t packet_length,
|
||||
const uint8_t* payload,
|
||||
uint16_t payload_length,
|
||||
int64_t timestamp_ms,
|
||||
bool is_first_packet) {
|
||||
TRACE_EVENT2("webrtc_rtp", "Video::ParseRtp",
|
||||
"seqnum", rtp_header->header.sequenceNumber,
|
||||
"timestamp", rtp_header->header.timestamp);
|
||||
rtp_header->type.Video.codec = specific_payload.Video.videoCodecType;
|
||||
const uint8_t* payload_data =
|
||||
ModuleRTPUtility::GetPayloadData(rtp_header->header, packet);
|
||||
const uint16_t payload_data_length =
|
||||
ModuleRTPUtility::GetPayloadDataLength(rtp_header->header, packet_length);
|
||||
const uint16_t payload_data_length = payload_length -
|
||||
rtp_header->header.paddingLength;
|
||||
return ParseVideoCodecSpecific(rtp_header,
|
||||
payload_data,
|
||||
payload,
|
||||
payload_data_length,
|
||||
specific_payload.Video.videoCodecType,
|
||||
is_red,
|
||||
packet,
|
||||
packet_length,
|
||||
timestamp_ms,
|
||||
is_first_packet);
|
||||
}
|
||||
@ -119,57 +97,31 @@ int32_t RTPReceiverVideo::InvokeOnInitializeDecoder(
|
||||
return 0;
|
||||
}
|
||||
|
||||
// we have no critext when calling this
|
||||
// we are not allowed to have any critsects when calling
|
||||
// CallbackOfReceivedPayloadData
|
||||
// We are not allowed to hold a critical section when calling this function.
|
||||
int32_t RTPReceiverVideo::ParseVideoCodecSpecific(
|
||||
WebRtcRTPHeader* rtp_header,
|
||||
const uint8_t* payload_data,
|
||||
uint16_t payload_data_length,
|
||||
RtpVideoCodecTypes video_type,
|
||||
bool is_red,
|
||||
const uint8_t* incoming_rtp_packet,
|
||||
uint16_t incoming_rtp_packet_size,
|
||||
int64_t now_ms,
|
||||
bool is_first_packet) {
|
||||
int32_t ret_val = 0;
|
||||
WEBRTC_TRACE(kTraceStream,
|
||||
kTraceRtpRtcp,
|
||||
id_,
|
||||
"%s(timestamp:%u)",
|
||||
__FUNCTION__,
|
||||
rtp_header->header.timestamp);
|
||||
|
||||
crit_sect_->Enter();
|
||||
|
||||
if (is_red) {
|
||||
if (receive_fec_ == NULL) {
|
||||
crit_sect_->Leave();
|
||||
return -1;
|
||||
}
|
||||
crit_sect_->Leave();
|
||||
bool FECpacket = false;
|
||||
ret_val = receive_fec_->AddReceivedFECPacket(
|
||||
rtp_header, incoming_rtp_packet, payload_data_length, FECpacket);
|
||||
if (ret_val != -1) {
|
||||
ret_val = receive_fec_->ProcessReceivedFEC();
|
||||
}
|
||||
|
||||
if (ret_val == 0 && FECpacket) {
|
||||
// Callback with the received FEC packet.
|
||||
// The normal packets are delivered after parsing.
|
||||
// This contains the original RTP packet header but with
|
||||
// empty payload and data length.
|
||||
rtp_header->frameType = kFrameEmpty;
|
||||
// We need this for the routing.
|
||||
rtp_header->type.Video.codec = video_type;
|
||||
// Pass the length of FEC packets so that they can be accounted for in
|
||||
// the bandwidth estimator.
|
||||
ret_val = data_callback_->OnReceivedPayloadData(
|
||||
NULL, payload_data_length, rtp_header);
|
||||
}
|
||||
} else {
|
||||
// will leave the crit_sect_ critsect
|
||||
ret_val = ParseVideoCodecSpecificSwitch(rtp_header,
|
||||
payload_data,
|
||||
payload_data_length,
|
||||
is_first_packet);
|
||||
switch (rtp_header->type.Video.codec) {
|
||||
case kRtpVideoGeneric:
|
||||
rtp_header->type.Video.isFirstPacket = is_first_packet;
|
||||
return ReceiveGenericCodec(rtp_header, payload_data, payload_data_length);
|
||||
case kRtpVideoVp8:
|
||||
return ReceiveVp8Codec(rtp_header, payload_data, payload_data_length);
|
||||
case kRtpVideoNone:
|
||||
break;
|
||||
}
|
||||
return ret_val;
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t RTPReceiverVideo::BuildRTPheader(
|
||||
@ -208,35 +160,6 @@ int32_t RTPReceiverVideo::BuildRTPheader(
|
||||
return rtp_header_length;
|
||||
}
|
||||
|
||||
int32_t RTPReceiverVideo::ParseVideoCodecSpecificSwitch(
|
||||
WebRtcRTPHeader* rtp_header,
|
||||
const uint8_t* payload_data,
|
||||
uint16_t payload_data_length,
|
||||
bool is_first_packet) {
|
||||
WEBRTC_TRACE(kTraceStream,
|
||||
kTraceRtpRtcp,
|
||||
id_,
|
||||
"%s(timestamp:%u)",
|
||||
__FUNCTION__,
|
||||
rtp_header->header.timestamp);
|
||||
|
||||
// Critical section has already been taken.
|
||||
switch (rtp_header->type.Video.codec) {
|
||||
case kRtpVideoGeneric:
|
||||
rtp_header->type.Video.isFirstPacket = is_first_packet;
|
||||
return ReceiveGenericCodec(rtp_header, payload_data, payload_data_length);
|
||||
case kRtpVideoVp8:
|
||||
return ReceiveVp8Codec(rtp_header, payload_data, payload_data_length);
|
||||
case kRtpVideoFec:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
}
|
||||
// Releasing the already taken critical section here.
|
||||
crit_sect_->Leave();
|
||||
return -1;
|
||||
}
|
||||
|
||||
int32_t RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtp_header,
|
||||
const uint8_t* payload_data,
|
||||
uint16_t payload_data_length) {
|
||||
@ -246,13 +169,16 @@ int32_t RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtp_header,
|
||||
success = true;
|
||||
parsed_packet.info.VP8.dataLength = 0;
|
||||
} else {
|
||||
uint32_t id = 0;
|
||||
{
|
||||
CriticalSectionScoped cs(crit_sect_.get());
|
||||
id = id_;
|
||||
}
|
||||
ModuleRTPUtility::RTPPayloadParser rtp_payload_parser(
|
||||
kRtpVideoVp8, payload_data, payload_data_length, id_);
|
||||
kRtpVideoVp8, payload_data, payload_data_length, id);
|
||||
|
||||
success = rtp_payload_parser.Parse(parsed_packet);
|
||||
}
|
||||
// from here down we only work on local data
|
||||
crit_sect_->Leave();
|
||||
|
||||
if (!success) {
|
||||
return -1;
|
||||
@ -315,8 +241,6 @@ int32_t RTPReceiverVideo::ReceiveGenericCodec(
|
||||
rtp_header->type.Video.isFirstPacket =
|
||||
(generic_header & RtpFormatVideoGeneric::kFirstPacketBit) != 0;
|
||||
|
||||
crit_sect_->Leave();
|
||||
|
||||
if (data_callback_->OnReceivedPayloadData(
|
||||
payload_data, payload_data_length, rtp_header) != 0) {
|
||||
return -1;
|
||||
|
||||
@ -19,10 +19,6 @@
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
class CriticalSectionWrapper;
|
||||
class ModuleRtpRtcpImpl;
|
||||
class ReceiverFEC;
|
||||
class RtpReceiver;
|
||||
|
||||
class RTPReceiverVideo : public RTPReceiverStrategy {
|
||||
public:
|
||||
@ -65,12 +61,6 @@ class RTPReceiverVideo : public RTPReceiverStrategy {
|
||||
void SetPacketOverHead(uint16_t packet_over_head);
|
||||
|
||||
protected:
|
||||
int32_t ParseVideoCodecSpecificSwitch(
|
||||
WebRtcRTPHeader* rtp_header,
|
||||
const uint8_t* payload_data,
|
||||
uint16_t payload_data_length,
|
||||
bool is_first_packet);
|
||||
|
||||
int32_t ReceiveGenericCodec(WebRtcRTPHeader* rtp_header,
|
||||
const uint8_t* payload_data,
|
||||
uint16_t payload_data_length);
|
||||
@ -88,16 +78,10 @@ class RTPReceiverVideo : public RTPReceiverStrategy {
|
||||
const uint8_t* payload_data,
|
||||
uint16_t payload_data_length,
|
||||
RtpVideoCodecTypes video_type,
|
||||
bool is_red,
|
||||
const uint8_t* incoming_rtp_packet,
|
||||
uint16_t incoming_rtp_packet_size,
|
||||
int64_t now_ms,
|
||||
bool is_first_packet);
|
||||
|
||||
int32_t id_;
|
||||
|
||||
// FEC
|
||||
ReceiverFEC* receive_fec_;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
|
||||
@ -28,6 +28,7 @@
|
||||
},
|
||||
'sources': [
|
||||
# Common
|
||||
'../interface/fec_receiver.h',
|
||||
'../interface/receive_statistics.h',
|
||||
'../interface/rtp_header_parser.h',
|
||||
'../interface/rtp_payload_registry.h',
|
||||
@ -36,6 +37,8 @@
|
||||
'../interface/rtp_rtcp_defines.h',
|
||||
'bitrate.cc',
|
||||
'bitrate.h',
|
||||
'fec_receiver_impl.cc',
|
||||
'fec_receiver_impl.h',
|
||||
'receive_statistics_impl.cc',
|
||||
'receive_statistics_impl.h',
|
||||
'rtp_header_parser.cc',
|
||||
@ -87,8 +90,6 @@
|
||||
'rtp_receiver_video.h',
|
||||
'rtp_sender_video.cc',
|
||||
'rtp_sender_video.h',
|
||||
'receiver_fec.cc',
|
||||
'receiver_fec.h',
|
||||
'video_codec_information.h',
|
||||
'rtp_format_vp8.cc',
|
||||
'rtp_format_vp8.h',
|
||||
|
||||
@ -19,7 +19,6 @@
|
||||
#include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.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_utility.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
@ -41,6 +40,18 @@ const int kAudioPayload = 103;
|
||||
|
||||
using testing::_;
|
||||
|
||||
const uint8_t* GetPayloadData(const RTPHeader& rtp_header,
|
||||
const uint8_t* packet) {
|
||||
return packet + rtp_header.headerLength;
|
||||
}
|
||||
|
||||
uint16_t GetPayloadDataLength(const RTPHeader& rtp_header,
|
||||
const uint16_t packet_length) {
|
||||
uint16_t length = packet_length - rtp_header.headerLength -
|
||||
rtp_header.paddingLength;
|
||||
return static_cast<uint16_t>(length);
|
||||
}
|
||||
|
||||
class LoopbackTransportTest : public webrtc::Transport {
|
||||
public:
|
||||
LoopbackTransportTest()
|
||||
@ -464,13 +475,12 @@ TEST_F(RtpSenderTest, SendGenericVideo) {
|
||||
webrtc::RTPHeader rtp_header;
|
||||
ASSERT_TRUE(rtp_parser.Parse(rtp_header));
|
||||
|
||||
const uint8_t* payload_data = ModuleRTPUtility::GetPayloadData(rtp_header,
|
||||
const uint8_t* payload_data = GetPayloadData(rtp_header,
|
||||
transport_.last_sent_packet_);
|
||||
uint8_t generic_header = *payload_data++;
|
||||
|
||||
ASSERT_EQ(sizeof(payload) + sizeof(generic_header),
|
||||
ModuleRTPUtility::GetPayloadDataLength(rtp_header,
|
||||
transport_.last_sent_packet_len_));
|
||||
GetPayloadDataLength(rtp_header, transport_.last_sent_packet_len_));
|
||||
|
||||
EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit);
|
||||
EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit);
|
||||
@ -490,16 +500,14 @@ TEST_F(RtpSenderTest, SendGenericVideo) {
|
||||
transport_.last_sent_packet_len_);
|
||||
ASSERT_TRUE(rtp_parser.Parse(rtp_header));
|
||||
|
||||
payload_data = ModuleRTPUtility::GetPayloadData(rtp_header,
|
||||
transport_.last_sent_packet_);
|
||||
payload_data = GetPayloadData(rtp_header, transport_.last_sent_packet_);
|
||||
generic_header = *payload_data++;
|
||||
|
||||
EXPECT_FALSE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit);
|
||||
EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit);
|
||||
|
||||
ASSERT_EQ(sizeof(payload) + sizeof(generic_header),
|
||||
ModuleRTPUtility::GetPayloadDataLength(rtp_header,
|
||||
transport_.last_sent_packet_len_));
|
||||
GetPayloadDataLength(rtp_header, transport_.last_sent_packet_len_));
|
||||
|
||||
EXPECT_EQ(0, memcmp(payload, payload_data, sizeof(payload)));
|
||||
}
|
||||
@ -574,10 +582,10 @@ TEST_F(RtpSenderAudioTest, SendAudio) {
|
||||
webrtc::RTPHeader rtp_header;
|
||||
ASSERT_TRUE(rtp_parser.Parse(rtp_header));
|
||||
|
||||
const uint8_t* payload_data = ModuleRTPUtility::GetPayloadData(rtp_header,
|
||||
const uint8_t* payload_data = GetPayloadData(rtp_header,
|
||||
transport_.last_sent_packet_);
|
||||
|
||||
ASSERT_EQ(sizeof(payload), ModuleRTPUtility::GetPayloadDataLength(rtp_header,
|
||||
ASSERT_EQ(sizeof(payload), GetPayloadDataLength(rtp_header,
|
||||
transport_.last_sent_packet_len_));
|
||||
|
||||
EXPECT_EQ(0, memcmp(payload, payload_data, sizeof(payload)));
|
||||
@ -605,11 +613,11 @@ TEST_F(RtpSenderAudioTest, SendAudioWithAudioLevelExtension) {
|
||||
webrtc::RTPHeader rtp_header;
|
||||
ASSERT_TRUE(rtp_parser.Parse(rtp_header));
|
||||
|
||||
const uint8_t* payload_data = ModuleRTPUtility::GetPayloadData(rtp_header,
|
||||
transport_.last_sent_packet_);
|
||||
const uint8_t* payload_data = GetPayloadData(rtp_header,
|
||||
transport_.last_sent_packet_);
|
||||
|
||||
ASSERT_EQ(sizeof(payload), ModuleRTPUtility::GetPayloadDataLength(rtp_header,
|
||||
transport_.last_sent_packet_len_));
|
||||
ASSERT_EQ(sizeof(payload), GetPayloadDataLength(
|
||||
rtp_header, transport_.last_sent_packet_len_));
|
||||
|
||||
EXPECT_EQ(0, memcmp(payload, payload_data, sizeof(payload)));
|
||||
|
||||
|
||||
@ -113,18 +113,6 @@ uint32_t ConvertNTPTimeToMS(uint32_t NTPsec, uint32_t NTPfrac) {
|
||||
* Misc utility routines
|
||||
*/
|
||||
|
||||
const uint8_t* GetPayloadData(const RTPHeader& rtp_header,
|
||||
const uint8_t* packet) {
|
||||
return packet + rtp_header.headerLength;
|
||||
}
|
||||
|
||||
uint16_t GetPayloadDataLength(const RTPHeader& rtp_header,
|
||||
const uint16_t packet_length) {
|
||||
uint16_t length = packet_length - rtp_header.paddingLength -
|
||||
rtp_header.headerLength;
|
||||
return static_cast<uint16_t>(length);
|
||||
}
|
||||
|
||||
#if defined(_WIN32)
|
||||
bool StringCompare(const char* str1, const char* str2,
|
||||
const uint32_t length) {
|
||||
|
||||
@ -56,14 +56,6 @@ namespace ModuleRTPUtility
|
||||
|
||||
uint32_t pow2(uint8_t exp);
|
||||
|
||||
// Returns a pointer to the payload data given a packet.
|
||||
const uint8_t* GetPayloadData(const RTPHeader& rtp_header,
|
||||
const uint8_t* packet);
|
||||
|
||||
// Returns payload length given a packet.
|
||||
uint16_t GetPayloadDataLength(const RTPHeader& rtp_header,
|
||||
const uint16_t packet_length);
|
||||
|
||||
// Returns true if |newTimestamp| is older than |existingTimestamp|.
|
||||
// |wrapped| will be set to true if there has been a wraparound between the
|
||||
// two timestamps.
|
||||
|
||||
@ -110,11 +110,11 @@ TEST_F(RtpRtcpAPITest, RTCP) {
|
||||
EXPECT_FALSE(module->TMMBR());
|
||||
|
||||
EXPECT_EQ(kNackOff, rtp_receiver_->NACK());
|
||||
EXPECT_EQ(0, rtp_receiver_->SetNACKStatus(kNackRtcp, 450));
|
||||
rtp_receiver_->SetNACKStatus(kNackRtcp);
|
||||
EXPECT_EQ(kNackRtcp, rtp_receiver_->NACK());
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, RTXSender) {
|
||||
TEST_F(RtpRtcpAPITest, RtxSender) {
|
||||
unsigned int ssrc = 0;
|
||||
RtxMode rtx_mode = kRtxOff;
|
||||
const int kRtxPayloadType = 119;
|
||||
@ -138,19 +138,20 @@ TEST_F(RtpRtcpAPITest, RTXSender) {
|
||||
EXPECT_EQ(kRtxPayloadType, payload_type);
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, RTXReceiver) {
|
||||
bool enable = false;
|
||||
unsigned int ssrc = 0;
|
||||
TEST_F(RtpRtcpAPITest, RtxReceiver) {
|
||||
const uint32_t kRtxSsrc = 1;
|
||||
const int kRtxPayloadType = 119;
|
||||
int payload_type = -1;
|
||||
rtp_receiver_->SetRTXStatus(true, 1);
|
||||
rtp_receiver_->SetRtxPayloadType(kRtxPayloadType);
|
||||
rtp_receiver_->RTXStatus(&enable, &ssrc, &payload_type);
|
||||
EXPECT_TRUE(enable);
|
||||
EXPECT_EQ(1u, ssrc);
|
||||
EXPECT_EQ(kRtxPayloadType ,payload_type);
|
||||
rtp_receiver_->SetRTXStatus(false, 0);
|
||||
rtp_receiver_->RTXStatus(&enable, &ssrc, &payload_type);
|
||||
EXPECT_FALSE(enable);
|
||||
EXPECT_EQ(kRtxPayloadType ,payload_type);
|
||||
rtp_payload_registry_->SetRtxStatus(true, kRtxSsrc);
|
||||
rtp_payload_registry_->SetRtxPayloadType(kRtxPayloadType);
|
||||
EXPECT_TRUE(rtp_payload_registry_->RtxEnabled());
|
||||
RTPHeader rtx_header;
|
||||
rtx_header.ssrc = kRtxSsrc;
|
||||
rtx_header.payloadType = kRtxPayloadType;
|
||||
EXPECT_TRUE(rtp_payload_registry_->IsRtx(rtx_header));
|
||||
rtx_header.ssrc = 0;
|
||||
EXPECT_FALSE(rtp_payload_registry_->IsRtx(rtx_header));
|
||||
rtp_payload_registry_->SetRtxStatus(false, kRtxSsrc);
|
||||
EXPECT_FALSE(rtp_payload_registry_->RtxEnabled());
|
||||
rtx_header.ssrc = kRtxSsrc;
|
||||
EXPECT_FALSE(rtp_payload_registry_->IsRtx(rtx_header));
|
||||
}
|
||||
|
||||
@ -60,8 +60,8 @@ class LoopBackTransport : public webrtc::Transport {
|
||||
header.payloadType, &payload_specific)) {
|
||||
return -1;
|
||||
}
|
||||
receive_statistics_->IncomingPacket(header, len, false, true);
|
||||
if (!rtp_receiver_->IncomingRtpPacket(&header,
|
||||
receive_statistics_->IncomingPacket(header, len, false);
|
||||
if (!rtp_receiver_->IncomingRtpPacket(header,
|
||||
static_cast<const uint8_t*>(data),
|
||||
len, payload_specific, true)) {
|
||||
return -1;
|
||||
|
||||
@ -52,7 +52,7 @@ class RtpRtcpVideoTest : public ::testing::Test {
|
||||
|
||||
EXPECT_EQ(0, video_module_->SetRTCPStatus(kRtcpCompound));
|
||||
EXPECT_EQ(0, video_module_->SetSSRC(test_ssrc_));
|
||||
EXPECT_EQ(0, rtp_receiver_->SetNACKStatus(kNackRtcp, 450));
|
||||
rtp_receiver_->SetNACKStatus(kNackRtcp);
|
||||
EXPECT_EQ(0, video_module_->SetStorePacketsStatus(true, 600));
|
||||
EXPECT_EQ(0, video_module_->SetSendingStatus(true));
|
||||
|
||||
@ -176,11 +176,13 @@ TEST_F(RtpRtcpVideoTest, PaddingOnlyFrames) {
|
||||
PayloadUnion payload_specific;
|
||||
EXPECT_TRUE(rtp_payload_registry_.GetPayloadSpecifics(header.payloadType,
|
||||
&payload_specific));
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(&header, padding_packet,
|
||||
packet_size,
|
||||
const uint8_t* payload = padding_packet + header.headerLength;
|
||||
const int payload_length = packet_size - header.headerLength;
|
||||
EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(header, payload,
|
||||
payload_length,
|
||||
payload_specific, true));
|
||||
EXPECT_EQ(0, receiver_->payload_size());
|
||||
EXPECT_EQ(packet_size - 12, receiver_->rtp_header().header.paddingLength);
|
||||
EXPECT_EQ(payload_length, receiver_->rtp_header().header.paddingLength);
|
||||
}
|
||||
timestamp += 3000;
|
||||
fake_clock.AdvanceTimeMilliseconds(33);
|
||||
|
||||
@ -111,11 +111,6 @@ void VCMPacket::CopyCodecSpecifics(const RTPVideoHeader& videoHeader)
|
||||
codec = kVideoCodecVP8;
|
||||
break;
|
||||
}
|
||||
case kRtpVideoI420:
|
||||
{
|
||||
codec = kVideoCodecI420;
|
||||
break;
|
||||
}
|
||||
default:
|
||||
{
|
||||
codec = kVideoCodecUnknown;
|
||||
|
||||
@ -257,8 +257,7 @@ int MTRxTxTest(CmdArgs& args)
|
||||
FecProtectionParams delta_params = protectionCallback.DeltaFecParameters();
|
||||
FecProtectionParams key_params = protectionCallback.KeyFecParameters();
|
||||
rtp->SetFecParameters(&delta_params, &key_params);
|
||||
rtp_receiver->SetNACKStatus(nackEnabled ? kNackRtcp : kNackOff,
|
||||
kMaxPacketAgeToNack);
|
||||
rtp_receiver->SetNACKStatus(nackEnabled ? kNackRtcp : kNackOff);
|
||||
|
||||
vcm->SetChannelParameters(static_cast<uint32_t>(1000 * bitRate),
|
||||
(uint8_t) lossRate, rttMS);
|
||||
|
||||
@ -102,7 +102,7 @@ TransportCallback::TransportPackets()
|
||||
header.payloadType, &payload_specific)) {
|
||||
return -1;
|
||||
}
|
||||
if (!rtp_receiver_->IncomingRtpPacket(&header, packet->data,
|
||||
if (!rtp_receiver_->IncomingRtpPacket(header, packet->data,
|
||||
packet->length, payload_specific,
|
||||
true))
|
||||
{
|
||||
|
||||
@ -102,9 +102,6 @@ VCMNTEncodeCompleteCallback::SendData(
|
||||
rtpInfo.type.Video.codecHeader.VP8.pictureId =
|
||||
videoHdr->codecHeader.VP8.pictureId;
|
||||
break;
|
||||
case kVideoCodecI420:
|
||||
rtpInfo.type.Video.codec = kRtpVideoI420;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
return -1;
|
||||
|
||||
@ -226,10 +226,7 @@ class SsrcHandlers {
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (handler->rtp_module_->SetNACKStatus(kNackOff,
|
||||
kMaxPacketAgeToNack) < 0) {
|
||||
return -1;
|
||||
}
|
||||
handler->rtp_module_->SetNACKStatus(kNackOff);
|
||||
handler->rtp_header_parser_->RegisterRtpHeaderExtension(
|
||||
kRtpExtensionTransmissionTimeOffset,
|
||||
kDefaultTransmissionTimeOffsetExtensionId);
|
||||
@ -262,10 +259,8 @@ class SsrcHandlers {
|
||||
PayloadUnion payload_specific;
|
||||
it->second->rtp_payload_registry_->GetPayloadSpecifics(
|
||||
header.payloadType, &payload_specific);
|
||||
bool in_order =
|
||||
it->second->rtp_module_->InOrderPacket(header.sequenceNumber);
|
||||
it->second->rtp_module_->IncomingRtpPacket(&header, data, length,
|
||||
payload_specific, in_order);
|
||||
it->second->rtp_module_->IncomingRtpPacket(header, data, length,
|
||||
payload_specific, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -82,8 +82,6 @@ VCMEncodeCompleteCallback::SendData(
|
||||
rtpInfo.type.Video.codecHeader.VP8.pictureId =
|
||||
videoHdr->codecHeader.VP8.pictureId;
|
||||
break;
|
||||
case webrtc::kRtpVideoI420:
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
return -1;
|
||||
@ -308,7 +306,7 @@ RTPSendCompleteCallback::SendPacket(int channel, const void *data, int len)
|
||||
header.payloadType, &payload_specific)) {
|
||||
return -1;
|
||||
}
|
||||
if (!rtp_receiver_->IncomingRtpPacket(&header, packet->data,
|
||||
if (!rtp_receiver_->IncomingRtpPacket(header, packet->data,
|
||||
packet->length, payload_specific,
|
||||
true))
|
||||
{
|
||||
|
||||
@ -150,8 +150,6 @@ int32_t FileOutputFrameReceiver::FrameToRender(
|
||||
webrtc::RtpVideoCodecTypes ConvertCodecType(const char* plname) {
|
||||
if (strncmp(plname,"VP8" , 3) == 0) {
|
||||
return webrtc::kRtpVideoVp8;
|
||||
} else if (strncmp(plname,"I420" , 5) == 0) {
|
||||
return webrtc::kRtpVideoI420;
|
||||
} else {
|
||||
return webrtc::kRtpVideoNone; // Default value
|
||||
}
|
||||
|
||||
@ -37,6 +37,9 @@
|
||||
#include "webrtc/video_engine/test/libvietest/include/tb_external_transport.h"
|
||||
#include "webrtc/voice_engine/include/voe_base.h"
|
||||
|
||||
const uint32_t kSsrc = 0x01234567;
|
||||
const uint32_t kRtxSsrc = 0x01234568;
|
||||
const int kRtxPayloadType = 98;
|
||||
#define VCM_RED_PAYLOAD_TYPE 96
|
||||
#define VCM_ULPFEC_PAYLOAD_TYPE 97
|
||||
|
||||
@ -219,21 +222,47 @@ int VideoEngineSampleCode(void* window1, void* window2)
|
||||
|
||||
// Setting SSRC manually (arbitrary value), as otherwise we will get a clash
|
||||
// (loopback), and a new SSRC will be set, which will reset the receiver.
|
||||
error = ptrViERtpRtcp->SetLocalSSRC(videoChannel, 0x01234567);
|
||||
if (error == -1)
|
||||
{
|
||||
printf("ERROR in ViERTP_RTCP::SetLocalSSRC\n");
|
||||
return -1;
|
||||
error = ptrViERtpRtcp->SetLocalSSRC(videoChannel, kSsrc);
|
||||
if (error == -1) {
|
||||
printf("ERROR in ViERTP_RTCP::SetLocalSSRC\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = ptrViERtpRtcp->SetLocalSSRC(videoChannel, kRtxSsrc,
|
||||
webrtc::kViEStreamTypeRtx, 0);
|
||||
if (error == -1) {
|
||||
printf("ERROR in ViERTP_RTCP::SetLocalSSRC\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = ptrViERtpRtcp->SetRemoteSSRCType(videoChannel,
|
||||
webrtc::kViEStreamTypeRtx,
|
||||
kRtxSsrc);
|
||||
|
||||
if (error == -1) {
|
||||
printf("ERROR in ViERTP_RTCP::SetRtxReceivePayloadType\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = ptrViERtpRtcp->SetRtxSendPayloadType(videoChannel, kRtxPayloadType);
|
||||
if (error == -1) {
|
||||
printf("ERROR in ViERTP_RTCP::SetRtxSendPayloadType\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
error = ptrViERtpRtcp->SetRtxReceivePayloadType(videoChannel,
|
||||
kRtxPayloadType);
|
||||
if (error == -1) {
|
||||
printf("ERROR in ViERTP_RTCP::SetRtxReceivePayloadType\n");
|
||||
return -1;
|
||||
}
|
||||
//
|
||||
// Set up rendering
|
||||
//
|
||||
webrtc::ViERender* ptrViERender = webrtc::ViERender::GetInterface(ptrViE);
|
||||
if (ptrViERender == NULL)
|
||||
{
|
||||
printf("ERROR in ViERender::GetInterface\n");
|
||||
return -1;
|
||||
if (ptrViERender == NULL) {
|
||||
printf("ERROR in ViERender::GetInterface\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
error
|
||||
|
||||
@ -74,7 +74,7 @@ class StreamObserver : public newapi::Transport, public RemoteBitrateObserver {
|
||||
RTPHeader header;
|
||||
EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length),
|
||||
&header));
|
||||
receive_stats_->IncomingPacket(header, length, false, true);
|
||||
receive_stats_->IncomingPacket(header, length, false);
|
||||
rtp_rtcp_->SetRemoteSSRC(header.ssrc);
|
||||
remote_bitrate_estimator_->IncomingPacket(clock_->TimeInMilliseconds(),
|
||||
static_cast<int>(length - 12),
|
||||
|
||||
@ -662,13 +662,7 @@ int32_t ViEChannel::ProcessNACKRequest(const bool enable) {
|
||||
"%s: Could not enable NACK, RTPC not on ", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
if (!vie_receiver_.SetNackStatus(true,
|
||||
max_nack_reordering_threshold_)) {
|
||||
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_),
|
||||
"%s: Could not set NACK method %d", __FUNCTION__,
|
||||
nackMethod);
|
||||
return -1;
|
||||
}
|
||||
vie_receiver_.SetNackStatus(true, max_nack_reordering_threshold_);
|
||||
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_),
|
||||
"%s: Using NACK method %d", __FUNCTION__, nackMethod);
|
||||
rtp_rtcp_->SetStorePacketsStatus(true, nack_history_size_sender_);
|
||||
@ -699,12 +693,7 @@ int32_t ViEChannel::ProcessNACKRequest(const bool enable) {
|
||||
if (paced_sender_ == NULL) {
|
||||
rtp_rtcp_->SetStorePacketsStatus(false, 0);
|
||||
}
|
||||
if (!vie_receiver_.SetNackStatus(false,
|
||||
max_nack_reordering_threshold_)) {
|
||||
WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_),
|
||||
"%s: Could not turn off NACK", __FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
vie_receiver_.SetNackStatus(false, max_nack_reordering_threshold_);
|
||||
// When NACK is off, allow decoding with errors. Otherwise, the video
|
||||
// will freeze, and will only recover with a complete key frame.
|
||||
vcm_.SetDecodeErrorMode(kWithErrors);
|
||||
@ -1189,10 +1178,10 @@ int32_t ViEChannel::SendApplicationDefinedRTCPPacket(
|
||||
}
|
||||
|
||||
int32_t ViEChannel::GetSendRtcpStatistics(uint16_t* fraction_lost,
|
||||
uint32_t* cumulative_lost,
|
||||
uint32_t* extended_max,
|
||||
uint32_t* jitter_samples,
|
||||
int32_t* rtt_ms) {
|
||||
uint32_t* cumulative_lost,
|
||||
uint32_t* extended_max,
|
||||
uint32_t* jitter_samples,
|
||||
int32_t* rtt_ms) {
|
||||
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s",
|
||||
__FUNCTION__);
|
||||
|
||||
|
||||
@ -13,6 +13,7 @@
|
||||
#include <vector>
|
||||
|
||||
#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
|
||||
#include "webrtc/modules/rtp_rtcp/interface/fec_receiver.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_payload_registry.h"
|
||||
@ -40,13 +41,15 @@ ViEReceiver::ViEReceiver(const int32_t channel_id,
|
||||
rtp_payload_registry_.get())),
|
||||
rtp_receive_statistics_(ReceiveStatistics::Create(
|
||||
Clock::GetRealTimeClock())),
|
||||
fec_receiver_(FecReceiver::Create(channel_id, this)),
|
||||
rtp_rtcp_(NULL),
|
||||
vcm_(module_vcm),
|
||||
remote_bitrate_estimator_(remote_bitrate_estimator),
|
||||
external_decryption_(NULL),
|
||||
decryption_buffer_(NULL),
|
||||
rtp_dump_(NULL),
|
||||
receiving_(false) {
|
||||
receiving_(false),
|
||||
restored_packet_in_use_(false) {
|
||||
assert(remote_bitrate_estimator);
|
||||
}
|
||||
|
||||
@ -83,18 +86,24 @@ bool ViEReceiver::RegisterPayload(const VideoCodec& video_codec) {
|
||||
video_codec.maxBitrate) == 0;
|
||||
}
|
||||
|
||||
bool ViEReceiver::SetNackStatus(bool enable,
|
||||
void ViEReceiver::SetNackStatus(bool enable,
|
||||
int max_nack_reordering_threshold) {
|
||||
return rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff,
|
||||
max_nack_reordering_threshold) == 0;
|
||||
if (!enable) {
|
||||
// Reset the threshold back to the lower default threshold when NACK is
|
||||
// disabled since we no longer will be receiving retransmissions.
|
||||
max_nack_reordering_threshold = kDefaultMaxReorderingThreshold;
|
||||
}
|
||||
rtp_receive_statistics_->SetMaxReorderingThreshold(
|
||||
max_nack_reordering_threshold);
|
||||
rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff);
|
||||
}
|
||||
|
||||
void ViEReceiver::SetRtxStatus(bool enable, uint32_t ssrc) {
|
||||
rtp_receiver_->SetRTXStatus(true, ssrc);
|
||||
rtp_payload_registry_->SetRtxStatus(enable, ssrc);
|
||||
}
|
||||
|
||||
void ViEReceiver::SetRtxPayloadType(uint32_t payload_type) {
|
||||
rtp_receiver_->SetRtxPayloadType(payload_type);
|
||||
rtp_payload_registry_->SetRtxPayloadType(payload_type);
|
||||
}
|
||||
|
||||
uint32_t ViEReceiver::GetRemoteSsrc() const {
|
||||
@ -182,9 +191,6 @@ int ViEReceiver::ReceivedRTCPPacket(const void* rtcp_packet,
|
||||
int32_t ViEReceiver::OnReceivedPayloadData(
|
||||
const uint8_t* payload_data, const uint16_t payload_size,
|
||||
const WebRtcRTPHeader* rtp_header) {
|
||||
if (rtp_header == NULL) {
|
||||
return 0;
|
||||
}
|
||||
if (vcm_->IncomingPacket(payload_data, payload_size, *rtp_header) != 0) {
|
||||
// Check this...
|
||||
return -1;
|
||||
@ -201,14 +207,7 @@ bool ViEReceiver::OnRecoveredPacket(const uint8_t* rtp_packet,
|
||||
return false;
|
||||
}
|
||||
header.payload_type_frequency = kVideoPayloadTypeFrequency;
|
||||
PayloadUnion payload_specific;
|
||||
if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
|
||||
&payload_specific)) {
|
||||
return false;
|
||||
}
|
||||
return rtp_receiver_->IncomingRtpPacket(&header, rtp_packet,
|
||||
rtp_packet_length,
|
||||
payload_specific, false);
|
||||
return ReceivePacket(rtp_packet, rtp_packet_length, header, false);
|
||||
}
|
||||
|
||||
int ViEReceiver::InsertRTPPacket(const int8_t* rtp_packet,
|
||||
@ -253,25 +252,78 @@ int ViEReceiver::InsertRTPPacket(const int8_t* rtp_packet,
|
||||
if (!rtp_header_parser_->Parse(received_packet, received_packet_length,
|
||||
&header)) {
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_,
|
||||
"IncomingPacket invalid RTP header");
|
||||
"Incoming packet: Invalid RTP header");
|
||||
return -1;
|
||||
}
|
||||
const int payload_size = received_packet_length - header.headerLength;
|
||||
int payload_length = received_packet_length - header.headerLength;
|
||||
remote_bitrate_estimator_->IncomingPacket(TickTime::MillisecondTimestamp(),
|
||||
payload_size, header);
|
||||
payload_length, header);
|
||||
header.payload_type_frequency = kVideoPayloadTypeFrequency;
|
||||
bool in_order = rtp_receiver_->InOrderPacket(header.sequenceNumber);
|
||||
bool retransmitted = !in_order && IsPacketRetransmitted(header);
|
||||
|
||||
rtp_receive_statistics_->IncomingPacket(header, received_packet_length,
|
||||
retransmitted, in_order);
|
||||
IsPacketRetransmitted(header));
|
||||
rtp_payload_registry_->SetIncomingPayloadType(header);
|
||||
return ReceivePacket(received_packet, received_packet_length, header,
|
||||
IsPacketInOrder(header)) ? 0 : -1;
|
||||
}
|
||||
|
||||
bool ViEReceiver::ReceivePacket(const uint8_t* packet,
|
||||
int packet_length,
|
||||
const RTPHeader& header,
|
||||
bool in_order) {
|
||||
if (rtp_payload_registry_->IsEncapsulated(header)) {
|
||||
return ParseAndHandleEncapsulatingHeader(packet, packet_length, header);
|
||||
}
|
||||
const uint8_t* payload = packet + header.headerLength;
|
||||
int payload_length = packet_length - header.headerLength;
|
||||
assert(payload_length >= 0);
|
||||
PayloadUnion payload_specific;
|
||||
if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
|
||||
&payload_specific)) {
|
||||
return -1;
|
||||
return false;
|
||||
}
|
||||
return rtp_receiver_->IncomingRtpPacket(&header, received_packet,
|
||||
received_packet_length,
|
||||
payload_specific, in_order) ? 0 : -1;
|
||||
return rtp_receiver_->IncomingRtpPacket(header, payload, payload_length,
|
||||
payload_specific, in_order);
|
||||
}
|
||||
|
||||
bool ViEReceiver::ParseAndHandleEncapsulatingHeader(const uint8_t* packet,
|
||||
int packet_length,
|
||||
const RTPHeader& header) {
|
||||
if (rtp_payload_registry_->IsRed(header)) {
|
||||
if (fec_receiver_->AddReceivedRedPacket(
|
||||
header, packet, packet_length,
|
||||
rtp_payload_registry_->ulpfec_payload_type()) != 0) {
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_,
|
||||
"Incoming RED packet error");
|
||||
return false;
|
||||
}
|
||||
return fec_receiver_->ProcessReceivedFec() == 0;
|
||||
} else if (rtp_payload_registry_->IsRtx(header)) {
|
||||
// Remove the RTX header and parse the original RTP header.
|
||||
if (packet_length < header.headerLength)
|
||||
return false;
|
||||
if (packet_length > static_cast<int>(sizeof(restored_packet_)))
|
||||
return false;
|
||||
CriticalSectionScoped cs(receive_cs_.get());
|
||||
if (restored_packet_in_use_) {
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_,
|
||||
"Multiple RTX headers detected, dropping packet");
|
||||
return false;
|
||||
}
|
||||
uint8_t* restored_packet_ptr = restored_packet_;
|
||||
if (!rtp_payload_registry_->RestoreOriginalPacket(
|
||||
&restored_packet_ptr, packet, &packet_length, rtp_receiver_->SSRC(),
|
||||
header)) {
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_,
|
||||
"Incoming RTX packet: invalid RTP header");
|
||||
return false;
|
||||
}
|
||||
restored_packet_in_use_ = true;
|
||||
bool ret = OnRecoveredPacket(restored_packet_ptr, packet_length);
|
||||
restored_packet_in_use_ = false;
|
||||
return ret;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int ViEReceiver::InsertRTCPPacket(const int8_t* rtcp_packet,
|
||||
@ -397,23 +449,26 @@ ReceiveStatistics* ViEReceiver::GetReceiveStatistics() const {
|
||||
return rtp_receive_statistics_.get();
|
||||
}
|
||||
|
||||
bool ViEReceiver::IsPacketInOrder(const RTPHeader& header) const {
|
||||
StreamStatistician* statistician =
|
||||
rtp_receive_statistics_->GetStatistician(header.ssrc);
|
||||
if (!statistician)
|
||||
return false;
|
||||
return statistician->IsPacketInOrder(header.sequenceNumber);
|
||||
}
|
||||
|
||||
bool ViEReceiver::IsPacketRetransmitted(const RTPHeader& header) const {
|
||||
bool rtx_enabled = false;
|
||||
uint32_t rtx_ssrc = 0;
|
||||
int rtx_payload_type = 0;
|
||||
rtp_receiver_->RTXStatus(&rtx_enabled, &rtx_ssrc, &rtx_payload_type);
|
||||
if (!rtx_enabled) {
|
||||
// Check if this is a retransmission.
|
||||
StreamStatistician::Statistics stats;
|
||||
StreamStatistician* statistician =
|
||||
rtp_receive_statistics_->GetStatistician(header.ssrc);
|
||||
if (statistician && statistician->GetStatistics(&stats, false)) {
|
||||
uint16_t min_rtt = 0;
|
||||
rtp_rtcp_->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL);
|
||||
return rtp_receiver_->RetransmitOfOldPacket(header, stats.jitter,
|
||||
min_rtt);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
// Retransmissions are handled separately if RTX is enabled.
|
||||
if (rtp_payload_registry_->RtxEnabled())
|
||||
return false;
|
||||
StreamStatistician* statistician =
|
||||
rtp_receive_statistics_->GetStatistician(header.ssrc);
|
||||
if (!statistician)
|
||||
return false;
|
||||
// Check if this is a retransmission.
|
||||
uint16_t min_rtt = 0;
|
||||
rtp_rtcp_->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL);
|
||||
return !IsPacketInOrder(header) &&
|
||||
statistician->IsRetransmitOfOldPacket(header, min_rtt);
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
||||
@ -24,6 +24,7 @@ namespace webrtc {
|
||||
|
||||
class CriticalSectionWrapper;
|
||||
class Encryption;
|
||||
class FecReceiver;
|
||||
class ReceiveStatistics;
|
||||
class RemoteBitrateEstimator;
|
||||
class RtpDump;
|
||||
@ -43,7 +44,7 @@ class ViEReceiver : public RtpData {
|
||||
bool SetReceiveCodec(const VideoCodec& video_codec);
|
||||
bool RegisterPayload(const VideoCodec& video_codec);
|
||||
|
||||
bool SetNackStatus(bool enable, int max_nack_reordering_threshold);
|
||||
void SetNackStatus(bool enable, int max_nack_reordering_threshold);
|
||||
void SetRtxStatus(bool enable, uint32_t ssrc);
|
||||
void SetRtxPayloadType(uint32_t payload_type);
|
||||
|
||||
@ -86,7 +87,15 @@ class ViEReceiver : public RtpData {
|
||||
|
||||
private:
|
||||
int InsertRTPPacket(const int8_t* rtp_packet, int rtp_packet_length);
|
||||
bool ReceivePacket(const uint8_t* packet, int packet_length,
|
||||
const RTPHeader& header, bool in_order);
|
||||
// Parses and handles for instance RTX and RED headers.
|
||||
// This function assumes that it's being called from only one thread.
|
||||
bool ParseAndHandleEncapsulatingHeader(const uint8_t* packet,
|
||||
int packet_length,
|
||||
const RTPHeader& header);
|
||||
int InsertRTCPPacket(const int8_t* rtcp_packet, int rtcp_packet_length);
|
||||
bool IsPacketInOrder(const RTPHeader& header) const;
|
||||
bool IsPacketRetransmitted(const RTPHeader& header) const;
|
||||
|
||||
scoped_ptr<CriticalSectionWrapper> receive_cs_;
|
||||
@ -95,6 +104,7 @@ class ViEReceiver : public RtpData {
|
||||
scoped_ptr<RTPPayloadRegistry> rtp_payload_registry_;
|
||||
scoped_ptr<RtpReceiver> rtp_receiver_;
|
||||
scoped_ptr<ReceiveStatistics> rtp_receive_statistics_;
|
||||
scoped_ptr<FecReceiver> fec_receiver_;
|
||||
RtpRtcp* rtp_rtcp_;
|
||||
std::list<RtpRtcp*> rtp_rtcp_simulcast_;
|
||||
VideoCodingModule* vcm_;
|
||||
@ -104,6 +114,8 @@ class ViEReceiver : public RtpData {
|
||||
uint8_t* decryption_buffer_;
|
||||
RtpDump* rtp_dump_;
|
||||
bool receiving_;
|
||||
uint8_t restored_packet_[kViEMaxMtu];
|
||||
bool restored_packet_in_use_;
|
||||
};
|
||||
|
||||
} // namespace webrt
|
||||
|
||||
@ -653,6 +653,21 @@ Channel::OnReceivedPayloadData(const uint8_t* payloadData,
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Channel::OnRecoveredPacket(const uint8_t* rtp_packet,
|
||||
int rtp_packet_length) {
|
||||
RTPHeader header;
|
||||
if (!rtp_header_parser_->Parse(rtp_packet, rtp_packet_length, &header)) {
|
||||
WEBRTC_TRACE(kTraceDebug, webrtc::kTraceVoice, _channelId,
|
||||
"IncomingPacket invalid RTP header");
|
||||
return false;
|
||||
}
|
||||
header.payload_type_frequency =
|
||||
rtp_payload_registry_->GetPayloadTypeFrequency(header.payloadType);
|
||||
if (header.payload_type_frequency < 0)
|
||||
return false;
|
||||
return ReceivePacket(rtp_packet, rtp_packet_length, header, false);
|
||||
}
|
||||
|
||||
int32_t Channel::GetAudioFrame(int32_t id, AudioFrame& audioFrame)
|
||||
{
|
||||
WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
|
||||
@ -989,7 +1004,8 @@ Channel::Channel(int32_t channelId,
|
||||
_RxVadDetection(false),
|
||||
_rxApmIsEnabled(false),
|
||||
_rxAgcIsEnabled(false),
|
||||
_rxNsIsEnabled(false)
|
||||
_rxNsIsEnabled(false),
|
||||
restored_packet_in_use_(false)
|
||||
{
|
||||
WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId),
|
||||
"Channel::Channel() - ctor");
|
||||
@ -2190,60 +2206,94 @@ int32_t Channel::ReceivedRTPPacket(const int8_t* data, int32_t length) {
|
||||
VoEId(_instanceId,_channelId),
|
||||
"Channel::SendPacket() RTP dump to input file failed");
|
||||
}
|
||||
const uint8_t* received_packet = reinterpret_cast<const uint8_t*>(data);
|
||||
RTPHeader header;
|
||||
if (!rtp_header_parser_->Parse(reinterpret_cast<const uint8_t*>(data),
|
||||
static_cast<uint16_t>(length), &header)) {
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo,
|
||||
VoEId(_instanceId,_channelId),
|
||||
"IncomingPacket invalid RTP header");
|
||||
if (!rtp_header_parser_->Parse(received_packet, length, &header)) {
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId,
|
||||
"Incoming packet: invalid RTP header");
|
||||
return -1;
|
||||
}
|
||||
header.payload_type_frequency =
|
||||
rtp_payload_registry_->GetPayloadTypeFrequency(header.payloadType);
|
||||
if (header.payload_type_frequency < 0) {
|
||||
if (header.payload_type_frequency < 0)
|
||||
return -1;
|
||||
rtp_receive_statistics_->IncomingPacket(header, length,
|
||||
IsPacketRetransmitted(header));
|
||||
rtp_payload_registry_->SetIncomingPayloadType(header);
|
||||
return ReceivePacket(received_packet, length, header,
|
||||
IsPacketInOrder(header)) ? 0 : -1;
|
||||
}
|
||||
|
||||
bool Channel::ReceivePacket(const uint8_t* packet,
|
||||
int packet_length,
|
||||
const RTPHeader& header,
|
||||
bool in_order) {
|
||||
if (rtp_payload_registry_->IsEncapsulated(header)) {
|
||||
return HandleEncapsulation(packet, packet_length, header);
|
||||
}
|
||||
bool retransmitted = IsPacketRetransmitted(header);
|
||||
bool in_order = rtp_receiver_->InOrderPacket(header.sequenceNumber);
|
||||
rtp_receive_statistics_->IncomingPacket(header, static_cast<uint16_t>(length),
|
||||
retransmitted, in_order);
|
||||
const uint8_t* payload = packet + header.headerLength;
|
||||
int payload_length = packet_length - header.headerLength;
|
||||
assert(payload_length >= 0);
|
||||
PayloadUnion payload_specific;
|
||||
if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
|
||||
&payload_specific)) {
|
||||
return -1;
|
||||
&payload_specific)) {
|
||||
return false;
|
||||
}
|
||||
// Deliver RTP packet to RTP/RTCP module for parsing
|
||||
// The packet will be pushed back to the channel thru the
|
||||
// OnReceivedPayloadData callback so we don't push it to the ACM here
|
||||
if (!rtp_receiver_->IncomingRtpPacket(&header,
|
||||
reinterpret_cast<const uint8_t*>(data),
|
||||
static_cast<uint16_t>(length),
|
||||
payload_specific, in_order)) {
|
||||
_engineStatisticsPtr->SetLastError(
|
||||
VE_SOCKET_TRANSPORT_MODULE_ERROR, kTraceWarning,
|
||||
"Channel::IncomingRTPPacket() RTP packet is invalid");
|
||||
return rtp_receiver_->IncomingRtpPacket(header, payload, payload_length,
|
||||
payload_specific, in_order);
|
||||
}
|
||||
|
||||
bool Channel::HandleEncapsulation(const uint8_t* packet,
|
||||
int packet_length,
|
||||
const RTPHeader& header) {
|
||||
if (!rtp_payload_registry_->IsRtx(header))
|
||||
return false;
|
||||
|
||||
// Remove the RTX header and parse the original RTP header.
|
||||
if (packet_length < header.headerLength)
|
||||
return false;
|
||||
if (packet_length > kVoiceEngineMaxIpPacketSizeBytes)
|
||||
return false;
|
||||
if (restored_packet_in_use_) {
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId,
|
||||
"Multiple RTX headers detected, dropping packet");
|
||||
return false;
|
||||
}
|
||||
return 0;
|
||||
uint8_t* restored_packet_ptr = restored_packet_;
|
||||
if (!rtp_payload_registry_->RestoreOriginalPacket(
|
||||
&restored_packet_ptr, packet, &packet_length, rtp_receiver_->SSRC(),
|
||||
header)) {
|
||||
WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId,
|
||||
"Incoming RTX packet: invalid RTP header");
|
||||
return false;
|
||||
}
|
||||
restored_packet_in_use_ = true;
|
||||
bool ret = OnRecoveredPacket(restored_packet_ptr, packet_length);
|
||||
restored_packet_in_use_ = false;
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool Channel::IsPacketInOrder(const RTPHeader& header) const {
|
||||
StreamStatistician* statistician =
|
||||
rtp_receive_statistics_->GetStatistician(header.ssrc);
|
||||
if (!statistician)
|
||||
return false;
|
||||
return statistician->IsPacketInOrder(header.sequenceNumber);
|
||||
}
|
||||
|
||||
bool Channel::IsPacketRetransmitted(const RTPHeader& header) const {
|
||||
bool rtx_enabled = false;
|
||||
uint32_t rtx_ssrc = 0;
|
||||
int rtx_payload_type = 0;
|
||||
rtp_receiver_->RTXStatus(&rtx_enabled, &rtx_ssrc, &rtx_payload_type);
|
||||
if (!rtx_enabled) {
|
||||
// Check if this is a retransmission.
|
||||
StreamStatistician::Statistics stats;
|
||||
StreamStatistician* statistician =
|
||||
rtp_receive_statistics_->GetStatistician(header.ssrc);
|
||||
if (statistician && statistician->GetStatistics(&stats, false)) {
|
||||
uint16_t min_rtt = 0;
|
||||
_rtpRtcpModule->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL);
|
||||
return rtp_receiver_->RetransmitOfOldPacket(header, stats.jitter,
|
||||
min_rtt);
|
||||
}
|
||||
}
|
||||
return false;
|
||||
// Retransmissions are handled separately if RTX is enabled.
|
||||
if (rtp_payload_registry_->RtxEnabled())
|
||||
return false;
|
||||
StreamStatistician* statistician =
|
||||
rtp_receive_statistics_->GetStatistician(header.ssrc);
|
||||
if (!statistician)
|
||||
return false;
|
||||
// Check if this is a retransmission.
|
||||
uint16_t min_rtt = 0;
|
||||
_rtpRtcpModule->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL);
|
||||
return !IsPacketInOrder(header) &&
|
||||
statistician->IsRetransmitOfOldPacket(header, min_rtt);
|
||||
}
|
||||
|
||||
int32_t Channel::ReceivedRTCPPacket(const int8_t* data, int32_t length) {
|
||||
@ -4182,8 +4232,8 @@ Channel::GetFECStatus(bool& enabled, int& redPayloadtype)
|
||||
void Channel::SetNACKStatus(bool enable, int maxNumberOfPackets) {
|
||||
// None of these functions can fail.
|
||||
_rtpRtcpModule->SetStorePacketsStatus(enable, maxNumberOfPackets);
|
||||
rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff,
|
||||
maxNumberOfPackets);
|
||||
rtp_receive_statistics_->SetMaxReorderingThreshold(maxNumberOfPackets);
|
||||
rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff);
|
||||
if (enable)
|
||||
_audioCodingModule.EnableNack(maxNumberOfPackets);
|
||||
else
|
||||
|
||||
@ -306,10 +306,7 @@ public:
|
||||
uint16_t payloadSize,
|
||||
const WebRtcRTPHeader* rtpHeader);
|
||||
|
||||
bool OnRecoveredPacket(const uint8_t* packet, int packet_length) {
|
||||
// Generic FEC not supported for audio.
|
||||
return true;
|
||||
}
|
||||
bool OnRecoveredPacket(const uint8_t* packet, int packet_length);
|
||||
|
||||
public:
|
||||
// From RtpFeedback in the RTP/RTCP module
|
||||
@ -439,6 +436,12 @@ public:
|
||||
uint32_t EncodeAndSend();
|
||||
|
||||
private:
|
||||
bool ReceivePacket(const uint8_t* packet, int packet_length,
|
||||
const RTPHeader& header, bool in_order);
|
||||
bool HandleEncapsulation(const uint8_t* packet,
|
||||
int packet_length,
|
||||
const RTPHeader& header);
|
||||
bool IsPacketInOrder(const RTPHeader& header) const;
|
||||
bool IsPacketRetransmitted(const RTPHeader& header) const;
|
||||
int ResendPackets(const uint16_t* sequence_numbers, int length);
|
||||
int InsertInbandDtmfTone();
|
||||
@ -502,6 +505,7 @@ private:
|
||||
uint32_t playout_delay_ms_;
|
||||
uint32_t _numberOfDiscardedPackets;
|
||||
uint16_t send_sequence_number_;
|
||||
uint8_t restored_packet_[kVoiceEngineMaxIpPacketSizeBytes];
|
||||
|
||||
private:
|
||||
// uses
|
||||
@ -571,6 +575,7 @@ private:
|
||||
bool _rxApmIsEnabled;
|
||||
bool _rxAgcIsEnabled;
|
||||
bool _rxNsIsEnabled;
|
||||
bool restored_packet_in_use_;
|
||||
};
|
||||
|
||||
} // namespace voe
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user