#include "H264FUFrame.h"
#include <string.h>
#ifdef _WIN32
#include <WinSock2.h>
#else
#include <arpa/inet.h>
#endif
namespace fu
{
enum
{
FU_A = 28
};
static const unsigned char gs_H264Header[4] = {0x00,0x00,0x00,0x01};
static const unsigned short gs_H264HeaderLen = sizeof(gs_H264Header);
struct RTPHeader
{
#ifdef RTP_BIG_ENDIAN
unsigned char version:2;
unsigned char padding:1;
unsigned char extension:1;
unsigned char csrccount:4;
unsigned char marker:1;
unsigned char payloadtype:7;
#else // little endian
unsigned char csrccount:4;
unsigned char extension:1;
unsigned char padding:1;
unsigned char version:2;
unsigned char payloadtype:7;
unsigned char marker:1;
#endif // RTP_BIG_ENDIAN
unsigned short sequencenumber;
unsigned int timestamp;
unsigned int ssrc;
};
struct RTPExtensionHeader
{
unsigned short extid;
unsigned short length;
};
NaluFUFrame::NaluFUFrame(InputPacketType t)
:m_pSink(NULL),
m_length(gs_H264HeaderLen),
m_type(t)
{
memcpy(m_frameBuffer,gs_H264Header,gs_H264HeaderLen);
}
NaluFUFrame::~NaluFUFrame()
{
}
bool NaluFUFrame::inputData(unsigned char* data, size_t length)
{
if (m_type == RAW_PACKET)
{
return inputRawData(data,length);
}
return inputRTPData(data,length);
}
bool NaluFUFrame::inputRawData(unsigned char* data, size_t length)
{
if (length < 2 || data == NULL)
{
m_length = gs_H264HeaderLen;
return false;
}
unsigned char fCurPacketNALUnitType = (data[0] & 0x1F);
switch (fCurPacketNALUnitType)
{
case FU_A:
case FU_A + 1:
{
unsigned char startBit = data[1]&0x80;
unsigned char endBit = data[1]&0x40;
if( startBit )
{
//取开始包的 nalu header;
m_frameBuffer[m_length] = ( data[0] & 0xE0 ) | ( data[1] & 0x1F );
m_length ++;
}
length -= 2;
if ( m_length + length >= sizeof(m_frameBuffer) )
{
m_length = gs_H264HeaderLen;
return false;
}
memcpy(m_frameBuffer + m_length,data + 2 ,length );
m_length += length ;
if( endBit == 0 )
{
return true;
}
fCurPacketNALUnitType = m_frameBuffer[gs_H264HeaderLen] & 0x1F;
break;
}
default:
m_length = gs_H264HeaderLen;
if (length + m_length >= sizeof(m_frameBuffer)) ///over memory????
{
return false;
}
// This packet contains one , decodable NAL units
memcpy(m_frameBuffer + m_length,data,length);
m_length += length;
break;
}
if (m_pSink != NULL)
{
int frameType = FRAME_VIDEO_I;
switch( fCurPacketNALUnitType)
{
case 7:
frameType = FRAME_VIDEO_SPS;
break;
case 8:
frameType = FRAME_VIDEO_PPS;
break;
case 5:
frameType = FRAME_VIDEO_I;
break;
default:
frameType = FRAME_VIDEO_P;
break;
}
m_pSink->writePacket(m_frameBuffer,m_length,frameType);
}
m_length = gs_H264HeaderLen;
return true;
}
bool NaluFUFrame::inputAudioData(unsigned char payloadtype,
unsigned char* data,
size_t length)
{
if (m_pSink != NULL)
{
m_pSink->writePacket(data,length,payloadtype);
return true;
}
return false;
}
bool NaluFUFrame::inputRTPData(unsigned char* rtpdata, size_t rtplength)
{
if ( (rtpdata == NULL)
|| (rtplength + m_length >= sizeof(m_frameBuffer))
|| (rtplength <= sizeof(RTPHeader)) )
{
m_length = gs_H264HeaderLen;
return false;
}
unsigned char *packetbytes = rtpdata;
RTPHeader *rtpheader = (RTPHeader*)packetbytes;
if (rtpheader->version != 2)
{
m_length = gs_H264HeaderLen;
return false;
}
size_t packetlen = rtplength;
bool marker = (rtpheader->marker == 0)?false:true;
unsigned char payloadtype = rtpheader->payloadtype;
int csrccount = rtpheader->csrccount;
int payloadoffset = sizeof(RTPHeader) + (int)(csrccount * sizeof(unsigned int));
int numpadbytes = 0;
if (rtpheader->padding) // adjust payload length to take padding into account
{
numpadbytes = (int)packetbytes[packetlen - 1]; // last byte contains number of padding bytes
if (numpadbytes <= 0)
{
m_length = gs_H264HeaderLen;
return false;
}
}
RTPExtensionHeader *rtpextheader = NULL;
bool hasextension = (rtpheader->extension == 0)?false:true;
if (hasextension) // got header extension
{
rtpextheader = (RTPExtensionHeader *)(packetbytes + payloadoffset);
payloadoffset += sizeof(RTPExtensionHeader);
unsigned short exthdrlen = ntohs(rtpextheader->length);
payloadoffset += ((int)exthdrlen)*sizeof(unsigned int);
}
int payloadlength = packetlen - numpadbytes - payloadoffset;
unsigned char *payload = packetbytes + payloadoffset;
if ( (payloadlength < 2) || (payloadlength >= sizeof(m_frameBuffer)) )
{
m_length = gs_H264HeaderLen;
return false;
}
if (rtpheader->payloadtype < 40)
{
return inputAudioData(payloadtype,payload,payloadlength);
}
return inputRawData(payload,payloadlength);
}
void NaluFUFrame::setSink(StreamSink* pSink)
{
m_pSink = pSink;
}
}