OpenRTMFP/Cumulus Primer(18)AMF解析之AMFReader(续2)

阅读更多

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

-

你可能感兴趣的:(OpenRTMFP/Cumulus Primer(18)AMF解析之AMFReader(续2))