OpenRTMFP/Cumulus Primer(18)AMF解析之AMFReader(续2)
- Author: 柳大·Poechant(钟超)
- Email: zhongchao.ustc#gmail.com (#->@)
- Blog:Blog.CSDN.net/Poechant
- Date: April 24th, 2012
1 开始引用与结束引用
如下这两个函数会在 FlowConnection 中调用。
inline void AMFReader::startReferencing() {
_referencing = true;
}
inline void AMFReader::stopReferencing() {
_referencing = false;
}
2 解析 AS3 ByteArray
先回顾一下 AMF3 中的ByteArray 的数据格式:
注意到,首先要读取一个变长无符号 32 位整数,但是最低位是 1,只有 28 位用于表示数据长度。解释完这里,下面的解析过程才好理解。
BinaryReader& AMFReader::readByteArray(UInt32& size) {
惯例:
reset();
AMF::Type type = followingType();
Null 就返回 BinaryReaderNull。
if (type == AMF::Null) {
reader.next(1);
return BinaryReader::BinaryReaderNull;
}
如果不是 ByteArray,也返回 BinaryReaderNull:
if (type != AMF::ByteArray) {
ERROR("Type %.2x is not a AMF ByteArray type",type);
return BinaryReader::BinaryReaderNull;
}
跳过这个字节:
reader.next(1);
注意 position 返回的是相对位置,与 AS3 中一样。reference 表示这个地址(简单说,引用就是地址嘛)。
UInt32 reference = reader.position();
读取一个变长 32 位无符号整数:
size = reader.read7BitValue();
最低位是 1 的话,isInline 是 true,否则为 false。
bool isInline = size & 0x01;
右移一位,因为那一位是标志位,上面解释过了。
size >>= 1;
如果 isInline 是 true,表示是 ByteArray:
if (isInline) {
如果 _referencing 为 true 的话(这是一个 vector),push back this reference:
if (_referencing)
_references.push_back(reference);
}
不符合 ByteArray 的格式定义的话:
else {
if (size > _references.size()) {
ERROR("AMF3 reference not found")
return BinaryReader::BinaryReaderNull;
}
_reset = reader.position();
移动到这个 reference 的位置,_references[size] 就是这个位置(相对)。
reader.reset(_references[size]); // TODO size 作为索引,还没有完全理解
读取这个 reference 的 size 值给 size对象(注意 size 是这个函数传入的引用参数,其值可以被修改)。
size = reader.read7BitValue() >> 1;
}
把读取完 ByteArraty 的 PacketReader 返回:
return reader;
}
最后强调一点,ByteArray 的数据段最大长度为 228-1 字节,约为 256 MB。
2 解析 AS3 Date
先看下 Date 的数据格式:
下面开始分析:
Timestamp AMFReader::readDate() {
惯例:
reset();
AMF::Type type = followingType();
Null 的话,就返回当前时间:
if (type == AMF::Null) {
reader.next(1);
return Timestamp(0);
}
如果不是 Date 类型,也返回当前时间:
if (type != AMF::Date) {
ERROR("Type %.2x is not a AMF Date type",type);
return Timestamp(0);
}
reader.next(1);
double result = 0;
如果是 AMF3:
if(_amf3) {
先读取 flag,最低一位必须是 1,其他位丢到垃圾桶。
UInt32 flags = reader.read7BitValue();
当前相对位置。
UInt32 reference = reader.position();
是 1 就 push back 到 _references 里。
bool isInline = flags & 0x01;
if (isInline) {
if(_referencing)
_references.push_back(reference);
读取一个 double,到 result 里(result 也是 double 类型哦~)。
reader >> result;
}
如果标志位不是 1,麻烦不少哒。。。
else {
flags >>= 1;
如果 flag 超了,就返回当前时间作为时间戳作为 Date。
if (flags > _references.size()) {
ERROR("AMF3 reference not found")
return Timestamp(0);
}
这段与 ByteArray 那段一样:
_reset = reader.position();
reader.reset(_references[flags]);
reader >> result;
reset();
}
返回喽~
return Timestamp((Timestamp::TimeVal) result * 1000);
}
reader >> result;
读俩,因为是 double(64 位):
reader.next(2); // Timezone, useless
返回喽~
return Timestamp((Timestamp::TimeVal) result * 1000);
}
3 解析 AS3 Dictionary
bool AMFReader::readDictionary(bool& weakKeys) {
下面这段咱就略了。。
reset();
AMF::Type type = followingType();
if (type == AMF::Null) {
reader.next(1);
return false;
}
if (type != AMF::Dictionary) {
ERROR("Type %.2x is not a AMF Dictionary type",type);
return false;
}
跳过 type:
// AMF3
reader.next(1); // marker
当前相对位置值作为 reference,再读个 size,还是最低位必须为 1,不是就返回 false。
UInt32 reference = reader.position();
UInt32 size = reader.read7BitValue();
bool isInline = size & 0x01;
size >>= 1;
if(!isInline && size>_references.size()) {
ERROR("AMF3 reference not found")
return false;
}
下面要调用到 ObjectRef 构造函数,这里再把其实现拿出来看看,其实主要是初始化了哪些成员。
ObjectDef(UInt32 amf3,UInt8 arrayType=0)
: amf3(amf3),
reset(0),
dynamic(false),
externalizable(false),
count(0),
arrayType(arrayType) {
}
可以看到要有一个 amf3,还有 reset 置为 0,dynamic 置为 false,externalizable 也是 false,count 是 0,arrayType 成员要赋值。
上面是插播哦,下面还要继续哒。创建这么一个对象,注意是 new 出来的,所以我们在《OpenRTMFP/Cumulus Primer(16)AMF解析之AMFReader》一文中提到了 AMFReader 的析构函数中要对 _objectRef 的每个元素逐一析构的。arrayType 就设置为 AMF3_DICTIONARY。
ObjectDef* pObjectDef = new ObjectDef(_amf3, AMF3_DICTIONARY);
pObjectDef->dynamic=true;
_objectDefs.push_back(pObjectDef);
如果标志位是 1,就直接 push back,跟之前一样。不过这里多了一个 pObjectDef,所以还要设置一下它的计数为 size,就是 dictionary 数据大小。
if (isInline) {
if (_referencing)
_references.push_back(reference);
pObjectDef->count = size;
}
如果标志位是 0,就把 count 设置为下一个变长整数值。
else {
pObjectDef->reset = reader.position();
reader.reset(_references[size]);
pObjectDef->count = reader.read7BitValue() >> 1;
}
pObjectDef->count *= 2;
读一个字节,如果最小位是 1,weakKeys 就是 true,否则为 false。
weakKeys = reader.read8() & 0x01;
return true;
}
-
转载请注明来自柳大的CSDN博客:Blog.CSDN.net/Poechant
-