rtp_sender.cc revision 20ed36dada62ad56ec01263fc0eef0ed198f6476
1/*
2 *  Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
3 *
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 "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
12
13#include <cstdlib> // srand
14
15#include "webrtc/modules/pacing/include/paced_sender.h"
16#include "webrtc/modules/rtp_rtcp/source/rtp_packet_history.h"
17#include "webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h"
18#include "webrtc/modules/rtp_rtcp/source/rtp_sender_video.h"
19#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
20#include "webrtc/system_wrappers/interface/trace.h"
21
22namespace webrtc {
23RTPSender::RTPSender(const WebRtc_Word32 id,
24                     const bool audio,
25                     Clock* clock,
26                     Transport* transport,
27                     RtpAudioFeedback* audio_feedback,
28                     PacedSender* paced_sender)
29    : Bitrate(clock),
30      _id(id),
31      _audioConfigured(audio),
32      _audio(NULL),
33      _video(NULL),
34      paced_sender_(paced_sender),
35      _sendCritsect(CriticalSectionWrapper::CreateCriticalSection()),
36      _transport(transport),
37      _sendingMedia(true),  // Default to sending media
38
39      _maxPayloadLength(IP_PACKET_SIZE-28),  // default is IP-v4/UDP
40      _targetSendBitrate(0),
41      _packetOverHead(28),
42
43      _payloadType(-1),
44      _payloadTypeMap(),
45
46      _rtpHeaderExtensionMap(),
47      _transmissionTimeOffset(0),
48
49      // NACK
50      _nackByteCountTimes(),
51      _nackByteCount(),
52      _nackBitrate(clock),
53      _packetHistory(new RTPPacketHistory(clock)),
54
55      // statistics
56      _packetsSent(0),
57      _payloadBytesSent(0),
58
59      _startTimeStampForced(false),
60      _startTimeStamp(0),
61      _ssrcDB(*SSRCDatabase::GetSSRCDatabase()),
62      _remoteSSRC(0),
63      _sequenceNumberForced(false),
64      _sequenceNumber(0),
65      _sequenceNumberRTX(0),
66      _ssrcForced(false),
67      _ssrc(0),
68      _timeStamp(0),
69      _CSRCs(0),
70      _CSRC(),
71      _includeCSRCs(true),
72      _RTX(false),
73      _ssrcRTX(0) {
74  memset(_nackByteCountTimes, 0, sizeof(_nackByteCountTimes));
75  memset(_nackByteCount, 0, sizeof(_nackByteCount));
76  memset(_CSRC, 0, sizeof(_CSRC));
77  // We need to seed the random generator.
78  srand( (WebRtc_UWord32)clock_.TimeInMilliseconds() );
79  _ssrc = _ssrcDB.CreateSSRC();  // Can't be 0.
80
81  if (audio) {
82    _audio = new RTPSenderAudio(id, &clock_, this);
83    _audio->RegisterAudioCallback(audio_feedback);
84  } else {
85    _video = new RTPSenderVideo(id, &clock_, this);
86  }
87  WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__);
88}
89
90RTPSender::~RTPSender() {
91  if (_remoteSSRC != 0) {
92    _ssrcDB.ReturnSSRC(_remoteSSRC);
93  }
94  _ssrcDB.ReturnSSRC(_ssrc);
95
96  SSRCDatabase::ReturnSSRCDatabase();
97  delete _sendCritsect;
98  while (!_payloadTypeMap.empty()) {
99    std::map<WebRtc_Word8, ModuleRTPUtility::Payload*>::iterator it =
100        _payloadTypeMap.begin();
101    delete it->second;
102    _payloadTypeMap.erase(it);
103  }
104  delete _packetHistory;
105  delete _audio;
106  delete _video;
107
108  WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__);
109}
110
111void RTPSender::SetTargetSendBitrate(const WebRtc_UWord32 bits) {
112  _targetSendBitrate = static_cast<uint16_t>(bits / 1000);
113}
114
115WebRtc_UWord16 RTPSender::ActualSendBitrateKbit() const {
116    return (WebRtc_UWord16) (Bitrate::BitrateNow() / 1000);
117}
118
119WebRtc_UWord32 RTPSender::VideoBitrateSent() const {
120  if (_video) {
121    return _video->VideoBitrateSent();
122  }
123  return 0;
124}
125
126WebRtc_UWord32 RTPSender::FecOverheadRate() const {
127  if (_video) {
128    return _video->FecOverheadRate();
129  }
130  return 0;
131}
132
133WebRtc_UWord32 RTPSender::NackOverheadRate() const {
134  return _nackBitrate.BitrateLast();
135}
136
137WebRtc_Word32 RTPSender::SetTransmissionTimeOffset(
138    const WebRtc_Word32 transmissionTimeOffset) {
139  if (transmissionTimeOffset > (0x800000 - 1) ||
140      transmissionTimeOffset < -(0x800000 - 1)) {  // Word24
141    return -1;
142  }
143  CriticalSectionScoped cs(_sendCritsect);
144  _transmissionTimeOffset = transmissionTimeOffset;
145  return 0;
146}
147
148WebRtc_Word32 RTPSender::RegisterRtpHeaderExtension(const RTPExtensionType type,
149                                                    const WebRtc_UWord8 id) {
150  CriticalSectionScoped cs(_sendCritsect);
151  return _rtpHeaderExtensionMap.Register(type, id);
152}
153
154WebRtc_Word32 RTPSender::DeregisterRtpHeaderExtension(
155    const RTPExtensionType type) {
156  CriticalSectionScoped cs(_sendCritsect);
157  return _rtpHeaderExtensionMap.Deregister(type);
158}
159
160WebRtc_UWord16 RTPSender::RtpHeaderExtensionTotalLength() const {
161  CriticalSectionScoped cs(_sendCritsect);
162  return _rtpHeaderExtensionMap.GetTotalLengthInBytes();
163}
164
165WebRtc_Word32 RTPSender::RegisterPayload(
166    const char payloadName[RTP_PAYLOAD_NAME_SIZE],
167    const WebRtc_Word8 payloadNumber,
168    const WebRtc_UWord32 frequency,
169    const WebRtc_UWord8 channels,
170    const WebRtc_UWord32 rate) {
171  assert(payloadName);
172  CriticalSectionScoped cs(_sendCritsect);
173
174  std::map<WebRtc_Word8, ModuleRTPUtility::Payload*>::iterator it =
175      _payloadTypeMap.find(payloadNumber);
176
177  if (_payloadTypeMap.end() != it) {
178    // we already use this payload type
179    ModuleRTPUtility::Payload* payload = it->second;
180    assert(payload);
181
182    // check if it's the same as we already have
183    if (ModuleRTPUtility::StringCompare(payload->name, payloadName,
184                                        RTP_PAYLOAD_NAME_SIZE - 1)) {
185      if (_audioConfigured && payload->audio &&
186          payload->typeSpecific.Audio.frequency == frequency &&
187          (payload->typeSpecific.Audio.rate == rate ||
188              payload->typeSpecific.Audio.rate == 0 || rate == 0)) {
189        payload->typeSpecific.Audio.rate = rate;
190        // Ensure that we update the rate if new or old is zero
191        return 0;
192      }
193      if (!_audioConfigured && !payload->audio) {
194        return 0;
195      }
196    }
197    return -1;
198  }
199  WebRtc_Word32 retVal = -1;
200  ModuleRTPUtility::Payload* payload = NULL;
201  if (_audioConfigured) {
202    retVal = _audio->RegisterAudioPayload(payloadName, payloadNumber, frequency,
203                                          channels, rate, payload);
204  } else {
205    retVal = _video->RegisterVideoPayload(payloadName, payloadNumber, rate,
206                                          payload);
207  }
208  if (payload) {
209    _payloadTypeMap[payloadNumber] = payload;
210  }
211  return retVal;
212}
213
214WebRtc_Word32 RTPSender::DeRegisterSendPayload(const WebRtc_Word8 payloadType) {
215  CriticalSectionScoped lock(_sendCritsect);
216
217  std::map<WebRtc_Word8, ModuleRTPUtility::Payload*>::iterator it =
218      _payloadTypeMap.find(payloadType);
219
220  if (_payloadTypeMap.end() == it) {
221    return -1;
222  }
223  ModuleRTPUtility::Payload* payload = it->second;
224  delete payload;
225  _payloadTypeMap.erase(it);
226  return 0;
227}
228
229WebRtc_Word8 RTPSender::SendPayloadType() const {
230  return _payloadType;
231}
232
233int RTPSender::SendPayloadFrequency() const {
234  return _audio->AudioFrequency();
235}
236
237WebRtc_Word32 RTPSender::SetMaxPayloadLength(
238    const WebRtc_UWord16 maxPayloadLength,
239    const WebRtc_UWord16 packetOverHead) {
240  // sanity check
241  if (maxPayloadLength < 100 || maxPayloadLength > IP_PACKET_SIZE) {
242    WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
243                 "%s invalid argument", __FUNCTION__);
244    return -1;
245  }
246  CriticalSectionScoped cs(_sendCritsect);
247  _maxPayloadLength = maxPayloadLength;
248  _packetOverHead = packetOverHead;
249
250  WEBRTC_TRACE(kTraceInfo, kTraceRtpRtcp, _id,
251               "SetMaxPayloadLength to %d.", maxPayloadLength);
252  return 0;
253}
254
255WebRtc_UWord16 RTPSender::MaxDataPayloadLength() const {
256  if (_audioConfigured) {
257    return _maxPayloadLength - RTPHeaderLength();
258  } else {
259    return _maxPayloadLength - RTPHeaderLength() -
260        _video->FECPacketOverhead() - ((_RTX) ? 2 : 0);
261        // Include the FEC/ULP/RED overhead.
262  }
263}
264
265WebRtc_UWord16 RTPSender::MaxPayloadLength() const {
266  return _maxPayloadLength;
267}
268
269WebRtc_UWord16 RTPSender::PacketOverHead() const {
270  return _packetOverHead;
271}
272
273void RTPSender::SetRTXStatus(const bool enable,
274                             const bool setSSRC,
275                             const WebRtc_UWord32 SSRC) {
276  CriticalSectionScoped cs(_sendCritsect);
277  _RTX = enable;
278  if (enable) {
279    if (setSSRC) {
280     _ssrcRTX = SSRC;
281    } else {
282     _ssrcRTX = _ssrcDB.CreateSSRC();   // can't be 0
283    }
284  }
285}
286
287void RTPSender::RTXStatus(bool* enable, WebRtc_UWord32* SSRC) const {
288  CriticalSectionScoped cs(_sendCritsect);
289  *enable = _RTX;
290  *SSRC = _ssrcRTX;
291}
292
293WebRtc_Word32 RTPSender::CheckPayloadType(const WebRtc_Word8 payloadType,
294                                          RtpVideoCodecTypes& videoType) {
295  CriticalSectionScoped cs(_sendCritsect);
296
297  if (payloadType < 0) {
298    WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
299                 "\tinvalid payloadType (%d)", payloadType);
300    return -1;
301  }
302  if (_audioConfigured) {
303    WebRtc_Word8 redPlType = -1;
304    if (_audio->RED(redPlType) == 0) {
305      // We have configured RED.
306      if (redPlType == payloadType) {
307        // And it's a match...
308        return 0;
309      }
310    }
311  }
312  if (_payloadType == payloadType) {
313    if (!_audioConfigured) {
314      videoType = _video->VideoCodecType();
315    }
316    return 0;
317  }
318  std::map<WebRtc_Word8, ModuleRTPUtility::Payload*>::iterator it =
319      _payloadTypeMap.find(payloadType);
320  if (it == _payloadTypeMap.end()) {
321    WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
322                 "\tpayloadType:%d not registered", payloadType);
323    return -1;
324  }
325  _payloadType = payloadType;
326  ModuleRTPUtility::Payload* payload = it->second;
327  assert(payload);
328  if (!payload->audio && !_audioConfigured) {
329    _video->SetVideoCodecType(payload->typeSpecific.Video.videoCodecType);
330    videoType = payload->typeSpecific.Video.videoCodecType;
331    _video->SetMaxConfiguredBitrateVideo(payload->typeSpecific.Video.maxRate);
332  }
333  return 0;
334}
335
336WebRtc_Word32 RTPSender::SendOutgoingData(
337    const FrameType frame_type,
338    const WebRtc_Word8 payload_type,
339    const WebRtc_UWord32 capture_timestamp,
340    int64_t capture_time_ms,
341    const WebRtc_UWord8* payload_data,
342    const WebRtc_UWord32 payload_size,
343    const RTPFragmentationHeader* fragmentation,
344    VideoCodecInformation* codec_info,
345    const RTPVideoTypeHeader* rtp_type_hdr) {
346  {
347    // Drop this packet if we're not sending media packets.
348    CriticalSectionScoped cs(_sendCritsect);
349    if (!_sendingMedia) {
350      return 0;
351    }
352  }
353  RtpVideoCodecTypes video_type = kRtpNoVideo;
354  if (CheckPayloadType(payload_type, video_type) != 0) {
355    WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
356                 "%s invalid argument failed to find payloadType:%d",
357                 __FUNCTION__, payload_type);
358    return -1;
359  }
360
361  if (_audioConfigured) {
362    assert(frame_type == kAudioFrameSpeech ||
363           frame_type == kAudioFrameCN ||
364           frame_type == kFrameEmpty);
365
366    return _audio->SendAudio(frame_type, payload_type, capture_timestamp,
367                             payload_data, payload_size,fragmentation);
368  } else {
369    assert(frame_type != kAudioFrameSpeech &&
370           frame_type != kAudioFrameCN);
371
372    if (frame_type == kFrameEmpty) {
373      return SendPaddingAccordingToBitrate(payload_type, capture_timestamp,
374                                           capture_time_ms);
375    }
376    return _video->SendVideo(video_type,
377                             frame_type,
378                             payload_type,
379                             capture_timestamp,
380                             capture_time_ms,
381                             payload_data,
382                             payload_size,
383                             fragmentation,
384                             codec_info,
385                             rtp_type_hdr);
386  }
387}
388
389WebRtc_Word32 RTPSender::SendPaddingAccordingToBitrate(
390    WebRtc_Word8 payload_type,
391    WebRtc_UWord32 capture_timestamp,
392    int64_t capture_time_ms) {
393  // Current bitrate since last estimate(1 second) averaged with the
394  // estimate since then, to get the most up to date bitrate.
395  uint32_t current_bitrate = BitrateNow();
396  int bitrate_diff = _targetSendBitrate * 1000 - current_bitrate;
397  if (bitrate_diff <= 0) {
398    return 0;
399  }
400  int bytes = 0;
401  if (current_bitrate == 0) {
402    // Start up phase. Send one 33.3 ms batch to start with.
403    bytes = (bitrate_diff / 8) / 30;
404  } else {
405    bytes = (bitrate_diff / 8);
406    // Cap at 200 ms of target send data.
407    int bytes_cap = _targetSendBitrate * 25;  // 1000 / 8 / 5
408    if (bytes > bytes_cap) {
409      bytes = bytes_cap;
410    }
411  }
412  return SendPadData(payload_type, capture_timestamp, capture_time_ms, bytes);
413}
414
415WebRtc_Word32 RTPSender::SendPadData(WebRtc_Word8 payload_type,
416                                     WebRtc_UWord32 capture_timestamp,
417                                     int64_t capture_time_ms,
418                                     WebRtc_Word32 bytes) {
419  // Drop this packet if we're not sending media packets
420  if (!_sendingMedia) {
421    return 0;
422  }
423  // Max in the RFC 3550 is 255 bytes, we limit it to be modulus 32 for SRTP.
424  int max_length = 224;
425  WebRtc_UWord8 data_buffer[IP_PACKET_SIZE];
426
427  for (; bytes > 0; bytes -= max_length) {
428    int padding_bytes_in_packet = max_length;
429    if (bytes < max_length) {
430      padding_bytes_in_packet = (bytes + 16) & 0xffe0;  // Keep our modulus 32.
431    }
432    if (padding_bytes_in_packet < 32) {
433       // Sanity don't send empty packets.
434       break;
435    }
436    // Correct seq num, timestamp and payload type.
437    int header_length = BuildRTPheader(data_buffer,
438                                       payload_type,
439                                       false,  // No markerbit.
440                                       capture_timestamp,
441                                       true,  // Timestamp provided.
442                                       true);  // Increment sequence number.
443    data_buffer[0] |= 0x20;  // Set padding bit.
444    WebRtc_Word32* data =
445        reinterpret_cast<WebRtc_Word32*>(&(data_buffer[header_length]));
446
447    // Fill data buffer with random data.
448    for (int j = 0; j < (padding_bytes_in_packet >> 2); j++) {
449      data[j] = rand();
450    }
451    // Set number of padding bytes in the last byte of the packet.
452    data_buffer[header_length + padding_bytes_in_packet - 1] =
453        padding_bytes_in_packet;
454    // Send the packet
455    if (0 > SendToNetwork(data_buffer,
456                          padding_bytes_in_packet,
457                          header_length,
458                          capture_time_ms,
459                          kDontRetransmit)) {
460      // Error sending the packet.
461      break;
462    }
463  }
464  if (bytes > 31) {  // 31 due to our modulus 32.
465    // We did not manage to send all bytes.
466    return -1;
467  }
468  return 0;
469}
470
471void RTPSender::SetStorePacketsStatus(
472    const bool enable,
473    const WebRtc_UWord16 numberToStore) {
474  _packetHistory->SetStorePacketsStatus(enable, numberToStore);
475}
476
477bool RTPSender::StorePackets() const {
478  return _packetHistory->StorePackets();
479}
480
481WebRtc_Word32 RTPSender::ReSendPacket(WebRtc_UWord16 packet_id,
482                                      WebRtc_UWord32 min_resend_time) {
483
484  WebRtc_UWord16 length = IP_PACKET_SIZE;
485  WebRtc_UWord8 data_buffer[IP_PACKET_SIZE];
486  WebRtc_UWord8* buffer_to_send_ptr = data_buffer;
487
488  int64_t stored_time_in_ms;
489  StorageType type;
490  bool found = _packetHistory->GetRTPPacket(packet_id,
491      min_resend_time, data_buffer, &length, &stored_time_in_ms, &type);
492  if (!found) {
493    // Packet not found.
494    return 0;
495  }
496  if (length == 0 || type == kDontRetransmit) {
497    // No bytes copied (packet recently resent, skip resending) or
498    // packet should not be retransmitted.
499    return 0;
500  }
501  WebRtc_UWord8 data_buffer_rtx[IP_PACKET_SIZE];
502  if (_RTX) {
503    buffer_to_send_ptr = data_buffer_rtx;
504
505    CriticalSectionScoped cs(_sendCritsect);
506    // Add RTX header.
507    ModuleRTPUtility::RTPHeaderParser rtpParser(
508        reinterpret_cast<const WebRtc_UWord8*>(data_buffer),
509        length);
510
511    WebRtcRTPHeader rtp_header;
512    rtpParser.Parse(rtp_header);
513
514    // Add original RTP header.
515    memcpy(data_buffer_rtx, data_buffer, rtp_header.header.headerLength);
516
517    // Replace sequence number.
518    WebRtc_UWord8* ptr = data_buffer_rtx + 2;
519    ModuleRTPUtility::AssignUWord16ToBuffer(ptr, _sequenceNumberRTX++);
520
521    // Replace SSRC.
522    ptr += 6;
523    ModuleRTPUtility::AssignUWord32ToBuffer(ptr, _ssrcRTX);
524
525    // Add OSN (original sequence number).
526    ptr = data_buffer_rtx + rtp_header.header.headerLength;
527    ModuleRTPUtility::AssignUWord16ToBuffer(
528        ptr, rtp_header.header.sequenceNumber);
529    ptr += 2;
530
531    // Add original payload data.
532    memcpy(ptr,
533           data_buffer + rtp_header.header.headerLength,
534           length - rtp_header.header.headerLength);
535    length += 2;
536  }
537  WebRtc_Word32 bytes_sent = ReSendToNetwork(buffer_to_send_ptr, length);
538  if (bytes_sent <= 0) {
539    WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
540                 "Transport failed to resend packet_id %u", packet_id);
541    return -1;
542  }
543  // Store the time when the packet was last resent.
544  _packetHistory->UpdateResendTime(packet_id);
545  return bytes_sent;
546}
547
548WebRtc_Word32 RTPSender::ReSendToNetwork(const WebRtc_UWord8* packet,
549                                         const WebRtc_UWord32 size) {
550  WebRtc_Word32 bytes_sent = -1;
551  if (_transport) {
552    bytes_sent = _transport->SendPacket(_id, packet, size);
553  }
554  if (bytes_sent <= 0) {
555    return -1;
556  }
557  // Update send statistics
558  CriticalSectionScoped cs(_sendCritsect);
559  Bitrate::Update(bytes_sent);
560  _packetsSent++;
561  // We on purpose don't add to _payloadBytesSent since this is a
562  // re-transmit and not new payload data.
563  return bytes_sent;
564}
565
566int RTPSender::SelectiveRetransmissions() const {
567  if (!_video) return -1;
568  return _video->SelectiveRetransmissions();
569}
570
571int RTPSender::SetSelectiveRetransmissions(uint8_t settings) {
572  if (!_video) return -1;
573  return _video->SetSelectiveRetransmissions(settings);
574}
575
576void RTPSender::OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength,
577                               const WebRtc_UWord16* nackSequenceNumbers,
578                               const WebRtc_UWord16 avgRTT) {
579  const WebRtc_Word64 now = clock_.TimeInMilliseconds();
580  WebRtc_UWord32 bytesReSent = 0;
581
582  // Enough bandwidth to send NACK?
583  if (!ProcessNACKBitRate(now)) {
584    WEBRTC_TRACE(kTraceStream,
585                 kTraceRtpRtcp,
586                 _id,
587                 "NACK bitrate reached. Skip sending NACK response. Target %d",
588                 _targetSendBitrate);
589    return;
590  }
591
592  for (WebRtc_UWord16 i = 0; i < nackSequenceNumbersLength; ++i) {
593    const WebRtc_Word32 bytesSent = ReSendPacket(nackSequenceNumbers[i],
594                                                 5+avgRTT);
595    if (bytesSent > 0) {
596      bytesReSent += bytesSent;
597    } else if (bytesSent == 0) {
598      // The packet has previously been resent.
599      // Try resending next packet in the list.
600      continue;
601    } else if (bytesSent < 0) {
602      // Failed to send one Sequence number. Give up the rest in this nack.
603      WEBRTC_TRACE(kTraceWarning,
604                   kTraceRtpRtcp,
605                   _id,
606                   "Failed resending RTP packet %d, Discard rest of packets",
607                   nackSequenceNumbers[i]);
608      break;
609    }
610    // delay bandwidth estimate (RTT * BW)
611    if (_targetSendBitrate != 0 && avgRTT) {
612      // kbits/s * ms = bits => bits/8 = bytes
613      WebRtc_UWord32 targetBytes =
614          (static_cast<WebRtc_UWord32>(_targetSendBitrate) * avgRTT) >> 3;
615      if (bytesReSent > targetBytes) {
616        break; // ignore the rest of the packets in the list
617      }
618    }
619  }
620  if (bytesReSent > 0) {
621    // TODO(pwestin) consolidate these two methods.
622    UpdateNACKBitRate(bytesReSent, now);
623    _nackBitrate.Update(bytesReSent);
624  }
625}
626
627bool RTPSender::ProcessNACKBitRate(const WebRtc_UWord32 now) {
628  WebRtc_UWord32 num = 0;
629  WebRtc_Word32 byteCount = 0;
630  const WebRtc_UWord32 avgInterval=1000;
631
632  CriticalSectionScoped cs(_sendCritsect);
633
634  if (_targetSendBitrate == 0) {
635    return true;
636  }
637  for (num = 0; num < NACK_BYTECOUNT_SIZE; num++) {
638    if ((now - _nackByteCountTimes[num]) > avgInterval) {
639      // don't use data older than 1sec
640      break;
641    } else {
642      byteCount += _nackByteCount[num];
643    }
644  }
645  WebRtc_Word32 timeInterval = avgInterval;
646  if (num == NACK_BYTECOUNT_SIZE) {
647    // More than NACK_BYTECOUNT_SIZE nack messages has been received
648    // during the last msgInterval
649    timeInterval = now - _nackByteCountTimes[num-1];
650    if (timeInterval < 0) {
651      timeInterval = avgInterval;
652    }
653  }
654  return (byteCount*8) < (_targetSendBitrate * timeInterval);
655}
656
657void RTPSender::UpdateNACKBitRate(const WebRtc_UWord32 bytes,
658                                  const WebRtc_UWord32 now) {
659  CriticalSectionScoped cs(_sendCritsect);
660
661  // save bitrate statistics
662  if (bytes > 0) {
663    if (now == 0) {
664      // add padding length
665      _nackByteCount[0] += bytes;
666    } else {
667      if (_nackByteCountTimes[0] == 0) {
668        // first no shift
669      } else {
670        // shift
671        for (int i = (NACK_BYTECOUNT_SIZE-2); i >= 0 ; i--) {
672          _nackByteCount[i+1] = _nackByteCount[i];
673          _nackByteCountTimes[i+1] = _nackByteCountTimes[i];
674        }
675      }
676      _nackByteCount[0] = bytes;
677      _nackByteCountTimes[0] = now;
678    }
679  }
680}
681
682void RTPSender::TimeToSendPacket(uint16_t sequence_number,
683                                 int64_t capture_time_ms) {
684  StorageType type;
685  uint16_t length = IP_PACKET_SIZE;
686  uint8_t data_buffer[IP_PACKET_SIZE];
687  int64_t stored_time_ms;  // TODO(pwestin) can we depricate this?
688
689  if (_packetHistory == NULL) {
690    return;
691  }
692  if (!_packetHistory->GetRTPPacket(sequence_number, 0, data_buffer,
693                                    &length, &stored_time_ms, &type)) {
694    assert(false);
695    return;
696  }
697  assert(length > 0);
698
699  ModuleRTPUtility::RTPHeaderParser rtpParser(data_buffer, length);
700  WebRtcRTPHeader rtp_header;
701  rtpParser.Parse(rtp_header);
702
703  int64_t diff_ms = clock_.TimeInMilliseconds() - capture_time_ms;
704  if (UpdateTransmissionTimeOffset(data_buffer, length, rtp_header, diff_ms)) {
705    // Update stored packet in case of receiving a re-transmission request.
706    _packetHistory->ReplaceRTPHeader(data_buffer,
707                                     rtp_header.header.sequenceNumber,
708                                     rtp_header.header.headerLength);
709  }
710  int bytes_sent = -1;
711  if (_transport) {
712    bytes_sent = _transport->SendPacket(_id, data_buffer, length);
713  }
714  if (bytes_sent <= 0) {
715    return;
716  }
717  // Update send statistics
718  CriticalSectionScoped cs(_sendCritsect);
719  Bitrate::Update(bytes_sent);
720  _packetsSent++;
721  if (bytes_sent > rtp_header.header.headerLength) {
722    _payloadBytesSent += bytes_sent - rtp_header.header.headerLength;
723  }
724}
725
726// TODO(pwestin): send in the RTPHeaderParser to avoid parsing it again
727WebRtc_Word32 RTPSender::SendToNetwork(uint8_t* buffer,
728                                       int payload_length,
729                                       int rtp_header_length,
730                                       int64_t capture_time_ms,
731                                       StorageType storage) {
732  ModuleRTPUtility::RTPHeaderParser rtpParser(buffer,
733      payload_length + rtp_header_length);
734  WebRtcRTPHeader rtp_header;
735  rtpParser.Parse(rtp_header);
736
737  // |capture_time_ms| <= 0 is considered invalid.
738  // TODO(holmer): This should be changed all over Video Engine so that negative
739  // time is consider invalid, while 0 is considered a valid time.
740  if (capture_time_ms > 0) {
741    int64_t time_now = clock_.TimeInMilliseconds();
742    UpdateTransmissionTimeOffset(buffer, payload_length + rtp_header_length,
743                                 rtp_header, time_now - capture_time_ms);
744  }
745  // Used for NACK and to spread out the transmission of packets.
746  if (_packetHistory->PutRTPPacket(buffer, rtp_header_length + payload_length,
747      _maxPayloadLength, capture_time_ms, storage) != 0) {
748    return -1;
749  }
750  if (paced_sender_) {
751    if (!paced_sender_ ->SendPacket(PacedSender::kNormalPriority,
752                                    rtp_header.header.ssrc,
753                                    rtp_header.header.sequenceNumber,
754                                    capture_time_ms,
755                                    payload_length + rtp_header_length)) {
756      // We can't send the packet right now.
757      // We will be called when it is time.
758      return payload_length + rtp_header_length;
759    }
760  }
761  // Send packet
762  WebRtc_Word32 bytes_sent = -1;
763  if (_transport) {
764    bytes_sent = _transport->SendPacket(_id,
765                                        buffer,
766                                        payload_length + rtp_header_length);
767  }
768  if (bytes_sent <= 0) {
769    return -1;
770  }
771  // Update send statistics
772  CriticalSectionScoped cs(_sendCritsect);
773  Bitrate::Update(bytes_sent);
774  _packetsSent++;
775  if (bytes_sent > rtp_header_length) {
776    _payloadBytesSent += bytes_sent - rtp_header_length;
777  }
778  return 0;
779}
780
781void RTPSender::ProcessBitrate() {
782  CriticalSectionScoped cs(_sendCritsect);
783  Bitrate::Process();
784  _nackBitrate.Process();
785  if (_audioConfigured) {
786    return;
787  }
788  _video->ProcessBitrate();
789}
790
791WebRtc_UWord16 RTPSender::RTPHeaderLength() const {
792  WebRtc_UWord16 rtpHeaderLength = 12;
793  if (_includeCSRCs) {
794    rtpHeaderLength += sizeof(WebRtc_UWord32)*_CSRCs;
795  }
796  rtpHeaderLength += RtpHeaderExtensionTotalLength();
797  return rtpHeaderLength;
798}
799
800WebRtc_UWord16 RTPSender::IncrementSequenceNumber() {
801  CriticalSectionScoped cs(_sendCritsect);
802  return _sequenceNumber++;
803}
804
805void RTPSender::ResetDataCounters() {
806  _packetsSent = 0;
807  _payloadBytesSent = 0;
808}
809
810WebRtc_UWord32 RTPSender::Packets() const {
811  // Don't use critsect to avoid potental deadlock
812  return _packetsSent;
813}
814
815// number of sent RTP bytes
816// dont use critsect to avoid potental deadlock
817WebRtc_UWord32 RTPSender::Bytes() const {
818  return _payloadBytesSent;
819}
820
821WebRtc_Word32 RTPSender::BuildRTPheader(WebRtc_UWord8* dataBuffer,
822                                        const WebRtc_Word8 payloadType,
823                                        const bool markerBit,
824                                        const WebRtc_UWord32 captureTimeStamp,
825                                        const bool timeStampProvided,
826                                        const bool incSequenceNumber) {
827  assert(payloadType>=0);
828  CriticalSectionScoped cs(_sendCritsect);
829
830  dataBuffer[0] = static_cast<WebRtc_UWord8>(0x80);  // version 2
831  dataBuffer[1] = static_cast<WebRtc_UWord8>(payloadType);
832  if (markerBit) {
833    dataBuffer[1] |= kRtpMarkerBitMask;  // MarkerBit is set
834  }
835  if (timeStampProvided) {
836    _timeStamp = _startTimeStamp + captureTimeStamp;
837  } else {
838    // make a unique time stamp
839    // we can't inc by the actual time, since then we increase the risk of back
840    // timing.
841    _timeStamp++;
842  }
843  ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+2, _sequenceNumber);
844  ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+4, _timeStamp);
845  ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+8, _ssrc);
846  WebRtc_Word32 rtpHeaderLength = 12;
847
848  // Add the CSRCs if any
849  if (_includeCSRCs && _CSRCs > 0) {
850    if (_CSRCs > kRtpCsrcSize) {
851      // error
852      assert(false);
853      return -1;
854    }
855    WebRtc_UWord8* ptr = &dataBuffer[rtpHeaderLength];
856    for (WebRtc_UWord32 i = 0; i < _CSRCs; ++i) {
857      ModuleRTPUtility::AssignUWord32ToBuffer(ptr, _CSRC[i]);
858      ptr +=4;
859    }
860    dataBuffer[0] = (dataBuffer[0]&0xf0) | _CSRCs;
861
862    // Update length of header
863    rtpHeaderLength += sizeof(WebRtc_UWord32)*_CSRCs;
864  }
865  _sequenceNumber++;  // prepare for next packet
866
867  WebRtc_UWord16 len = BuildRTPHeaderExtension(dataBuffer + rtpHeaderLength);
868  if (len) {
869    dataBuffer[0] |= 0x10;  // set eXtension bit
870    rtpHeaderLength += len;
871  }
872  return rtpHeaderLength;
873}
874
875WebRtc_UWord16 RTPSender::BuildRTPHeaderExtension(
876    WebRtc_UWord8* dataBuffer) const {
877  if (_rtpHeaderExtensionMap.Size() <= 0) {
878    return 0;
879  }
880  /* RTP header extension, RFC 3550.
881     0                   1                   2                   3
882     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
883    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
884    |      defined by profile       |           length              |
885    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
886    |                        header extension                       |
887    |                             ....                              |
888  */
889  const WebRtc_UWord32 kPosLength = 2;
890  const WebRtc_UWord32 kHeaderLength = RTP_ONE_BYTE_HEADER_LENGTH_IN_BYTES;
891
892  // Add extension ID (0xBEDE).
893  ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer,
894                                          RTP_ONE_BYTE_HEADER_EXTENSION);
895
896  // Add extensions.
897  WebRtc_UWord16 total_block_length = 0;
898
899  RTPExtensionType type = _rtpHeaderExtensionMap.First();
900  while (type != kRtpExtensionNone) {
901    WebRtc_UWord8 block_length = 0;
902    if (type == kRtpExtensionTransmissionTimeOffset) {
903      block_length = BuildTransmissionTimeOffsetExtension(
904          dataBuffer + kHeaderLength + total_block_length);
905    }
906    total_block_length += block_length;
907    type = _rtpHeaderExtensionMap.Next(type);
908  }
909  if (total_block_length == 0) {
910    // No extension added.
911    return 0;
912  }
913  // Set header length (in number of Word32, header excluded).
914  assert(total_block_length % 4 == 0);
915  ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer + kPosLength,
916                                          total_block_length / 4);
917  // Total added length.
918  return kHeaderLength + total_block_length;
919}
920
921WebRtc_UWord8 RTPSender::BuildTransmissionTimeOffsetExtension(
922    WebRtc_UWord8* dataBuffer) const {
923  // From RFC 5450: Transmission Time Offsets in RTP Streams.
924  //
925  // The transmission time is signaled to the receiver in-band using the
926  // general mechanism for RTP header extensions [RFC5285]. The payload
927  // of this extension (the transmitted value) is a 24-bit signed integer.
928  // When added to the RTP timestamp of the packet, it represents the
929  // "effective" RTP transmission time of the packet, on the RTP
930  // timescale.
931  //
932  // The form of the transmission offset extension block:
933  //
934  //    0                   1                   2                   3
935  //    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
936  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
937  //   |  ID   | len=2 |              transmission offset              |
938  //   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
939
940  // Get id defined by user.
941  WebRtc_UWord8 id;
942  if (_rtpHeaderExtensionMap.GetId(kRtpExtensionTransmissionTimeOffset, &id)
943      != 0) {
944    // Not registered.
945    return 0;
946  }
947  int pos = 0;
948  const WebRtc_UWord8 len = 2;
949  dataBuffer[pos++] = (id << 4) + len;
950  ModuleRTPUtility::AssignUWord24ToBuffer(dataBuffer + pos,
951                                          _transmissionTimeOffset);
952  pos += 3;
953  assert(pos == TRANSMISSION_TIME_OFFSET_LENGTH_IN_BYTES);
954  return TRANSMISSION_TIME_OFFSET_LENGTH_IN_BYTES;
955}
956
957bool RTPSender::UpdateTransmissionTimeOffset(
958    WebRtc_UWord8* rtp_packet,
959    const WebRtc_UWord16 rtp_packet_length,
960    const WebRtcRTPHeader& rtp_header,
961    const WebRtc_Word64 time_diff_ms) const {
962  CriticalSectionScoped cs(_sendCritsect);
963
964  // Get length until start of transmission block.
965  int transmission_block_pos =
966      _rtpHeaderExtensionMap.GetLengthUntilBlockStartInBytes(
967          kRtpExtensionTransmissionTimeOffset);
968  if (transmission_block_pos < 0) {
969    WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
970                 "Failed to update transmission time offset, not registered.");
971    return false;
972  }
973  int block_pos = 12 + rtp_header.header.numCSRCs + transmission_block_pos;
974  if (rtp_packet_length < block_pos + 4 ||
975      rtp_header.header.headerLength < block_pos + 4) {
976    WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
977                 "Failed to update transmission time offset, invalid length.");
978    return false;
979  }
980  // Verify that header contains extension.
981  if (!((rtp_packet[12 + rtp_header.header.numCSRCs] == 0xBE) &&
982      (rtp_packet[12 + rtp_header.header.numCSRCs + 1] == 0xDE))) {
983    WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
984        "Failed to update transmission time offset, hdr extension not found.");
985    return false;
986  }
987  // Get id.
988  WebRtc_UWord8 id = 0;
989  if (_rtpHeaderExtensionMap.GetId(kRtpExtensionTransmissionTimeOffset,
990                                   &id) != 0) {
991    WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
992                 "Failed to update transmission time offset, no id.");
993    return false;
994  }
995  // Verify first byte in block.
996  const WebRtc_UWord8 first_block_byte = (id << 4) + 2;
997  if (rtp_packet[block_pos] != first_block_byte) {
998    WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
999                 "Failed to update transmission time offset.");
1000    return false;
1001  }
1002  // Update transmission offset field.
1003  ModuleRTPUtility::AssignUWord24ToBuffer(rtp_packet + block_pos + 1,
1004                                          time_diff_ms * 90);  // RTP timestamp.
1005  return true;
1006}
1007
1008void RTPSender::SetSendingStatus(const bool enabled) {
1009  if (enabled) {
1010    WebRtc_UWord32 frequency_hz;
1011    if (_audioConfigured) {
1012      WebRtc_UWord32 frequency = _audio->AudioFrequency();
1013
1014      // sanity
1015      switch(frequency) {
1016        case 8000:
1017        case 12000:
1018        case 16000:
1019        case 24000:
1020        case 32000:
1021          break;
1022        default:
1023          assert(false);
1024          return;
1025      }
1026      frequency_hz = frequency;
1027    } else {
1028      frequency_hz = kDefaultVideoFrequency;
1029    }
1030    WebRtc_UWord32 RTPtime = ModuleRTPUtility::GetCurrentRTP(&clock_,
1031                                                             frequency_hz);
1032
1033    // will be ignored if it's already configured via API
1034    SetStartTimestamp(RTPtime, false);
1035  } else {
1036    if (!_ssrcForced) {
1037      // generate a new SSRC
1038      _ssrcDB.ReturnSSRC(_ssrc);
1039      _ssrc = _ssrcDB.CreateSSRC();   // can't be 0
1040
1041    }
1042    // Don't initialize seq number if SSRC passed externally.
1043    if (!_sequenceNumberForced && !_ssrcForced) {
1044      // generate a new sequence number
1045      _sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER);
1046    }
1047  }
1048}
1049
1050void RTPSender::SetSendingMediaStatus(const bool enabled) {
1051  CriticalSectionScoped cs(_sendCritsect);
1052  _sendingMedia = enabled;
1053}
1054
1055bool RTPSender::SendingMedia() const {
1056  CriticalSectionScoped cs(_sendCritsect);
1057  return _sendingMedia;
1058}
1059
1060WebRtc_UWord32 RTPSender::Timestamp() const {
1061  CriticalSectionScoped cs(_sendCritsect);
1062  return _timeStamp;
1063}
1064
1065void RTPSender::SetStartTimestamp(WebRtc_UWord32 timestamp, bool force) {
1066  CriticalSectionScoped cs(_sendCritsect);
1067  if (force) {
1068    _startTimeStampForced = force;
1069    _startTimeStamp = timestamp;
1070  } else {
1071    if (!_startTimeStampForced) {
1072      _startTimeStamp = timestamp;
1073    }
1074  }
1075}
1076
1077WebRtc_UWord32 RTPSender::StartTimestamp() const {
1078  CriticalSectionScoped cs(_sendCritsect);
1079  return _startTimeStamp;
1080}
1081
1082WebRtc_UWord32 RTPSender::GenerateNewSSRC() {
1083  // if configured via API, return 0
1084  CriticalSectionScoped cs(_sendCritsect);
1085
1086  if (_ssrcForced) {
1087    return 0;
1088  }
1089  _ssrc = _ssrcDB.CreateSSRC();   // can't be 0
1090  return _ssrc;
1091}
1092
1093void RTPSender::SetSSRC(WebRtc_UWord32 ssrc) {
1094  // this is configured via the API
1095  CriticalSectionScoped cs(_sendCritsect);
1096
1097  if (_ssrc == ssrc && _ssrcForced) {
1098    return; // since it's same ssrc, don't reset anything
1099  }
1100  _ssrcForced = true;
1101  _ssrcDB.ReturnSSRC(_ssrc);
1102  _ssrcDB.RegisterSSRC(ssrc);
1103  _ssrc = ssrc;
1104  if (!_sequenceNumberForced) {
1105    _sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER);
1106  }
1107}
1108
1109WebRtc_UWord32 RTPSender::SSRC() const {
1110  CriticalSectionScoped cs(_sendCritsect);
1111  return _ssrc;
1112}
1113
1114void RTPSender::SetCSRCStatus(const bool include) {
1115  _includeCSRCs = include;
1116}
1117
1118void RTPSender::SetCSRCs(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize],
1119                         const WebRtc_UWord8 arrLength) {
1120  assert(arrLength <= kRtpCsrcSize);
1121  CriticalSectionScoped cs(_sendCritsect);
1122
1123  for (int i = 0; i < arrLength;i++) {
1124    _CSRC[i] = arrOfCSRC[i];
1125  }
1126  _CSRCs = arrLength;
1127}
1128
1129WebRtc_Word32 RTPSender::CSRCs(WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const {
1130  assert(arrOfCSRC);
1131  CriticalSectionScoped cs(_sendCritsect);
1132  for (int i = 0; i < _CSRCs && i < kRtpCsrcSize;i++) {
1133    arrOfCSRC[i] = _CSRC[i];
1134  }
1135  return _CSRCs;
1136}
1137
1138void RTPSender::SetSequenceNumber(WebRtc_UWord16 seq) {
1139  CriticalSectionScoped cs(_sendCritsect);
1140  _sequenceNumberForced = true;
1141  _sequenceNumber = seq;
1142}
1143
1144WebRtc_UWord16 RTPSender::SequenceNumber() const {
1145  CriticalSectionScoped cs(_sendCritsect);
1146  return _sequenceNumber;
1147}
1148
1149/*
1150 *    Audio
1151 */
1152WebRtc_Word32 RTPSender::SendTelephoneEvent(const WebRtc_UWord8 key,
1153                                            const WebRtc_UWord16 time_ms,
1154                                            const WebRtc_UWord8 level) {
1155  if (!_audioConfigured) {
1156    return -1;
1157  }
1158  return _audio->SendTelephoneEvent(key, time_ms, level);
1159}
1160
1161bool RTPSender::SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const {
1162  if (!_audioConfigured) {
1163    return false;
1164  }
1165  return _audio->SendTelephoneEventActive(telephoneEvent);
1166}
1167
1168WebRtc_Word32 RTPSender::SetAudioPacketSize(
1169    const WebRtc_UWord16 packetSizeSamples) {
1170  if (!_audioConfigured) {
1171    return -1;
1172  }
1173  return _audio->SetAudioPacketSize(packetSizeSamples);
1174}
1175
1176WebRtc_Word32
1177RTPSender::SetAudioLevelIndicationStatus(const bool enable,
1178                                         const WebRtc_UWord8 ID) {
1179  if (!_audioConfigured) {
1180    return -1;
1181  }
1182  return _audio->SetAudioLevelIndicationStatus(enable, ID);
1183}
1184
1185WebRtc_Word32 RTPSender::AudioLevelIndicationStatus(bool& enable,
1186                                                    WebRtc_UWord8& ID) const {
1187  return _audio->AudioLevelIndicationStatus(enable, ID);
1188}
1189
1190WebRtc_Word32 RTPSender::SetAudioLevel(const WebRtc_UWord8 level_dBov) {
1191  return _audio->SetAudioLevel(level_dBov);
1192}
1193
1194WebRtc_Word32 RTPSender::SetRED(const WebRtc_Word8 payloadType) {
1195  if (!_audioConfigured) {
1196    return -1;
1197  }
1198  return _audio->SetRED(payloadType);
1199}
1200
1201WebRtc_Word32 RTPSender::RED(WebRtc_Word8& payloadType) const {
1202  if (!_audioConfigured) {
1203    return -1;
1204  }
1205  return _audio->RED(payloadType);
1206}
1207
1208/*
1209 *    Video
1210 */
1211VideoCodecInformation* RTPSender::CodecInformationVideo() {
1212  if (_audioConfigured) {
1213    return NULL;
1214  }
1215  return _video->CodecInformationVideo();
1216}
1217
1218RtpVideoCodecTypes RTPSender::VideoCodecType() const {
1219  if (_audioConfigured) {
1220    return kRtpNoVideo;
1221  }
1222  return _video->VideoCodecType();
1223}
1224
1225WebRtc_UWord32 RTPSender::MaxConfiguredBitrateVideo() const {
1226  if (_audioConfigured) {
1227    return 0;
1228  }
1229  return _video->MaxConfiguredBitrateVideo();
1230}
1231
1232WebRtc_Word32 RTPSender::SendRTPIntraRequest() {
1233  if (_audioConfigured) {
1234    return -1;
1235  }
1236  return _video->SendRTPIntraRequest();
1237}
1238
1239WebRtc_Word32 RTPSender::SetGenericFECStatus(
1240    const bool enable,
1241    const WebRtc_UWord8 payloadTypeRED,
1242    const WebRtc_UWord8 payloadTypeFEC) {
1243  if (_audioConfigured) {
1244    return -1;
1245  }
1246  return _video->SetGenericFECStatus(enable, payloadTypeRED, payloadTypeFEC);
1247}
1248
1249WebRtc_Word32 RTPSender::GenericFECStatus(bool& enable,
1250                                          WebRtc_UWord8& payloadTypeRED,
1251                                          WebRtc_UWord8& payloadTypeFEC) const {
1252  if (_audioConfigured) {
1253    return -1;
1254  }
1255  return _video->GenericFECStatus(enable, payloadTypeRED, payloadTypeFEC);
1256}
1257
1258WebRtc_Word32 RTPSender::SetFecParameters(
1259    const FecProtectionParams* delta_params,
1260    const FecProtectionParams* key_params) {
1261  if (_audioConfigured) {
1262    return -1;
1263  }
1264  return _video->SetFecParameters(delta_params, key_params);
1265}
1266}  // namespace webrtc
1267