1.RTP包头的封装
打包时,每帧数据包均需要添加二中所述RTP包头,对于每路视频流,RTP包头除序列号外,基本保持不变;另一个要注意的时,由于包头需要按位填充,当填充/发送数据时,应注意发送端数据的大小端限制。
2.数据流的包头
此处以h264,h265,,在rtp包头后,应按格式封装自己的数据头,其中h264为一个字节,h265为两个字节,
H264打包:
视频包时需要考虑到MTU的大小,每包数据不能超过MTU的大小,通常情况下,设备是不知道当前网络的MTU大小的。
H264数据打包格式如下:
RTP头+h264字节头+视频数据帧
h264字节头为两个字节,格式如下
第一个字节:
live555 : F为0,此处type为28,
nri = buff[0] & 0x60;
buff[0] 为视频帧的第一个数据
第二个字节
此处type = buff[0] & 0x1f;
其中S E R的解释如下,注意当数据包被分成夺宝数据后,处S E R三位,其他均与第一包保持一致,不得改变
H265打包:
打包和h264过程类似,区别就是H265的数据头为三个字节,
h265的三个字节,结构如下:
第一第二个字节
参考live555代码 此处F为0,Type为49,LayerId为0,TID为1
第三个字节
void H264or5Fragmenter::doGetNextFrame() {
if (fNumValidDataBytes == 1) {
// We have no NAL unit data currently in the buffer. Read a new one:
fInputSource->getNextFrame(&fInputBuffer[1], fInputBufferSize - 1,
afterGettingFrame, this,
FramedSource::handleClosure, this);
} else {
// We have NAL unit data in the buffer. There are three cases to consider:
// 1. There is a new NAL unit in the buffer, and it's small enough to deliver
// to the RTP sink (as is).
// 2. There is a new NAL unit in the buffer, but it's too large to deliver to
// the RTP sink in its entirety. Deliver the first fragment of this data,
// as a FU packet, with one extra preceding header byte (for the "FU header").
// 3. There is a NAL unit in the buffer, and we've already delivered some
// fragment(s) of this. Deliver the next fragment of this data,
// as a FU packet, with two (H.264) or three (H.265) extra preceding header bytes
// (for the "NAL header" and the "FU header").
if (fMaxSize < fMaxOutputPacketSize) { // shouldn't happen
envir() << "H264or5Fragmenter::doGetNextFrame(): fMaxSize ("
<< fMaxSize << ") is smaller than expected\n";
} else {
fMaxSize = fMaxOutputPacketSize;
}
fLastFragmentCompletedNALUnit = True; // by default
if (fCurDataOffset == 1) { // case 1 or 2
if (fNumValidDataBytes - 1 <= fMaxSize) { // case 1
memmove(fTo, &fInputBuffer[1], fNumValidDataBytes - 1);
fFrameSize = fNumValidDataBytes - 1;
fCurDataOffset = fNumValidDataBytes;
} else { // case 2
// We need to send the NAL unit data as FU packets. Deliver the first
// packet now. Note that we add "NAL header" and "FU header" bytes to the front
// of the packet (overwriting the existing "NAL header").
if (fHNumber == 264) {
fInputBuffer[0] = (fInputBuffer[1] & 0xE0) | 28; // FU indicator
fInputBuffer[1] = 0x80 | (fInputBuffer[1] & 0x1F); // FU header (with S bit)
} else { // 265
u_int8_t nal_unit_type = (fInputBuffer[1]&0x7E)>>1;
fInputBuffer[0] = (fInputBuffer[1] & 0x81) | (49<<1); // Payload header (1st byte)
fInputBuffer[1] = fInputBuffer[2]; // Payload header (2nd byte)
fInputBuffer[2] = 0x80 | nal_unit_type; // FU header (with S bit)
}
memmove(fTo, fInputBuffer, fMaxSize);
fFrameSize = fMaxSize;
fCurDataOffset += fMaxSize - 1;
fLastFragmentCompletedNALUnit = False;
}
} else { // case 3
// We are sending this NAL unit data as FU packets. We've already sent the
// first packet (fragment). Now, send the next fragment. Note that we add
// "NAL header" and "FU header" bytes to the front. (We reuse these bytes that
// we already sent for the first fragment, but clear the S bit, and add the E
// bit if this is the last fragment.)
unsigned numExtraHeaderBytes;
if (fHNumber == 264) {
fInputBuffer[fCurDataOffset-2] = fInputBuffer[0]; // FU indicator
fInputBuffer[fCurDataOffset-1] = fInputBuffer[1]&~0x80; // FU header (no S bit)
numExtraHeaderBytes = 2;
} else { // 265
fInputBuffer[fCurDataOffset-3] = fInputBuffer[0]; // Payload header (1st byte)
fInputBuffer[fCurDataOffset-2] = fInputBuffer[1]; // Payload header (2nd byte)
fInputBuffer[fCurDataOffset-1] = fInputBuffer[2]&~0x80; // FU header (no S bit)
numExtraHeaderBytes = 3;
}
unsigned numBytesToSend = numExtraHeaderBytes + (fNumValidDataBytes - fCurDataOffset);
if (numBytesToSend > fMaxSize) {
// We can't send all of the remaining data this time:
numBytesToSend = fMaxSize;
fLastFragmentCompletedNALUnit = False;
} else {
// This is the last fragment:
fInputBuffer[fCurDataOffset-1] |= 0x40; // set the E bit in the FU header
fNumTruncatedBytes = fSaveNumTruncatedBytes;
}
memmove(fTo, &fInputBuffer[fCurDataOffset-numExtraHeaderBytes], numBytesToSend);
fFrameSize = numBytesToSend;
fCurDataOffset += numBytesToSend - numExtraHeaderBytes;
}
if (fCurDataOffset >= fNumValidDataBytes) {
// We're done with this data. Reset the pointers for receiving new data:
fNumValidDataBytes = fCurDataOffset = 1;
}
// Complete delivery to the client:
FramedSource::afterGetting(this);
}
}