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

鍾超發表於2012-04-24

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 的資料格式:

Resize icon

注意到,首先要讀取一個變長無符號 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 的資料格式:

Resize icon

下面開始分析:

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

-

相關文章