summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--modules/interface/module_common_types.h1
-rw-r--r--modules/rtp_rtcp/interface/rtp_rtcp_defines.h1
-rw-r--r--modules/rtp_rtcp/source/rtp_header_extension.h19
-rw-r--r--modules/rtp_rtcp/source/rtp_sender.cc147
-rw-r--r--modules/rtp_rtcp/source/rtp_sender.h9
-rw-r--r--modules/rtp_rtcp/source/rtp_sender_unittest.cc170
-rw-r--r--modules/rtp_rtcp/source/rtp_utility.cc21
7 files changed, 328 insertions, 40 deletions
diff --git a/modules/interface/module_common_types.h b/modules/interface/module_common_types.h
index f0b492a0..b46630aa 100644
--- a/modules/interface/module_common_types.h
+++ b/modules/interface/module_common_types.h
@@ -43,6 +43,7 @@ struct RTPHeader
struct RTPHeaderExtension
{
int32_t transmissionTimeOffset;
+ uint32_t absoluteSendTime;
};
struct RTPAudioHeader
diff --git a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
index 3da5d752..55c2fb1d 100644
--- a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
+++ b/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
@@ -53,6 +53,7 @@ enum RTPExtensionType
kRtpExtensionNone,
kRtpExtensionTransmissionTimeOffset,
kRtpExtensionAudioLevel,
+ kRtpExtensionAbsoluteSendTime
};
enum RTCPAppSubTypes
diff --git a/modules/rtp_rtcp/source/rtp_header_extension.h b/modules/rtp_rtcp/source/rtp_header_extension.h
index 722bc391..35391d2c 100644
--- a/modules/rtp_rtcp/source/rtp_header_extension.h
+++ b/modules/rtp_rtcp/source/rtp_header_extension.h
@@ -22,15 +22,26 @@ const uint16_t kRtpOneByteHeaderExtensionId = 0xBEDE;
const size_t kRtpOneByteHeaderLength = 4;
const size_t kTransmissionTimeOffsetLength = 4;
+const size_t kAbsoluteSendTimeLength = 4;
struct HeaderExtension {
HeaderExtension(RTPExtensionType extension_type)
: type(extension_type),
length(0) {
- if (type == kRtpExtensionTransmissionTimeOffset) {
- length = kTransmissionTimeOffsetLength;
- }
- }
+ // TODO(solenberg): Create handler classes for header extensions so we can
+ // get rid of switches like these as well as handling code spread out all
+ // over.
+ switch (type) {
+ case kRtpExtensionTransmissionTimeOffset:
+ length = kTransmissionTimeOffsetLength;
+ break;
+ case kRtpExtensionAbsoluteSendTime:
+ length = kAbsoluteSendTimeLength;
+ break;
+ default:
+ assert(false);
+ }
+ }
const RTPExtensionType type;
uint8_t length;
diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc
index cfc7c662..91dd1d26 100644
--- a/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/modules/rtp_rtcp/source/rtp_sender.cc
@@ -49,7 +49,7 @@ RTPSender::RTPSender(const int32_t id, const bool audio, Clock *clock,
max_payload_length_(IP_PACKET_SIZE - 28), // Default is IP-v4/UDP.
target_send_bitrate_(0), packet_over_head_(28), payload_type_(-1),
payload_type_map_(), rtp_header_extension_map_(),
- transmission_time_offset_(0),
+ transmission_time_offset_(0), absolute_send_time_(0),
// NACK.
nack_byte_count_times_(), nack_byte_count_(), nack_bitrate_(clock),
packet_history_(new RTPPacketHistory(clock)),
@@ -137,6 +137,16 @@ int32_t RTPSender::SetTransmissionTimeOffset(
return 0;
}
+int32_t RTPSender::SetAbsoluteSendTime(
+ const uint32_t absolute_send_time) {
+ if (absolute_send_time > 0xffffff) { // UWord24.
+ return -1;
+ }
+ CriticalSectionScoped cs(send_critsect_);
+ absolute_send_time_ = absolute_send_time;
+ return 0;
+}
+
int32_t RTPSender::RegisterRtpHeaderExtension(const RTPExtensionType type,
const uint8_t id) {
CriticalSectionScoped cs(send_critsect_);
@@ -350,7 +360,7 @@ int32_t RTPSender::SendOutgoingData(
"timestamp", capture_timestamp);
} else {
TRACE_EVENT_INSTANT2("webrtc_rtp", "SendFrame",
- "timestsamp", capture_timestamp,
+ "timestamp", capture_timestamp,
"frame_type", FrameTypeToString(frame_type));
}
@@ -675,8 +685,13 @@ void RTPSender::TimeToSendPacket(uint16_t sequence_number,
"timestamp", rtp_header.header.timestamp,
"seqnum", sequence_number);
- int64_t diff_ms = clock_->TimeInMilliseconds() - capture_time_ms;
- if (UpdateTransmissionTimeOffset(data_buffer, length, rtp_header, diff_ms)) {
+ int64_t now_ms = clock_->TimeInMilliseconds();
+ int64_t diff_ms = now_ms - capture_time_ms;
+ bool updated_transmission_time_offset =
+ UpdateTransmissionTimeOffset(data_buffer, length, rtp_header, diff_ms);
+ bool updated_abs_send_time =
+ UpdateAbsoluteSendTime(data_buffer, length, rtp_header, now_ms);
+ if (updated_transmission_time_offset || updated_abs_send_time) {
// Update stored packet in case of receiving a re-transmission request.
packet_history_->ReplaceRTPHeader(data_buffer,
rtp_header.header.sequenceNumber,
@@ -694,14 +709,19 @@ int32_t RTPSender::SendToNetwork(
WebRtcRTPHeader rtp_header;
rtp_parser.Parse(rtp_header);
+ int64_t now_ms = clock_->TimeInMilliseconds();
+
// |capture_time_ms| <= 0 is considered invalid.
// TODO(holmer): This should be changed all over Video Engine so that negative
// time is consider invalid, while 0 is considered a valid time.
if (capture_time_ms > 0) {
- int64_t time_now = clock_->TimeInMilliseconds();
UpdateTransmissionTimeOffset(buffer, payload_length + rtp_header_length,
- rtp_header, time_now - capture_time_ms);
+ rtp_header, now_ms - capture_time_ms);
}
+
+ UpdateAbsoluteSendTime(buffer, payload_length + rtp_header_length,
+ rtp_header, now_ms);
+
// Used for NACK and to spread out the transmission of packets.
if (packet_history_->PutRTPPacket(buffer, rtp_header_length + payload_length,
max_payload_length_, capture_time_ms,
@@ -867,9 +887,17 @@ uint16_t RTPSender::BuildRTPHeaderExtension(
RTPExtensionType type = rtp_header_extension_map_.First();
while (type != kRtpExtensionNone) {
uint8_t block_length = 0;
- if (type == kRtpExtensionTransmissionTimeOffset) {
- block_length = BuildTransmissionTimeOffsetExtension(
- data_buffer + kHeaderLength + total_block_length);
+ switch (type) {
+ case kRtpExtensionTransmissionTimeOffset:
+ block_length = BuildTransmissionTimeOffsetExtension(
+ data_buffer + kHeaderLength + total_block_length);
+ break;
+ case kRtpExtensionAbsoluteSendTime:
+ block_length = BuildAbsoluteSendTimeExtension(
+ data_buffer + kHeaderLength + total_block_length);
+ break;
+ default:
+ assert(false);
}
total_block_length += block_length;
type = rtp_header_extension_map_.Next(type);
@@ -922,23 +950,59 @@ uint8_t RTPSender::BuildTransmissionTimeOffsetExtension(
return kTransmissionTimeOffsetLength;
}
+uint8_t RTPSender::BuildAbsoluteSendTimeExtension(
+ uint8_t* data_buffer) const {
+ // Absolute send time in RTP streams.
+ //
+ // The absolute send time is signaled to the receiver in-band using the
+ // general mechanism for RTP header extensions [RFC5285]. The payload
+ // of this extension (the transmitted value) is a 24-bit unsigned integer
+ // containing the sender's current time in seconds as a fixed point number
+ // with 18 bits fractional part.
+ //
+ // The form of the absolute send time extension block:
+ //
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | ID | len=2 | absolute send time |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ // Get id defined by user.
+ uint8_t id;
+ if (rtp_header_extension_map_.GetId(kRtpExtensionAbsoluteSendTime,
+ &id) != 0) {
+ // Not registered.
+ return 0;
+ }
+ size_t pos = 0;
+ const uint8_t len = 2;
+ data_buffer[pos++] = (id << 4) + len;
+ ModuleRTPUtility::AssignUWord24ToBuffer(data_buffer + pos,
+ absolute_send_time_);
+ pos += 3;
+ assert(pos == kAbsoluteSendTimeLength);
+ return kAbsoluteSendTimeLength;
+}
+
bool RTPSender::UpdateTransmissionTimeOffset(
uint8_t *rtp_packet, const uint16_t rtp_packet_length,
const WebRtcRTPHeader &rtp_header, const int64_t time_diff_ms) const {
CriticalSectionScoped cs(send_critsect_);
- // Get length until start of transmission block.
- int transmission_block_pos =
+ // Get length until start of header extension block.
+ int extension_block_pos =
rtp_header_extension_map_.GetLengthUntilBlockStartInBytes(
kRtpExtensionTransmissionTimeOffset);
- if (transmission_block_pos < 0) {
+ if (extension_block_pos < 0) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
"Failed to update transmission time offset, not registered.");
return false;
}
- int block_pos = 12 + rtp_header.header.numCSRCs + transmission_block_pos;
- if (rtp_packet_length < block_pos + 4 ||
- rtp_header.header.headerLength < block_pos + 4) {
+ int block_pos = 12 + rtp_header.header.numCSRCs + extension_block_pos;
+ if (rtp_packet_length < block_pos + kTransmissionTimeOffsetLength ||
+ rtp_header.header.headerLength <
+ block_pos + kTransmissionTimeOffsetLength) {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
"Failed to update transmission time offset, invalid length.");
return false;
@@ -966,12 +1030,63 @@ bool RTPSender::UpdateTransmissionTimeOffset(
"Failed to update transmission time offset.");
return false;
}
- // Update transmission offset field.
+ // Update transmission offset field (converting to a 90 kHz timestamp).
ModuleRTPUtility::AssignUWord24ToBuffer(rtp_packet + block_pos + 1,
time_diff_ms * 90); // RTP timestamp.
return true;
}
+bool RTPSender::UpdateAbsoluteSendTime(
+ uint8_t *rtp_packet, const uint16_t rtp_packet_length,
+ const WebRtcRTPHeader &rtp_header, const int64_t now_ms) const {
+ CriticalSectionScoped cs(send_critsect_);
+
+ // Get length until start of header extension block.
+ int extension_block_pos =
+ rtp_header_extension_map_.GetLengthUntilBlockStartInBytes(
+ kRtpExtensionAbsoluteSendTime);
+ if (extension_block_pos < 0) {
+ WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
+ "Failed to update absolute send time, not registered.");
+ return false;
+ }
+ int block_pos = 12 + rtp_header.header.numCSRCs + extension_block_pos;
+ if (rtp_packet_length < block_pos + kAbsoluteSendTimeLength ||
+ rtp_header.header.headerLength < block_pos + kAbsoluteSendTimeLength) {
+ WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
+ "Failed to update absolute send time, invalid length.");
+ return false;
+ }
+ // Verify that header contains extension.
+ if (!((rtp_packet[12 + rtp_header.header.numCSRCs] == 0xBE) &&
+ (rtp_packet[12 + rtp_header.header.numCSRCs + 1] == 0xDE))) {
+ WEBRTC_TRACE(
+ kTraceStream, kTraceRtpRtcp, id_,
+ "Failed to update absolute send time, hdr extension not found.");
+ return false;
+ }
+ // Get id.
+ uint8_t id = 0;
+ if (rtp_header_extension_map_.GetId(kRtpExtensionAbsoluteSendTime,
+ &id) != 0) {
+ WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
+ "Failed to update absolute send time, no id.");
+ return false;
+ }
+ // Verify first byte in block.
+ const uint8_t first_block_byte = (id << 4) + 2;
+ if (rtp_packet[block_pos] != first_block_byte) {
+ WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
+ "Failed to update absolute send time.");
+ return false;
+ }
+ // Update absolute send time field (convert ms to 24-bit unsigned with 18 bit
+ // fractional part).
+ ModuleRTPUtility::AssignUWord24ToBuffer(rtp_packet + block_pos + 1,
+ ((now_ms << 18) / 1000) & 0x00ffffff);
+ return true;
+}
+
void RTPSender::SetSendingStatus(const bool enabled) {
if (enabled) {
uint32_t frequency_hz;
diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h
index 2e207b52..2aa43d2e 100644
--- a/modules/rtp_rtcp/source/rtp_sender.h
+++ b/modules/rtp_rtcp/source/rtp_sender.h
@@ -136,6 +136,8 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
// RTP header extension
int32_t SetTransmissionTimeOffset(
const int32_t transmission_time_offset);
+ int32_t SetAbsoluteSendTime(
+ const uint32_t absolute_send_time);
int32_t RegisterRtpHeaderExtension(const RTPExtensionType type,
const uint8_t id);
@@ -148,11 +150,17 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
uint8_t BuildTransmissionTimeOffsetExtension(
uint8_t *data_buffer) const;
+ uint8_t BuildAbsoluteSendTimeExtension(
+ uint8_t* data_buffer) const;
bool UpdateTransmissionTimeOffset(uint8_t *rtp_packet,
const uint16_t rtp_packet_length,
const WebRtcRTPHeader &rtp_header,
const int64_t time_diff_ms) const;
+ bool UpdateAbsoluteSendTime(uint8_t *rtp_packet,
+ const uint16_t rtp_packet_length,
+ const WebRtcRTPHeader &rtp_header,
+ const int64_t now_ms) const;
void TimeToSendPacket(uint16_t sequence_number, int64_t capture_time_ms);
@@ -283,6 +291,7 @@ class RTPSender : public Bitrate, public RTPSenderInterface {
RtpHeaderExtensionMap rtp_header_extension_map_;
int32_t transmission_time_offset_;
+ uint32_t absolute_send_time_;
// NACK
uint32_t nack_byte_count_times_[NACK_BYTECOUNT_SIZE];
diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index 02448bd0..f62de5d6 100644
--- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -26,13 +26,14 @@
namespace webrtc {
namespace {
-const int kId = 1;
-const int kTypeLength = kTransmissionTimeOffsetLength;
+const int kTransmissionTimeOffsetExtensionId = 1;
+const int kAbsoluteSendTimeExtensionId = 14;
const int kPayload = 100;
const uint32_t kTimestamp = 10;
const uint16_t kSeqNum = 33;
const int kTimeOffset = 22222;
const int kMaxPacketLength = 1500;
+const uint32_t kAbsoluteSendTime = 0x00aabbcc;
} // namespace
using testing::_;
@@ -60,12 +61,11 @@ class LoopbackTransportTest : public webrtc::Transport {
class RtpSenderTest : public ::testing::Test {
protected:
RtpSenderTest()
- : fake_clock_(123456),
+ : fake_clock_(123456789),
mock_paced_sender_(),
rtp_sender_(new RTPSender(0, false, &fake_clock_, &transport_, NULL,
&mock_paced_sender_)),
- kMarkerBit(true),
- kType(kRtpExtensionTransmissionTimeOffset) {
+ kMarkerBit(true) {
rtp_sender_->SetSequenceNumber(kSeqNum);
EXPECT_CALL(mock_paced_sender_,
SendPacket(_, _, _, _, _)).WillRepeatedly(testing::Return(true));
@@ -75,7 +75,6 @@ class RtpSenderTest : public ::testing::Test {
scoped_ptr<RTPSender> rtp_sender_;
LoopbackTransportTest transport_;
const bool kMarkerBit;
- RTPExtensionType kType;
uint8_t packet_[kMaxPacketLength];
void VerifyRTPHeaderCommon(const WebRtcRTPHeader& rtp_header) {
@@ -89,12 +88,44 @@ class RtpSenderTest : public ::testing::Test {
}
};
-TEST_F(RtpSenderTest, RegisterRtpHeaderExtension) {
+TEST_F(RtpSenderTest, RegisterRtpTransmissionTimeOffsetHeaderExtension) {
EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
- EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(kType, kId));
- EXPECT_EQ(kRtpOneByteHeaderLength + kTypeLength,
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId));
+ EXPECT_EQ(kRtpOneByteHeaderLength + kTransmissionTimeOffsetLength,
rtp_sender_->RtpHeaderExtensionTotalLength());
- EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension(kType));
+ EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension(
+ kRtpExtensionTransmissionTimeOffset));
+ EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
+}
+
+TEST_F(RtpSenderTest, RegisterRtpAbsoluteSendTimeHeaderExtension) {
+ EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
+ EXPECT_EQ(kRtpOneByteHeaderLength + kAbsoluteSendTimeLength,
+ rtp_sender_->RtpHeaderExtensionTotalLength());
+ EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension(
+ kRtpExtensionAbsoluteSendTime));
+ EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
+}
+
+TEST_F(RtpSenderTest, RegisterRtpHeaderExtensions) {
+ EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId));
+ EXPECT_EQ(kRtpOneByteHeaderLength + kTransmissionTimeOffsetLength,
+ rtp_sender_->RtpHeaderExtensionTotalLength());
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
+ EXPECT_EQ(kRtpOneByteHeaderLength + kTransmissionTimeOffsetLength +
+ kAbsoluteSendTimeLength, rtp_sender_->RtpHeaderExtensionTotalLength());
+ EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension(
+ kRtpExtensionTransmissionTimeOffset));
+ EXPECT_EQ(kRtpOneByteHeaderLength + kAbsoluteSendTimeLength,
+ rtp_sender_->RtpHeaderExtensionTotalLength());
+ EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension(
+ kRtpExtensionAbsoluteSendTime));
EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
}
@@ -110,7 +141,8 @@ TEST_F(RtpSenderTest, BuildRTPPacket) {
webrtc::WebRtcRTPHeader rtp_header;
RtpHeaderExtensionMap map;
- map.Register(kType, kId);
+ map.Register(kRtpExtensionTransmissionTimeOffset,
+ kTransmissionTimeOffsetExtensionId);
const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map);
ASSERT_TRUE(valid_rtp_header);
@@ -118,11 +150,13 @@ TEST_F(RtpSenderTest, BuildRTPPacket) {
VerifyRTPHeaderCommon(rtp_header);
EXPECT_EQ(length, rtp_header.header.headerLength);
EXPECT_EQ(0, rtp_header.extension.transmissionTimeOffset);
+ EXPECT_EQ(0u, rtp_header.extension.absoluteSendTime);
}
TEST_F(RtpSenderTest, BuildRTPPacketWithTransmissionOffsetExtension) {
EXPECT_EQ(0, rtp_sender_->SetTransmissionTimeOffset(kTimeOffset));
- EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(kType, kId));
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId));
int32_t length = rtp_sender_->BuildRTPheader(packet_,
kPayload,
@@ -135,7 +169,8 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithTransmissionOffsetExtension) {
webrtc::WebRtcRTPHeader rtp_header;
RtpHeaderExtensionMap map;
- map.Register(kType, kId);
+ map.Register(kRtpExtensionTransmissionTimeOffset,
+ kTransmissionTimeOffsetExtensionId);
const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map);
ASSERT_TRUE(valid_rtp_header);
@@ -157,7 +192,8 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithTransmissionOffsetExtension) {
TEST_F(RtpSenderTest, BuildRTPPacketWithNegativeTransmissionOffsetExtension) {
const int kNegTimeOffset = -500;
EXPECT_EQ(0, rtp_sender_->SetTransmissionTimeOffset(kNegTimeOffset));
- EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(kType, kId));
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId));
int32_t length = rtp_sender_->BuildRTPheader(packet_,
kPayload,
@@ -170,7 +206,8 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithNegativeTransmissionOffsetExtension) {
webrtc::WebRtcRTPHeader rtp_header;
RtpHeaderExtensionMap map;
- map.Register(kType, kId);
+ map.Register(kRtpExtensionTransmissionTimeOffset,
+ kTransmissionTimeOffsetExtensionId);
const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map);
ASSERT_TRUE(valid_rtp_header);
@@ -180,13 +217,93 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithNegativeTransmissionOffsetExtension) {
EXPECT_EQ(kNegTimeOffset, rtp_header.extension.transmissionTimeOffset);
}
-TEST_F(RtpSenderTest, TrafficSmoothingWithTimeOffset) {
+TEST_F(RtpSenderTest, BuildRTPPacketWithAbsoluteSendTimeExtension) {
+ EXPECT_EQ(0, rtp_sender_->SetAbsoluteSendTime(kAbsoluteSendTime));
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
+
+ int32_t length = rtp_sender_->BuildRTPheader(packet_,
+ kPayload,
+ kMarkerBit,
+ kTimestamp);
+ EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length);
+
+ // Verify
+ webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length);
+ webrtc::WebRtcRTPHeader rtp_header;
+
+ RtpHeaderExtensionMap map;
+ map.Register(kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId);
+ const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map);
+
+ ASSERT_TRUE(valid_rtp_header);
+ ASSERT_FALSE(rtp_parser.RTCP());
+ VerifyRTPHeaderCommon(rtp_header);
+ EXPECT_EQ(length, rtp_header.header.headerLength);
+ EXPECT_EQ(kAbsoluteSendTime, rtp_header.extension.absoluteSendTime);
+
+ // Parse without map extension
+ webrtc::WebRtcRTPHeader rtp_header2;
+ const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, NULL);
+
+ ASSERT_TRUE(valid_rtp_header2);
+ VerifyRTPHeaderCommon(rtp_header2);
+ EXPECT_EQ(length, rtp_header2.header.headerLength);
+ EXPECT_EQ(0u, rtp_header2.extension.absoluteSendTime);
+}
+
+TEST_F(RtpSenderTest, BuildRTPPacketWithHeaderExtensions) {
+ EXPECT_EQ(0, rtp_sender_->SetTransmissionTimeOffset(kTimeOffset));
+ EXPECT_EQ(0, rtp_sender_->SetAbsoluteSendTime(kAbsoluteSendTime));
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId));
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
+
+ int32_t length = rtp_sender_->BuildRTPheader(packet_,
+ kPayload,
+ kMarkerBit,
+ kTimestamp);
+ EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length);
+
+ // Verify
+ webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length);
+ webrtc::WebRtcRTPHeader rtp_header;
+
+ RtpHeaderExtensionMap map;
+ map.Register(kRtpExtensionTransmissionTimeOffset,
+ kTransmissionTimeOffsetExtensionId);
+ map.Register(kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId);
+ const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map);
+
+ ASSERT_TRUE(valid_rtp_header);
+ ASSERT_FALSE(rtp_parser.RTCP());
+ VerifyRTPHeaderCommon(rtp_header);
+ EXPECT_EQ(length, rtp_header.header.headerLength);
+ EXPECT_EQ(kTimeOffset, rtp_header.extension.transmissionTimeOffset);
+ EXPECT_EQ(kAbsoluteSendTime, rtp_header.extension.absoluteSendTime);
+
+ // Parse without map extension
+ webrtc::WebRtcRTPHeader rtp_header2;
+ const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, NULL);
+
+ ASSERT_TRUE(valid_rtp_header2);
+ VerifyRTPHeaderCommon(rtp_header2);
+ EXPECT_EQ(length, rtp_header2.header.headerLength);
+ EXPECT_EQ(0, rtp_header2.extension.transmissionTimeOffset);
+ EXPECT_EQ(0u, rtp_header2.extension.absoluteSendTime);
+}
+
+TEST_F(RtpSenderTest, TrafficSmoothingWithExtensions) {
EXPECT_CALL(mock_paced_sender_,
SendPacket(PacedSender::kNormalPriority, _, kSeqNum, _, _)).
WillOnce(testing::Return(false));
rtp_sender_->SetStorePacketsStatus(true, 10);
- EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(kType, kId));
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId));
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
rtp_sender_->SetTargetSendBitrate(300000);
int32_t rtp_length = rtp_sender_->BuildRTPheader(packet_,
kPayload,
@@ -217,12 +334,17 @@ TEST_F(RtpSenderTest, TrafficSmoothingWithTimeOffset) {
transport_.last_sent_packet_, rtp_length);
webrtc::WebRtcRTPHeader rtp_header;
RtpHeaderExtensionMap map;
- map.Register(kType, kId);
+ map.Register(kRtpExtensionTransmissionTimeOffset,
+ kTransmissionTimeOffsetExtensionId);
+ map.Register(kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId);
const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map);
ASSERT_TRUE(valid_rtp_header);
// Verify transmission time offset.
EXPECT_EQ(kStoredTimeInMs * 90, rtp_header.extension.transmissionTimeOffset);
+ uint64_t expected_send_time =
+ 0x00fffffful & ((fake_clock_.TimeInMilliseconds() << 18) / 1000);
+ EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
}
TEST_F(RtpSenderTest, TrafficSmoothingRetransmits) {
@@ -231,7 +353,10 @@ TEST_F(RtpSenderTest, TrafficSmoothingRetransmits) {
WillOnce(testing::Return(false));
rtp_sender_->SetStorePacketsStatus(true, 10);
- EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(kType, kId));
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId));
+ EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+ kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
rtp_sender_->SetTargetSendBitrate(300000);
int32_t rtp_length = rtp_sender_->BuildRTPheader(packet_,
kPayload,
@@ -270,12 +395,17 @@ TEST_F(RtpSenderTest, TrafficSmoothingRetransmits) {
transport_.last_sent_packet_, rtp_length);
webrtc::WebRtcRTPHeader rtp_header;
RtpHeaderExtensionMap map;
- map.Register(kType, kId);
+ map.Register(kRtpExtensionTransmissionTimeOffset,
+ kTransmissionTimeOffsetExtensionId);
+ map.Register(kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId);
const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map);
ASSERT_TRUE(valid_rtp_header);
// Verify transmission time offset.
EXPECT_EQ(kStoredTimeInMs * 90, rtp_header.extension.transmissionTimeOffset);
+ uint64_t expected_send_time =
+ 0x00fffffful & ((fake_clock_.TimeInMilliseconds() << 18) / 1000);
+ EXPECT_EQ(expected_send_time, rtp_header.extension.absoluteSendTime);
}
TEST_F(RtpSenderTest, SendGenericVideo) {
diff --git a/modules/rtp_rtcp/source/rtp_utility.cc b/modules/rtp_rtcp/source/rtp_utility.cc
index b0011013..5f3a229a 100644
--- a/modules/rtp_rtcp/source/rtp_utility.cc
+++ b/modules/rtp_rtcp/source/rtp_utility.cc
@@ -355,6 +355,9 @@ bool RTPHeaderParser::Parse(WebRtcRTPHeader& parsedPacket,
// is zero.
parsedPacket.extension.transmissionTimeOffset = 0;
+ // May not be present in packet.
+ parsedPacket.extension.absoluteSendTime = 0;
+
if (X) {
/* RTP header extension, RFC 3550.
0 1 2 3
@@ -466,6 +469,24 @@ void RTPHeaderParser::ParseOneByteExtensionHeader(
// level=%u", ID, len, V, level);
break;
}
+ case kRtpExtensionAbsoluteSendTime: {
+ if (len != 2) {
+ WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
+ "Incorrect absolute send time len: %d", len);
+ return;
+ }
+ // 0 1 2 3
+ // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ // | ID | len=2 | absolute send time |
+ // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+ uint32_t absoluteSendTime = *ptr++ << 16;
+ absoluteSendTime += *ptr++ << 8;
+ absoluteSendTime += *ptr++;
+ parsedPacket.extension.absoluteSendTime = absoluteSendTime;
+ break;
+ }
default: {
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"Extension type not implemented.");