stefan@webrtc.org 7bb8f02274 Adds support for combining RTX and FEC/RED.
This is accomplished by breaking out RTX and FEC/RED functionality from the RTP module and keeping track of the base payload type, that is the payload type received when not receiving RTX.

Enables retransmissions over RTX by default in the loopback test.

BUG=1811
TESTS=voe/vie_auto_test --automated and trybots.
R=mflodman@webrtc.org, pbos@webrtc.org, xians@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/2154004

git-svn-id: http://webrtc.googlecode.com/svn/trunk@4692 4adac7df-926f-26a2-2b94-8c16560cd09d
2013-09-06 13:40:11 +00:00

482 lines
13 KiB
C++

/*
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/modules/video_coding/main/test/test_callbacks.h"
#include <math.h>
#include "webrtc/common_video/libyuv/include/webrtc_libyuv.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h"
#include "webrtc/modules/rtp_rtcp/interface/rtp_receiver.h"
#include "webrtc/modules/utility/interface/rtp_dump.h"
#include "webrtc/modules/video_coding/main/test/test_macros.h"
#include "webrtc/system_wrappers/interface/clock.h"
namespace webrtc {
/******************************
* VCMEncodeCompleteCallback
*****************************/
// Basic callback implementation
// passes the encoded frame directly to the encoder
// Packetization callback implementation
VCMEncodeCompleteCallback::VCMEncodeCompleteCallback(FILE* encodedFile):
_encodedFile(encodedFile),
_encodedBytes(0),
_VCMReceiver(NULL),
_seqNo(0),
_encodeComplete(false),
_width(0),
_height(0),
_codecType(kRtpVideoNone)
{
//
}
VCMEncodeCompleteCallback::~VCMEncodeCompleteCallback()
{
}
void
VCMEncodeCompleteCallback::RegisterTransportCallback(
VCMPacketizationCallback* transport)
{
}
int32_t
VCMEncodeCompleteCallback::SendData(
const FrameType frameType,
const uint8_t payloadType,
const uint32_t timeStamp,
int64_t capture_time_ms,
const uint8_t* payloadData,
const uint32_t payloadSize,
const RTPFragmentationHeader& fragmentationHeader,
const RTPVideoHeader* videoHdr)
{
// will call the VCMReceiver input packet
_frameType = frameType;
// writing encodedData into file
if (fwrite(payloadData, 1, payloadSize, _encodedFile) != payloadSize) {
return -1;
}
WebRtcRTPHeader rtpInfo;
rtpInfo.header.markerBit = true; // end of frame
rtpInfo.type.Video.isFirstPacket = true;
rtpInfo.type.Video.codec = _codecType;
rtpInfo.type.Video.height = (uint16_t)_height;
rtpInfo.type.Video.width = (uint16_t)_width;
switch (_codecType)
{
case webrtc::kRtpVideoVp8:
rtpInfo.type.Video.codecHeader.VP8.InitRTPVideoHeaderVP8();
rtpInfo.type.Video.codecHeader.VP8.nonReference =
videoHdr->codecHeader.VP8.nonReference;
rtpInfo.type.Video.codecHeader.VP8.pictureId =
videoHdr->codecHeader.VP8.pictureId;
break;
default:
assert(false);
return -1;
}
rtpInfo.header.payloadType = payloadType;
rtpInfo.header.sequenceNumber = _seqNo++;
rtpInfo.header.ssrc = 0;
rtpInfo.header.timestamp = timeStamp;
rtpInfo.frameType = frameType;
// Size should also be received from that table, since the payload type
// defines the size.
_encodedBytes += payloadSize;
// directly to receiver
int ret = _VCMReceiver->IncomingPacket(payloadData, payloadSize, rtpInfo);
_encodeComplete = true;
return ret;
}
float
VCMEncodeCompleteCallback::EncodedBytes()
{
return _encodedBytes;
}
bool
VCMEncodeCompleteCallback::EncodeComplete()
{
if (_encodeComplete)
{
_encodeComplete = false;
return true;
}
return false;
}
void
VCMEncodeCompleteCallback::Initialize()
{
_encodeComplete = false;
_encodedBytes = 0;
_seqNo = 0;
return;
}
void
VCMEncodeCompleteCallback::ResetByteCount()
{
_encodedBytes = 0;
}
/***********************************/
/* VCMRTPEncodeCompleteCallback */
/***********************************/
// Encode Complete callback implementation
// passes the encoded frame via the RTP module to the decoder
// Packetization callback implementation
int32_t
VCMRTPEncodeCompleteCallback::SendData(
const FrameType frameType,
const uint8_t payloadType,
const uint32_t timeStamp,
int64_t capture_time_ms,
const uint8_t* payloadData,
const uint32_t payloadSize,
const RTPFragmentationHeader& fragmentationHeader,
const RTPVideoHeader* videoHdr)
{
_frameType = frameType;
_encodedBytes+= payloadSize;
_encodeComplete = true;
return _RTPModule->SendOutgoingData(frameType,
payloadType,
timeStamp,
capture_time_ms,
payloadData,
payloadSize,
&fragmentationHeader,
videoHdr);
}
float
VCMRTPEncodeCompleteCallback::EncodedBytes()
{
// only good for one call - after which will reset value;
float tmp = _encodedBytes;
_encodedBytes = 0;
return tmp;
}
bool
VCMRTPEncodeCompleteCallback::EncodeComplete()
{
if (_encodeComplete)
{
_encodeComplete = false;
return true;
}
return false;
}
// Decoded Frame Callback Implementation
int32_t
VCMDecodeCompleteCallback::FrameToRender(I420VideoFrame& videoFrame)
{
if (PrintI420VideoFrame(videoFrame, _decodedFile) < 0) {
return -1;
}
_decodedBytes+= CalcBufferSize(kI420, videoFrame.width(),
videoFrame.height());
return VCM_OK;
}
int32_t
VCMDecodeCompleteCallback::DecodedBytes()
{
return _decodedBytes;
}
RTPSendCompleteCallback::RTPSendCompleteCallback(Clock* clock,
const char* filename):
_clock(clock),
_sendCount(0),
rtp_payload_registry_(NULL),
rtp_receiver_(NULL),
_rtp(NULL),
_lossPct(0),
_burstLength(0),
_networkDelayMs(0),
_jitterVar(0),
_prevLossState(0),
_totalSentLength(0),
_rtpPackets(),
_rtpDump(NULL)
{
if (filename != NULL)
{
_rtpDump = RtpDump::CreateRtpDump();
_rtpDump->Start(filename);
}
}
RTPSendCompleteCallback::~RTPSendCompleteCallback()
{
if (_rtpDump != NULL)
{
_rtpDump->Stop();
RtpDump::DestroyRtpDump(_rtpDump);
}
// Delete remaining packets
while (!_rtpPackets.empty())
{
// Take first packet in list
delete _rtpPackets.front();
_rtpPackets.pop_front();
}
}
int
RTPSendCompleteCallback::SendPacket(int channel, const void *data, int len)
{
_sendCount++;
_totalSentLength += len;
if (_rtpDump != NULL)
{
if (_rtpDump->DumpPacket((const uint8_t*)data, len) != 0)
{
return -1;
}
}
bool transmitPacket = true;
transmitPacket = PacketLoss();
int64_t now = _clock->TimeInMilliseconds();
// Insert outgoing packet into list
if (transmitPacket)
{
RtpPacket* newPacket = new RtpPacket();
memcpy(newPacket->data, data, len);
newPacket->length = len;
// Simulate receive time = network delay + packet jitter
// simulated as a Normal distribution random variable with
// mean = networkDelay and variance = jitterVar
int32_t
simulatedDelay = (int32_t)NormalDist(_networkDelayMs,
sqrt(_jitterVar));
newPacket->receiveTime = now + simulatedDelay;
_rtpPackets.push_back(newPacket);
}
// Are we ready to send packets to the receiver?
RtpPacket* packet = NULL;
while (!_rtpPackets.empty())
{
// Take first packet in list
packet = _rtpPackets.front();
int64_t timeToReceive = packet->receiveTime - now;
if (timeToReceive > 0)
{
// No available packets to send
break;
}
_rtpPackets.pop_front();
assert(_rtp); // We must have a configured RTP module for this test.
// Send to receive side
RTPHeader header;
scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
if (!parser->Parse(packet->data, packet->length, &header)) {
delete packet;
return -1;
}
PayloadUnion payload_specific;
if (!rtp_payload_registry_->GetPayloadSpecifics(
header.payloadType, &payload_specific)) {
return -1;
}
if (!rtp_receiver_->IncomingRtpPacket(header, packet->data,
packet->length, payload_specific,
true))
{
delete packet;
return -1;
}
delete packet;
packet = NULL;
}
return len; // OK
}
int
RTPSendCompleteCallback::SendRTCPPacket(int channel, const void *data, int len)
{
// Incorporate network conditions
return SendPacket(channel, data, len);
}
void
RTPSendCompleteCallback::SetLossPct(double lossPct)
{
_lossPct = lossPct;
return;
}
void
RTPSendCompleteCallback::SetBurstLength(double burstLength)
{
_burstLength = burstLength;
return;
}
bool
RTPSendCompleteCallback::PacketLoss()
{
bool transmitPacket = true;
if (_burstLength <= 1.0)
{
// Random loss: if _burstLength parameter is not set, or <=1
if (UnifomLoss(_lossPct))
{
// drop
transmitPacket = false;
}
}
else
{
// Simulate bursty channel (Gilbert model)
// (1st order) Markov chain model with memory of the previous/last
// packet state (loss or received)
// 0 = received state
// 1 = loss state
// probTrans10: if previous packet is lost, prob. to -> received state
// probTrans11: if previous packet is lost, prob. to -> loss state
// probTrans01: if previous packet is received, prob. to -> loss state
// probTrans00: if previous packet is received, prob. to -> received
// Map the two channel parameters (average loss rate and burst length)
// to the transition probabilities:
double probTrans10 = 100 * (1.0 / _burstLength);
double probTrans11 = (100.0 - probTrans10);
double probTrans01 = (probTrans10 * ( _lossPct / (100.0 - _lossPct)));
// Note: Random loss (Bernoulli) model is a special case where:
// burstLength = 100.0 / (100.0 - _lossPct) (i.e., p10 + p01 = 100)
if (_prevLossState == 0 )
{
// previous packet was received
if (UnifomLoss(probTrans01))
{
// drop, update previous state to loss
_prevLossState = 1;
transmitPacket = false;
}
}
else if (_prevLossState == 1)
{
_prevLossState = 0;
// previous packet was lost
if (UnifomLoss(probTrans11))
{
// drop, update previous state to loss
_prevLossState = 1;
transmitPacket = false;
}
}
}
return transmitPacket;
}
bool
RTPSendCompleteCallback::UnifomLoss(double lossPct)
{
double randVal = (std::rand() + 1.0)/(RAND_MAX + 1.0);
return randVal < lossPct/100;
}
int32_t
PacketRequester::ResendPackets(const uint16_t* sequenceNumbers,
uint16_t length)
{
return _rtp.SendNACK(sequenceNumbers, length);
}
int32_t
SendStatsTest::SendStatistics(const uint32_t bitRate,
const uint32_t frameRate)
{
TEST(frameRate <= _framerate);
TEST(bitRate > _bitrate / 2 && bitRate < 3 * _bitrate / 2);
printf("VCM 1 sec: Bit rate: %u\tFrame rate: %u\n", bitRate, frameRate);
return 0;
}
int32_t KeyFrameReqTest::RequestKeyFrame() {
printf("Key frame requested\n");
return 0;
}
VideoProtectionCallback::VideoProtectionCallback():
delta_fec_params_(),
key_fec_params_()
{
memset(&delta_fec_params_, 0, sizeof(delta_fec_params_));
memset(&key_fec_params_, 0, sizeof(key_fec_params_));
}
VideoProtectionCallback::~VideoProtectionCallback()
{
//
}
int32_t
VideoProtectionCallback::ProtectionRequest(
const FecProtectionParams* delta_fec_params,
const FecProtectionParams* key_fec_params,
uint32_t* sent_video_rate_bps,
uint32_t* sent_nack_rate_bps,
uint32_t* sent_fec_rate_bps)
{
key_fec_params_ = *key_fec_params;
delta_fec_params_ = *delta_fec_params;
// Update RTP
if (_rtp->SetFecParameters(&delta_fec_params_,
&key_fec_params_) != 0)
{
printf("Error in Setting FEC rate\n");
return -1;
}
return 0;
}
FecProtectionParams VideoProtectionCallback::DeltaFecParameters() const
{
return delta_fec_params_;
}
FecProtectionParams VideoProtectionCallback::KeyFecParameters() const
{
return key_fec_params_;
}
} // namespace webrtc