H.264 NAL 单元分段和重组

最近在参考live555 源码,学习 RTSP,留意到VCL  Unit  在网络发送前 封装成  NAL Unit时候,会涉及分段问题。

在live555  rtspserver端,当我们的程序获取NALU 后,处理NAL Unit 时,可能会出现的三种情况:

1)当NALU大小小于RTP净荷最大长度时, 单独封装成单个RTP包的净荷。

2)当NALU大小大于RTP净荷最大长度时,按FU-A分片的格式封装成单个RTP包的净荷。



本荷载类型允许分片一个NAL单元到几个RTP包中。下图 表示FU-A的RTP荷载格式。FU-A由1字节的分片单元指示,1字节的分片单元头,和分片单元荷载组成。

H.264 NAL 单元分段和重组_第1张图片

FU指示字节有以下格式:


FU指示字节的类型域的2829表示FU-AFU-BF的使用在53描述。NRI域的值必须根据分片NAL单元的NRI域的值设置。

FU头的格式如下:


S: 1 bit

当设置成1,开始位指示分片NAL单元的开始。当跟随的FU荷载不是分片NAL单元荷载的开始,开始位设为0

E: 1 bit

当设置成1, 结束位指示分片NAL单元的结束,即荷载的最后字节也是分片NAL单元的最后一个字节。当跟随的

FU荷载不是分片NAL单元的最后分片,结束位设置为0

R: 1 bit

保留位必须设置为0,接收者必须忽略该位。

Type: 5 bits

      NAL单元荷载类型定义在[1]的表7-1.


部分源代码:
void H264FUAFragmenter::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-A packet, with one extra preceding header byte.
		// 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-A packet, with two extra preceding header bytes.

		if (fMaxSize < fMaxOutputPacketSize) 
		{ // shouldn't happen
			envir() << "H264FUAFragmenter::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-A packets.  Deliver the first
				// packet now.  Note that we add FU indicator and FU header bytes to the front
				// of the packet (reusing the existing NAL header byte for the FU header).
				fInputBuffer[0] = (fInputBuffer[1] & 0xE0) | 28; // FU indicator
				fInputBuffer[1] = 0x80 | (fInputBuffer[1] & 0x1F); // 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-A packets.  We've already sent the
				// first packet (fragment).  Now, send the next fragment.  Note that we add
				// FU indicator 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.)
				fInputBuffer[fCurDataOffset-2] = fInputBuffer[0]; // FU indicator
				fInputBuffer[fCurDataOffset-1] = fInputBuffer[1]&~0x80; // FU header (no S bit)
				unsigned numBytesToSend = 2 + 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-2], numBytesToSend);
			  fFrameSize = numBytesToSend;
			  fCurDataOffset += numBytesToSend - 2;
		 }

			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);
	}
}



参考:
[1] live555 源码文件, H264FUAFragmenter.cpp、 H264FUAFragmenter.h等
[2]百度空间:http://apps.hi.baidu.com/share/detail/1111592

你可能感兴趣的:(header,百度,buffer,byte,h.264,Pointers)