blob: d61acdc003cfd9435438599385708ee91c1a5aec [file] [log] [blame]
niklase@google.com470e71d2011-07-07 08:21:25 +00001/*
pwestin@webrtc.orgf6bb77a2012-01-24 17:16:59 +00002 * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
niklase@google.com470e71d2011-07-07 08:21:25 +00003 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include <cstdlib> // srand
12
13#include "rtp_sender.h"
14
15#include "critical_section_wrapper.h"
16#include "trace.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000017
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +000018#include "rtp_packet_history.h"
niklase@google.com470e71d2011-07-07 08:21:25 +000019#include "rtp_sender_audio.h"
20#include "rtp_sender_video.h"
21
22namespace webrtc {
pwestin@webrtc.org0644b1d2011-12-01 15:42:31 +000023RTPSender::RTPSender(const WebRtc_Word32 id,
24 const bool audio,
25 RtpRtcpClock* clock) :
26 Bitrate(clock),
niklase@google.com470e71d2011-07-07 08:21:25 +000027 _id(id),
28 _audioConfigured(audio),
29 _audio(NULL),
30 _video(NULL),
henrike@webrtc.org65573f22011-12-13 19:17:27 +000031 _sendCritsect(CriticalSectionWrapper::CreateCriticalSection()),
32 _transportCritsect(CriticalSectionWrapper::CreateCriticalSection()),
niklase@google.com470e71d2011-07-07 08:21:25 +000033
34 _transport(NULL),
35
36 _sendingMedia(true), // Default to sending media
37
38 _maxPayloadLength(IP_PACKET_SIZE-28), // default is IP/UDP
39 _targetSendBitrate(0),
40 _packetOverHead(28),
41
42 _payloadType(-1),
43 _payloadTypeMap(),
44
asapersson@webrtc.org5249cc82011-12-16 14:31:37 +000045 _rtpHeaderExtensionMap(),
46 _transmissionTimeOffset(0),
47
niklase@google.com470e71d2011-07-07 08:21:25 +000048 // NACK
49 _nackByteCountTimes(),
50 _nackByteCount(),
pwestin@webrtc.org0644b1d2011-12-01 15:42:31 +000051 _nackBitrate(clock),
niklase@google.com470e71d2011-07-07 08:21:25 +000052
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +000053 _packetHistory(new RTPPacketHistory(clock)),
asapersson@webrtc.org23fd5592012-09-24 12:07:13 +000054 _sendBucket(clock),
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +000055 _timeLastSendToNetworkUpdate(clock->GetTimeInMS()),
56 _transmissionSmoothing(false),
57
niklase@google.com470e71d2011-07-07 08:21:25 +000058 // statistics
59 _packetsSent(0),
60 _payloadBytesSent(0),
61
62 // RTP variables
63 _startTimeStampForced(false),
64 _startTimeStamp(0),
65 _ssrcDB(*SSRCDatabase::GetSSRCDatabase()),
66 _remoteSSRC(0),
67 _sequenceNumberForced(false),
68 _sequenceNumber(0),
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +000069 _sequenceNumberRTX(0),
niklase@google.com470e71d2011-07-07 08:21:25 +000070 _ssrcForced(false),
71 _ssrc(0),
72 _timeStamp(0),
73 _CSRCs(0),
74 _CSRC(),
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +000075 _includeCSRCs(true),
76 _RTX(false),
77 _ssrcRTX(0)
niklase@google.com470e71d2011-07-07 08:21:25 +000078{
79 memset(_nackByteCountTimes, 0, sizeof(_nackByteCountTimes));
80 memset(_nackByteCount, 0, sizeof(_nackByteCount));
81
82 memset(_CSRC, 0, sizeof(_CSRC));
83
84 // we need to seed the random generator, otherwise we get 26500 each time, hardly a random value :)
pwestin@webrtc.org0644b1d2011-12-01 15:42:31 +000085 srand( (WebRtc_UWord32)_clock.GetTimeInMS() );
niklase@google.com470e71d2011-07-07 08:21:25 +000086
87 _ssrc = _ssrcDB.CreateSSRC(); // can't be 0
88
89 if(audio)
90 {
pwestin@webrtc.org0644b1d2011-12-01 15:42:31 +000091 _audio = new RTPSenderAudio(id, &_clock, this);
niklase@google.com470e71d2011-07-07 08:21:25 +000092 } else
93 {
pwestin@webrtc.org0644b1d2011-12-01 15:42:31 +000094 _video = new RTPSenderVideo(id, &_clock, this);
niklase@google.com470e71d2011-07-07 08:21:25 +000095 }
96 WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__);
97}
98
pwestin@webrtc.org00741872012-01-19 15:56:10 +000099RTPSender::~RTPSender() {
100 if(_remoteSSRC != 0) {
101 _ssrcDB.ReturnSSRC(_remoteSSRC);
102 }
103 _ssrcDB.ReturnSSRC(_ssrc);
niklase@google.com470e71d2011-07-07 08:21:25 +0000104
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000105 SSRCDatabase::ReturnSSRCDatabase();
106 delete _sendCritsect;
107 delete _transportCritsect;
108 while (!_payloadTypeMap.empty()) {
109 std::map<WebRtc_Word8, ModuleRTPUtility::Payload*>::iterator it =
110 _payloadTypeMap.begin();
111 delete it->second;
112 _payloadTypeMap.erase(it);
113 }
114 delete _packetHistory;
115 delete _audio;
116 delete _video;
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000117
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000118 WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__);
niklase@google.com470e71d2011-07-07 08:21:25 +0000119}
pwestin@webrtc.org2853dde2012-05-11 11:08:54 +0000120/*
niklase@google.com470e71d2011-07-07 08:21:25 +0000121WebRtc_Word32
122RTPSender::Init(const WebRtc_UWord32 remoteSSRC)
123{
124 CriticalSectionScoped cs(_sendCritsect);
125
126 // reset to default generation
127 _ssrcForced = false;
128 _startTimeStampForced = false;
129
130 // register a remote SSRC if we have it to avoid collisions
131 if(remoteSSRC != 0)
132 {
133 if(_ssrc == remoteSSRC)
134 {
135 // collision detected
136 _ssrc = _ssrcDB.CreateSSRC(); // can't be 0
137 }
138 _remoteSSRC = remoteSSRC;
139 _ssrcDB.RegisterSSRC(remoteSSRC);
140 }
141 _sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER);
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000142 _sequenceNumberRTX = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER);
niklase@google.com470e71d2011-07-07 08:21:25 +0000143 _packetsSent = 0;
144 _payloadBytesSent = 0;
145 _packetOverHead = 28;
146
asapersson@webrtc.org5249cc82011-12-16 14:31:37 +0000147 _rtpHeaderExtensionMap.Erase();
148
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000149 while (!_payloadTypeMap.empty()) {
150 std::map<WebRtc_Word8, ModuleRTPUtility::Payload*>::iterator it =
151 _payloadTypeMap.begin();
152 delete it->second;
153 _payloadTypeMap.erase(it);
154 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000155
156 memset(_CSRC, 0, sizeof(_CSRC));
157
158 memset(_nackByteCount, 0, sizeof(_nackByteCount));
159 memset(_nackByteCountTimes, 0, sizeof(_nackByteCountTimes));
stefan@webrtc.orgd0bdab02011-10-14 14:24:54 +0000160 _nackBitrate.Init();
niklase@google.com470e71d2011-07-07 08:21:25 +0000161
162 SetStorePacketsStatus(false, 0);
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000163 _sendBucket.Reset();
niklase@google.com470e71d2011-07-07 08:21:25 +0000164
165 Bitrate::Init();
166
167 if(_audioConfigured)
168 {
169 _audio->Init();
170 } else
171 {
172 _video->Init();
173 }
174 return(0);
175}
pwestin@webrtc.org2853dde2012-05-11 11:08:54 +0000176*/
niklase@google.com470e71d2011-07-07 08:21:25 +0000177
pwestin@webrtc.org49888ce2012-04-27 05:25:53 +0000178void RTPSender::SetTargetSendBitrate(const WebRtc_UWord32 bits) {
179 _targetSendBitrate = static_cast<uint16_t>(bits / 1000);
niklase@google.com470e71d2011-07-07 08:21:25 +0000180}
stefan@webrtc.orgd0bdab02011-10-14 14:24:54 +0000181
niklase@google.com470e71d2011-07-07 08:21:25 +0000182WebRtc_UWord16
183RTPSender::ActualSendBitrateKbit() const
184{
185 return (WebRtc_UWord16) (Bitrate::BitrateNow()/1000);
186}
187
stefan@webrtc.orgd0bdab02011-10-14 14:24:54 +0000188WebRtc_UWord32
stefan@webrtc.orgfbea4e52011-10-27 16:08:29 +0000189RTPSender::VideoBitrateSent() const {
190 if (_video)
191 return _video->VideoBitrateSent();
192 else
193 return 0;
194}
195
196WebRtc_UWord32
stefan@webrtc.orgd0bdab02011-10-14 14:24:54 +0000197RTPSender::FecOverheadRate() const {
stefan@webrtc.orgfbea4e52011-10-27 16:08:29 +0000198 if (_video)
199 return _video->FecOverheadRate();
200 else
201 return 0;
stefan@webrtc.orgd0bdab02011-10-14 14:24:54 +0000202}
203
204WebRtc_UWord32
205RTPSender::NackOverheadRate() const {
206 return _nackBitrate.BitrateLast();
207}
208
asapersson@webrtc.org5249cc82011-12-16 14:31:37 +0000209WebRtc_Word32
210RTPSender::SetTransmissionTimeOffset(
211 const WebRtc_Word32 transmissionTimeOffset)
212{
213 if (transmissionTimeOffset > (0x800000 - 1) ||
214 transmissionTimeOffset < -(0x800000 - 1)) // Word24
215 {
216 return -1;
217 }
218 CriticalSectionScoped cs(_sendCritsect);
219 _transmissionTimeOffset = transmissionTimeOffset;
220 return 0;
221}
222
223WebRtc_Word32
224RTPSender::RegisterRtpHeaderExtension(const RTPExtensionType type,
225 const WebRtc_UWord8 id)
226{
227 CriticalSectionScoped cs(_sendCritsect);
228 return _rtpHeaderExtensionMap.Register(type, id);
229}
230
231WebRtc_Word32
232RTPSender::DeregisterRtpHeaderExtension(const RTPExtensionType type)
233{
234 CriticalSectionScoped cs(_sendCritsect);
235 return _rtpHeaderExtensionMap.Deregister(type);
236}
237
238WebRtc_UWord16
239RTPSender::RtpHeaderExtensionTotalLength() const
240{
241 CriticalSectionScoped cs(_sendCritsect);
242 return _rtpHeaderExtensionMap.GetTotalLengthInBytes();
243}
244
niklase@google.com470e71d2011-07-07 08:21:25 +0000245//can be called multiple times
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000246WebRtc_Word32 RTPSender::RegisterPayload(
pwestin@webrtc.orgf6bb77a2012-01-24 17:16:59 +0000247 const char payloadName[RTP_PAYLOAD_NAME_SIZE],
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000248 const WebRtc_Word8 payloadNumber,
249 const WebRtc_UWord32 frequency,
250 const WebRtc_UWord8 channels,
251 const WebRtc_UWord32 rate) {
pwestin@webrtc.orgf6bb77a2012-01-24 17:16:59 +0000252 assert(payloadName);
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000253 CriticalSectionScoped cs(_sendCritsect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000254
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000255 std::map<WebRtc_Word8, ModuleRTPUtility::Payload*>::iterator it =
256 _payloadTypeMap.find(payloadNumber);
niklase@google.com470e71d2011-07-07 08:21:25 +0000257
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000258 if (_payloadTypeMap.end() != it) {
259 // we already use this payload type
260 ModuleRTPUtility::Payload* payload = it->second;
261 assert(payload);
niklase@google.com470e71d2011-07-07 08:21:25 +0000262
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000263 // check if it's the same as we already have
pwestin@webrtc.orgf6bb77a2012-01-24 17:16:59 +0000264 if (ModuleRTPUtility::StringCompare(payload->name, payloadName,
265 RTP_PAYLOAD_NAME_SIZE - 1)) {
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000266 if (_audioConfigured && payload->audio &&
267 payload->typeSpecific.Audio.frequency == frequency &&
268 (payload->typeSpecific.Audio.rate == rate ||
269 payload->typeSpecific.Audio.rate == 0 || rate == 0)) {
270 payload->typeSpecific.Audio.rate = rate;
271 // Ensure that we update the rate if new or old is zero
niklase@google.com470e71d2011-07-07 08:21:25 +0000272 return 0;
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000273 }
274 if(!_audioConfigured && !payload->audio) {
275 return 0;
276 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000277 }
278 return -1;
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000279 }
280 WebRtc_Word32 retVal = -1;
281 ModuleRTPUtility::Payload* payload = NULL;
282 if (_audioConfigured) {
283 retVal = _audio->RegisterAudioPayload(payloadName, payloadNumber, frequency,
284 channels, rate, payload);
285 } else {
286 retVal = _video->RegisterVideoPayload(payloadName, payloadNumber, rate,
287 payload);
288 }
289 if(payload) {
290 _payloadTypeMap[payloadNumber] = payload;
291 }
292 return retVal;
niklase@google.com470e71d2011-07-07 08:21:25 +0000293}
294
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000295WebRtc_Word32 RTPSender::DeRegisterSendPayload(const WebRtc_Word8 payloadType) {
296 CriticalSectionScoped lock(_sendCritsect);
297
298 std::map<WebRtc_Word8, ModuleRTPUtility::Payload*>::iterator it =
299 _payloadTypeMap.find(payloadType);
300
301 if (_payloadTypeMap.end() == it) return -1;
302
303 ModuleRTPUtility::Payload* payload = it->second;
304 delete payload;
305 _payloadTypeMap.erase(it);
306 return 0;
307}
niklase@google.com470e71d2011-07-07 08:21:25 +0000308
309WebRtc_Word8 RTPSender::SendPayloadType() const
310{
311 return _payloadType;
312}
313
314
315int RTPSender::SendPayloadFrequency() const
316{
317 return _audio->AudioFrequency();
318}
319
320
niklase@google.com470e71d2011-07-07 08:21:25 +0000321WebRtc_Word32
322RTPSender::SetMaxPayloadLength(const WebRtc_UWord16 maxPayloadLength, const WebRtc_UWord16 packetOverHead)
323{
324 // sanity check
325 if(maxPayloadLength < 100 || maxPayloadLength > IP_PACKET_SIZE)
326 {
327 WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__);
328 return -1;
329 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000330
331 CriticalSectionScoped cs(_sendCritsect);
332 _maxPayloadLength = maxPayloadLength;
333 _packetOverHead = packetOverHead;
334
335 WEBRTC_TRACE(kTraceInfo, kTraceRtpRtcp, _id, "SetMaxPayloadLength to %d.", maxPayloadLength);
336 return 0;
337}
338
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000339WebRtc_UWord16 RTPSender::MaxDataPayloadLength() const {
340 if(_audioConfigured) {
341 return _maxPayloadLength - RTPHeaderLength();
342 } else {
343 return _maxPayloadLength - RTPHeaderLength() -
344 _video->FECPacketOverhead() - ((_RTX) ? 2 : 0);
345 // Include the FEC/ULP/RED overhead.
346 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000347}
348
349WebRtc_UWord16
350RTPSender::MaxPayloadLength() const
351{
352 return _maxPayloadLength;
353}
354
355WebRtc_UWord16
356RTPSender::PacketOverHead() const
357{
358 return _packetOverHead;
359}
360
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000361void RTPSender::SetTransmissionSmoothingStatus(const bool enable) {
362 CriticalSectionScoped cs(_sendCritsect);
363 _transmissionSmoothing = enable;
364}
365
366bool RTPSender::TransmissionSmoothingStatus() const {
367 CriticalSectionScoped cs(_sendCritsect);
368 return _transmissionSmoothing;
369}
370
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000371void RTPSender::SetRTXStatus(const bool enable,
372 const bool setSSRC,
373 const WebRtc_UWord32 SSRC) {
374 CriticalSectionScoped cs(_sendCritsect);
375 _RTX = enable;
376 if (enable) {
377 if (setSSRC) {
378 _ssrcRTX = SSRC;
379 } else {
380 _ssrcRTX = _ssrcDB.CreateSSRC(); // can't be 0
381 }
382 }
383}
384
385void RTPSender::RTXStatus(bool* enable,
386 WebRtc_UWord32* SSRC) const {
387 CriticalSectionScoped cs(_sendCritsect);
388 *enable = _RTX;
389 *SSRC = _ssrcRTX;
390}
391
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000392WebRtc_Word32 RTPSender::CheckPayloadType(const WebRtc_Word8 payloadType,
393 RtpVideoCodecTypes& videoType) {
394 CriticalSectionScoped cs(_sendCritsect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000395
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000396 if (payloadType < 0) {
397 WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
398 "\tinvalid payloadType (%d)", payloadType);
399 return -1;
400 }
401 if (_audioConfigured) {
402 WebRtc_Word8 redPlType = -1;
403 if (_audio->RED(redPlType) == 0) {
404 // We have configured RED.
405 if(redPlType == payloadType) {
406 // And it's a match...
407 return 0;
408 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000409 }
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000410 }
411 if (_payloadType == payloadType) {
412 if (!_audioConfigured) {
413 videoType = _video->VideoCodecType();
niklase@google.com470e71d2011-07-07 08:21:25 +0000414 }
415 return 0;
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000416 }
417 std::map<WebRtc_Word8, ModuleRTPUtility::Payload*>::iterator it =
418 _payloadTypeMap.find(payloadType);
419 if (it == _payloadTypeMap.end()) {
420 WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
421 "\tpayloadType:%d not registered", payloadType);
422 return -1;
423 }
424 _payloadType = payloadType;
425 ModuleRTPUtility::Payload* payload = it->second;
426 assert(payload);
stefan@webrtc.org7c3523c2012-09-11 07:00:42 +0000427 if (!payload->audio && !_audioConfigured) {
428 _video->SetVideoCodecType(payload->typeSpecific.Video.videoCodecType);
429 videoType = payload->typeSpecific.Video.videoCodecType;
430 _video->SetMaxConfiguredBitrateVideo(
431 payload->typeSpecific.Video.maxRate);
pwestin@webrtc.org00741872012-01-19 15:56:10 +0000432 }
433 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000434}
435
436WebRtc_Word32
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000437RTPSender::SendOutgoingData(const FrameType frame_type,
438 const WebRtc_Word8 payload_type,
439 const WebRtc_UWord32 capture_timestamp,
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000440 int64_t capture_time_ms,
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000441 const WebRtc_UWord8* payload_data,
442 const WebRtc_UWord32 payload_size,
niklase@google.com470e71d2011-07-07 08:21:25 +0000443 const RTPFragmentationHeader* fragmentation,
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000444 VideoCodecInformation* codec_info,
445 const RTPVideoTypeHeader* rtp_type_hdr)
niklase@google.com470e71d2011-07-07 08:21:25 +0000446{
447 {
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000448 // Drop this packet if we're not sending media packets.
niklase@google.com470e71d2011-07-07 08:21:25 +0000449 CriticalSectionScoped cs(_sendCritsect);
450 if (!_sendingMedia)
451 {
452 return 0;
453 }
454 }
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000455 RtpVideoCodecTypes video_type = kRtpNoVideo;
456 if (CheckPayloadType(payload_type, video_type) != 0)
niklase@google.com470e71d2011-07-07 08:21:25 +0000457 {
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000458 WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
459 "%s invalid argument failed to find payloadType:%d",
460 __FUNCTION__, payload_type);
niklase@google.com470e71d2011-07-07 08:21:25 +0000461 return -1;
462 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000463
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000464 if (_audioConfigured)
niklase@google.com470e71d2011-07-07 08:21:25 +0000465 {
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000466 assert(frame_type == kAudioFrameSpeech ||
467 frame_type == kAudioFrameCN ||
468 frame_type == kFrameEmpty);
niklase@google.com470e71d2011-07-07 08:21:25 +0000469
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000470 return _audio->SendAudio(frame_type, payload_type, capture_timestamp,
471 payload_data, payload_size,fragmentation);
472 } else {
473 assert(frame_type != kAudioFrameSpeech &&
474 frame_type != kAudioFrameCN);
niklase@google.com470e71d2011-07-07 08:21:25 +0000475
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000476 if (frame_type == kFrameEmpty) {
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000477 return SendPaddingAccordingToBitrate(payload_type, capture_timestamp,
478 capture_time_ms);
pwestin@webrtc.orgddab60b2012-04-23 14:52:15 +0000479 }
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000480 return _video->SendVideo(video_type,
481 frame_type,
482 payload_type,
483 capture_timestamp,
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000484 capture_time_ms,
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000485 payload_data,
486 payload_size,
niklase@google.com470e71d2011-07-07 08:21:25 +0000487 fragmentation,
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000488 codec_info,
489 rtp_type_hdr);
niklase@google.com470e71d2011-07-07 08:21:25 +0000490 }
491}
492
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000493WebRtc_Word32 RTPSender::SendPaddingAccordingToBitrate(
494 WebRtc_Word8 payload_type,
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000495 WebRtc_UWord32 capture_timestamp,
496 int64_t capture_time_ms) {
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000497 // Current bitrate since last estimate(1 second) averaged with the
498 // estimate since then, to get the most up to date bitrate.
499 uint32_t current_bitrate = BitrateNow();
500 int bitrate_diff = _targetSendBitrate * 1000 - current_bitrate;
501 if (bitrate_diff > 0) {
502 int bytes = 0;
503 if (current_bitrate == 0) {
504 // Start up phase. Send one 33.3 ms batch to start with.
505 bytes = (bitrate_diff / 8) / 30;
506 } else {
507 bytes = (bitrate_diff / 8);
508 // Cap at 200 ms of target send data.
509 int bytes_cap = _targetSendBitrate * 25; // 1000 / 8 / 5
510 if (bytes > bytes_cap) {
511 bytes = bytes_cap;
512 }
513 }
514 // Send padding data.
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000515 return SendPadData(payload_type, capture_timestamp, capture_time_ms, bytes);
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000516 }
517 return 0;
518}
519
pwestin@webrtc.org12d97f62012-01-05 10:54:44 +0000520WebRtc_Word32 RTPSender::SendPadData(WebRtc_Word8 payload_type,
521 WebRtc_UWord32 capture_timestamp,
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000522 int64_t capture_time_ms,
pwestin@webrtc.org12d97f62012-01-05 10:54:44 +0000523 WebRtc_Word32 bytes) {
524 // Drop this packet if we're not sending media packets
525 if (!_sendingMedia) {
526 return 0;
527 }
528 // Max in the RFC 3550 is 255 bytes, we limit it to be modulus 32 for SRTP.
529 int max_length = 224;
530 WebRtc_UWord8 data_buffer[IP_PACKET_SIZE];
531
532 for (; bytes > 0; bytes -= max_length) {
asapersson@webrtc.org63a34f42012-04-20 13:20:27 +0000533 int padding_bytes_in_packet = max_length;
534 if (bytes < max_length) {
535 padding_bytes_in_packet = (bytes + 16) & 0xffe0; // Keep our modulus 32.
536 }
537 if (padding_bytes_in_packet < 32) {
538 // Sanity don't send empty packets.
539 break;
540 }
541
pwestin@webrtc.org12d97f62012-01-05 10:54:44 +0000542 WebRtc_Word32 header_length;
543 {
544 // Correct seq num, timestamp and payload type.
545 header_length = BuildRTPheader(data_buffer,
546 payload_type,
547 false, // No markerbit.
548 capture_timestamp,
549 true, // Timestamp provided.
550 true); // Increment sequence number.
551 }
552 data_buffer[0] |= 0x20; // Set padding bit.
553 WebRtc_Word32* data =
554 reinterpret_cast<WebRtc_Word32*>(&(data_buffer[header_length]));
555
pwestin@webrtc.org12d97f62012-01-05 10:54:44 +0000556 // Fill data buffer with random data.
557 for(int j = 0; j < (padding_bytes_in_packet >> 2); j++) {
558 data[j] = rand();
559 }
560 // Set number of padding bytes in the last byte of the packet.
561 data_buffer[header_length + padding_bytes_in_packet - 1] =
562 padding_bytes_in_packet;
563 // Send the packet
564 if (0 > SendToNetwork(data_buffer,
565 padding_bytes_in_packet,
566 header_length,
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000567 capture_time_ms,
pwestin@webrtc.org12d97f62012-01-05 10:54:44 +0000568 kDontRetransmit)) {
569 // Error sending the packet.
570 break;
571 }
572 }
573 if (bytes > 31) { // 31 due to our modulus 32.
574 // We did not manage to send all bytes.
575 return -1;
576 }
577 return 0;
578}
579
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000580WebRtc_Word32 RTPSender::SetStorePacketsStatus(
581 const bool enable,
582 const WebRtc_UWord16 numberToStore) {
583 _packetHistory->SetStorePacketsStatus(enable, numberToStore);
584 return 0;
niklase@google.com470e71d2011-07-07 08:21:25 +0000585}
586
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000587bool RTPSender::StorePackets() const {
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000588 return _packetHistory->StorePackets();
niklase@google.com470e71d2011-07-07 08:21:25 +0000589}
590
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000591WebRtc_Word32 RTPSender::ReSendPacket(WebRtc_UWord16 packet_id,
592 WebRtc_UWord32 min_resend_time) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000593
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000594 WebRtc_UWord16 length = IP_PACKET_SIZE;
595 WebRtc_UWord8 data_buffer[IP_PACKET_SIZE];
596 WebRtc_UWord8* buffer_to_send_ptr = data_buffer;
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000597
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000598 int64_t stored_time_in_ms;
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000599 StorageType type;
600 bool found = _packetHistory->GetRTPPacket(packet_id,
601 min_resend_time, data_buffer, &length, &stored_time_in_ms, &type);
602 if (!found) {
603 // Packet not found.
asapersson@webrtc.org83ed0a42012-04-23 12:43:05 +0000604 return 0;
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000605 }
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000606
607 if (length == 0 || type == kDontRetransmit) {
608 // No bytes copied (packet recently resent, skip resending) or
609 // packet should not be retransmitted.
610 return 0;
611 }
612
pwestin@webrtc.orgb30f0ed2012-01-23 16:23:31 +0000613 WebRtc_UWord8 data_buffer_rtx[IP_PACKET_SIZE];
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000614 if (_RTX) {
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000615 buffer_to_send_ptr = data_buffer_rtx;
616
617 CriticalSectionScoped cs(_sendCritsect);
618 // Add RTX header.
619 ModuleRTPUtility::RTPHeaderParser rtpParser(
620 reinterpret_cast<const WebRtc_UWord8*>(data_buffer),
621 length);
622
623 WebRtcRTPHeader rtp_header;
624 rtpParser.Parse(rtp_header);
625
626 // Add original RTP header.
627 memcpy(data_buffer_rtx, data_buffer, rtp_header.header.headerLength);
628
629 // Replace sequence number.
630 WebRtc_UWord8* ptr = data_buffer_rtx + 2;
631 ModuleRTPUtility::AssignUWord16ToBuffer(ptr, _sequenceNumberRTX++);
632
633 // Replace SSRC.
634 ptr += 6;
635 ModuleRTPUtility::AssignUWord32ToBuffer(ptr, _ssrcRTX);
636
637 // Add OSN (original sequence number).
638 ptr = data_buffer_rtx + rtp_header.header.headerLength;
639 ModuleRTPUtility::AssignUWord16ToBuffer(
640 ptr, rtp_header.header.sequenceNumber);
641 ptr += 2;
642
643 // Add original payload data.
644 memcpy(ptr,
645 data_buffer + rtp_header.header.headerLength,
646 length - rtp_header.header.headerLength);
647 length += 2;
648 }
649
650 WebRtc_Word32 bytes_sent = ReSendToNetwork(buffer_to_send_ptr, length);
651 if (bytes_sent <= 0) {
652 WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
653 "Transport failed to resend packet_id %u", packet_id);
654 return -1;
655 }
656
657 // Store the time when the packet was last resent.
658 _packetHistory->UpdateResendTime(packet_id);
659
660 return bytes_sent;
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000661}
662
663WebRtc_Word32 RTPSender::ReSendToNetwork(const WebRtc_UWord8* packet,
664 const WebRtc_UWord32 size) {
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000665 WebRtc_Word32 bytes_sent = -1;
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000666 {
667 CriticalSectionScoped lock(_transportCritsect);
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000668 if (_transport) {
669 bytes_sent = _transport->SendPacket(_id, packet, size);
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000670 }
671 }
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000672
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000673 if (bytes_sent <= 0) {
674 return -1;
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000675 }
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000676
677 // Update send statistics
678 CriticalSectionScoped cs(_sendCritsect);
679 Bitrate::Update(bytes_sent);
680 _packetsSent++;
681 // We on purpose don't add to _payloadBytesSent since this is a
682 // re-transmit and not new payload data.
683 return bytes_sent;
niklase@google.com470e71d2011-07-07 08:21:25 +0000684}
685
stefan@webrtc.org6a4bef42011-12-22 12:52:41 +0000686int RTPSender::SelectiveRetransmissions() const {
687 if (!_video) return -1;
688 return _video->SelectiveRetransmissions();
689}
690
691int RTPSender::SetSelectiveRetransmissions(uint8_t settings) {
692 if (!_video) return -1;
693 return _video->SetSelectiveRetransmissions(settings);
694}
695
niklase@google.com470e71d2011-07-07 08:21:25 +0000696void
697RTPSender::OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength,
698 const WebRtc_UWord16* nackSequenceNumbers,
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000699 const WebRtc_UWord16 avgRTT) {
pwestin@webrtc.org18530052012-07-03 10:41:54 +0000700 const WebRtc_Word64 now = _clock.GetTimeInMS();
niklase@google.com470e71d2011-07-07 08:21:25 +0000701 WebRtc_UWord32 bytesReSent = 0;
702
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000703 // Enough bandwidth to send NACK?
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000704 if (!ProcessNACKBitRate(now)) {
705 WEBRTC_TRACE(kTraceStream,
706 kTraceRtpRtcp,
707 _id,
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000708 "NACK bitrate reached. Skip sending NACK response. Target %d",
pwestin@webrtc.org49888ce2012-04-27 05:25:53 +0000709 _targetSendBitrate);
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000710 return;
711 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000712
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000713 for (WebRtc_UWord16 i = 0; i < nackSequenceNumbersLength; ++i) {
714 const WebRtc_Word32 bytesSent = ReSendPacket(nackSequenceNumbers[i],
715 5+avgRTT);
716 if (bytesSent > 0) {
717 bytesReSent += bytesSent;
718 } else if (bytesSent == 0) {
719 // The packet has previously been resent.
720 // Try resending next packet in the list.
721 continue;
722 } else if (bytesSent < 0) {
723 // Failed to send one Sequence number. Give up the rest in this nack.
724 WEBRTC_TRACE(kTraceWarning,
725 kTraceRtpRtcp,
726 _id,
727 "Failed resending RTP packet %d, Discard rest of packets",
728 nackSequenceNumbers[i]);
729 break;
niklase@google.com470e71d2011-07-07 08:21:25 +0000730 }
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000731 // delay bandwidth estimate (RTT * BW)
pwestin@webrtc.org49888ce2012-04-27 05:25:53 +0000732 if (_targetSendBitrate != 0 && avgRTT) {
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000733 // kbits/s * ms = bits => bits/8 = bytes
734 WebRtc_UWord32 targetBytes =
pwestin@webrtc.org49888ce2012-04-27 05:25:53 +0000735 (static_cast<WebRtc_UWord32>(_targetSendBitrate) * avgRTT) >> 3;
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000736 if (bytesReSent > targetBytes) {
737 break; // ignore the rest of the packets in the list
738 }
739 }
740 }
741 if (bytesReSent > 0) {
742 // TODO(pwestin) consolidate these two methods.
743 UpdateNACKBitRate(bytesReSent, now);
744 _nackBitrate.Update(bytesReSent);
745 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000746}
747
748/**
749* @return true if the nack bitrate is lower than the requested max bitrate
750*/
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000751bool RTPSender::ProcessNACKBitRate(const WebRtc_UWord32 now) {
752 WebRtc_UWord32 num = 0;
753 WebRtc_Word32 byteCount = 0;
754 const WebRtc_UWord32 avgInterval=1000;
niklase@google.com470e71d2011-07-07 08:21:25 +0000755
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000756 CriticalSectionScoped cs(_sendCritsect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000757
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000758 if (_targetSendBitrate == 0) {
759 return true;
760 }
761 for (num = 0; num < NACK_BYTECOUNT_SIZE; num++) {
762 if ((now - _nackByteCountTimes[num]) > avgInterval) {
763 // don't use data older than 1sec
764 break;
765 } else {
766 byteCount += _nackByteCount[num];
niklase@google.com470e71d2011-07-07 08:21:25 +0000767 }
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000768 }
769 WebRtc_Word32 timeInterval = avgInterval;
770 if (num == NACK_BYTECOUNT_SIZE) {
771 // More than NACK_BYTECOUNT_SIZE nack messages has been received
772 // during the last msgInterval
773 timeInterval = now - _nackByteCountTimes[num-1];
774 if(timeInterval < 0) {
775 timeInterval = avgInterval;
niklase@google.com470e71d2011-07-07 08:21:25 +0000776 }
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000777 }
778 return (byteCount*8) < (_targetSendBitrate * timeInterval);
niklase@google.com470e71d2011-07-07 08:21:25 +0000779}
780
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000781void RTPSender::UpdateNACKBitRate(const WebRtc_UWord32 bytes,
782 const WebRtc_UWord32 now) {
783 CriticalSectionScoped cs(_sendCritsect);
niklase@google.com470e71d2011-07-07 08:21:25 +0000784
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000785 // save bitrate statistics
786 if(bytes > 0) {
787 if(now == 0) {
788 // add padding length
789 _nackByteCount[0] += bytes;
790 } else {
791 if(_nackByteCountTimes[0] == 0) {
792 // first no shift
793 } else {
794 // shift
795 for(int i = (NACK_BYTECOUNT_SIZE-2); i >= 0 ; i--) {
796 _nackByteCount[i+1] = _nackByteCount[i];
797 _nackByteCountTimes[i+1] = _nackByteCountTimes[i];
niklase@google.com470e71d2011-07-07 08:21:25 +0000798 }
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000799 }
800 _nackByteCount[0] = bytes;
801 _nackByteCountTimes[0] = now;
niklase@google.com470e71d2011-07-07 08:21:25 +0000802 }
pwestin@webrtc.org8281e7d2012-01-10 14:09:18 +0000803 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000804}
805
pwestin@webrtc.orgddab60b2012-04-23 14:52:15 +0000806// Function triggered by timer.
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000807void RTPSender::ProcessSendToNetwork() {
pwestin@webrtc.org18530052012-07-03 10:41:54 +0000808 WebRtc_Word64 delta_time_ms;
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000809 {
810 CriticalSectionScoped cs(_sendCritsect);
811
812 if (!_transmissionSmoothing) {
813 return;
814 }
pwestin@webrtc.org18530052012-07-03 10:41:54 +0000815 WebRtc_Word64 now = _clock.GetTimeInMS();
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000816 delta_time_ms = now - _timeLastSendToNetworkUpdate;
817 _timeLastSendToNetworkUpdate = now;
818 }
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000819 _sendBucket.UpdateBytesPerInterval(delta_time_ms, _targetSendBitrate);
820
821 while (!_sendBucket.Empty()) {
822
823 WebRtc_Word32 seq_num = _sendBucket.GetNextPacket();
824 if (seq_num < 0) {
825 break;
826 }
827
828 WebRtc_UWord8 data_buffer[IP_PACKET_SIZE];
829 WebRtc_UWord16 length = IP_PACKET_SIZE;
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000830 int64_t stored_time_ms;
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000831 StorageType type;
asapersson@webrtc.org869ce2d2012-01-16 11:58:36 +0000832 bool found = _packetHistory->GetRTPPacket(seq_num, 0, data_buffer, &length,
833 &stored_time_ms, &type);
834 if (!found) {
835 assert(false);
836 return;
837 }
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000838 assert(length > 0);
839
pwestin@webrtc.org18530052012-07-03 10:41:54 +0000840 WebRtc_Word64 diff_ms = _clock.GetTimeInMS() - stored_time_ms;
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000841
842 ModuleRTPUtility::RTPHeaderParser rtpParser(data_buffer, length);
843 WebRtcRTPHeader rtp_header;
asapersson@webrtc.org869ce2d2012-01-16 11:58:36 +0000844 rtpParser.Parse(rtp_header);
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000845
846 UpdateTransmissionTimeOffset(data_buffer, length, rtp_header, diff_ms);
847
848 // Send packet
849 WebRtc_Word32 bytes_sent = -1;
850 {
851 CriticalSectionScoped cs(_transportCritsect);
852 if (_transport) {
853 bytes_sent = _transport->SendPacket(_id, data_buffer, length);
854 }
855 }
856
857 // Update send statistics
858 if (bytes_sent > 0) {
859 CriticalSectionScoped cs(_sendCritsect);
860 Bitrate::Update(bytes_sent);
861 _packetsSent++;
862 if (bytes_sent > rtp_header.header.headerLength) {
863 _payloadBytesSent += bytes_sent - rtp_header.header.headerLength;
864 }
865 }
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +0000866 }
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000867}
868
niklase@google.com470e71d2011-07-07 08:21:25 +0000869WebRtc_Word32
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000870RTPSender::SendToNetwork(WebRtc_UWord8* buffer,
niklase@google.com470e71d2011-07-07 08:21:25 +0000871 const WebRtc_UWord16 length,
872 const WebRtc_UWord16 rtpLength,
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000873 int64_t capture_time_ms,
stefan@webrtc.org6a4bef42011-12-22 12:52:41 +0000874 const StorageType storage)
niklase@google.com470e71d2011-07-07 08:21:25 +0000875{
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000876 // Used for NACK or to spead out the transmission of packets.
877 if (_packetHistory->PutRTPPacket(
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000878 buffer, rtpLength + length, _maxPayloadLength, capture_time_ms, storage)
879 != 0) {
niklase@google.com470e71d2011-07-07 08:21:25 +0000880 return -1;
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000881 }
niklase@google.com470e71d2011-07-07 08:21:25 +0000882
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000883 if (_transmissionSmoothing) {
884 const WebRtc_UWord16 sequenceNumber = (buffer[2] << 8) + buffer[3];
asapersson@webrtc.org23fd5592012-09-24 12:07:13 +0000885 const WebRtc_UWord32 timestamp = (buffer[4] << 24) + (buffer[5] << 16) +
886 (buffer[6] << 8) + buffer[7];
887 _sendBucket.Fill(sequenceNumber, timestamp, rtpLength + length);
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000888 // Packet will be sent at a later time.
889 return 0;
890 }
stefan@webrtc.org6a4bef42011-12-22 12:52:41 +0000891
stefan@webrtc.org715faaf2012-08-28 15:20:39 +0000892 // |capture_time_ms| <= 0 is considered invalid.
893 // TODO(holmer): This should be changed all over Video Engine so that negative
894 // time is consider invalid, while 0 is considered a valid time.
895 if (capture_time_ms > 0) {
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000896 ModuleRTPUtility::RTPHeaderParser rtpParser(buffer, length);
897 WebRtcRTPHeader rtp_header;
mflodman@webrtc.org90071dd2012-08-13 17:13:27 +0000898 rtpParser.Parse(rtp_header);
stefan@webrtc.orgddfdfed2012-07-03 13:21:22 +0000899 int64_t time_now = _clock.GetTimeInMS();
900 UpdateTransmissionTimeOffset(buffer, length, rtp_header,
901 time_now - capture_time_ms);
902 }
903
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000904 // Send packet
905 WebRtc_Word32 bytes_sent = -1;
906 {
907 CriticalSectionScoped cs(_transportCritsect);
908 if (_transport) {
909 bytes_sent = _transport->SendPacket(_id, buffer, length + rtpLength);
stefan@webrtc.org6a4bef42011-12-22 12:52:41 +0000910 }
911 }
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +0000912
913 if (bytes_sent <= 0) {
914 return -1;
915 }
916
917 // Update send statistics
918 CriticalSectionScoped cs(_sendCritsect);
919 Bitrate::Update(bytes_sent);
920 _packetsSent++;
921 if (bytes_sent > rtpLength) {
922 _payloadBytesSent += bytes_sent - rtpLength;
923 }
924 return 0;
stefan@webrtc.org6a4bef42011-12-22 12:52:41 +0000925}
926
niklase@google.com470e71d2011-07-07 08:21:25 +0000927void
928RTPSender::ProcessBitrate()
929{
930 CriticalSectionScoped cs(_sendCritsect);
931
932 Bitrate::Process();
stefan@webrtc.orgd0bdab02011-10-14 14:24:54 +0000933 _nackBitrate.Process();
wu@webrtc.org76aea652011-10-17 21:40:32 +0000934
935 if (_audioConfigured)
936 return;
stefan@webrtc.orgd0bdab02011-10-14 14:24:54 +0000937 _video->ProcessBitrate();
niklase@google.com470e71d2011-07-07 08:21:25 +0000938}
939
940WebRtc_UWord16
941RTPSender::RTPHeaderLength() const
942{
943 WebRtc_UWord16 rtpHeaderLength = 12;
944
945 if(_includeCSRCs)
946 {
947 rtpHeaderLength += sizeof(WebRtc_UWord32)*_CSRCs;
948 }
asapersson@webrtc.org5249cc82011-12-16 14:31:37 +0000949 rtpHeaderLength += RtpHeaderExtensionTotalLength();
950
niklase@google.com470e71d2011-07-07 08:21:25 +0000951 return rtpHeaderLength;
952}
953
954WebRtc_UWord16
955RTPSender::IncrementSequenceNumber()
956{
957 CriticalSectionScoped cs(_sendCritsect);
958 return _sequenceNumber++;
959}
960
961WebRtc_Word32
962RTPSender::ResetDataCounters()
963{
964 _packetsSent = 0;
965 _payloadBytesSent = 0;
966
967 return 0;
968}
969
970// number of sent RTP packets
971// dont use critsect to avoid potental deadlock
972WebRtc_UWord32
973RTPSender::Packets() const
974{
975 return _packetsSent;
976}
977
978// number of sent RTP bytes
979// dont use critsect to avoid potental deadlock
980WebRtc_UWord32
981RTPSender::Bytes() const
982{
983 return _payloadBytesSent;
984}
985
986WebRtc_Word32
987RTPSender::BuildRTPheader(WebRtc_UWord8* dataBuffer,
988 const WebRtc_Word8 payloadType,
989 const bool markerBit,
990 const WebRtc_UWord32 captureTimeStamp,
991 const bool timeStampProvided,
992 const bool incSequenceNumber)
993{
994 assert(payloadType>=0);
995
996 CriticalSectionScoped cs(_sendCritsect);
997
998 dataBuffer[0] = static_cast<WebRtc_UWord8>(0x80); // version 2
999 dataBuffer[1] = static_cast<WebRtc_UWord8>(payloadType);
1000 if (markerBit)
1001 {
1002 dataBuffer[1] |= kRtpMarkerBitMask; // MarkerBit is set
1003 }
1004
1005 if(timeStampProvided)
1006 {
1007 _timeStamp = _startTimeStamp + captureTimeStamp;
1008 } else
1009 {
1010 // make a unique time stamp
1011 // used for inband signaling
1012 // we can't inc by the actual time, since then we increase the risk of back timing
1013 _timeStamp++;
1014 }
1015
1016 ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+2, _sequenceNumber);
1017 ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+4, _timeStamp);
1018 ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+8, _ssrc);
1019
1020 WebRtc_Word32 rtpHeaderLength = 12;
1021
1022 // Add the CSRCs if any
1023 if (_includeCSRCs && _CSRCs > 0)
1024 {
1025 if(_CSRCs > kRtpCsrcSize)
1026 {
1027 // error
1028 assert(false);
1029 return -1;
1030 }
1031 WebRtc_UWord8* ptr = &dataBuffer[rtpHeaderLength];
1032 for (WebRtc_UWord32 i = 0; i < _CSRCs; ++i)
1033 {
1034 ModuleRTPUtility::AssignUWord32ToBuffer(ptr, _CSRC[i]);
1035 ptr +=4;
1036 }
1037 dataBuffer[0] = (dataBuffer[0]&0xf0) | _CSRCs;
1038
1039 // Update length of header
1040 rtpHeaderLength += sizeof(WebRtc_UWord32)*_CSRCs;
1041 }
1042 {
1043 _sequenceNumber++; // prepare for next packet
1044 }
1045
asapersson@webrtc.org5249cc82011-12-16 14:31:37 +00001046 WebRtc_UWord16 len = BuildRTPHeaderExtension(dataBuffer + rtpHeaderLength);
1047 if (len)
1048 {
1049 dataBuffer[0] |= 0x10; // set eXtension bit
1050 rtpHeaderLength += len;
1051 }
1052
niklase@google.com470e71d2011-07-07 08:21:25 +00001053 return rtpHeaderLength;
1054}
1055
asapersson@webrtc.org5249cc82011-12-16 14:31:37 +00001056WebRtc_UWord16
1057RTPSender::BuildRTPHeaderExtension(WebRtc_UWord8* dataBuffer) const
1058{
1059 if (_rtpHeaderExtensionMap.Size() <= 0) {
1060 return 0;
1061 }
1062
1063 /* RTP header extension, RFC 3550.
1064 0 1 2 3
1065 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
1066 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1067 | defined by profile | length |
1068 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1069 | header extension |
1070 | .... |
1071 */
1072
1073 const WebRtc_UWord32 kPosLength = 2;
1074 const WebRtc_UWord32 kHeaderLength = RTP_ONE_BYTE_HEADER_LENGTH_IN_BYTES;
1075
1076 // Add extension ID (0xBEDE).
1077 ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer,
1078 RTP_ONE_BYTE_HEADER_EXTENSION);
1079
1080 // Add extensions.
1081 WebRtc_UWord16 total_block_length = 0;
1082
1083 RTPExtensionType type = _rtpHeaderExtensionMap.First();
pwestin@webrtc.org6c1d4152012-01-04 17:04:51 +00001084 while (type != kRtpExtensionNone)
asapersson@webrtc.org5249cc82011-12-16 14:31:37 +00001085 {
1086 WebRtc_UWord8 block_length = 0;
pwestin@webrtc.org6c1d4152012-01-04 17:04:51 +00001087 if (type == kRtpExtensionTransmissionTimeOffset)
asapersson@webrtc.org5249cc82011-12-16 14:31:37 +00001088 {
1089 block_length = BuildTransmissionTimeOffsetExtension(
1090 dataBuffer + kHeaderLength + total_block_length);
1091 }
1092 total_block_length += block_length;
1093 type = _rtpHeaderExtensionMap.Next(type);
1094 }
1095
1096 if (total_block_length == 0)
1097 {
1098 // No extension added.
1099 return 0;
1100 }
1101
1102 // Set header length (in number of Word32, header excluded).
1103 assert(total_block_length % 4 == 0);
1104 ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer + kPosLength,
1105 total_block_length / 4);
1106
1107 // Total added length.
1108 return kHeaderLength + total_block_length;
1109}
1110
1111WebRtc_UWord8
1112RTPSender::BuildTransmissionTimeOffsetExtension(WebRtc_UWord8* dataBuffer) const
1113{
1114 // From RFC 5450: Transmission Time Offsets in RTP Streams.
1115 //
1116 // The transmission time is signaled to the receiver in-band using the
1117 // general mechanism for RTP header extensions [RFC5285]. The payload
1118 // of this extension (the transmitted value) is a 24-bit signed integer.
1119 // When added to the RTP timestamp of the packet, it represents the
1120 // "effective" RTP transmission time of the packet, on the RTP
1121 // timescale.
1122 //
1123 // The form of the transmission offset extension block:
1124 //
1125 // 0 1 2 3
1126 // 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
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +00001127 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
asapersson@webrtc.org5249cc82011-12-16 14:31:37 +00001128 // | ID | len=2 | transmission offset |
1129 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
1130
1131 // Get id defined by user.
1132 WebRtc_UWord8 id;
pwestin@webrtc.org6c1d4152012-01-04 17:04:51 +00001133 if (_rtpHeaderExtensionMap.GetId(kRtpExtensionTransmissionTimeOffset, &id)
1134 != 0) {
asapersson@webrtc.org5249cc82011-12-16 14:31:37 +00001135 // Not registered.
1136 return 0;
1137 }
1138
1139 int pos = 0;
1140 const WebRtc_UWord8 len = 2;
1141 dataBuffer[pos++] = (id << 4) + len;
1142 ModuleRTPUtility::AssignUWord24ToBuffer(dataBuffer + pos,
1143 _transmissionTimeOffset);
1144 pos += 3;
1145 assert(pos == TRANSMISSION_TIME_OFFSET_LENGTH_IN_BYTES);
1146 return TRANSMISSION_TIME_OFFSET_LENGTH_IN_BYTES;
1147}
1148
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +00001149void RTPSender::UpdateTransmissionTimeOffset(
1150 WebRtc_UWord8* rtp_packet,
1151 const WebRtc_UWord16 rtp_packet_length,
1152 const WebRtcRTPHeader& rtp_header,
mflodman@webrtc.orgba853c92012-08-10 14:30:53 +00001153 const WebRtc_Word64 time_diff_ms) const {
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +00001154 CriticalSectionScoped cs(_sendCritsect);
1155
1156 // Get length until start of transmission block.
1157 int transmission_block_pos =
1158 _rtpHeaderExtensionMap.GetLengthUntilBlockStartInBytes(
1159 kRtpExtensionTransmissionTimeOffset);
1160 if (transmission_block_pos < 0) {
1161 WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
1162 "Failed to update transmission time offset, not registered.");
1163 return;
1164 }
1165
1166 int block_pos = 12 + rtp_header.header.numCSRCs + transmission_block_pos;
mflodman@webrtc.orgba853c92012-08-10 14:30:53 +00001167 if (rtp_packet_length < block_pos + 4 ||
1168 rtp_header.header.headerLength < block_pos + 4) {
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +00001169 WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
1170 "Failed to update transmission time offset, invalid length.");
1171 return;
1172 }
1173
1174 // Verify that header contains extension.
1175 if (!((rtp_packet[12 + rtp_header.header.numCSRCs] == 0xBE) &&
1176 (rtp_packet[12 + rtp_header.header.numCSRCs + 1] == 0xDE))) {
1177 WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
1178 "Failed to update transmission time offset, hdr extension not found.");
1179 return;
1180 }
1181
1182 // Get id.
1183 WebRtc_UWord8 id = 0;
1184 if (_rtpHeaderExtensionMap.GetId(kRtpExtensionTransmissionTimeOffset,
1185 &id) != 0) {
1186 WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
1187 "Failed to update transmission time offset, no id.");
1188 return;
1189 }
phoglund@webrtc.orgbaaf2432012-05-31 10:47:35 +00001190
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +00001191 // Verify first byte in block.
1192 const WebRtc_UWord8 first_block_byte = (id << 4) + 2;
1193 if (rtp_packet[block_pos] != first_block_byte) {
1194 WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
1195 "Failed to update transmission time offset.");
1196 return;
1197 }
1198
1199 // Update transmission offset field.
1200 ModuleRTPUtility::AssignUWord24ToBuffer(rtp_packet + block_pos + 1,
mflodman@webrtc.orgba853c92012-08-10 14:30:53 +00001201 time_diff_ms * 90); // RTP timestamp.
asapersson@webrtc.org0b3c35a2012-01-16 11:06:31 +00001202}
1203
niklase@google.com470e71d2011-07-07 08:21:25 +00001204WebRtc_Word32
1205RTPSender::RegisterSendTransport(Transport* transport)
1206{
1207 CriticalSectionScoped cs(_transportCritsect);
1208 _transport = transport;
1209 return 0;
1210}
1211
1212void
1213RTPSender::SetSendingStatus(const bool enabled)
1214{
1215 if(enabled)
1216 {
1217 WebRtc_UWord32 freq;
1218 if(_audioConfigured)
1219 {
1220 WebRtc_UWord32 frequency = _audio->AudioFrequency();
1221
1222 // sanity
1223 switch(frequency)
1224 {
1225 case 8000:
1226 case 12000:
1227 case 16000:
1228 case 24000:
1229 case 32000:
1230 break;
1231 default:
1232 assert(false);
1233 return;
1234 }
1235 freq = frequency;
1236 } else
1237 {
1238 freq = 90000; // 90 KHz for all video
1239 }
pwestin@webrtc.org0644b1d2011-12-01 15:42:31 +00001240 WebRtc_UWord32 RTPtime = ModuleRTPUtility::GetCurrentRTP(&_clock, freq);
niklase@google.com470e71d2011-07-07 08:21:25 +00001241
1242 SetStartTimestamp(RTPtime); // will be ignored if it's already configured via API
1243
1244 } else
1245 {
1246 if(!_ssrcForced)
1247 {
1248 // generate a new SSRC
1249 _ssrcDB.ReturnSSRC(_ssrc);
1250 _ssrc = _ssrcDB.CreateSSRC(); // can't be 0
1251
1252 }
1253 if(!_sequenceNumberForced && !_ssrcForced) // don't initialize seq number if SSRC passed externally
1254 {
1255 // generate a new sequence number
1256 _sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER);
1257 }
1258 }
1259}
1260
1261void
1262RTPSender::SetSendingMediaStatus(const bool enabled)
1263{
1264 CriticalSectionScoped cs(_sendCritsect);
1265 _sendingMedia = enabled;
1266}
1267
1268bool
1269RTPSender::SendingMedia() const
1270{
1271 CriticalSectionScoped cs(_sendCritsect);
1272 return _sendingMedia;
1273}
1274
1275WebRtc_UWord32
1276RTPSender::Timestamp() const
1277{
1278 CriticalSectionScoped cs(_sendCritsect);
1279 return _timeStamp;
1280}
1281
1282
1283WebRtc_Word32
1284RTPSender::SetStartTimestamp( const WebRtc_UWord32 timestamp, const bool force)
1285{
1286 CriticalSectionScoped cs(_sendCritsect);
1287 if(force)
1288 {
1289 _startTimeStampForced = force;
1290 _startTimeStamp = timestamp;
1291 } else
1292 {
1293 if(!_startTimeStampForced)
1294 {
1295 _startTimeStamp = timestamp;
1296 }
1297 }
1298 return 0;
1299}
1300
1301WebRtc_UWord32
1302RTPSender::StartTimestamp() const
1303{
1304 CriticalSectionScoped cs(_sendCritsect);
1305 return _startTimeStamp;
1306}
1307
1308WebRtc_UWord32
1309RTPSender::GenerateNewSSRC()
1310{
1311 // if configured via API, return 0
1312 CriticalSectionScoped cs(_sendCritsect);
1313
1314 if(_ssrcForced)
1315 {
1316 return 0;
1317 }
1318 _ssrc = _ssrcDB.CreateSSRC(); // can't be 0
1319 return _ssrc;
1320}
1321
1322WebRtc_Word32
1323RTPSender::SetSSRC(WebRtc_UWord32 ssrc)
1324{
1325 // this is configured via the API
1326 CriticalSectionScoped cs(_sendCritsect);
1327
1328 if (_ssrc == ssrc && _ssrcForced)
1329 {
1330 return 0; // since it's same ssrc, don't reset anything
1331 }
1332
1333 _ssrcForced = true;
1334
1335 _ssrcDB.ReturnSSRC(_ssrc);
1336 _ssrcDB.RegisterSSRC(ssrc);
1337 _ssrc = ssrc;
1338
1339 if(!_sequenceNumberForced)
1340 {
1341 _sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER);
1342 }
1343 return 0;
1344}
1345
1346WebRtc_UWord32
1347RTPSender::SSRC() const
1348{
1349 CriticalSectionScoped cs(_sendCritsect);
1350 return _ssrc;
1351}
1352
1353WebRtc_Word32
1354RTPSender::SetCSRCStatus(const bool include)
1355{
1356 _includeCSRCs = include;
1357 return 0;
1358}
1359
1360WebRtc_Word32
1361RTPSender::SetCSRCs(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize],
1362 const WebRtc_UWord8 arrLength)
1363{
1364 if(arrLength > kRtpCsrcSize)
1365 {
1366 assert(false);
1367 return -1;
1368 }
1369
1370 CriticalSectionScoped cs(_sendCritsect);
1371
1372 for(int i = 0; i < arrLength;i++)
1373 {
1374 _CSRC[i] = arrOfCSRC[i];
1375 }
1376 _CSRCs = arrLength;
1377 return 0;
1378}
1379
1380WebRtc_Word32
1381RTPSender::CSRCs(WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const
1382{
1383 CriticalSectionScoped cs(_sendCritsect);
1384
1385 if(arrOfCSRC == NULL)
1386 {
1387 assert(false);
1388 return -1;
1389 }
1390 for(int i = 0; i < _CSRCs && i < kRtpCsrcSize;i++)
1391 {
1392 arrOfCSRC[i] = _CSRC[i];
1393 }
1394 return _CSRCs;
1395}
1396
1397WebRtc_Word32
1398RTPSender::SetSequenceNumber(WebRtc_UWord16 seq)
1399{
1400 CriticalSectionScoped cs(_sendCritsect);
1401 _sequenceNumberForced = true;
1402 _sequenceNumber = seq;
1403 return 0;
1404}
1405
1406WebRtc_UWord16
1407RTPSender::SequenceNumber() const
1408{
1409 CriticalSectionScoped cs(_sendCritsect);
1410 return _sequenceNumber;
1411}
1412
1413
1414 /*
1415 * Audio
1416 */
1417WebRtc_Word32
1418RTPSender::RegisterAudioCallback(RtpAudioFeedback* messagesCallback)
1419{
1420 if(!_audioConfigured)
1421 {
1422 return -1;
1423 }
1424 return _audio->RegisterAudioCallback(messagesCallback);
1425}
1426
1427 // Send a DTMF tone, RFC 2833 (4733)
1428WebRtc_Word32
1429RTPSender::SendTelephoneEvent(const WebRtc_UWord8 key,
1430 const WebRtc_UWord16 time_ms,
1431 const WebRtc_UWord8 level)
1432{
1433 if(!_audioConfigured)
1434 {
1435 return -1;
1436 }
1437 return _audio->SendTelephoneEvent(key, time_ms, level);
1438}
1439
1440bool
1441RTPSender::SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const
1442{
1443 if(!_audioConfigured)
1444 {
1445 return false;
1446 }
1447 return _audio->SendTelephoneEventActive(telephoneEvent);
1448}
1449
1450 // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG)
1451WebRtc_Word32
1452RTPSender::SetAudioPacketSize(const WebRtc_UWord16 packetSizeSamples)
1453{
1454 if(!_audioConfigured)
1455 {
1456 return -1;
1457 }
1458 return _audio->SetAudioPacketSize(packetSizeSamples);
1459}
1460
1461WebRtc_Word32
1462RTPSender::SetAudioLevelIndicationStatus(const bool enable,
1463 const WebRtc_UWord8 ID)
1464{
1465 if(!_audioConfigured)
1466 {
1467 return -1;
1468 }
1469 return _audio->SetAudioLevelIndicationStatus(enable, ID);
1470}
1471
1472WebRtc_Word32
1473RTPSender::AudioLevelIndicationStatus(bool& enable,
1474 WebRtc_UWord8& ID) const
1475{
1476 return _audio->AudioLevelIndicationStatus(enable, ID);
1477}
1478
1479WebRtc_Word32
1480RTPSender::SetAudioLevel(const WebRtc_UWord8 level_dBov)
1481{
1482 return _audio->SetAudioLevel(level_dBov);
1483}
1484
1485 // Set payload type for Redundant Audio Data RFC 2198
1486WebRtc_Word32
1487RTPSender::SetRED(const WebRtc_Word8 payloadType)
1488{
1489 if(!_audioConfigured)
1490 {
1491 return -1;
1492 }
1493 return _audio->SetRED(payloadType);
1494}
1495
1496 // Get payload type for Redundant Audio Data RFC 2198
1497WebRtc_Word32
1498RTPSender::RED(WebRtc_Word8& payloadType) const
1499{
1500 if(!_audioConfigured)
1501 {
andrew@webrtc.org4f390002011-08-24 20:35:35 +00001502 return -1;
niklase@google.com470e71d2011-07-07 08:21:25 +00001503 }
1504 return _audio->RED(payloadType);
1505}
1506
1507 /*
1508 * Video
1509 */
1510VideoCodecInformation*
1511RTPSender::CodecInformationVideo()
1512{
1513 if(_audioConfigured)
1514 {
1515 return NULL;
1516 }
1517 return _video->CodecInformationVideo();
1518}
1519
1520RtpVideoCodecTypes
1521RTPSender::VideoCodecType() const
1522{
1523 if(_audioConfigured)
1524 {
1525 return kRtpNoVideo;
1526 }
1527 return _video->VideoCodecType();
1528}
1529
1530WebRtc_UWord32
1531RTPSender::MaxConfiguredBitrateVideo() const
1532{
1533 if(_audioConfigured)
1534 {
1535 return 0;
1536 }
1537 return _video->MaxConfiguredBitrateVideo();
1538}
1539
1540WebRtc_Word32
1541RTPSender::SendRTPIntraRequest()
1542{
1543 if(_audioConfigured)
1544 {
1545 return -1;
1546 }
1547 return _video->SendRTPIntraRequest();
1548}
1549
1550// FEC
1551WebRtc_Word32
1552RTPSender::SetGenericFECStatus(const bool enable,
1553 const WebRtc_UWord8 payloadTypeRED,
1554 const WebRtc_UWord8 payloadTypeFEC)
1555{
1556 if(_audioConfigured)
1557 {
1558 return -1;
1559 }
1560 return _video->SetGenericFECStatus(enable, payloadTypeRED, payloadTypeFEC);
1561}
1562
1563WebRtc_Word32
1564RTPSender::GenericFECStatus(bool& enable,
1565 WebRtc_UWord8& payloadTypeRED,
1566 WebRtc_UWord8& payloadTypeFEC) const
1567{
1568 if(_audioConfigured)
1569 {
1570 return -1;
1571 }
1572 return _video->GenericFECStatus(enable, payloadTypeRED, payloadTypeFEC);
1573}
1574
stefan@webrtc.orge0d6fa42012-03-20 22:10:56 +00001575WebRtc_Word32 RTPSender::SetFecParameters(
1576 const FecProtectionParams* delta_params,
1577 const FecProtectionParams* key_params) {
1578 if (_audioConfigured) {
1579 return -1;
1580 }
1581 return _video->SetFecParameters(delta_params, key_params);
marpan@google.com80c5d7a2011-07-15 21:32:40 +00001582}
niklase@google.com470e71d2011-07-07 08:21:25 +00001583} // namespace webrtc