/* * Copyright (c) 2011 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 // srand #include "rtp_sender.h" #include "critical_section_wrapper.h" #include "trace.h" #include "rtp_packet_history.h" #include "rtp_sender_audio.h" #include "rtp_sender_video.h" namespace webrtc { RTPSender::RTPSender(const WebRtc_Word32 id, const bool audio, RtpRtcpClock* clock) : Bitrate(clock), _id(id), _audioConfigured(audio), _audio(NULL), _video(NULL), _sendCritsect(CriticalSectionWrapper::CreateCriticalSection()), _transportCritsect(CriticalSectionWrapper::CreateCriticalSection()), _transport(NULL), _sendingMedia(true), // Default to sending media _maxPayloadLength(IP_PACKET_SIZE-28), // default is IP/UDP _targetSendBitrate(0), _packetOverHead(28), _payloadType(-1), _payloadTypeMap(), _rtpHeaderExtensionMap(), _transmissionTimeOffset(0), _keepAliveIsActive(false), _keepAlivePayloadType(-1), _keepAliveLastSent(0), _keepAliveDeltaTimeSend(0), // NACK _nackByteCountTimes(), _nackByteCount(), _nackBitrate(clock), _packetHistory(new RTPPacketHistory(clock)), _sendBucket(), _timeLastSendToNetworkUpdate(clock->GetTimeInMS()), _transmissionSmoothing(false), // statistics _packetsSent(0), _payloadBytesSent(0), // RTP variables _startTimeStampForced(false), _startTimeStamp(0), _ssrcDB(*SSRCDatabase::GetSSRCDatabase()), _remoteSSRC(0), _sequenceNumberForced(false), _sequenceNumber(0), _sequenceNumberRTX(0), _ssrcForced(false), _ssrc(0), _timeStamp(0), _CSRCs(0), _CSRC(), _includeCSRCs(true), _RTX(false), _ssrcRTX(0) { memset(_nackByteCountTimes, 0, sizeof(_nackByteCountTimes)); memset(_nackByteCount, 0, sizeof(_nackByteCount)); memset(_CSRC, 0, sizeof(_CSRC)); // we need to seed the random generator, otherwise we get 26500 each time, hardly a random value :) srand( (WebRtc_UWord32)_clock.GetTimeInMS() ); _ssrc = _ssrcDB.CreateSSRC(); // can't be 0 if(audio) { _audio = new RTPSenderAudio(id, &_clock, this); } else { _video = new RTPSenderVideo(id, &_clock, this); } WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); } RTPSender::~RTPSender() { if(_remoteSSRC != 0) { _ssrcDB.ReturnSSRC(_remoteSSRC); } _ssrcDB.ReturnSSRC(_ssrc); SSRCDatabase::ReturnSSRCDatabase(); delete _sendCritsect; delete _transportCritsect; while (!_payloadTypeMap.empty()) { std::map::iterator it = _payloadTypeMap.begin(); delete it->second; _payloadTypeMap.erase(it); } delete _packetHistory; delete _audio; delete _video; WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__); } WebRtc_Word32 RTPSender::Init(const WebRtc_UWord32 remoteSSRC) { CriticalSectionScoped cs(_sendCritsect); // reset to default generation _ssrcForced = false; _startTimeStampForced = false; // register a remote SSRC if we have it to avoid collisions if(remoteSSRC != 0) { if(_ssrc == remoteSSRC) { // collision detected _ssrc = _ssrcDB.CreateSSRC(); // can't be 0 } _remoteSSRC = remoteSSRC; _ssrcDB.RegisterSSRC(remoteSSRC); } _sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER); _sequenceNumberRTX = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER); _packetsSent = 0; _payloadBytesSent = 0; _packetOverHead = 28; _keepAlivePayloadType = -1; _rtpHeaderExtensionMap.Erase(); while (!_payloadTypeMap.empty()) { std::map::iterator it = _payloadTypeMap.begin(); delete it->second; _payloadTypeMap.erase(it); } memset(_CSRC, 0, sizeof(_CSRC)); memset(_nackByteCount, 0, sizeof(_nackByteCount)); memset(_nackByteCountTimes, 0, sizeof(_nackByteCountTimes)); _nackBitrate.Init(); SetStorePacketsStatus(false, 0); _sendBucket.Reset(); Bitrate::Init(); if(_audioConfigured) { _audio->Init(); } else { _video->Init(); } return(0); } void RTPSender::ChangeUniqueId(const WebRtc_Word32 id) { _id = id; if(_audioConfigured) { _audio->ChangeUniqueId(id); } else { _video->ChangeUniqueId(id); } } WebRtc_Word32 RTPSender::SetTargetSendBitrate(const WebRtc_UWord32 bits) { _targetSendBitrate = (WebRtc_UWord16)(bits/1000); return 0; } WebRtc_UWord16 RTPSender::TargetSendBitrateKbit() const { return _targetSendBitrate; } WebRtc_UWord16 RTPSender::ActualSendBitrateKbit() const { return (WebRtc_UWord16) (Bitrate::BitrateNow()/1000); } WebRtc_UWord32 RTPSender::VideoBitrateSent() const { if (_video) return _video->VideoBitrateSent(); else return 0; } WebRtc_UWord32 RTPSender::FecOverheadRate() const { if (_video) return _video->FecOverheadRate(); else return 0; } WebRtc_UWord32 RTPSender::NackOverheadRate() const { return _nackBitrate.BitrateLast(); } WebRtc_Word32 RTPSender::SetTransmissionTimeOffset( const WebRtc_Word32 transmissionTimeOffset) { if (transmissionTimeOffset > (0x800000 - 1) || transmissionTimeOffset < -(0x800000 - 1)) // Word24 { return -1; } CriticalSectionScoped cs(_sendCritsect); _transmissionTimeOffset = transmissionTimeOffset; return 0; } WebRtc_Word32 RTPSender::RegisterRtpHeaderExtension(const RTPExtensionType type, const WebRtc_UWord8 id) { CriticalSectionScoped cs(_sendCritsect); return _rtpHeaderExtensionMap.Register(type, id); } WebRtc_Word32 RTPSender::DeregisterRtpHeaderExtension(const RTPExtensionType type) { CriticalSectionScoped cs(_sendCritsect); return _rtpHeaderExtensionMap.Deregister(type); } WebRtc_UWord16 RTPSender::RtpHeaderExtensionTotalLength() const { CriticalSectionScoped cs(_sendCritsect); return _rtpHeaderExtensionMap.GetTotalLengthInBytes(); } //can be called multiple times WebRtc_Word32 RTPSender::RegisterPayload( const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], const WebRtc_Word8 payloadNumber, const WebRtc_UWord32 frequency, const WebRtc_UWord8 channels, const WebRtc_UWord32 rate) { if (!payloadName) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); return -1; } CriticalSectionScoped cs(_sendCritsect); if (payloadNumber == _keepAlivePayloadType) { WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "invalid state", __FUNCTION__); return -1; } std::map::iterator it = _payloadTypeMap.find(payloadNumber); if (_payloadTypeMap.end() != it) { // we already use this payload type ModuleRTPUtility::Payload* payload = it->second; assert(payload); // check if it's the same as we already have WebRtc_Word32 payloadNameLength = (WebRtc_Word32)strlen(payloadName); WebRtc_Word32 nameLength = (WebRtc_Word32)strlen(payload->name); if (payloadNameLength == nameLength && ModuleRTPUtility::StringCompare(payload->name, payloadName, nameLength)) { if (_audioConfigured && payload->audio && payload->typeSpecific.Audio.frequency == frequency && (payload->typeSpecific.Audio.rate == rate || payload->typeSpecific.Audio.rate == 0 || rate == 0)) { payload->typeSpecific.Audio.rate = rate; // Ensure that we update the rate if new or old is zero return 0; } if(!_audioConfigured && !payload->audio) { return 0; } } return -1; } WebRtc_Word32 retVal = -1; ModuleRTPUtility::Payload* payload = NULL; if (_audioConfigured) { retVal = _audio->RegisterAudioPayload(payloadName, payloadNumber, frequency, channels, rate, payload); } else { retVal = _video->RegisterVideoPayload(payloadName, payloadNumber, rate, payload); } if(payload) { _payloadTypeMap[payloadNumber] = payload; } return retVal; } WebRtc_Word32 RTPSender::DeRegisterSendPayload(const WebRtc_Word8 payloadType) { CriticalSectionScoped lock(_sendCritsect); std::map::iterator it = _payloadTypeMap.find(payloadType); if (_payloadTypeMap.end() == it) return -1; ModuleRTPUtility::Payload* payload = it->second; delete payload; _payloadTypeMap.erase(it); return 0; } WebRtc_Word8 RTPSender::SendPayloadType() const { return _payloadType; } int RTPSender::SendPayloadFrequency() const { return _audio->AudioFrequency(); } // See http://www.ietf.org/internet-drafts/draft-ietf-avt-app-rtp-keepalive-04.txt // for details about this method. Only Section 4.6 is implemented so far. bool RTPSender::RTPKeepalive() const { return _keepAliveIsActive; } WebRtc_Word32 RTPSender::RTPKeepaliveStatus(bool* enable, WebRtc_Word8* unknownPayloadType, WebRtc_UWord16* deltaTransmitTimeMS) const { CriticalSectionScoped cs(_sendCritsect); if(enable) { *enable = _keepAliveIsActive; } if(unknownPayloadType) { *unknownPayloadType = _keepAlivePayloadType; } if(deltaTransmitTimeMS) { *deltaTransmitTimeMS =_keepAliveDeltaTimeSend; } return 0; } WebRtc_Word32 RTPSender::EnableRTPKeepalive( const WebRtc_Word8 unknownPayloadType, const WebRtc_UWord16 deltaTransmitTimeMS) { CriticalSectionScoped cs(_sendCritsect); std::map::iterator it = _payloadTypeMap.find(unknownPayloadType); if (it != _payloadTypeMap.end()) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); return -1; } _keepAliveIsActive = true; _keepAlivePayloadType = unknownPayloadType; _keepAliveLastSent = _clock.GetTimeInMS(); _keepAliveDeltaTimeSend = deltaTransmitTimeMS; return 0; } WebRtc_Word32 RTPSender::DisableRTPKeepalive() { _keepAliveIsActive = false; return 0; } bool RTPSender::TimeToSendRTPKeepalive() const { CriticalSectionScoped cs(_sendCritsect); bool timeToSend(false); WebRtc_UWord32 dT = _clock.GetTimeInMS() - _keepAliveLastSent; if (dT > _keepAliveDeltaTimeSend) { timeToSend = true; } return timeToSend; } // ---------------------------------------------------------------------------- // From the RFC draft: // // 4.6. RTP Packet with Unknown Payload Type // // The application sends an RTP packet of 0 length with a dynamic // payload type that has not been negotiated by the peers (e.g. not // negotiated within the SDP offer/answer, and thus not mapped to any // media format). // // The sequence number is incremented by one for each packet, as it is // sent within the same RTP session as the actual media. The timestamp // contains the same value a media packet would have at this time. The // marker bit is not significant for the keepalive packets and is thus // set to zero. // // Normally the peer will ignore this packet, as RTP [RFC3550] states // that "a receiver MUST ignore packets with payload types that it does // not understand". // // Cons: // o [RFC4566] and [RFC3264] mandate not to send media with inactive // and recvonly attributes, however this is mitigated as no real // media is sent with this mechanism. // // Recommendation: // o This method should be used for RTP keepalive. // // 7. Timing and Transport Considerations // // An application supporting this specification must transmit keepalive // packets every Tr seconds during the whole duration of the media // session. Tr SHOULD be configurable, and otherwise MUST default to 15 // seconds. // // Keepalives packets within a particular RTP session MUST use the tuple // (source IP address, source TCP/UDP ports, target IP address, target // TCP/UDP Port) of the regular RTP packets. // // The agent SHOULD only send RTP keepalive when it does not send // regular RTP packets. // // http://www.ietf.org/internet-drafts/draft-ietf-avt-app-rtp-keepalive-04.txt // ---------------------------------------------------------------------------- WebRtc_Word32 RTPSender::SendRTPKeepalivePacket() { // RFC summary: // // - Send an RTP packet of 0 length; // - dynamic payload type has not been negotiated (not mapped to any media); // - sequence number is incremented by one for each packet; // - timestamp contains the same value a media packet would have at this time; // - marker bit is set to zero. WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE]; WebRtc_UWord16 rtpHeaderLength = 12; { CriticalSectionScoped cs(_sendCritsect); WebRtc_UWord32 now = _clock.GetTimeInMS(); WebRtc_UWord32 dT = now -_keepAliveLastSent; // delta time in MS WebRtc_UWord32 freqKHz = 90; // video if(_audioConfigured) { freqKHz = _audio->AudioFrequency()/1000; } WebRtc_UWord32 dSamples = dT*freqKHz; // set timestamp _timeStamp += dSamples; _keepAliveLastSent = now; rtpHeaderLength = RTPHeaderLength(); // correct seq num, time stamp and payloadtype BuildRTPheader(dataBuffer, _keepAlivePayloadType, false, 0, false); } return SendToNetwork(dataBuffer, 0, rtpHeaderLength, kAllowRetransmission); } WebRtc_Word32 RTPSender::SetMaxPayloadLength(const WebRtc_UWord16 maxPayloadLength, const WebRtc_UWord16 packetOverHead) { // sanity check if(maxPayloadLength < 100 || maxPayloadLength > IP_PACKET_SIZE) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); return -1; } CriticalSectionScoped cs(_sendCritsect); _maxPayloadLength = maxPayloadLength; _packetOverHead = packetOverHead; WEBRTC_TRACE(kTraceInfo, kTraceRtpRtcp, _id, "SetMaxPayloadLength to %d.", maxPayloadLength); return 0; } WebRtc_UWord16 RTPSender::MaxDataPayloadLength() const { if(_audioConfigured) { return _maxPayloadLength - RTPHeaderLength(); } else { return _maxPayloadLength - RTPHeaderLength() - _video->FECPacketOverhead() - ((_RTX) ? 2 : 0); // Include the FEC/ULP/RED overhead. } } WebRtc_UWord16 RTPSender::MaxPayloadLength() const { return _maxPayloadLength; } WebRtc_UWord16 RTPSender::PacketOverHead() const { return _packetOverHead; } void RTPSender::SetTransmissionSmoothingStatus(const bool enable) { CriticalSectionScoped cs(_sendCritsect); _transmissionSmoothing = enable; } bool RTPSender::TransmissionSmoothingStatus() const { CriticalSectionScoped cs(_sendCritsect); return _transmissionSmoothing; } void RTPSender::SetRTXStatus(const bool enable, const bool setSSRC, const WebRtc_UWord32 SSRC) { CriticalSectionScoped cs(_sendCritsect); _RTX = enable; if (enable) { if (setSSRC) { _ssrcRTX = SSRC; } else { _ssrcRTX = _ssrcDB.CreateSSRC(); // can't be 0 } } } void RTPSender::RTXStatus(bool* enable, WebRtc_UWord32* SSRC) const { CriticalSectionScoped cs(_sendCritsect); *enable = _RTX; *SSRC = _ssrcRTX; } WebRtc_Word32 RTPSender::CheckPayloadType(const WebRtc_Word8 payloadType, RtpVideoCodecTypes& videoType) { CriticalSectionScoped cs(_sendCritsect); if (payloadType < 0) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "\tinvalid payloadType (%d)", payloadType); return -1; } if (_audioConfigured) { WebRtc_Word8 redPlType = -1; if (_audio->RED(redPlType) == 0) { // We have configured RED. if(redPlType == payloadType) { // And it's a match... return 0; } } } if (_payloadType == payloadType) { if (!_audioConfigured) { videoType = _video->VideoCodecType(); } return 0; } std::map::iterator it = _payloadTypeMap.find(payloadType); if (it == _payloadTypeMap.end()) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "\tpayloadType:%d not registered", payloadType); return -1; } _payloadType = payloadType; ModuleRTPUtility::Payload* payload = it->second; assert(payload); if (payload->audio) { if (_audioConfigured) { // Extract payload frequency int payloadFreqHz; if (ModuleRTPUtility::StringCompare(payload->name,"g722",4)&& (payload->name[4] == 0)) { //Check that strings end there, g722.1... // Special case for G.722, bug in spec payloadFreqHz=8000; } else { payloadFreqHz=payload->typeSpecific.Audio.frequency; } //we don't do anything if it's CN if ((_audio->AudioFrequency() != payloadFreqHz)&& (!ModuleRTPUtility::StringCompare(payload->name,"cn",2))) { _audio->SetAudioFrequency(payloadFreqHz); // We need to correct the timestamp again, // since this might happen after we've set it WebRtc_UWord32 RTPtime = ModuleRTPUtility::GetCurrentRTP(&_clock, payloadFreqHz); SetStartTimestamp(RTPtime); // will be ignored if it's already configured via API } } } else { if(!_audioConfigured) { _video->SetVideoCodecType(payload->typeSpecific.Video.videoCodecType); videoType = payload->typeSpecific.Video.videoCodecType; _video->SetMaxConfiguredBitrateVideo( payload->typeSpecific.Video.maxRate); } } return 0; } WebRtc_Word32 RTPSender::SendOutgoingData(const FrameType frameType, const WebRtc_Word8 payloadType, const WebRtc_UWord32 captureTimeStamp, const WebRtc_UWord8* payloadData, const WebRtc_UWord32 payloadSize, const RTPFragmentationHeader* fragmentation, VideoCodecInformation* codecInfo, const RTPVideoTypeHeader* rtpTypeHdr) { { // Drop this packet if we're not sending media packets CriticalSectionScoped cs(_sendCritsect); if (!_sendingMedia) { return 0; } } RtpVideoCodecTypes videoType = kRtpNoVideo; if(CheckPayloadType(payloadType, videoType) != 0) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument failed to find payloadType:%d", __FUNCTION__, payloadType); return -1; } // update keepalive so that we don't trigger keepalive messages while sending data _keepAliveLastSent = _clock.GetTimeInMS(); if(_audioConfigured) { // assert video frameTypes assert(frameType == kAudioFrameSpeech || frameType == kAudioFrameCN || frameType == kFrameEmpty); return _audio->SendAudio(frameType, payloadType, captureTimeStamp, payloadData, payloadSize,fragmentation); } else { // assert audio frameTypes assert(frameType == kVideoFrameKey || frameType == kVideoFrameDelta || frameType == kVideoFrameGolden || frameType == kVideoFrameAltRef); return _video->SendVideo(videoType, frameType, payloadType, captureTimeStamp, payloadData, payloadSize, fragmentation, codecInfo, rtpTypeHdr); } } WebRtc_Word32 RTPSender::SendPadData(WebRtc_Word8 payload_type, WebRtc_UWord32 capture_timestamp, WebRtc_Word32 bytes) { // Drop this packet if we're not sending media packets if (!_sendingMedia) { return 0; } // Max in the RFC 3550 is 255 bytes, we limit it to be modulus 32 for SRTP. int max_length = 224; WebRtc_UWord8 data_buffer[IP_PACKET_SIZE]; for (; bytes > 0; bytes -= max_length) { WebRtc_Word32 header_length; { // Correct seq num, timestamp and payload type. header_length = BuildRTPheader(data_buffer, payload_type, false, // No markerbit. capture_timestamp, true, // Timestamp provided. true); // Increment sequence number. } data_buffer[0] |= 0x20; // Set padding bit. WebRtc_Word32* data = reinterpret_cast(&(data_buffer[header_length])); int padding_bytes_in_packet = max_length; if (bytes < max_length) { padding_bytes_in_packet = (bytes + 16) & 0xffe0; // Keep our modulus 32. } if (padding_bytes_in_packet < 32) { // Sanity don't send empty packets. break; } // Fill data buffer with random data. for(int j = 0; j < (padding_bytes_in_packet >> 2); j++) { data[j] = rand(); } // Set number of padding bytes in the last byte of the packet. data_buffer[header_length + padding_bytes_in_packet - 1] = padding_bytes_in_packet; // Send the packet if (0 > SendToNetwork(data_buffer, padding_bytes_in_packet, header_length, kDontRetransmit)) { // Error sending the packet. break; } } if (bytes > 31) { // 31 due to our modulus 32. // We did not manage to send all bytes. return -1; } return 0; } WebRtc_Word32 RTPSender::SetStorePacketsStatus( const bool enable, const WebRtc_UWord16 numberToStore) { _packetHistory->SetStorePacketsStatus(enable, numberToStore); return 0; } bool RTPSender::StorePackets() const { return _packetHistory->StorePackets(); } WebRtc_Word32 RTPSender::ReSendPacket(WebRtc_UWord16 packet_id, WebRtc_UWord32 min_resend_time) { WebRtc_UWord16 length = IP_PACKET_SIZE; WebRtc_UWord8 data_buffer[IP_PACKET_SIZE]; WebRtc_UWord8* buffer_to_send_ptr = data_buffer; WebRtc_UWord32 stored_time_in_ms; StorageType type; bool found = _packetHistory->GetRTPPacket(packet_id, min_resend_time, data_buffer, &length, &stored_time_in_ms, &type); if (!found) { // Packet not found. return -1; } if (length == 0 || type == kDontRetransmit) { // No bytes copied (packet recently resent, skip resending) or // packet should not be retransmitted. return 0; } if (_RTX) { WebRtc_UWord8 data_buffer_rtx[IP_PACKET_SIZE]; buffer_to_send_ptr = data_buffer_rtx; CriticalSectionScoped cs(_sendCritsect); // Add RTX header. ModuleRTPUtility::RTPHeaderParser rtpParser( reinterpret_cast(data_buffer), length); WebRtcRTPHeader rtp_header; rtpParser.Parse(rtp_header); // Add original RTP header. memcpy(data_buffer_rtx, data_buffer, rtp_header.header.headerLength); // Replace sequence number. WebRtc_UWord8* ptr = data_buffer_rtx + 2; ModuleRTPUtility::AssignUWord16ToBuffer(ptr, _sequenceNumberRTX++); // Replace SSRC. ptr += 6; ModuleRTPUtility::AssignUWord32ToBuffer(ptr, _ssrcRTX); // Add OSN (original sequence number). ptr = data_buffer_rtx + rtp_header.header.headerLength; ModuleRTPUtility::AssignUWord16ToBuffer( ptr, rtp_header.header.sequenceNumber); ptr += 2; // Add original payload data. memcpy(ptr, data_buffer + rtp_header.header.headerLength, length - rtp_header.header.headerLength); length += 2; } WebRtc_Word32 bytes_sent = ReSendToNetwork(buffer_to_send_ptr, length); if (bytes_sent <= 0) { WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "Transport failed to resend packet_id %u", packet_id); return -1; } // Store the time when the packet was last resent. _packetHistory->UpdateResendTime(packet_id); return bytes_sent; } WebRtc_Word32 RTPSender::ReSendToNetwork(const WebRtc_UWord8* packet, const WebRtc_UWord32 size) { WebRtc_Word32 bytes_sent = -1; { CriticalSectionScoped lock(_transportCritsect); if (_transport) { bytes_sent = _transport->SendPacket(_id, packet, size); } } if (bytes_sent <= 0) { return -1; } // Update send statistics CriticalSectionScoped cs(_sendCritsect); Bitrate::Update(bytes_sent); _packetsSent++; // We on purpose don't add to _payloadBytesSent since this is a // re-transmit and not new payload data. return bytes_sent; } int RTPSender::SelectiveRetransmissions() const { if (!_video) return -1; return _video->SelectiveRetransmissions(); } int RTPSender::SetSelectiveRetransmissions(uint8_t settings) { if (!_video) return -1; return _video->SetSelectiveRetransmissions(settings); } void RTPSender::OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength, const WebRtc_UWord16* nackSequenceNumbers, const WebRtc_UWord16 avgRTT) { const WebRtc_UWord32 now = _clock.GetTimeInMS(); WebRtc_UWord32 bytesReSent = 0; // Enough bandwidth to send NACK? if (!ProcessNACKBitRate(now)) { WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "NACK bitrate reached. Skip sending NACK response. Target %d", TargetSendBitrateKbit()); return; } for (WebRtc_UWord16 i = 0; i < nackSequenceNumbersLength; ++i) { const WebRtc_Word32 bytesSent = ReSendPacket(nackSequenceNumbers[i], 5+avgRTT); if (bytesSent > 0) { bytesReSent += bytesSent; } else if (bytesSent == 0) { // The packet has previously been resent. // Try resending next packet in the list. continue; } else if (bytesSent < 0) { // Failed to send one Sequence number. Give up the rest in this nack. WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "Failed resending RTP packet %d, Discard rest of packets", nackSequenceNumbers[i]); break; } // delay bandwidth estimate (RTT * BW) if (TargetSendBitrateKbit() != 0 && avgRTT) { // kbits/s * ms = bits => bits/8 = bytes WebRtc_UWord32 targetBytes = (static_cast(TargetSendBitrateKbit()) * avgRTT) >> 3; if (bytesReSent > targetBytes) { break; // ignore the rest of the packets in the list } } } if (bytesReSent > 0) { // TODO(pwestin) consolidate these two methods. UpdateNACKBitRate(bytesReSent, now); _nackBitrate.Update(bytesReSent); } } /** * @return true if the nack bitrate is lower than the requested max bitrate */ bool RTPSender::ProcessNACKBitRate(const WebRtc_UWord32 now) { WebRtc_UWord32 num = 0; WebRtc_Word32 byteCount = 0; const WebRtc_UWord32 avgInterval=1000; CriticalSectionScoped cs(_sendCritsect); if (_targetSendBitrate == 0) { return true; } for (num = 0; num < NACK_BYTECOUNT_SIZE; num++) { if ((now - _nackByteCountTimes[num]) > avgInterval) { // don't use data older than 1sec break; } else { byteCount += _nackByteCount[num]; } } WebRtc_Word32 timeInterval = avgInterval; if (num == NACK_BYTECOUNT_SIZE) { // More than NACK_BYTECOUNT_SIZE nack messages has been received // during the last msgInterval timeInterval = now - _nackByteCountTimes[num-1]; if(timeInterval < 0) { timeInterval = avgInterval; } } return (byteCount*8) < (_targetSendBitrate * timeInterval); } void RTPSender::UpdateNACKBitRate(const WebRtc_UWord32 bytes, const WebRtc_UWord32 now) { CriticalSectionScoped cs(_sendCritsect); // save bitrate statistics if(bytes > 0) { if(now == 0) { // add padding length _nackByteCount[0] += bytes; } else { if(_nackByteCountTimes[0] == 0) { // first no shift } else { // shift for(int i = (NACK_BYTECOUNT_SIZE-2); i >= 0 ; i--) { _nackByteCount[i+1] = _nackByteCount[i]; _nackByteCountTimes[i+1] = _nackByteCountTimes[i]; } } _nackByteCount[0] = bytes; _nackByteCountTimes[0] = now; } } } void RTPSender::ProcessSendToNetwork() { // triggered by timer WebRtc_UWord32 delta_time_ms; { CriticalSectionScoped cs(_sendCritsect); if (!_transmissionSmoothing) { return; } WebRtc_UWord32 now = _clock.GetTimeInMS(); delta_time_ms = now - _timeLastSendToNetworkUpdate; _timeLastSendToNetworkUpdate = now; } _sendBucket.UpdateBytesPerInterval(delta_time_ms, _targetSendBitrate); while (!_sendBucket.Empty()) { WebRtc_Word32 seq_num = _sendBucket.GetNextPacket(); if (seq_num < 0) { break; } WebRtc_UWord8 data_buffer[IP_PACKET_SIZE]; WebRtc_UWord16 length = IP_PACKET_SIZE; WebRtc_UWord32 stored_time_ms; StorageType type; bool found = _packetHistory->GetRTPPacket(seq_num, 0, data_buffer, &length, &stored_time_ms, &type); if (!found) { assert(false); return; } assert(length > 0); WebRtc_UWord32 diff_ms = _clock.GetTimeInMS() - stored_time_ms; ModuleRTPUtility::RTPHeaderParser rtpParser(data_buffer, length); WebRtcRTPHeader rtp_header; rtpParser.Parse(rtp_header); UpdateTransmissionTimeOffset(data_buffer, length, rtp_header, diff_ms); // Send packet WebRtc_Word32 bytes_sent = -1; { CriticalSectionScoped cs(_transportCritsect); if (_transport) { bytes_sent = _transport->SendPacket(_id, data_buffer, length); } } // Update send statistics if (bytes_sent > 0) { CriticalSectionScoped cs(_sendCritsect); Bitrate::Update(bytes_sent); _packetsSent++; if (bytes_sent > rtp_header.header.headerLength) { _payloadBytesSent += bytes_sent - rtp_header.header.headerLength; } } } } WebRtc_Word32 RTPSender::SendToNetwork(const WebRtc_UWord8* buffer, const WebRtc_UWord16 length, const WebRtc_UWord16 rtpLength, const StorageType storage) { // Used for NACK or to spead out the transmission of packets. if (_packetHistory->PutRTPPacket( buffer, rtpLength + length, _maxPayloadLength, storage) != 0) { return -1; } if (_transmissionSmoothing) { const WebRtc_UWord16 sequenceNumber = (buffer[2] << 8) + buffer[3]; _sendBucket.Fill(sequenceNumber, rtpLength + length); // Packet will be sent at a later time. return 0; } // Send packet WebRtc_Word32 bytes_sent = -1; { CriticalSectionScoped cs(_transportCritsect); if (_transport) { bytes_sent = _transport->SendPacket(_id, buffer, length + rtpLength); } } if (bytes_sent <= 0) { return -1; } // Update send statistics CriticalSectionScoped cs(_sendCritsect); Bitrate::Update(bytes_sent); _packetsSent++; if (bytes_sent > rtpLength) { _payloadBytesSent += bytes_sent - rtpLength; } return 0; } void RTPSender::ProcessBitrate() { CriticalSectionScoped cs(_sendCritsect); Bitrate::Process(); _nackBitrate.Process(); if (_audioConfigured) return; _video->ProcessBitrate(); } WebRtc_UWord16 RTPSender::RTPHeaderLength() const { WebRtc_UWord16 rtpHeaderLength = 12; if(_includeCSRCs) { rtpHeaderLength += sizeof(WebRtc_UWord32)*_CSRCs; } rtpHeaderLength += RtpHeaderExtensionTotalLength(); return rtpHeaderLength; } WebRtc_UWord16 RTPSender::IncrementSequenceNumber() { CriticalSectionScoped cs(_sendCritsect); return _sequenceNumber++; } WebRtc_Word32 RTPSender::ResetDataCounters() { _packetsSent = 0; _payloadBytesSent = 0; return 0; } // number of sent RTP packets // dont use critsect to avoid potental deadlock WebRtc_UWord32 RTPSender::Packets() const { return _packetsSent; } // number of sent RTP bytes // dont use critsect to avoid potental deadlock WebRtc_UWord32 RTPSender::Bytes() const { return _payloadBytesSent; } WebRtc_Word32 RTPSender::BuildRTPheader(WebRtc_UWord8* dataBuffer, const WebRtc_Word8 payloadType, const bool markerBit, const WebRtc_UWord32 captureTimeStamp, const bool timeStampProvided, const bool incSequenceNumber) { assert(payloadType>=0); CriticalSectionScoped cs(_sendCritsect); dataBuffer[0] = static_cast(0x80); // version 2 dataBuffer[1] = static_cast(payloadType); if (markerBit) { dataBuffer[1] |= kRtpMarkerBitMask; // MarkerBit is set } if(timeStampProvided) { _timeStamp = _startTimeStamp + captureTimeStamp; } else { // make a unique time stamp // used for inband signaling // we can't inc by the actual time, since then we increase the risk of back timing _timeStamp++; } ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+2, _sequenceNumber); ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+4, _timeStamp); ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+8, _ssrc); WebRtc_Word32 rtpHeaderLength = 12; // Add the CSRCs if any if (_includeCSRCs && _CSRCs > 0) { if(_CSRCs > kRtpCsrcSize) { // error assert(false); return -1; } WebRtc_UWord8* ptr = &dataBuffer[rtpHeaderLength]; for (WebRtc_UWord32 i = 0; i < _CSRCs; ++i) { ModuleRTPUtility::AssignUWord32ToBuffer(ptr, _CSRC[i]); ptr +=4; } dataBuffer[0] = (dataBuffer[0]&0xf0) | _CSRCs; // Update length of header rtpHeaderLength += sizeof(WebRtc_UWord32)*_CSRCs; } { _sequenceNumber++; // prepare for next packet } WebRtc_UWord16 len = BuildRTPHeaderExtension(dataBuffer + rtpHeaderLength); if (len) { dataBuffer[0] |= 0x10; // set eXtension bit rtpHeaderLength += len; } return rtpHeaderLength; } WebRtc_UWord16 RTPSender::BuildRTPHeaderExtension(WebRtc_UWord8* dataBuffer) const { if (_rtpHeaderExtensionMap.Size() <= 0) { return 0; } /* RTP header extension, RFC 3550. 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 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | defined by profile | length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | header extension | | .... | */ const WebRtc_UWord32 kPosLength = 2; const WebRtc_UWord32 kHeaderLength = RTP_ONE_BYTE_HEADER_LENGTH_IN_BYTES; // Add extension ID (0xBEDE). ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer, RTP_ONE_BYTE_HEADER_EXTENSION); // Add extensions. WebRtc_UWord16 total_block_length = 0; RTPExtensionType type = _rtpHeaderExtensionMap.First(); while (type != kRtpExtensionNone) { WebRtc_UWord8 block_length = 0; if (type == kRtpExtensionTransmissionTimeOffset) { block_length = BuildTransmissionTimeOffsetExtension( dataBuffer + kHeaderLength + total_block_length); } total_block_length += block_length; type = _rtpHeaderExtensionMap.Next(type); } if (total_block_length == 0) { // No extension added. return 0; } // Set header length (in number of Word32, header excluded). assert(total_block_length % 4 == 0); ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer + kPosLength, total_block_length / 4); // Total added length. return kHeaderLength + total_block_length; } WebRtc_UWord8 RTPSender::BuildTransmissionTimeOffsetExtension(WebRtc_UWord8* dataBuffer) const { // From RFC 5450: Transmission Time Offsets in RTP Streams. // // The transmission 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 signed integer. // When added to the RTP timestamp of the packet, it represents the // "effective" RTP transmission time of the packet, on the RTP // timescale. // // The form of the transmission offset 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 | transmission offset | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // Get id defined by user. WebRtc_UWord8 id; if (_rtpHeaderExtensionMap.GetId(kRtpExtensionTransmissionTimeOffset, &id) != 0) { // Not registered. return 0; } int pos = 0; const WebRtc_UWord8 len = 2; dataBuffer[pos++] = (id << 4) + len; ModuleRTPUtility::AssignUWord24ToBuffer(dataBuffer + pos, _transmissionTimeOffset); pos += 3; assert(pos == TRANSMISSION_TIME_OFFSET_LENGTH_IN_BYTES); return TRANSMISSION_TIME_OFFSET_LENGTH_IN_BYTES; } void RTPSender::UpdateTransmissionTimeOffset( WebRtc_UWord8* rtp_packet, const WebRtc_UWord16 rtp_packet_length, const WebRtcRTPHeader& rtp_header, const WebRtc_UWord32 time_ms) const { CriticalSectionScoped cs(_sendCritsect); // Get length until start of transmission block. int transmission_block_pos = _rtpHeaderExtensionMap.GetLengthUntilBlockStartInBytes( kRtpExtensionTransmissionTimeOffset); if (transmission_block_pos < 0) { WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "Failed to update transmission time offset, not registered."); return; } int block_pos = 12 + rtp_header.header.numCSRCs + transmission_block_pos; if ((rtp_packet_length < block_pos + 4)) { WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "Failed to update transmission time offset, invalid length."); return; } // 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 transmission time offset, hdr extension not found."); return; } // Get id. WebRtc_UWord8 id = 0; if (_rtpHeaderExtensionMap.GetId(kRtpExtensionTransmissionTimeOffset, &id) != 0) { WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "Failed to update transmission time offset, no id."); return; } // Verify first byte in block. const WebRtc_UWord8 first_block_byte = (id << 4) + 2; if (rtp_packet[block_pos] != first_block_byte) { WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "Failed to update transmission time offset."); return; } // Update transmission offset field. ModuleRTPUtility::AssignUWord24ToBuffer(rtp_packet + block_pos + 1, time_ms * 90); // RTP timestamp } WebRtc_Word32 RTPSender::RegisterSendTransport(Transport* transport) { CriticalSectionScoped cs(_transportCritsect); _transport = transport; return 0; } void RTPSender::SetSendingStatus(const bool enabled) { if(enabled) { WebRtc_UWord32 freq; if(_audioConfigured) { WebRtc_UWord32 frequency = _audio->AudioFrequency(); // sanity switch(frequency) { case 8000: case 12000: case 16000: case 24000: case 32000: break; default: assert(false); return; } freq = frequency; } else { freq = 90000; // 90 KHz for all video } WebRtc_UWord32 RTPtime = ModuleRTPUtility::GetCurrentRTP(&_clock, freq); SetStartTimestamp(RTPtime); // will be ignored if it's already configured via API } else { if(!_ssrcForced) { // generate a new SSRC _ssrcDB.ReturnSSRC(_ssrc); _ssrc = _ssrcDB.CreateSSRC(); // can't be 0 } if(!_sequenceNumberForced && !_ssrcForced) // don't initialize seq number if SSRC passed externally { // generate a new sequence number _sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER); } } } void RTPSender::SetSendingMediaStatus(const bool enabled) { CriticalSectionScoped cs(_sendCritsect); _sendingMedia = enabled; } bool RTPSender::SendingMedia() const { CriticalSectionScoped cs(_sendCritsect); return _sendingMedia; } WebRtc_UWord32 RTPSender::Timestamp() const { CriticalSectionScoped cs(_sendCritsect); return _timeStamp; } WebRtc_Word32 RTPSender::SetStartTimestamp( const WebRtc_UWord32 timestamp, const bool force) { CriticalSectionScoped cs(_sendCritsect); if(force) { _startTimeStampForced = force; _startTimeStamp = timestamp; } else { if(!_startTimeStampForced) { _startTimeStamp = timestamp; } } return 0; } WebRtc_UWord32 RTPSender::StartTimestamp() const { CriticalSectionScoped cs(_sendCritsect); return _startTimeStamp; } WebRtc_UWord32 RTPSender::GenerateNewSSRC() { // if configured via API, return 0 CriticalSectionScoped cs(_sendCritsect); if(_ssrcForced) { return 0; } _ssrc = _ssrcDB.CreateSSRC(); // can't be 0 return _ssrc; } WebRtc_Word32 RTPSender::SetSSRC(WebRtc_UWord32 ssrc) { // this is configured via the API CriticalSectionScoped cs(_sendCritsect); if (_ssrc == ssrc && _ssrcForced) { return 0; // since it's same ssrc, don't reset anything } _ssrcForced = true; _ssrcDB.ReturnSSRC(_ssrc); _ssrcDB.RegisterSSRC(ssrc); _ssrc = ssrc; if(!_sequenceNumberForced) { _sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER); } return 0; } WebRtc_UWord32 RTPSender::SSRC() const { CriticalSectionScoped cs(_sendCritsect); return _ssrc; } WebRtc_Word32 RTPSender::SetCSRCStatus(const bool include) { _includeCSRCs = include; return 0; } WebRtc_Word32 RTPSender::SetCSRCs(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize], const WebRtc_UWord8 arrLength) { if(arrLength > kRtpCsrcSize) { assert(false); return -1; } CriticalSectionScoped cs(_sendCritsect); for(int i = 0; i < arrLength;i++) { _CSRC[i] = arrOfCSRC[i]; } _CSRCs = arrLength; return 0; } WebRtc_Word32 RTPSender::CSRCs(WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const { CriticalSectionScoped cs(_sendCritsect); if(arrOfCSRC == NULL) { assert(false); return -1; } for(int i = 0; i < _CSRCs && i < kRtpCsrcSize;i++) { arrOfCSRC[i] = _CSRC[i]; } return _CSRCs; } WebRtc_Word32 RTPSender::SetSequenceNumber(WebRtc_UWord16 seq) { CriticalSectionScoped cs(_sendCritsect); _sequenceNumberForced = true; _sequenceNumber = seq; return 0; } WebRtc_UWord16 RTPSender::SequenceNumber() const { CriticalSectionScoped cs(_sendCritsect); return _sequenceNumber; } /* * Audio */ WebRtc_Word32 RTPSender::RegisterAudioCallback(RtpAudioFeedback* messagesCallback) { if(!_audioConfigured) { return -1; } return _audio->RegisterAudioCallback(messagesCallback); } // Send a DTMF tone, RFC 2833 (4733) WebRtc_Word32 RTPSender::SendTelephoneEvent(const WebRtc_UWord8 key, const WebRtc_UWord16 time_ms, const WebRtc_UWord8 level) { if(!_audioConfigured) { return -1; } return _audio->SendTelephoneEvent(key, time_ms, level); } bool RTPSender::SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const { if(!_audioConfigured) { return false; } return _audio->SendTelephoneEventActive(telephoneEvent); } // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG) WebRtc_Word32 RTPSender::SetAudioPacketSize(const WebRtc_UWord16 packetSizeSamples) { if(!_audioConfigured) { return -1; } return _audio->SetAudioPacketSize(packetSizeSamples); } WebRtc_Word32 RTPSender::SetAudioLevelIndicationStatus(const bool enable, const WebRtc_UWord8 ID) { if(!_audioConfigured) { return -1; } return _audio->SetAudioLevelIndicationStatus(enable, ID); } WebRtc_Word32 RTPSender::AudioLevelIndicationStatus(bool& enable, WebRtc_UWord8& ID) const { return _audio->AudioLevelIndicationStatus(enable, ID); } WebRtc_Word32 RTPSender::SetAudioLevel(const WebRtc_UWord8 level_dBov) { return _audio->SetAudioLevel(level_dBov); } // Set payload type for Redundant Audio Data RFC 2198 WebRtc_Word32 RTPSender::SetRED(const WebRtc_Word8 payloadType) { if(!_audioConfigured) { return -1; } return _audio->SetRED(payloadType); } // Get payload type for Redundant Audio Data RFC 2198 WebRtc_Word32 RTPSender::RED(WebRtc_Word8& payloadType) const { if(!_audioConfigured) { return -1; } return _audio->RED(payloadType); } /* * Video */ VideoCodecInformation* RTPSender::CodecInformationVideo() { if(_audioConfigured) { return NULL; } return _video->CodecInformationVideo(); } RtpVideoCodecTypes RTPSender::VideoCodecType() const { if(_audioConfigured) { return kRtpNoVideo; } return _video->VideoCodecType(); } WebRtc_UWord32 RTPSender::MaxConfiguredBitrateVideo() const { if(_audioConfigured) { return 0; } return _video->MaxConfiguredBitrateVideo(); } WebRtc_Word32 RTPSender::SendRTPIntraRequest() { if(_audioConfigured) { return -1; } return _video->SendRTPIntraRequest(); } // FEC WebRtc_Word32 RTPSender::SetGenericFECStatus(const bool enable, const WebRtc_UWord8 payloadTypeRED, const WebRtc_UWord8 payloadTypeFEC) { if(_audioConfigured) { return -1; } return _video->SetGenericFECStatus(enable, payloadTypeRED, payloadTypeFEC); } WebRtc_Word32 RTPSender::GenericFECStatus(bool& enable, WebRtc_UWord8& payloadTypeRED, WebRtc_UWord8& payloadTypeFEC) const { if(_audioConfigured) { return -1; } return _video->GenericFECStatus(enable, payloadTypeRED, payloadTypeFEC); } WebRtc_Word32 RTPSender::SetFECCodeRate(const WebRtc_UWord8 keyFrameCodeRate, const WebRtc_UWord8 deltaFrameCodeRate) { if(_audioConfigured) { return -1; } return _video->SetFECCodeRate(keyFrameCodeRate, deltaFrameCodeRate); } WebRtc_Word32 RTPSender::SetFECUepProtection(const bool keyUseUepProtection, const bool deltaUseUepProtection) { if(_audioConfigured) { return -1; } return _video->SetFECUepProtection(keyUseUepProtection, deltaUseUepProtection); } } // namespace webrtc