webrtc/modules/rtp_rtcp/source/rtp_sender_video.cc
hlundin@google.com d2c7bff3a1 Implement VP8 packetizer and unit tests
Implemented a new VP8 packetizer with three modes. The packetizer
class needs access to the fragmentation information, which is
now created in the codec wrapper and passed through the callback
chain to the RTPSenderVideo::SendVP8().

A unit test for the VP8 packetizer was also implemented. It tests the
three different modes. The tests could definitely be more elaborate.
Review URL: http://webrtc-codereview.appspot.com/34003

git-svn-id: http://webrtc.googlecode.com/svn/trunk@48 4adac7df-926f-26a2-2b94-8c16560cd09d
2011-06-07 12:23:14 +00:00

1135 lines
38 KiB
C++

/*
* Copyright (c) 2011 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "rtp_sender_video.h"
#include "critical_section_wrapper.h"
#include "trace.h"
#include "rtp_utility.h"
#include <string.h> // memcpy
#include <cassert> // assert
#include <cstdlib> // srand
#include "h263_information.h"
#include "rtp_format_vp8.h"
namespace webrtc {
enum { REDForFECHeaderLength = 1 };
RTPSenderVideo::RTPSenderVideo(const WebRtc_Word32 id, RTPSenderInterface* rtpSender) :
_id(id),
_rtpSender(*rtpSender),
_sendVideoCritsect(*CriticalSectionWrapper::CreateCriticalSection()),
_videoType(kRtpNoVideo),
_videoCodecInformation(NULL),
_maxBitrate(0),
// generic FEC
_fec(id),
_fecEnabled(false),
_payloadTypeRED(-1),
_payloadTypeFEC(-1),
_codeRateKey(0),
_codeRateDelta(0),
_fecProtectionFactor(0),
// H263
_savedByte(0),
_eBit(0)
{
}
RTPSenderVideo::~RTPSenderVideo()
{
if(_videoCodecInformation)
{
delete _videoCodecInformation;
}
delete &_sendVideoCritsect;
}
WebRtc_Word32
RTPSenderVideo::Init()
{
CriticalSectionScoped cs(_sendVideoCritsect);
_fecEnabled = false;
_payloadTypeRED = -1;
_payloadTypeFEC = -1;
_codeRateKey = 0;
_codeRateDelta = 0;
_fecProtectionFactor = 0;
return 0;
}
void
RTPSenderVideo::ChangeUniqueId(const WebRtc_Word32 id)
{
_id = id;
}
void
RTPSenderVideo::SetVideoCodecType(RtpVideoCodecTypes videoType)
{
CriticalSectionScoped cs(_sendVideoCritsect);
_videoType = videoType;
}
RtpVideoCodecTypes
RTPSenderVideo::VideoCodecType() const
{
return _videoType;
}
WebRtc_Word32
RTPSenderVideo::RegisterVideoPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE],
const WebRtc_Word8 payloadType,
const WebRtc_UWord32 maxBitRate,
ModuleRTPUtility::Payload*& payload)
{
CriticalSectionScoped cs(_sendVideoCritsect);
RtpVideoCodecTypes videoType = kRtpNoVideo;
if (ModuleRTPUtility::StringCompare(payloadName, "VP8",3))
{
videoType = kRtpVp8Video;
}
else if ((ModuleRTPUtility::StringCompare(payloadName, "H263-1998", 9)) ||
(ModuleRTPUtility::StringCompare(payloadName, "H263-2000", 9)))
{
videoType = kRtpH2631998Video;
}
else if (ModuleRTPUtility::StringCompare(payloadName, "H263", 4))
{
videoType = kRtpH263Video;
}
else if (ModuleRTPUtility::StringCompare(payloadName, "MP4V-ES", 7))
{
videoType = kRtpMpeg4Video;
} else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4))
{
videoType = kRtpNoVideo;
}else
{
videoType = kRtpNoVideo;
return -1;
}
payload = new ModuleRTPUtility::Payload;
memcpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE);
payload->typeSpecific.Video.videoCodecType = videoType;
payload->typeSpecific.Video.maxRate = maxBitRate;
payload->audio = false;
return 0;
}
struct RtpPacket
{
WebRtc_UWord16 rtpHeaderLength;
ForwardErrorCorrection::Packet* pkt;
};
WebRtc_Word32
RTPSenderVideo::SendVideoPacket(const FrameType frameType,
const WebRtc_UWord8* dataBuffer,
const WebRtc_UWord16 payloadLength,
const WebRtc_UWord16 rtpHeaderLength)
{
if(_fecEnabled)
{
WebRtc_Word32 retVal = 0;
const bool markerBit = (dataBuffer[1] & kRtpMarkerBitMask)?true:false;
RtpPacket* ptrGenericFEC = new RtpPacket;
ptrGenericFEC->pkt = new ForwardErrorCorrection::Packet;
ptrGenericFEC->pkt->length = payloadLength + rtpHeaderLength;
ptrGenericFEC->rtpHeaderLength = rtpHeaderLength;
memcpy(ptrGenericFEC->pkt->data, dataBuffer, ptrGenericFEC->pkt->length);
// add packet to FEC list
_rtpPacketListFec.PushBack(ptrGenericFEC);
_mediaPacketListFec.PushBack(ptrGenericFEC->pkt);
if (markerBit) // last packet in frame
{
// interface for FEC
ListWrapper fecPacketList;
// Retain the RTP header of the last media packet to construct the FEC
// packet RTP headers.
ForwardErrorCorrection::Packet lastMediaRtpHeader;
memcpy(lastMediaRtpHeader.data,
ptrGenericFEC->pkt->data,
ptrGenericFEC->rtpHeaderLength);
lastMediaRtpHeader.length = ptrGenericFEC->rtpHeaderLength;
lastMediaRtpHeader.data[1] = _payloadTypeRED; // Replace payload and clear
// marker bit.
retVal = _fec.GenerateFEC(_mediaPacketListFec, fecPacketList,_fecProtectionFactor);
while(!_rtpPacketListFec.Empty())
{
WebRtc_UWord8 newDataBuffer[IP_PACKET_SIZE];
memset(newDataBuffer, 0, sizeof(newDataBuffer));
ListItem* item = _rtpPacketListFec.First();
RtpPacket* packetToSend =
static_cast<RtpPacket*>(item->GetItem());
// copy RTP header
memcpy(newDataBuffer, packetToSend->pkt->data, packetToSend->rtpHeaderLength);
// get codec pltype
WebRtc_UWord8 payloadType = newDataBuffer[1] & 0x7f;
// replace pltype
newDataBuffer[1] &= 0x80; // reset
newDataBuffer[1] += _payloadTypeRED; // replace
// add RED header
newDataBuffer[packetToSend->rtpHeaderLength] = payloadType; // f-bit always 0
// copy payload data
memcpy(newDataBuffer + packetToSend->rtpHeaderLength + REDForFECHeaderLength,
packetToSend->pkt->data + packetToSend->rtpHeaderLength,
packetToSend->pkt->length - packetToSend->rtpHeaderLength);
_rtpPacketListFec.PopFront();
_mediaPacketListFec.PopFront();
// send normal packet with RED header
retVal |= _rtpSender.SendToNetwork(newDataBuffer,
packetToSend->pkt->length - packetToSend->rtpHeaderLength + REDForFECHeaderLength,
packetToSend->rtpHeaderLength);
delete packetToSend->pkt;
delete packetToSend;
packetToSend = NULL;
}
assert(_mediaPacketListFec.Empty());
assert(_rtpPacketListFec.Empty());
while(!fecPacketList.Empty())
{
WebRtc_UWord8 newDataBuffer[IP_PACKET_SIZE];
ListItem* item = fecPacketList.First();
// build FEC packets
ForwardErrorCorrection::Packet* packetToSend =
static_cast<ForwardErrorCorrection::Packet*>(item->GetItem());
// The returned FEC packets have no RTP headers.
// Copy the last media packet's modified RTP header.
memcpy(newDataBuffer, lastMediaRtpHeader.data, lastMediaRtpHeader.length);
// add sequence number
ModuleRTPUtility::AssignUWord16ToBuffer(&newDataBuffer[2],_rtpSender.IncrementSequenceNumber());
// add RED header
newDataBuffer[lastMediaRtpHeader.length] = _payloadTypeFEC; // f-bit always 0
// copy payload data
memcpy(newDataBuffer + lastMediaRtpHeader.length + REDForFECHeaderLength,
packetToSend->data,
packetToSend->length);
fecPacketList.PopFront();
assert(packetToSend->length != 0); // invalid FEC packet
// No marker bit on FEC packets, last media packet have the marker
// send FEC packet with RED header
retVal |= _rtpSender.SendToNetwork(newDataBuffer,
packetToSend->length + REDForFECHeaderLength,
lastMediaRtpHeader.length);
}
}
return retVal;
}
return _rtpSender.SendToNetwork(dataBuffer,
payloadLength,
rtpHeaderLength);
}
WebRtc_Word32
RTPSenderVideo::SendRTPIntraRequest()
{
// RFC 2032
// 5.2.1. Full intra-frame Request (FIR) packet
WebRtc_UWord16 length = 8;
WebRtc_UWord8 data[8];
data[0] = 0x80;
data[1] = 192;
data[2] = 0;
data[3] = 1; // length
ModuleRTPUtility::AssignUWord32ToBuffer(data+4, _rtpSender.SSRC());
return _rtpSender.SendToNetwork(data, 0, length);
}
WebRtc_Word32
RTPSenderVideo::SetGenericFECStatus(const bool enable,
const WebRtc_UWord8 payloadTypeRED,
const WebRtc_UWord8 payloadTypeFEC)
{
_fecEnabled = enable;
_payloadTypeRED = payloadTypeRED;
_payloadTypeFEC = payloadTypeFEC;
_codeRateKey = 0;
_codeRateDelta = 0;
return 0;
}
WebRtc_Word32
RTPSenderVideo::GenericFECStatus(bool& enable,
WebRtc_UWord8& payloadTypeRED,
WebRtc_UWord8& payloadTypeFEC) const
{
enable = _fecEnabled;
payloadTypeRED = _payloadTypeRED;
payloadTypeFEC = _payloadTypeFEC;
return 0;
}
WebRtc_UWord16
RTPSenderVideo::FECPacketOverhead() const
{
if (_fecEnabled)
{
return ForwardErrorCorrection::PacketOverhead() + REDForFECHeaderLength;
}
return 0;
}
WebRtc_Word32
RTPSenderVideo::SetFECCodeRate(const WebRtc_UWord8 keyFrameCodeRate,
const WebRtc_UWord8 deltaFrameCodeRate)
{
_codeRateKey = keyFrameCodeRate;
_codeRateDelta = deltaFrameCodeRate;
return 0;
}
WebRtc_Word32
RTPSenderVideo::SendVideo(const RtpVideoCodecTypes videoType,
const FrameType frameType,
const WebRtc_Word8 payloadType,
const WebRtc_UWord32 captureTimeStamp,
const WebRtc_UWord8* payloadData,
const WebRtc_UWord32 payloadSize,
const RTPFragmentationHeader* fragmentation,
VideoCodecInformation* codecInfo)
{
if( payloadSize == 0)
{
return -1;
}
if (frameType == kVideoFrameKey)
{
_fecProtectionFactor = _codeRateKey;
} else
{
_fecProtectionFactor = _codeRateDelta;
}
WebRtc_Word32 retVal = -1;
switch(videoType)
{
case kRtpNoVideo:
retVal = SendGeneric(payloadType,captureTimeStamp, payloadData, payloadSize );
break;
case kRtpH263Video:
retVal = SendH263(frameType,payloadType, captureTimeStamp, payloadData, payloadSize, codecInfo);
break;
case kRtpH2631998Video: //RFC 4629
retVal = SendH2631998(frameType,payloadType, captureTimeStamp, payloadData, payloadSize, codecInfo);
break;
case kRtpMpeg4Video: // RFC 3016
retVal = SendMPEG4(frameType,payloadType, captureTimeStamp, payloadData, payloadSize);
break;
case kRtpVp8Video:
retVal = SendVP8(frameType, payloadType, captureTimeStamp, payloadData, payloadSize, fragmentation);
break;
default:
assert(false);
break;
}
if(retVal <= 0)
{
return retVal;
}
return 0;
}
WebRtc_Word32
RTPSenderVideo::SendGeneric(const WebRtc_Word8 payloadType,
const WebRtc_UWord32 captureTimeStamp,
const WebRtc_UWord8* payloadData,
const WebRtc_UWord32 payloadSize)
{
WebRtc_UWord16 payloadBytesInPacket = 0;
WebRtc_UWord32 bytesSent = 0;
WebRtc_Word32 payloadBytesToSend = payloadSize;
const WebRtc_UWord8* data = payloadData;
WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength();
WebRtc_UWord16 maxLength = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength;
WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE];
// Fragment packet into packets of max MaxPayloadLength bytes payload.
while (payloadBytesToSend > 0)
{
if (payloadBytesToSend > maxLength)
{
payloadBytesInPacket = maxLength;
payloadBytesToSend -= payloadBytesInPacket;
if(_rtpSender.BuildRTPheader(dataBuffer, payloadType, false, captureTimeStamp) != rtpHeaderLength) //markerBit is 0
{
// error
return -1;
}
}
else
{
payloadBytesInPacket = (WebRtc_UWord16)payloadBytesToSend;
payloadBytesToSend = 0;
if(_rtpSender.BuildRTPheader(dataBuffer, payloadType, true, captureTimeStamp) != rtpHeaderLength) //markerBit is 1
{
// error
return -1;
}
}
// Put payload in packet
memcpy(&dataBuffer[rtpHeaderLength], &data[bytesSent], payloadBytesInPacket);
bytesSent += payloadBytesInPacket;
if(-1 == SendVideoPacket(kVideoFrameKey,
dataBuffer,
payloadBytesInPacket,
rtpHeaderLength))
{
return -1;
}
}
return 0;
}
WebRtc_Word32
RTPSenderVideo::SendPadData(const WebRtcRTPHeader* rtpHeader,
const WebRtc_UWord32 bytes)
{
const WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength();
WebRtc_UWord32 maxLength = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength;
WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE];
if(bytes<maxLength)
{
// for a small packet don't spend too much time
maxLength = bytes;
}
{
CriticalSectionScoped cs(_sendVideoCritsect);
// send paded data
// correct seq num, time stamp and payloadtype
// we reuse the last seq number
_rtpSender.BuildRTPheader(dataBuffer, rtpHeader->header.payloadType, false,0, false, false);
// version 0 to be compatible with old ViE
dataBuffer[0] &= !0x80;
// set relay SSRC
ModuleRTPUtility::AssignUWord32ToBuffer(dataBuffer+8, rtpHeader->header.ssrc);
WebRtc_Word32* data = (WebRtc_Word32*)&(dataBuffer[12]); // start at 12
// build data buffer
for(WebRtc_UWord32 j = 0; j < ((maxLength>>2)-4) && j < (bytes>>4); j++)
{
data[j] = rand();
}
}
// min
WebRtc_UWord16 length = (WebRtc_UWord16)(bytes<maxLength?bytes:maxLength);
// Send the packet
return _rtpSender.SendToNetwork(dataBuffer, length, rtpHeaderLength, true);
}
/*
* MPEG4
*/
WebRtc_Word32
RTPSenderVideo::SendMPEG4(const FrameType frameType,
const WebRtc_Word8 payloadType,
const WebRtc_UWord32 captureTimeStamp,
const WebRtc_UWord8* payloadData,
const WebRtc_UWord32 payloadSize)
{
WebRtc_Word32 payloadBytesToSend = payloadSize;
WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength();
WebRtc_UWord16 maxLength = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength;
WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE];
// Fragment packet WebRtc_Word32 packets of max MaxPayloadLength bytes payload.
const WebRtc_UWord8* data = payloadData;
while (payloadBytesToSend > 0)
{
WebRtc_UWord16 payloadBytes = 0;
WebRtc_Word32 dataOffset = rtpHeaderLength;
do
{
WebRtc_Word32 size = 0;
bool markerBit = false;
if(payloadBytesToSend > maxLength)
{
size = FindMPEG4NALU(data, maxLength);
}else
{
markerBit = true; // last in frame
size = payloadBytesToSend;
}
if(size <= 0)
{
return -1;
}
if(size > maxLength)
{
// we need to fragment NALU
return -1;
}
if(payloadBytes == 0)
{
// build RTP header
if(_rtpSender.BuildRTPheader(dataBuffer, payloadType, markerBit, captureTimeStamp) != rtpHeaderLength)
{
// error
return -1;
}
}
if( size + payloadBytes <= maxLength)
{
// Put payload in packet
memcpy(&dataBuffer[dataOffset], data, size);
dataOffset += size; //advance frame ptr
data += size; //advance packet ptr
payloadBytes += (WebRtc_UWord16)size;
payloadBytesToSend -= size;
} else
{
break; // send packet
}
}while(payloadBytesToSend);
if(-1 == SendVideoPacket(frameType, dataBuffer, payloadBytes, rtpHeaderLength))
{
return -1;
}
}
return 0;
}
WebRtc_Word32
RTPSenderVideo::FindMPEG4NALU(const WebRtc_UWord8* inData, WebRtc_Word32 maxLength)
{
WebRtc_Word32 size = 0;
for (WebRtc_Word32 i = maxLength; i > 4; i-=2) // Find NAL
{
// scan down
if (inData[i] == 0)
{
if (inData[i-1] == 0)
{
// i point at the last zero
size = i-1;
}else if(inData[i+1] == 0)
{
size = i;
}
if(size > 0)
{
return size;
}
}
}
return 0;
}
VideoCodecInformation*
RTPSenderVideo::CodecInformationVideo()
{
return _videoCodecInformation;
}
void
RTPSenderVideo::SetMaxConfiguredBitrateVideo(const WebRtc_UWord32 maxBitrate)
{
_maxBitrate = maxBitrate;
}
WebRtc_UWord32
RTPSenderVideo::MaxConfiguredBitrateVideo() const
{
return _maxBitrate;
}
/*
* H.263
*/
WebRtc_Word32
RTPSenderVideo::SendH263(const FrameType frameType,
const WebRtc_Word8 payloadType,
const WebRtc_UWord32 captureTimeStamp,
const WebRtc_UWord8* payloadData,
const WebRtc_UWord32 payloadSize,
VideoCodecInformation* codecInfo)
{
bool modeA = true;
WebRtc_UWord16 h263HeaderLength = 4;
WebRtc_UWord16 payloadBytesInPacket = 0;
WebRtc_Word32 payloadBytesToSend = payloadSize;
WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength();
// -2: one byte is possible old ebit -> sBit, one byte is new ebit if next GOB header is not byte aligned
WebRtc_UWord16 maxPayloadLengthH263 = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength - h263HeaderLength - 2; // (eventual sBit, eBit)
// Fragment packet into packets of max MaxPayloadLength bytes payload.
WebRtc_UWord8 numOfGOB = 0;
WebRtc_UWord16 prevOK = 0;
WebRtc_UWord32 payloadBytesSent = 0;
WebRtc_UWord8 sbit = 0;
_eBit = 0;
H263Information* h263Information = NULL;
if(codecInfo)
{
// another channel have already parsed this data
h263Information = static_cast<H263Information*>(codecInfo);
} else
{
if(_videoCodecInformation)
{
if(_videoCodecInformation->Type() != kRtpH263Video)
{
// wrong codec
delete _videoCodecInformation;
_videoCodecInformation = new H263Information();
} else
{
_videoCodecInformation->Reset();
}
} else
{
_videoCodecInformation = new H263Information();
}
h263Information = static_cast<H263Information*>(_videoCodecInformation);
}
WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE];
const WebRtc_UWord8* data = payloadData;
const H263Info* ptrH263Info = NULL;
if (h263Information->GetInfo(payloadData,payloadSize, ptrH263Info) == -1)
{
return -1;
}
while (payloadBytesToSend > 0)
{
prevOK = 0;
modeA = true;
if (payloadBytesToSend > maxPayloadLengthH263)
{
// Fragment packet at GOB boundary
for (; numOfGOB < ptrH263Info->numOfGOBs; numOfGOB++)
{
// Fit one or more GOBs into packet
if (WebRtc_Word32(ptrH263Info->ptrGOBbuffer[numOfGOB+1] - payloadBytesSent) < maxPayloadLengthH263)
{
prevOK = WebRtc_UWord16(ptrH263Info->ptrGOBbuffer[numOfGOB+1] - payloadBytesSent);
}else
{
break;
}
}
if (!prevOK)
{
// GOB larger than max MaxPayloadLength bytes -> Mode B required
// Fragment stream at MB boundaries
modeA = false;
// Get MB positions within GOB
const H263MBInfo* ptrInfoMB = NULL;
if (-1 == h263Information->GetMBInfo(payloadData,payloadSize,numOfGOB, ptrInfoMB))
{
return -1;
}
WebRtc_Word32 offset = ptrH263Info->CalculateMBOffset(numOfGOB);
if(offset < 0)
{
return -1;
}
// Send packets fragmented at MB boundaries
if (-1 == SendH263MBs(frameType, payloadType, captureTimeStamp, dataBuffer, data, rtpHeaderLength, numOfGOB, *ptrH263Info, *ptrInfoMB, offset))
{
return -1;
}
offset = ptrH263Info->CalculateMBOffset(numOfGOB+1);
if(offset < 0)
{
return -1;
}
WebRtc_Word32 numBytes = ptrInfoMB->ptrBuffer[offset-1] / 8;
WebRtc_Word32 numBytesRem = ptrInfoMB->ptrBuffer[offset-1] % 8;
if (numBytesRem)
{
// incase our GOB is not byte alligned
numBytes++;
}
payloadBytesToSend -= numBytes;
data += numBytes;
payloadBytesSent += numBytes;
numOfGOB++;
}
}
if (modeA)
{
h263HeaderLength = 4;
WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength();
// H.263 payload header (4 bytes)
dataBuffer[rtpHeaderLength] = 0; // First bit 0 == mode A, (00 000 000)
dataBuffer[rtpHeaderLength+1] = ptrH263Info->uiH263PTypeFmt << 5;
dataBuffer[rtpHeaderLength + 1] += ptrH263Info->codecBits << 1; // Last bit 0
dataBuffer[rtpHeaderLength + 2] = 0; // First 3 bits 0
dataBuffer[rtpHeaderLength + 3] = 0; // No pb frame
sbit = (8 - _eBit) % 8; // last packet eBit -> current packet sBit
if (payloadBytesToSend > maxPayloadLengthH263)
{
if (numOfGOB > 0)
{
// Check if GOB header is byte aligned
if(ptrH263Info->ptrGOBbufferSBit)
{
_eBit = (8 - ptrH263Info->ptrGOBbufferSBit[numOfGOB - 1]) % 8;
} else
{
_eBit = 0;
}
}
if (_eBit)
{
// next GOB header is not byte aligned, include this byte in packet
// Send the byte with eBits
prevOK++;
}
}
if (payloadBytesToSend > maxPayloadLengthH263)
{
payloadBytesInPacket = prevOK;
payloadBytesToSend -= payloadBytesInPacket;
_rtpSender.BuildRTPheader(dataBuffer, payloadType, false, captureTimeStamp);
} else
{
payloadBytesInPacket = (WebRtc_UWord16)payloadBytesToSend;
payloadBytesToSend = 0;
_rtpSender.BuildRTPheader(dataBuffer, payloadType, true, captureTimeStamp);
_eBit = 0;
}
if (sbit)
{
// Add last sent byte and put payload in packet
dataBuffer[rtpHeaderLength] = dataBuffer[rtpHeaderLength] | ((sbit & 0x7) << 3); // Set sBit
memcpy(&dataBuffer[rtpHeaderLength + h263HeaderLength], &_savedByte, 1);
memcpy(&dataBuffer[rtpHeaderLength + h263HeaderLength + 1], data, payloadBytesInPacket);
h263HeaderLength++;
}else
{
// Put payload in packet
memcpy(&dataBuffer[rtpHeaderLength + h263HeaderLength], data, payloadBytesInPacket);
}
if (_eBit)
{
// Save last byte to paste in next packet
dataBuffer[rtpHeaderLength] |= (_eBit & 0x7); // Set eBit
_savedByte = dataBuffer[payloadBytesInPacket + h263HeaderLength + rtpHeaderLength-1];
}
if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket + h263HeaderLength, rtpHeaderLength))
{
return -1;
}
payloadBytesSent += payloadBytesInPacket;
data += payloadBytesInPacket;
}
}
return 0;
}
WebRtc_Word32
RTPSenderVideo::SendH2631998(const FrameType frameType,
const WebRtc_Word8 payloadType,
const WebRtc_UWord32 captureTimeStamp,
const WebRtc_UWord8* payloadData,
const WebRtc_UWord32 payloadSize,
VideoCodecInformation* codecInfo)
{
const WebRtc_UWord16 h2631998HeaderLength = 2;
const WebRtc_UWord8 pLen = 0; // No extra header included
const WebRtc_UWord8 peBit = 0;
bool fragment = false;
WebRtc_UWord16 payloadBytesInPacket = 0;
WebRtc_Word32 payloadBytesToSend = payloadSize;
WebRtc_UWord16 numPayloadBytesToSend = 0;
WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength();
WebRtc_UWord8 p = 2; // P is not set in all packets, only packets that has a PictureStart or a GOB header
H263Information* h263Information = NULL;
if(codecInfo)
{
// another channel have already parsed this data
h263Information = static_cast<H263Information*>(codecInfo);
} else
{
if(_videoCodecInformation)
{
if(_videoCodecInformation->Type() != kRtpH263Video)
{
// wrong codec
delete _videoCodecInformation;
_videoCodecInformation = new H263Information();
} else
{
_videoCodecInformation->Reset();
}
} else
{
_videoCodecInformation = new H263Information();
}
h263Information = static_cast<H263Information*>(_videoCodecInformation);
}
const H263Info* ptrH263Info = NULL;
if (h263Information->GetInfo(payloadData,payloadSize, ptrH263Info) == -1)
{
return -1;
}
WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE];
const WebRtc_UWord16 maxPayloadLengthH2631998 = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength - h2631998HeaderLength;
const WebRtc_UWord8* data = payloadData;
WebRtc_UWord8 numOfGOB = 0;
WebRtc_UWord32 payloadBytesSent = 0;
while(payloadBytesToSend > 0)
{
WebRtc_Word32 prevOK = 0;
// Fragment packets at GOB boundaries
for (; numOfGOB < ptrH263Info->numOfGOBs; numOfGOB++)
{
// Fit one or more GOBs into packet
if (WebRtc_Word32(ptrH263Info->ptrGOBbuffer[numOfGOB+1] - payloadBytesSent) <= (maxPayloadLengthH2631998 + p))
{
prevOK = WebRtc_UWord16(ptrH263Info->ptrGOBbuffer[numOfGOB+1] - payloadBytesSent);
if(fragment)
{
// this is a fragment, send it
break;
}
}else
{
break;
}
}
if(!prevOK)
{
// GOB larger than MaxPayloadLength bytes
fragment = true;
numPayloadBytesToSend = maxPayloadLengthH2631998;
} else
{
fragment = false;
numPayloadBytesToSend = WebRtc_UWord16(prevOK - p);
}
dataBuffer[rtpHeaderLength] = (p << 1) + ((pLen >> 5) & 0x01);
dataBuffer[rtpHeaderLength+1] = ((pLen & 0x1F) << 3) + peBit;
if(p == 2)
{
data += 2; // inc data ptr (do not send first two bytes of picture or GOB start code)
payloadBytesToSend -= 2;
}
if(payloadBytesToSend > maxPayloadLengthH2631998)
{
payloadBytesInPacket = numPayloadBytesToSend;
payloadBytesToSend -= payloadBytesInPacket;
_rtpSender.BuildRTPheader(dataBuffer,payloadType, false, captureTimeStamp);
}else
{
payloadBytesInPacket = (WebRtc_UWord16)payloadBytesToSend;
payloadBytesToSend = 0;
_rtpSender.BuildRTPheader(dataBuffer, payloadType, true, captureTimeStamp); // markerBit is 1
}
// Put payload in packet
memcpy(&dataBuffer[rtpHeaderLength + h2631998HeaderLength], data, payloadBytesInPacket);
if(-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket + h2631998HeaderLength, rtpHeaderLength))
{
return -1;
}
data += payloadBytesInPacket;
payloadBytesSent += payloadBytesInPacket + p;
if(fragment)
{
p = 0;
}else
{
p = 2;
}
}
return 0;
}
WebRtc_Word32
RTPSenderVideo::SendH263MBs(const FrameType frameType,
const WebRtc_Word8 payloadType,
const WebRtc_UWord32 captureTimeStamp,
WebRtc_UWord8* dataBuffer,
const WebRtc_UWord8 *data,
const WebRtc_UWord16 rtpHeaderLength,
const WebRtc_UWord8 numOfGOB,
const H263Info& info,
const H263MBInfo& infoMB,
const WebRtc_Word32 offset)
{
// Mode B
WebRtc_UWord32 *sizeOfMBs = &infoMB.ptrBuffer[offset];
WebRtc_UWord8 *hmv1 = &infoMB.ptrBufferHMV[offset];
WebRtc_UWord8 *vmv1 = &infoMB.ptrBufferVMV[offset];
WebRtc_UWord16 h263HeaderLength = 8;
WebRtc_UWord16 payloadBytesInPacket = 0;
WebRtc_Word32 payloadBytesToSend = sizeOfMBs[info.ptrNumOfMBs[numOfGOB]-1] / 8;
WebRtc_UWord8 eBitLastByte = (WebRtc_UWord8)((8 - (sizeOfMBs[info.ptrNumOfMBs[numOfGOB]-1] % 8)) % 8);
WebRtc_Word32 sBit = 0;
WebRtc_Word32 firstMB = 0;
WebRtc_UWord32 bitsRem = 0;
WebRtc_UWord32 payloadBytesSent = 0;
WebRtc_Word32 numOfMB = 0;
WebRtc_Word32 prevOK = 0;
WebRtc_UWord16 maxPayloadLengthH263MB = _rtpSender.MaxPayloadLength() - FECPacketOverhead() - rtpHeaderLength - h263HeaderLength - 2; // (eventual sBit, eBit)
if (eBitLastByte)
{
payloadBytesToSend++;
}
// Fragment packet into packets of max MaxPayloadLength bytes payload.
while (payloadBytesToSend > 0)
{
prevOK = 0;
firstMB = numOfMB;
if (payloadBytesToSend > maxPayloadLengthH263MB)
{
// Fragment packet at MB boundary
for (; numOfMB < info.ptrNumOfMBs[numOfGOB]; numOfMB++)
{
// Fit one or more MBs into packet
if (WebRtc_Word32(sizeOfMBs[numOfMB] / 8 - payloadBytesSent) < maxPayloadLengthH263MB)
{
prevOK = sizeOfMBs[numOfMB] / 8 - payloadBytesSent;
bitsRem = sizeOfMBs[numOfMB] % 8;
if (bitsRem)
{
prevOK++;
}
}else
{
break;
}
}
if (!prevOK)
{
// MB does not fit in packet
return -1;
}
}
// H.263 payload header (8 bytes)
h263HeaderLength = 8;
dataBuffer[rtpHeaderLength] = (WebRtc_UWord8)0x80; // First bit 1 == mode B, 10 000 000
dataBuffer[rtpHeaderLength + 1] = (info.uiH263PTypeFmt) << 5; // Source format
if (numOfGOB == 0)
{
dataBuffer[rtpHeaderLength + 1] += info.pQuant; // Quantization value for first MB in packet
}
if (numOfGOB > 0 && firstMB > 0)
{
dataBuffer[rtpHeaderLength + 1] += info.ptrGQuant[numOfGOB]; // Quantization value for first MB in packet (0 if packet begins w/ a GOB header)
}
dataBuffer[rtpHeaderLength + 2] = numOfGOB << 3; // GOB #
dataBuffer[rtpHeaderLength + 2] += (WebRtc_UWord8)((firstMB >> 6)& 0x7); // First MB in the packet
dataBuffer[rtpHeaderLength + 3] = (WebRtc_UWord8)(firstMB << 2);
dataBuffer[rtpHeaderLength + 4] = (info.codecBits) << 4;
dataBuffer[rtpHeaderLength + 4] += (hmv1[firstMB] & 0x7F) >> 3; // Horizontal motion vector
dataBuffer[rtpHeaderLength + 5] = hmv1[firstMB] << 5;
dataBuffer[rtpHeaderLength + 5] += (vmv1[firstMB] & 0x7F) >> 2; // Vertical motion vector
dataBuffer[rtpHeaderLength + 6] = vmv1[firstMB] << 6;
dataBuffer[rtpHeaderLength + 7] = 0;
sBit = (8 - _eBit) % 8;
if (payloadBytesToSend > maxPayloadLengthH263MB)
{
payloadBytesInPacket = (WebRtc_UWord16)prevOK;
payloadBytesToSend -= payloadBytesInPacket;
_rtpSender.BuildRTPheader(dataBuffer, payloadType, false, captureTimeStamp);
_eBit = (WebRtc_UWord8)((8 - bitsRem) % 8);
}
else
{
payloadBytesInPacket = (WebRtc_UWord16)payloadBytesToSend;
payloadBytesToSend = 0;
if (numOfGOB == (info.numOfGOBs - 1))
{
_rtpSender.BuildRTPheader(dataBuffer, payloadType, true, captureTimeStamp);
_eBit = 0;
}
else
{
_rtpSender.BuildRTPheader(dataBuffer, payloadType, false, captureTimeStamp);
_eBit = eBitLastByte;
}
}
if (sBit)
{
// Add last sent byte and put payload in packet
dataBuffer[rtpHeaderLength] |= ((sBit & 0x7) << 3);
dataBuffer[rtpHeaderLength + h263HeaderLength] = _savedByte;
memcpy(&dataBuffer[rtpHeaderLength + h263HeaderLength + 1], data, payloadBytesInPacket);
h263HeaderLength++;
} else
{
// Put payload in packet
memcpy(&dataBuffer[rtpHeaderLength + h263HeaderLength], data, payloadBytesInPacket);
}
if (_eBit)
{
// Save last byte to paste in next packet
dataBuffer[rtpHeaderLength] |= (_eBit & 0x7);
_savedByte = dataBuffer[rtpHeaderLength + h263HeaderLength + payloadBytesInPacket - 1];
}
if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket + h263HeaderLength, rtpHeaderLength))
{
return -1;
}
data += payloadBytesInPacket;
payloadBytesSent += payloadBytesInPacket;
}
return 0;
}
/*
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| RSV |I|N|FI |B| PictureID (integer #bytes) |
+-+-+-+-+-+-+-+-+ |
: :
| +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| : (VP8 data or VP8 payload header; byte aligned)|
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*/
WebRtc_Word32
RTPSenderVideo::SendVP8(const FrameType frameType,
const WebRtc_Word8 payloadType,
const WebRtc_UWord32 captureTimeStamp,
const WebRtc_UWord8* payloadData,
const WebRtc_UWord32 payloadSize,
const RTPFragmentationHeader* fragmentation)
{
const WebRtc_UWord16 rtpHeaderLength = _rtpSender.RTPHeaderLength();
WebRtc_UWord16 vp8HeaderLength = 1;
int payloadBytesInPacket = 0;
WebRtc_Word32 bytesSent = 0;
WebRtc_Word32 payloadBytesToSend = payloadSize;
const WebRtc_UWord8* data = payloadData;
WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE];
WebRtc_UWord16 maxPayloadLengthVP8 = _rtpSender.MaxPayloadLength()
- FECPacketOverhead() - rtpHeaderLength;
RtpFormatVp8 packetizer(data, payloadBytesToSend, fragmentation, kStrict);
bool last = false;
while (!last)
{
// Write VP8 Payload Descriptor and VP8 payload.
if (packetizer.NextPacket(maxPayloadLengthVP8,
&dataBuffer[rtpHeaderLength], &payloadBytesInPacket, &last) < 0)
{
return -1;
}
// Write RTP header.
// Set marker bit true if this is the last packet in frame.
_rtpSender.BuildRTPheader(dataBuffer, payloadType, last,
captureTimeStamp);
if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket,
rtpHeaderLength))
{
return -1;
}
}
return 0;
}
} // namespace webrtc