您的位置:首页 > 其它

分片的NAL单元(FU-A分片)

2017-03-07 11:57 459 查看
  下面贴出安卓N版本AAVCAssembler::addFragmentedNALUnit原文

  

ARTPAssembler::AssemblyStatus AAVCAssembler::addFragmentedNALUnit(
List<sp<ABuffer> > *queue) {
CHECK(!queue->empty());

//queue里存放的第一个buffer表示的是NAL单元第一个分片,后续的buffer是NAL单元后续的分片
sp<ABuffer> buffer = *queue->begin();
const uint8_t *data = buffer->data();
size_t size = buffer->size();

CHECK(size > 0);
//data[0]指示这个buffer是NAL单元分片,由indicator & 0x1f,也即由
//indicator & 00011111计算得到的值,该值必为28
//data[0]也即第一个字节称为FU indicator
unsigned indicator = data[0];
CHECK((indicator & 0x1f) == 28);

if (size < 2) {
//一个NAL单元分片最小的长度是2个字节,第一个字节data[0]是FU indicator
//第二个字节data[1]是FU header
//如果小于最小的长度2(以字节为单位),则将分片单元从queue里删除,并将处理的序列号自增1
//即将mNextExpectedSeqNo的值自增1
ALOGV("Ignoring malformed FU buffer (size = %zu)", size);

queue->erase(queue->begin());
++mNextExpectedSeqNo;
return MALFORMED_PACKET;
}

//data[1]是FU header,结构如下:
//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单元在RTP包的荷载类型 

if (!(data[1] & 0x80)) {
//计算开始位:
//data[1] & 0x80 即 data[1] & 1000 0000
//queue里的第一个buffer必须是NAL分片单元的开始,否则进行错误处理
//错误处理的流程是将该buffer表示的NAL分片单元从queue里删除,然后将处理的序列号mNextExpectedSeqNo自增1,返回错误码MALFORMED_PACKET
// Start bit not set on the first buffer.

ALOGV("Start bit not set on first buffer");

queue->erase(queue->begin());
++mNextExpectedSeqNo;
return MALFORMED_PACKET;
}

//代码执行到这里说明跳过了前面的if语句块,也就是该buffer表示的NAL分片单元是对单个NAL单元进行分片后的第一个分片
//即data[1] & 0x80 的值为1
//然后计算该分片单元所属的单个NAL单元在RTP包的荷载类型nalType = data[1] & 0x1f 也即 nalType = data[1] & 0001 1111
uint32_t nalType = data[1] & 0x1f;
uint32_t nri = (data[0] >> 5) & 3;

//计算下一个需要被处理的序列号expectedSeqNo,为后续的循环处理将这些分片单元组成一个完整的NAL单元做好准备,因为在前面已经判定了queue里的第一个buffer表示的分片是NAL单元分片的开始
//计算得到要被处理的分片单元的范围totalSize,也即去掉了该buffer的前两个字节,分别是FU indicator和FU header
//totalCount表示当前处理得到的分片数,目前已经得到了分片的开始,所以totalCount初始值为1,每当处理一个分片,该值自增1
//布尔变量complete表示将所有的分片组合成一个完整的NAL单元是否完成,完成的标志是最后一个分片被处理,这个时候布尔变量complete设置为true,初始值设置为false
uint32_t expectedSeqNo = (uint32_t)buffer->int32Data() + 1;
size_t totalSize = size - 2;
size_t totalCount = 1;
bool complete = false;

//从当前该buffer第二字节data[1]也即FU header里计算得到结束位标志
//data[1] & 0x40 即 data[1] & 0100 0000
if (data[1] & 0x40) {
//目前正在处理的buffer已经成功判定开始位为1了,现在结束位也为1

4000
//即是开始也是结束,说明并没有进行分片,这就是一个完整的NAL单元,将布尔变量complete设置位true
// Huh? End bit also set on the first buffer.

ALOGV("Grrr. This isn't fragmented at all.");

complete = true;
} else {
//进入到else分支说明不止有一个分片
//下面循环处理queue里所有可能的和开始分片属于同一个NAL单元的分片

//将迭代器it指向queue里第二个buffer,第一个buffer在前面的代码已经被处理过了
List<sp<ABuffer> >::iterator it = ++queue->begin();
while (it != queue->end()) {
//开始循环处理queue里所有的buffer
ALOGV("sequence length %zu", totalCount);

//得到当前处理queue里一个buffer的引用
const sp<ABuffer> &buffer = *it;

//得到该buffer的数据开始位的指针和数据的大小(以字节位单位)
const uint8_t *data = buffer->data();
size_t size = buffer->size();

//当前buufer的序列号由buffer->int32Data()计算得到
//该值必须等于当前需要被处理的序列号expectedSeqNo,否则返回错误码WRONG_SEQUENCE_NUMBER
if ((uint32_t)buffer->int32Data() != expectedSeqNo) {
ALOGV("sequence not complete, expected seqNo %d, got %d",
expectedSeqNo, (uint32_t)buffer->int32Data());

return WRONG_SEQUENCE_NUMBER;
}

if (size < 2
|| data[0] != indicator
|| (data[1] & 0x1f) != nalType
|| (data[1] & 0x80)) {
ALOGV("Ignoring malformed FU buffer.");

//同第一个buffer表示的分片单元一样,data[0]是FU indicator,data[1]是FU header 
//所有的同属一个NAL单元的分片的FU indicator必须相同
//所有的同属一个NAL单元的分片的在RTP包的荷载类型也必须形同,由data[1] & 0x1f计算得到
//除了开始分片的开始位(由data[1] & 0x80计算得到)可以设置为1外,其他任何分片的开始位都不能设置为1
//如果上述条件任何一个条件不满足则整个queue里的buffer全部删除,然后将需要被处理的下一个序列号的值mNextExpectedSeqNo设置为当前处理的序列号expectedSeqNo加1,并返回错误码MALFORMED_PACKET
// Delete the whole start of the FU.

it = queue->begin();
for (size_t i = 0; i <= totalCount; ++i) {
it = queue->erase(it);
}

mNextExpectedSeqNo = expectedSeqNo + 1;

return MALFORMED_PACKET;
}

//将当前分片大小(去掉前两个字节FU indicator和FU header)加到totalSize里
//将totalCount的值自增1,表示又处理了一个分片
//将需要处理的下一个序列号expectedSeqNo自增1
totalSize += size - 2;
++totalCount;
expectedSeqNo = expectedSeqNo + 1;

if (data[1] & 0x40) {
//判断该分片的结束位data[1] & 0x40 即 data[1] & 0100 0000
//如果结束位被设置为1,说明是最后一个分片了。将结束标志complete设置为true,并用break跳出循环,不再处理queue里后续的buffer
// This is the last fragment.
complete = true;
break;
}

//迭代it指针,处理queue里下一个buffer
++it;
}
}

if (!complete) {
//循环处理完queue里buffer后,结束标志complete如果仍为false,说明没有找到结束分片,返回错误码NOT_ENOUGH_DATA
return NOT_ENOUGH_DATA;
}

//代码执行到这里说明结束标识complete的值为true,整个收集所有同属一个NAL单元的工作已经完成
//将expectedSeqNo表示的需要处理的下一个序列号的值赋值给mNextExpectedSeqNo
mNextExpectedSeqNo = expectedSeqNo;

// We found all the fragments that make up the complete NAL unit.

// Leave room for the header. So far totalSize did not include the
// header byte.
//下面需要将这些分片组合成一个完整的NAL单元,将totalSize加1,为header留一个字节
++totalSize;

//创建一个buffer来容纳这些同属一个NAL单元的分片,并将时间戳等信息添加到该buffer里
sp<ABuffer> unit = new ABuffer(totalSize);
CopyTimes(unit, *queue->begin());

//为该NAL单元构造头部,采用了以下技巧,
unit->data()[0] = (nri << 5) | nalType;

size_t offset = 1;
List<sp<ABuffer> >::iterator it = queue->begin();
for (size_t i = 0; i < totalCount; ++i) {
//将queue里totalCount个同属一个NAL单元的分片的数据部分拷贝到unit所指向的buffer里
const sp<ABuffer> &buffer = *it;

ALOGV("piece #%zu/%zu", i + 1, totalCount);
#if !LOG_NDEBUG
hexdump(buffer->data(), buffer->size());
#endif

memcpy(unit->data() + offset, buffer->data() + 2, buffer->size() - 2);
offset += buffer->size() - 2;

it = queue->erase(it);
}

//设置unit指向的buffer的数据范围
unit->setRange(0, totalSize);

//调用addSingleNALUnit对该单个NAL单元进行处理
addSingleNALUnit(unit);

ALOGV("successfully assembled a NAL unit from fragments.");

return OK;
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  NAL 分片单元 FU-A