From ebdb0e3ad0a787bee066d12cdcd115a38b0a10d1 Mon Sep 17 00:00:00 2001 From: "wu@webrtc.org" Date: Thu, 6 Mar 2014 23:49:08 +0000 Subject: [PATCH] Help to land 7969005 on behalf of solenberg. The review and try is done in 7969005. - Add ability to VoE to send Absolute Sender Time header extension. - Refactor handling of RTP header extensions in VoE to work the same as in ViE. - Add API to enable receiving Absolute Sender Time in VoE. This is part of the work to include audio packets in bandwidth estimation, for better accuracy in estimates. BUG= TBR=solenberg@webrtc.org,henrikg@webrtc.org,stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/9509004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5654 4adac7df-926f-26a2-2b94-8c16560cd09d --- .../modules/interface/module_common_types.h | 5 + webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h | 19 --- .../rtp_rtcp/source/rtp_header_extension.h | 8 +- .../modules/rtp_rtcp/source/rtp_rtcp_impl.cc | 23 --- .../modules/rtp_rtcp/source/rtp_rtcp_impl.h | 8 - webrtc/modules/rtp_rtcp/source/rtp_sender.cc | 105 ++++++++++-- webrtc/modules/rtp_rtcp/source/rtp_sender.h | 18 +- .../rtp_rtcp/source/rtp_sender_audio.cc | 89 ++-------- .../rtp_rtcp/source/rtp_sender_audio.h | 13 +- .../rtp_rtcp/source/rtp_sender_unittest.cc | 155 ++++++++++-------- webrtc/modules/rtp_rtcp/source/rtp_utility.cc | 13 +- webrtc/voice_engine/channel.cc | 44 +++-- webrtc/voice_engine/channel.h | 7 +- webrtc/voice_engine/include/voe_rtp_rtcp.h | 17 +- .../auto_test/standard/rtp_rtcp_extensions.cc | 147 +++++++++++++++++ webrtc/voice_engine/voe_rtp_rtcp_impl.cc | 102 ++++++++---- webrtc/voice_engine/voe_rtp_rtcp_impl.h | 16 +- webrtc/voice_engine/voice_engine.gyp | 1 + 18 files changed, 490 insertions(+), 300 deletions(-) create mode 100644 webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_extensions.cc diff --git a/webrtc/modules/interface/module_common_types.h b/webrtc/modules/interface/module_common_types.h index 2494d68b9..f9f1676f0 100644 --- a/webrtc/modules/interface/module_common_types.h +++ b/webrtc/modules/interface/module_common_types.h @@ -32,6 +32,11 @@ struct RTPHeaderExtension { int32_t transmissionTimeOffset; bool hasAbsoluteSendTime; uint32_t absoluteSendTime; + + // Audio Level includes both level in dBov and voiced/unvoiced bit. See: + // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/ + bool hasAudioLevel; + uint8_t audioLevel; }; struct RTPHeader { diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h index 080aafdb6..cd8550c8e 100644 --- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h +++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h @@ -670,25 +670,6 @@ class RtpRtcp : public Module { virtual int32_t SendREDPayloadType( int8_t& payloadType) const = 0; - /* - * Set status and ID for header-extension-for-audio-level-indication. - * See http://tools.ietf.org/html/rfc6464 for more details. - * - * return -1 on failure else 0 - */ - virtual int32_t SetRTPAudioLevelIndicationStatus( - const bool enable, - const uint8_t ID) = 0; - - /* - * Get status and ID for header-extension-for-audio-level-indication. - * - * return -1 on failure else 0 - */ - virtual int32_t GetRTPAudioLevelIndicationStatus( - bool& enable, - uint8_t& ID) const = 0; - /* * Store the audio level in dBov for header-extension-for-audio-level- * indication. diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h b/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h index fcc7587c1..b41653c8b 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h @@ -22,7 +22,7 @@ const uint16_t kRtpOneByteHeaderExtensionId = 0xBEDE; const size_t kRtpOneByteHeaderLength = 4; const size_t kTransmissionTimeOffsetLength = 4; -const size_t kAudioLevelLength = 2; +const size_t kAudioLevelLength = 4; const size_t kAbsoluteSendTimeLength = 4; struct HeaderExtension { @@ -37,11 +37,7 @@ struct HeaderExtension { length = kTransmissionTimeOffsetLength; break; case kRtpExtensionAudioLevel: - // TODO(solenberg): Because of how the audio level extension is handled - // in RTPSenderAudio::SendAudio(), we cannot set the actual length here - // but must leave it at zero. The consequence is that any other header - // extensions registered for an audio channel are effectively ignored. - // length = kAudioLevelLength; + length = kAudioLevelLength; break; case kRtpExtensionAbsoluteSendTime: length = kAbsoluteSendTimeLength; diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index b96094191..575da60a3 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -1209,29 +1209,6 @@ int32_t ModuleRtpRtcpImpl::SetAudioPacketSize( return rtp_sender_.SetAudioPacketSize(packet_size_samples); } -int32_t ModuleRtpRtcpImpl::SetRTPAudioLevelIndicationStatus( - const bool enable, - const uint8_t id) { - - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "SetRTPAudioLevelIndicationStatus(enable=%d, ID=%u)", - enable, - id); - return rtp_sender_.SetAudioLevelIndicationStatus(enable, id); -} - -int32_t ModuleRtpRtcpImpl::GetRTPAudioLevelIndicationStatus( - bool& enable, - uint8_t& id) const { - WEBRTC_TRACE(kTraceModuleCall, - kTraceRtpRtcp, - id_, - "GetRTPAudioLevelIndicationStatus()"); - return rtp_sender_.AudioLevelIndicationStatus(&enable, &id); -} - int32_t ModuleRtpRtcpImpl::SetAudioLevel( const uint8_t level_d_bov) { WEBRTC_TRACE(kTraceModuleCall, diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h index 34c4fa2fd..d8cbc80d5 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -295,14 +295,6 @@ class ModuleRtpRtcpImpl : public RtpRtcp { // Get payload type for Redundant Audio Data RFC 2198. virtual int32_t SendREDPayloadType(int8_t& payload_type) const OVERRIDE; - // Set status and id for header-extension-for-audio-level-indication. - virtual int32_t SetRTPAudioLevelIndicationStatus( - const bool enable, const uint8_t id) OVERRIDE; - - // Get status and id for header-extension-for-audio-level-indication. - virtual int32_t GetRTPAudioLevelIndicationStatus( - bool& enable, uint8_t& id) const OVERRIDE; - // Store the audio level in d_bov for header-extension-for-audio-level- // indication. virtual int32_t SetAudioLevel(const uint8_t level_d_bov) OVERRIDE; diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc index 0711356e7..af1c49bce 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc @@ -1117,9 +1117,8 @@ uint16_t RTPSender::BuildRTPHeaderExtension(uint8_t* data_buffer) const { data_buffer + kHeaderLength + total_block_length); break; case kRtpExtensionAudioLevel: - // Because AudioLevel is handled specially by RTPSenderAudio, we pretend - // we don't have to care about it here, which is true until we wan't to - // use it together with any of the other extensions we support. + block_length = BuildAudioLevelExtension( + data_buffer + kHeaderLength + total_block_length); break; case kRtpExtensionAbsoluteSendTime: block_length = BuildAbsoluteSendTimeExtension( @@ -1179,8 +1178,42 @@ uint8_t RTPSender::BuildTransmissionTimeOffsetExtension( return kTransmissionTimeOffsetLength; } -uint8_t RTPSender::BuildAbsoluteSendTimeExtension( - uint8_t* data_buffer) const { +uint8_t RTPSender::BuildAudioLevelExtension(uint8_t* data_buffer) const { + // An RTP Header Extension for Client-to-Mixer Audio Level Indication + // + // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/ + // + // The form of the audio level 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=0 |V| level | 0x00 | 0x00 | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // + // Note that we always include 2 pad bytes, which will result in legal and + // correctly parsed RTP, but may be a bit wasteful if more short extensions + // are implemented. Right now the pad bytes would anyway be required at end + // of the extension block, so it makes no difference. + + // Get id defined by user. + uint8_t id; + if (rtp_header_extension_map_.GetId(kRtpExtensionAudioLevel, &id) != 0) { + // Not registered. + return 0; + } + size_t pos = 0; + const uint8_t len = 0; + data_buffer[pos++] = (id << 4) + len; + data_buffer[pos++] = (1 << 7) + 0; // Voice, 0 dBov. + data_buffer[pos++] = 0; // Padding. + data_buffer[pos++] = 0; // Padding. + // kAudioLevelLength is including pad bytes. + assert(pos == kAudioLevelLength); + return kAudioLevelLength; +} + +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 @@ -1265,6 +1298,55 @@ bool RTPSender::UpdateTransmissionTimeOffset( return true; } +bool RTPSender::UpdateAudioLevel(uint8_t *rtp_packet, + const uint16_t rtp_packet_length, + const RTPHeader &rtp_header, + const bool is_voiced, + const uint8_t dBov) const { + CriticalSectionScoped cs(send_critsect_); + + // Get length until start of header extension block. + int extension_block_pos = + rtp_header_extension_map_.GetLengthUntilBlockStartInBytes( + kRtpExtensionAudioLevel); + if (extension_block_pos < 0) { + WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, + "Failed to update audio level, not registered."); + return false; + } + int block_pos = 12 + rtp_header.numCSRCs + extension_block_pos; + if (rtp_packet_length < block_pos + kAudioLevelLength || + rtp_header.headerLength < block_pos + kAudioLevelLength) { + WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, + "Failed to update audio level, invalid length."); + return false; + } + // Verify that header contains extension. + if (!((rtp_packet[12 + rtp_header.numCSRCs] == 0xBE) && + (rtp_packet[12 + rtp_header.numCSRCs + 1] == 0xDE))) { + WEBRTC_TRACE( + kTraceStream, kTraceRtpRtcp, id_, + "Failed to update audio level, hdr extension not found."); + return false; + } + // Get id. + uint8_t id = 0; + if (rtp_header_extension_map_.GetId(kRtpExtensionAudioLevel, &id) != 0) { + WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, + "Failed to update audio level, no id."); + return false; + } + // Verify first byte in block. + const uint8_t first_block_byte = (id << 4) + 0; + if (rtp_packet[block_pos] != first_block_byte) { + WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_, + "Failed to update audio level."); + return false; + } + rtp_packet[block_pos + 1] = (is_voiced ? 0x80 : 0x00) + (dBov & 0x7f); + return true; +} + bool RTPSender::UpdateAbsoluteSendTime( uint8_t *rtp_packet, const uint16_t rtp_packet_length, const RTPHeader &rtp_header, const int64_t now_ms) const { @@ -1463,19 +1545,6 @@ int32_t RTPSender::SetAudioPacketSize( return audio_->SetAudioPacketSize(packet_size_samples); } -int32_t RTPSender::SetAudioLevelIndicationStatus(const bool enable, - const uint8_t ID) { - if (!audio_configured_) { - return -1; - } - return audio_->SetAudioLevelIndicationStatus(enable, ID); -} - -int32_t RTPSender::AudioLevelIndicationStatus(bool *enable, - uint8_t* id) const { - return audio_->AudioLevelIndicationStatus(*enable, *id); -} - int32_t RTPSender::SetAudioLevel(const uint8_t level_d_bov) { return audio_->SetAudioLevel(level_d_bov); } diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.h b/webrtc/modules/rtp_rtcp/source/rtp_sender.h index e1cc3a182..8153396cf 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_sender.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.h @@ -153,15 +153,19 @@ class RTPSender : public RTPSenderInterface, public Bitrate::Observer { uint16_t BuildRTPHeaderExtension(uint8_t* data_buffer) const; - uint8_t BuildTransmissionTimeOffsetExtension( - uint8_t *data_buffer) const; - uint8_t BuildAbsoluteSendTimeExtension( - uint8_t* data_buffer) const; + uint8_t BuildTransmissionTimeOffsetExtension(uint8_t *data_buffer) const; + uint8_t BuildAudioLevelExtension(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 RTPHeader &rtp_header, const int64_t time_diff_ms) const; + bool UpdateAudioLevel(uint8_t *rtp_packet, + const uint16_t rtp_packet_length, + const RTPHeader &rtp_header, + const bool is_voiced, + const uint8_t dBov) const; bool UpdateAbsoluteSendTime(uint8_t *rtp_packet, const uint16_t rtp_packet_length, const RTPHeader &rtp_header, @@ -228,12 +232,6 @@ class RTPSender : public RTPSenderInterface, public Bitrate::Observer { // packet in silence (CNG). int32_t SetAudioPacketSize(const uint16_t packet_size_samples); - // Set status and ID for header-extension-for-audio-level-indication. - int32_t SetAudioLevelIndicationStatus(const bool enable, const uint8_t ID); - - // Get status and ID for header-extension-for-audio-level-indication. - int32_t AudioLevelIndicationStatus(bool *enable, uint8_t *id) const; - // Store the audio level in d_bov for // header-extension-for-audio-level-indication. int32_t SetAudioLevel(const uint8_t level_d_bov); diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc index f80014288..6b3e2276e 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc @@ -17,7 +17,7 @@ namespace webrtc { RTPSenderAudio::RTPSenderAudio(const int32_t id, Clock* clock, - RTPSenderInterface* rtpSender) : + RTPSender* rtpSender) : _id(id), _clock(clock), _rtpSender(rtpSender), @@ -42,8 +42,6 @@ RTPSenderAudio::RTPSenderAudio(const int32_t id, Clock* clock, _cngSWBPayloadType(-1), _cngFBPayloadType(-1), _lastPayloadType(-1), - _includeAudioLevelIndication(false), // @TODO - reset at Init()? - _audioLevelIndicationID(0), _audioLevel_dBov(0) { }; @@ -365,52 +363,12 @@ int32_t RTPSenderAudio::SendAudio( if (rtpHeaderLength <= 0) { return -1; } + if (maxPayloadLength < (rtpHeaderLength + payloadSize)) { + // Too large payload buffer. + return -1; + } { CriticalSectionScoped cs(_sendAudioCritsect); - - // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/ - if (_includeAudioLevelIndication) { - dataBuffer[0] |= 0x10; // set eXtension bit - /* - 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 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | 0xBE | 0xDE | length=1 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | ID | len=0 |V| level | 0x00 | 0x00 | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ - // add our ID (0xBEDE) - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, - RTP_AUDIO_LEVEL_UNIQUE_ID); - rtpHeaderLength += 2; - - // add the length (length=1) in number of word32 - const uint8_t length = 1; - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, - length); - rtpHeaderLength += 2; - - // add ID (defined by the user) and len(=0) byte - const uint8_t id = _audioLevelIndicationID; - const uint8_t len = 0; - dataBuffer[rtpHeaderLength++] = (id << 4) + len; - - // add voice-activity flag (V) bit and the audio level (in dBov) - const uint8_t V = (frameType == kAudioFrameSpeech); - uint8_t level = _audioLevel_dBov; - dataBuffer[rtpHeaderLength++] = (V << 7) + level; - - // add two bytes zero padding - ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, 0); - rtpHeaderLength += 2; - } - - if(maxPayloadLength < rtpHeaderLength + payloadSize ) { - // too large payload buffer - return -1; - } - if (_REDPayloadType >= 0 && // Have we configured RED? fragmentation && fragmentation->fragmentationVectorSize > 1 && @@ -474,6 +432,17 @@ int32_t RTPSenderAudio::SendAudio( } } _lastPayloadType = payloadType; + + // Update audio level extension, if included. + { + uint16_t packetSize = payloadSize + rtpHeaderLength; + ModuleRTPUtility::RTPHeaderParser rtp_parser(dataBuffer, packetSize); + RTPHeader rtp_header; + rtp_parser.Parse(rtp_header); + _rtpSender->UpdateAudioLevel(dataBuffer, packetSize, rtp_header, + (frameType == kAudioFrameSpeech), + _audioLevel_dBov); + } } // end critical section TRACE_EVENT_ASYNC_END2("webrtc", "Audio", captureTimeStamp, "timestamp", _rtpSender->Timestamp(), @@ -486,32 +455,6 @@ int32_t RTPSenderAudio::SendAudio( PacedSender::kHighPriority); } -int32_t -RTPSenderAudio::SetAudioLevelIndicationStatus(const bool enable, - const uint8_t ID) -{ - if(enable && (ID < 1 || ID > 14)) - { - return -1; - } - CriticalSectionScoped cs(_sendAudioCritsect); - - _includeAudioLevelIndication = enable; - _audioLevelIndicationID = ID; - - return 0; -} - -int32_t -RTPSenderAudio::AudioLevelIndicationStatus(bool& enable, - uint8_t& ID) const -{ - CriticalSectionScoped cs(_sendAudioCritsect); - enable = _includeAudioLevelIndication; - ID = _audioLevelIndicationID; - return 0; -} - // Audio level magnitude and voice activity flag are set for each RTP packet int32_t RTPSenderAudio::SetAudioLevel(const uint8_t level_dBov) diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h b/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h index 7074e7b29..732199c17 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h +++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h @@ -23,7 +23,7 @@ class RTPSenderAudio: public DTMFqueue { public: RTPSenderAudio(const int32_t id, Clock* clock, - RTPSenderInterface* rtpSender); + RTPSender* rtpSender); virtual ~RTPSenderAudio(); int32_t RegisterAudioPayload( @@ -44,13 +44,6 @@ public: // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG) int32_t SetAudioPacketSize(const uint16_t packetSizeSamples); - // Set status and ID for header-extension-for-audio-level-indication. - // Valid ID range is [1,14]. - int32_t SetAudioLevelIndicationStatus(const bool enable, const uint8_t ID); - - // Get status and ID for header-extension-for-audio-level-indication. - int32_t AudioLevelIndicationStatus(bool& enable, uint8_t& ID) const; - // Store the audio level in dBov for header-extension-for-audio-level-indication. // Valid range is [0,100]. Actual value is negative. int32_t SetAudioLevel(const uint8_t level_dBov); @@ -86,7 +79,7 @@ protected: private: int32_t _id; Clock* _clock; - RTPSenderInterface* _rtpSender; + RTPSender* _rtpSender; CriticalSectionWrapper* _audioFeedbackCritsect; RtpAudioFeedback* _audioFeedback; @@ -117,8 +110,6 @@ private: int8_t _lastPayloadType; // Audio level indication (https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/) - bool _includeAudioLevelIndication; - uint8_t _audioLevelIndicationID; uint8_t _audioLevel_dBov; }; } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc index ce615be04..8b72ce02b 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc @@ -160,11 +160,8 @@ TEST_F(RtpSenderTest, RegisterRtpAudioLevelHeaderExtension) { EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAudioLevel, kAudioLevelExtensionId)); - // Accounted size for audio level is zero because it is currently specially - // treated by RTPSenderAudio. - EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); - // EXPECT_EQ(kRtpOneByteHeaderLength + kAudioLevelLength, - // rtp_sender_->RtpHeaderExtensionTotalLength()); + EXPECT_EQ(kRtpOneByteHeaderLength + kAudioLevelLength, + rtp_sender_->RtpHeaderExtensionTotalLength()); EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension( kRtpExtensionAudioLevel)); EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); @@ -183,14 +180,16 @@ TEST_F(RtpSenderTest, RegisterRtpHeaderExtensions) { EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAudioLevel, kAudioLevelExtensionId)); EXPECT_EQ(kRtpOneByteHeaderLength + kTransmissionTimeOffsetLength + - kAbsoluteSendTimeLength, rtp_sender_->RtpHeaderExtensionTotalLength()); - EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension( - kRtpExtensionTransmissionTimeOffset)); - EXPECT_EQ(kRtpOneByteHeaderLength + kAbsoluteSendTimeLength, + kAbsoluteSendTimeLength + kAudioLevelLength, rtp_sender_->RtpHeaderExtensionTotalLength()); + EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension( + kRtpExtensionTransmissionTimeOffset)); + EXPECT_EQ(kRtpOneByteHeaderLength + kAbsoluteSendTimeLength + + kAudioLevelLength, rtp_sender_->RtpHeaderExtensionTotalLength()); EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension( kRtpExtensionAbsoluteSendTime)); - EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); + EXPECT_EQ(kRtpOneByteHeaderLength + kAudioLevelLength, + rtp_sender_->RtpHeaderExtensionTotalLength()); EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension( kRtpExtensionAudioLevel)); EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); @@ -202,23 +201,24 @@ TEST_F(RtpSenderTest, BuildRTPPacket) { kMarkerBit, kTimestamp, 0); - EXPECT_EQ(12, length); + EXPECT_EQ(kRtpHeaderSize, length); // Verify webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); webrtc::RTPHeader rtp_header; - RtpHeaderExtensionMap map; - map.Register(kRtpExtensionTransmissionTimeOffset, - kTransmissionTimeOffsetExtensionId); - const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map); + const bool valid_rtp_header = rtp_parser.Parse(rtp_header, NULL); ASSERT_TRUE(valid_rtp_header); ASSERT_FALSE(rtp_parser.RTCP()); VerifyRTPHeaderCommon(rtp_header); EXPECT_EQ(length, rtp_header.headerLength); + EXPECT_FALSE(rtp_header.extension.hasTransmissionTimeOffset); + EXPECT_FALSE(rtp_header.extension.hasAbsoluteSendTime); + EXPECT_FALSE(rtp_header.extension.hasAudioLevel); EXPECT_EQ(0, rtp_header.extension.transmissionTimeOffset); EXPECT_EQ(0u, rtp_header.extension.absoluteSendTime); + EXPECT_EQ(0u, rtp_header.extension.audioLevel); } TEST_F(RtpSenderTest, BuildRTPPacketWithTransmissionOffsetExtension) { @@ -231,7 +231,8 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithTransmissionOffsetExtension) { kMarkerBit, kTimestamp, 0); - EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length); + EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(), + length); // Verify webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); @@ -246,6 +247,7 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithTransmissionOffsetExtension) { ASSERT_FALSE(rtp_parser.RTCP()); VerifyRTPHeaderCommon(rtp_header); EXPECT_EQ(length, rtp_header.headerLength); + EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset); EXPECT_EQ(kTimeOffset, rtp_header.extension.transmissionTimeOffset); // Parse without map extension @@ -255,6 +257,7 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithTransmissionOffsetExtension) { ASSERT_TRUE(valid_rtp_header2); VerifyRTPHeaderCommon(rtp_header2); EXPECT_EQ(length, rtp_header2.headerLength); + EXPECT_FALSE(rtp_header2.extension.hasTransmissionTimeOffset); EXPECT_EQ(0, rtp_header2.extension.transmissionTimeOffset); } @@ -269,7 +272,8 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithNegativeTransmissionOffsetExtension) { kMarkerBit, kTimestamp, 0); - EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length); + EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(), + length); // Verify webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); @@ -284,6 +288,7 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithNegativeTransmissionOffsetExtension) { ASSERT_FALSE(rtp_parser.RTCP()); VerifyRTPHeaderCommon(rtp_header); EXPECT_EQ(length, rtp_header.headerLength); + EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset); EXPECT_EQ(kNegTimeOffset, rtp_header.extension.transmissionTimeOffset); } @@ -297,7 +302,8 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithAbsoluteSendTimeExtension) { kMarkerBit, kTimestamp, 0); - EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length); + EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(), + length); // Verify webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); @@ -311,6 +317,7 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithAbsoluteSendTimeExtension) { ASSERT_FALSE(rtp_parser.RTCP()); VerifyRTPHeaderCommon(rtp_header); EXPECT_EQ(length, rtp_header.headerLength); + EXPECT_TRUE(rtp_header.extension.hasAbsoluteSendTime); EXPECT_EQ(kAbsoluteSendTime, rtp_header.extension.absoluteSendTime); // Parse without map extension @@ -320,9 +327,54 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithAbsoluteSendTimeExtension) { ASSERT_TRUE(valid_rtp_header2); VerifyRTPHeaderCommon(rtp_header2); EXPECT_EQ(length, rtp_header2.headerLength); + EXPECT_FALSE(rtp_header2.extension.hasAbsoluteSendTime); EXPECT_EQ(0u, rtp_header2.extension.absoluteSendTime); } +TEST_F(RtpSenderTest, BuildRTPPacketWithAudioLevelExtension) { + EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( + kRtpExtensionAudioLevel, kAudioLevelExtensionId)); + + int32_t length = rtp_sender_->BuildRTPheader(packet_, + kPayload, + kMarkerBit, + kTimestamp, + 0); + EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(), + length); + + // Verify + webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); + webrtc::RTPHeader rtp_header; + + // Updating audio level is done in RTPSenderAudio, so simulate it here. + rtp_parser.Parse(rtp_header); + rtp_sender_->UpdateAudioLevel(packet_, length, rtp_header, true, kAudioLevel); + + RtpHeaderExtensionMap map; + map.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId); + 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.headerLength); + EXPECT_TRUE(rtp_header.extension.hasAudioLevel); + // Expect kAudioLevel + 0x80 because we set "voiced" to true in the call to + // UpdateAudioLevel(), above. + EXPECT_EQ(kAudioLevel + 0x80u, rtp_header.extension.audioLevel); + + // Parse without map extension + webrtc::RTPHeader 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.headerLength); + EXPECT_FALSE(rtp_header2.extension.hasAudioLevel); + EXPECT_EQ(0u, rtp_header2.extension.audioLevel); +} + TEST_F(RtpSenderTest, BuildRTPPacketWithHeaderExtensions) { EXPECT_EQ(0, rtp_sender_->SetTransmissionTimeOffset(kTimeOffset)); EXPECT_EQ(0, rtp_sender_->SetAbsoluteSendTime(kAbsoluteSendTime)); @@ -330,30 +382,42 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithHeaderExtensions) { kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId)); EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); + EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( + kRtpExtensionAudioLevel, kAudioLevelExtensionId)); int32_t length = rtp_sender_->BuildRTPheader(packet_, kPayload, kMarkerBit, kTimestamp, 0); - EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length); + EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(), + length); // Verify webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); webrtc::RTPHeader rtp_header; + // Updating audio level is done in RTPSenderAudio, so simulate it here. + rtp_parser.Parse(rtp_header); + rtp_sender_->UpdateAudioLevel(packet_, length, rtp_header, true, kAudioLevel); + RtpHeaderExtensionMap map; map.Register(kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId); map.Register(kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId); + map.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId); 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.headerLength); + EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset); + EXPECT_TRUE(rtp_header.extension.hasAbsoluteSendTime); + EXPECT_TRUE(rtp_header.extension.hasAudioLevel); EXPECT_EQ(kTimeOffset, rtp_header.extension.transmissionTimeOffset); EXPECT_EQ(kAbsoluteSendTime, rtp_header.extension.absoluteSendTime); + EXPECT_EQ(kAudioLevel + 0x80u, rtp_header.extension.audioLevel); // Parse without map extension webrtc::RTPHeader rtp_header2; @@ -362,8 +426,12 @@ TEST_F(RtpSenderTest, BuildRTPPacketWithHeaderExtensions) { ASSERT_TRUE(valid_rtp_header2); VerifyRTPHeaderCommon(rtp_header2); EXPECT_EQ(length, rtp_header2.headerLength); + EXPECT_FALSE(rtp_header2.extension.hasTransmissionTimeOffset); + EXPECT_FALSE(rtp_header2.extension.hasAbsoluteSendTime); + EXPECT_FALSE(rtp_header2.extension.hasAudioLevel); EXPECT_EQ(0, rtp_header2.extension.transmissionTimeOffset); EXPECT_EQ(0u, rtp_header2.extension.absoluteSendTime); + EXPECT_EQ(0u, rtp_header2.extension.audioLevel); } TEST_F(RtpSenderTest, TrafficSmoothingWithExtensions) { @@ -493,7 +561,7 @@ TEST_F(RtpSenderTest, SendPadding) { uint16_t seq_num = kSeqNum; uint32_t timestamp = kTimestamp; rtp_sender_->SetStorePacketsStatus(true, 10); - int rtp_header_len = 12; + int32_t rtp_header_len = kRtpHeaderSize; EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId)); rtp_header_len += 4; // 4 bytes extension. @@ -613,7 +681,7 @@ TEST_F(RtpSenderTest, SendRedundantPayloads) { uint16_t seq_num = kSeqNum; rtp_sender_->SetStorePacketsStatus(true, 10); - int rtp_header_len = 12; + int32_t rtp_header_len = kRtpHeaderSize; EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId)); rtp_header_len += 4; // 4 bytes extension. @@ -939,48 +1007,6 @@ TEST_F(RtpSenderTest, StreamDataCountersCallbacks) { rtp_sender_->RegisterRtpStatisticsCallback(NULL); } -TEST_F(RtpSenderAudioTest, BuildRTPPacketWithAudioLevelExtension) { - EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(true, - kAudioLevelExtensionId)); - EXPECT_EQ(0, rtp_sender_->SetAudioLevel(kAudioLevel)); - EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( - kRtpExtensionAudioLevel, kAudioLevelExtensionId)); - - int32_t length = rtp_sender_->BuildRTPheader(packet_, - kAudioPayload, - kMarkerBit, - kTimestamp, - 0); - EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length); - - // Currently, no space is added by for header extension by BuildRTPHeader(). - EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength()); - - // Verify - webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length); - webrtc::RTPHeader rtp_header; - - RtpHeaderExtensionMap map; - map.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId); - 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.headerLength); - // TODO(solenberg): Should verify that we got audio level in header extension. - - // Parse without map extension - webrtc::RTPHeader 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.headerLength); - // TODO(solenberg): Should verify that we didn't get audio level. - EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(false, 0)); -} - TEST_F(RtpSenderAudioTest, SendAudio) { char payload_name[RTP_PAYLOAD_NAME_SIZE] = "PAYLOAD_NAME"; const uint8_t payload_type = 127; @@ -1007,8 +1033,6 @@ TEST_F(RtpSenderAudioTest, SendAudio) { } TEST_F(RtpSenderAudioTest, SendAudioWithAudioLevelExtension) { - EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(true, - kAudioLevelExtensionId)); EXPECT_EQ(0, rtp_sender_->SetAudioLevel(kAudioLevel)); EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension( kRtpExtensionAudioLevel, kAudioLevelExtensionId)); @@ -1044,7 +1068,6 @@ TEST_F(RtpSenderAudioTest, SendAudioWithAudioLevelExtension) { EXPECT_EQ(0, memcmp(extension, payload_data - sizeof(extension), sizeof(extension))); - EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(false, 0)); } } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc index 102ebecb0..be51f73b2 100644 --- a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc +++ b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc @@ -398,6 +398,10 @@ bool RTPHeaderParser::Parse(RTPHeader& header, header.extension.hasAbsoluteSendTime = false; header.extension.absoluteSendTime = 0; + // May not be present in packet. + header.extension.hasAudioLevel = false; + header.extension.audioLevel = 0; + if (X) { /* RTP header extension, RFC 3550. 0 1 2 3 @@ -496,7 +500,11 @@ void RTPHeaderParser::ParseOneByteExtensionHeader( break; } case kRtpExtensionAudioLevel: { - // --- Only used for debugging --- + if (len != 0) { + WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, + "Incorrect audio level 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 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -509,6 +517,9 @@ void RTPHeaderParser::ParseOneByteExtensionHeader( // const uint8_t level = (*ptr & 0x7f); // DEBUG_PRINT("RTP_AUDIO_LEVEL_UNIQUE_ID: ID=%u, len=%u, V=%u, // level=%u", ID, len, V, level); + + header.extension.audioLevel = *ptr++; + header.extension.hasAudioLevel = true; break; } case kRtpExtensionAbsoluteSendTime: { diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc index 48f98549d..480ff4947 100644 --- a/webrtc/voice_engine/channel.cc +++ b/webrtc/voice_engine/channel.cc @@ -3503,9 +3503,7 @@ Channel::GetRemoteCSRCs(unsigned int arrCSRC[15]) return CSRCs; } -int -Channel::SetRTPAudioLevelIndicationStatus(bool enable, unsigned char ID) -{ +int Channel::SetSendAudioLevelIndicationStatus(bool enable, unsigned char id) { if (rtp_audioproc_.get() == NULL) { rtp_audioproc_.reset(AudioProcessing::Create(VoEModuleId(_instanceId, _channelId))); @@ -3519,23 +3517,24 @@ Channel::SetRTPAudioLevelIndicationStatus(bool enable, unsigned char ID) } _includeAudioLevelIndication = enable; - if (enable) { - rtp_header_parser_->RegisterRtpHeaderExtension(kRtpExtensionAudioLevel, - ID); - } else { - rtp_header_parser_->DeregisterRtpHeaderExtension(kRtpExtensionAudioLevel); - } - return _rtpRtcpModule->SetRTPAudioLevelIndicationStatus(enable, ID); + + return SetSendRtpHeaderExtension(enable, kRtpExtensionAudioLevel, id); } -int -Channel::GetRTPAudioLevelIndicationStatus(bool& enabled, unsigned char& ID) -{ - WEBRTC_TRACE(kTraceStateInfo, kTraceVoice, - VoEId(_instanceId,_channelId), - "GetRTPAudioLevelIndicationStatus() => enabled=%d, ID=%u", - enabled, ID); - return _rtpRtcpModule->GetRTPAudioLevelIndicationStatus(enabled, ID); +int Channel::SetSendAbsoluteSenderTimeStatus(bool enable, unsigned char id) { + return SetSendRtpHeaderExtension(enable, kRtpExtensionAbsoluteSendTime, id); +} + +int Channel::SetReceiveAbsoluteSenderTimeStatus(bool enable, unsigned char id) { + rtp_header_parser_->DeregisterRtpHeaderExtension( + kRtpExtensionAbsoluteSendTime); + if (enable) { + if (!rtp_header_parser_->RegisterRtpHeaderExtension( + kRtpExtensionAbsoluteSendTime, id)) { + return -1; + } + } + return 0; } int @@ -5059,5 +5058,14 @@ int Channel::SetRedPayloadType(int red_payload_type) { return 0; } +int Channel::SetSendRtpHeaderExtension(bool enable, RTPExtensionType type, + unsigned char id) { + int error = 0; + _rtpRtcpModule->DeregisterSendRtpHeaderExtension(type); + if (enable) { + error = _rtpRtcpModule->RegisterSendRtpHeaderExtension(type, id); + } + return error; +} } // namespace voe } // namespace webrtc diff --git a/webrtc/voice_engine/channel.h b/webrtc/voice_engine/channel.h index ed2bfc42a..954d6ea3d 100644 --- a/webrtc/voice_engine/channel.h +++ b/webrtc/voice_engine/channel.h @@ -253,8 +253,9 @@ public: int GetLocalSSRC(unsigned int& ssrc); int GetRemoteSSRC(unsigned int& ssrc); int GetRemoteCSRCs(unsigned int arrCSRC[15]); - int SetRTPAudioLevelIndicationStatus(bool enable, unsigned char ID); - int GetRTPAudioLevelIndicationStatus(bool& enable, unsigned char& ID); + int SetSendAudioLevelIndicationStatus(bool enable, unsigned char id); + int SetSendAbsoluteSenderTimeStatus(bool enable, unsigned char id); + int SetReceiveAbsoluteSenderTimeStatus(bool enable, unsigned char id); int SetRTCPStatus(bool enable); int GetRTCPStatus(bool& enabled); int SetRTCP_CNAME(const char cName[256]); @@ -438,6 +439,8 @@ private: void RegisterReceiveCodecsToRTPModule(); int SetRedPayloadType(int red_payload_type); + int SetSendRtpHeaderExtension(bool enable, RTPExtensionType type, + unsigned char id); CriticalSectionWrapper& _fileCritSect; CriticalSectionWrapper& _callbackCritSect; diff --git a/webrtc/voice_engine/include/voe_rtp_rtcp.h b/webrtc/voice_engine/include/voe_rtp_rtcp.h index 675a290d0..6c06ef0a6 100644 --- a/webrtc/voice_engine/include/voe_rtp_rtcp.h +++ b/webrtc/voice_engine/include/voe_rtp_rtcp.h @@ -152,12 +152,19 @@ public: virtual int GetRemoteSSRC(int channel, unsigned int& ssrc) = 0; // Sets the status of rtp-audio-level-indication on a specific |channel|. - virtual int SetRTPAudioLevelIndicationStatus( - int channel, bool enable, unsigned char ID = 1) = 0; + virtual int SetSendAudioLevelIndicationStatus(int channel, + bool enable, + unsigned char id = 1) = 0; - // Sets the status of rtp-audio-level-indication on a specific |channel|. - virtual int GetRTPAudioLevelIndicationStatus( - int channel, bool& enabled, unsigned char& ID) = 0; + // Sets the status of sending absolute sender time on a specific |channel|. + virtual int SetSendAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id) = 0; + + // Sets status of receiving absolute sender time on a specific |channel|. + virtual int SetReceiveAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id) = 0; // Gets the CSRCs of the incoming RTP packets. virtual int GetRemoteCSRCs(int channel, unsigned int arrCSRC[15]) = 0; diff --git a/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_extensions.cc b/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_extensions.cc new file mode 100644 index 000000000..11ac4d1b5 --- /dev/null +++ b/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_extensions.cc @@ -0,0 +1,147 @@ +/* + * Copyright (c) 2014 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h" +#include "webrtc/system_wrappers/interface/atomic32.h" +#include "webrtc/system_wrappers/interface/sleep.h" +#include "webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h" + +class ExtensionVerifyTransport : public webrtc::Transport { + public: + ExtensionVerifyTransport() + : received_packets_(0), + ok_packets_(0), + parser_(webrtc::RtpHeaderParser::Create()), + audio_level_id_(-1), + absolute_sender_time_id_(-1) { + } + + virtual int SendPacket(int channel, const void* data, int len) { + ++received_packets_; + webrtc::RTPHeader header = {0}; + if (parser_->Parse(static_cast(data), len, &header)) { + bool ok = true; + if (audio_level_id_ >= 0 && !header.extension.hasAudioLevel) { + ok = false; + } + if (absolute_sender_time_id_ >= 0 && + !header.extension.hasAbsoluteSendTime) { + ok = false; + } + if (ok) { + ++ok_packets_; + } + } + return len; + } + + virtual int SendRTCPPacket(int channel, const void* data, int len) { + return len; + } + + void SetAudioLevelId(int id) { + audio_level_id_ = id; + parser_->RegisterRtpHeaderExtension(webrtc::kRtpExtensionAudioLevel, id); + } + + void SetAbsoluteSenderTimeId(int id) { + absolute_sender_time_id_ = id; + parser_->RegisterRtpHeaderExtension(webrtc::kRtpExtensionAbsoluteSendTime, + id); + } + + bool WaitForNPackets(int count) { + while (received_packets_.Value() < count) { + webrtc::SleepMs(10); + } + return (ok_packets_.Value() == count); + } + + private: + webrtc::Atomic32 received_packets_; + webrtc::Atomic32 ok_packets_; + webrtc::scoped_ptr parser_; + int audio_level_id_; + int absolute_sender_time_id_; +}; + +class SendRtpRtcpHeaderExtensionsTest : public AfterStreamingFixture { + protected: + virtual void SetUp() { + PausePlaying(); + EXPECT_EQ(0, voe_network_->DeRegisterExternalTransport(channel_)); + EXPECT_EQ(0, voe_network_->RegisterExternalTransport(channel_, + verifying_transport_)); + } + virtual void TearDown() { + PausePlaying(); + } + + ExtensionVerifyTransport verifying_transport_; +}; + +TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAudioLevel) { + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAudioLevelIndicationStatus(channel_, true, + 9)); + verifying_transport_.SetAudioLevelId(9); + ResumePlaying(); + EXPECT_TRUE(verifying_transport_.WaitForNPackets(10)); +} + +TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAbsoluteSenderTime) { + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true, + 11)); + verifying_transport_.SetAbsoluteSenderTimeId(11); + ResumePlaying(); + EXPECT_TRUE(verifying_transport_.WaitForNPackets(10)); +} + +TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAllExtensions1) { + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAudioLevelIndicationStatus(channel_, true, + 9)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true, + 11)); + verifying_transport_.SetAudioLevelId(9); + verifying_transport_.SetAbsoluteSenderTimeId(11); + ResumePlaying(); + EXPECT_TRUE(verifying_transport_.WaitForNPackets(10)); +} + +TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAllExtensions2) { + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true, + 3)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAudioLevelIndicationStatus(channel_, true, + 9)); + verifying_transport_.SetAbsoluteSenderTimeId(3); + verifying_transport_.SetAudioLevelId(9); + ResumePlaying(); + EXPECT_TRUE(verifying_transport_.WaitForNPackets(10)); +} + +class ReceiveRtpRtcpHeaderExtensionsTest : public AfterStreamingFixture { + protected: + virtual void SetUp() { + PausePlaying(); + } +}; + +TEST_F(ReceiveRtpRtcpHeaderExtensionsTest, ReceivedAbsoluteSenderTimeWorks) { + EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true, + 11)); + EXPECT_EQ(0, voe_rtp_rtcp_->SetReceiveAbsoluteSenderTimeStatus(channel_, true, + 11)); + ResumePlaying(); + + // Ensure the RTP-RTCP process gets scheduled. + Sleep(1000); + + // TODO(solenberg): Verify received packets are forwarded to RBE. +} diff --git a/webrtc/voice_engine/voe_rtp_rtcp_impl.cc b/webrtc/voice_engine/voe_rtp_rtcp_impl.cc index ac79505e2..1a0b6c24c 100644 --- a/webrtc/voice_engine/voe_rtp_rtcp_impl.cc +++ b/webrtc/voice_engine/voe_rtp_rtcp_impl.cc @@ -211,62 +211,96 @@ int VoERTP_RTCPImpl::GetRemoteCSRCs(int channel, unsigned int arrCSRC[15]) return channelPtr->GetRemoteCSRCs(arrCSRC); } - -int VoERTP_RTCPImpl::SetRTPAudioLevelIndicationStatus(int channel, - bool enable, - unsigned char ID) +int VoERTP_RTCPImpl::SetSendAudioLevelIndicationStatus(int channel, + bool enable, + unsigned char id) { WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "SetRTPAudioLevelIndicationStatus(channel=%d, enable=%d," - " ID=%u)", channel, enable, ID); + "SetSendAudioLevelIndicationStatus(channel=%d, enable=%d," + " ID=%u)", channel, enable, id); if (!_shared->statistics().Initialized()) { _shared->SetLastError(VE_NOT_INITED, kTraceError); return -1; } - if (enable && (ID < kVoiceEngineMinRtpExtensionId || - ID > kVoiceEngineMaxRtpExtensionId)) + if (enable && (id < kVoiceEngineMinRtpExtensionId || + id > kVoiceEngineMaxRtpExtensionId)) { - // [RFC5285] The 4-bit ID is the local identifier of this element in + // [RFC5285] The 4-bit id is the local identifier of this element in // the range 1-14 inclusive. _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, - "SetRTPAudioLevelIndicationStatus() invalid ID parameter"); + "SetSendAudioLevelIndicationStatus() invalid ID parameter"); return -1; } - // Set state and ID for the specified channel. + // Set state and id for the specified channel. voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); voe::Channel* channelPtr = ch.channel(); if (channelPtr == NULL) { _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "SetRTPAudioLevelIndicationStatus() failed to locate channel"); + "SetSendAudioLevelIndicationStatus() failed to locate channel"); return -1; } - return channelPtr->SetRTPAudioLevelIndicationStatus(enable, ID); + return channelPtr->SetSendAudioLevelIndicationStatus(enable, id); } -int VoERTP_RTCPImpl::GetRTPAudioLevelIndicationStatus(int channel, - bool& enabled, - unsigned char& ID) -{ - WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), - "GetRTPAudioLevelIndicationStatus(channel=%d, enable=?, ID=?)", - channel); - if (!_shared->statistics().Initialized()) - { - _shared->SetLastError(VE_NOT_INITED, kTraceError); - return -1; - } - voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); - voe::Channel* channelPtr = ch.channel(); - if (channelPtr == NULL) - { - _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, - "GetRTPAudioLevelIndicationStatus() failed to locate channel"); - return -1; - } - return channelPtr->GetRTPAudioLevelIndicationStatus(enabled, ID); +int VoERTP_RTCPImpl::SetSendAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id) { + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "SetSendAbsoluteSenderTimeStatus(channel=%d, enable=%d, id=%u)", + channel, enable, id); + if (!_shared->statistics().Initialized()) { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + if (enable && (id < kVoiceEngineMinRtpExtensionId || + id > kVoiceEngineMaxRtpExtensionId)) { + // [RFC5285] The 4-bit id is the local identifier of this element in + // the range 1-14 inclusive. + _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, + "SetSendAbsoluteSenderTimeStatus() invalid id parameter"); + return -1; + } + // Set state and id for the specified channel. + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channelPtr = ch.channel(); + if (channelPtr == NULL) { + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "SetSendAbsoluteSenderTimeStatus() failed to locate channel"); + return -1; + } + return channelPtr->SetSendAbsoluteSenderTimeStatus(enable, id); +} + +int VoERTP_RTCPImpl::SetReceiveAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id) { + WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1), + "SetReceiveAbsoluteSenderTimeStatus(channel=%d, enable=%d, id=%u)", + channel, enable, id); + if (!_shared->statistics().Initialized()) { + _shared->SetLastError(VE_NOT_INITED, kTraceError); + return -1; + } + if (enable && (id < kVoiceEngineMinRtpExtensionId || + id > kVoiceEngineMaxRtpExtensionId)) { + // [RFC5285] The 4-bit id is the local identifier of this element in + // the range 1-14 inclusive. + _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError, + "SetReceiveAbsoluteSenderTimeStatus() invalid id parameter"); + return -1; + } + // Set state and id for the specified channel. + voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel); + voe::Channel* channelPtr = ch.channel(); + if (channelPtr == NULL) { + _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError, + "SetReceiveAbsoluteSenderTimeStatus() failed to locate channel"); + return -1; + } + return channelPtr->SetReceiveAbsoluteSenderTimeStatus(enable, id); } int VoERTP_RTCPImpl::SetRTCPStatus(int channel, bool enable) diff --git a/webrtc/voice_engine/voe_rtp_rtcp_impl.h b/webrtc/voice_engine/voe_rtp_rtcp_impl.h index 1b8b1e924..bd1308aec 100644 --- a/webrtc/voice_engine/voe_rtp_rtcp_impl.h +++ b/webrtc/voice_engine/voe_rtp_rtcp_impl.h @@ -63,13 +63,17 @@ public: virtual int GetRemoteSSRC(int channel, unsigned int& ssrc); // RTP Header Extension for Client-to-Mixer Audio Level Indication - virtual int SetRTPAudioLevelIndicationStatus(int channel, - bool enable, - unsigned char ID); + virtual int SetSendAudioLevelIndicationStatus(int channel, + bool enable, + unsigned char id); - virtual int GetRTPAudioLevelIndicationStatus(int channel, - bool& enabled, - unsigned char& ID); + // RTP Header Extension for Absolute Sender Time + virtual int SetSendAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id); + virtual int SetReceiveAbsoluteSenderTimeStatus(int channel, + bool enable, + unsigned char id); // CSRC virtual int GetRemoteCSRCs(int channel, unsigned int arrCSRC[15]); diff --git a/webrtc/voice_engine/voice_engine.gyp b/webrtc/voice_engine/voice_engine.gyp index 21aef1531..1ae4e0274 100644 --- a/webrtc/voice_engine/voice_engine.gyp +++ b/webrtc/voice_engine/voice_engine.gyp @@ -184,6 +184,7 @@ 'test/auto_test/standard/neteq_test.cc', 'test/auto_test/standard/network_test.cc', 'test/auto_test/standard/rtp_rtcp_before_streaming_test.cc', + 'test/auto_test/standard/rtp_rtcp_extensions.cc', 'test/auto_test/standard/rtp_rtcp_test.cc', 'test/auto_test/standard/voe_base_misc_test.cc', 'test/auto_test/standard/video_sync_test.cc',