diff options
-rw-r--r-- | modules/interface/module_common_types.h | 1 | ||||
-rw-r--r-- | modules/rtp_rtcp/interface/rtp_rtcp_defines.h | 1 | ||||
-rw-r--r-- | modules/rtp_rtcp/source/rtp_header_extension.h | 19 | ||||
-rw-r--r-- | modules/rtp_rtcp/source/rtp_sender.cc | 147 | ||||
-rw-r--r-- | modules/rtp_rtcp/source/rtp_sender.h | 9 | ||||
-rw-r--r-- | modules/rtp_rtcp/source/rtp_sender_unittest.cc | 170 | ||||
-rw-r--r-- | modules/rtp_rtcp/source/rtp_utility.cc | 21 |
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."); |