librtmp源碼分析之AMF格式

AMF是Adobe公司開發(fā)的一種高效的消息序列化和反序列化協(xié)議声畏,它包括兩種數(shù)據(jù)類型格式:AMF0和AMF3庶诡。其中AMF0是基本的消息格式拜秧,但是后來Adobe對(duì)AMF0進(jìn)行了優(yōu)化,開發(fā)了擴(kuò)展的AMF3尸疆,即AMF3并不是AMF0的完全替代臼膏,使用AMF3需要在外面套一層AMF0做為容器硼被。我們可以在librtmp的源碼中找到AMF的一個(gè)實(shí)現(xiàn)版本,但是我不清楚什么原因渗磅,居然有些函數(shù)如AMFProp_SetNumber()至今都沒有實(shí)現(xiàn)祷嘶。

”水滿則溢,月滿則虧”夺溢,librtmp雖然有些瑕疵,但是仍然不影響它是那么的優(yōu)秀烛谊,如果在朋友們的自研項(xiàng)目或者工作中有機(jī)會(huì)用到它风响,我還是非常推薦的。

結(jié)構(gòu)定義:

AMF0支持的類型定義:

typedef enum
{ 
    AMF_NUMBER = 0,  // 數(shù)字丹禀,用8字節(jié)double表示
    AMF_BOOLEAN,  // 布爾數(shù)状勤,用1字節(jié)整數(shù)表示
    AMF_STRING,  // 字符串,用LV范式表示
    AMF_OBJECT,  // 對(duì)象
    AMF_MOVIECLIP,  // 保留双泪,未使用
    AMF_NULL,  // 空類型
    AMF_UNDEFINED,  // 未定義
    AMF_REFERENCE,  // 引用
    AMF_ECMA_ARRAY,  // 數(shù)組
    AMF_OBJECT_END,  // 對(duì)象結(jié)束
    AMF_STRICT_ARRAY,  // 嚴(yán)格數(shù)組
    AMF_DATE,  // 日期
    AMF_LONG_STRING,  // 長字符串
    AMF_UNSUPPORTED,  // 未支持
    AMF_RECORDSET,  // 保留持搜,未使用
    AMF_XML_DOC,  // XML
    AMF_TYPED_OBJECT,  // 有類型的對(duì)象
    AMF_AVMPLUS,   // 需要擴(kuò)展到AMF3
    AMF_INVALID = 0xff  // 無效類型
} AMFDataType;

AMF3支持的類型定義:

typedef enum
{
    AMF3_UNDEFINED = 0,  // 未定義
    AMF3_NULL,  // 空類型
    AMF3_FALSE,  // FALSE
    AMF3_TRUE,  // TRUE
    AMF3_INTEGER,  // 整型
    AMF3_DOUBLE,  // 浮點(diǎn)型
    AMF3_STRING,  // 字符串
    AMF3_XML_DOC,  // XML文檔
    AMF3_DATE,  // 日期
    AMF3_ARRAY,  // 數(shù)組
    AMF3_OBJECT,  // 對(duì)象
    AMF3_XML,  // XML
    AMF3_BYTE_ARRAY  // 字節(jié)數(shù)組
} AMF3DataType;

字符串定義:

typedef struct AVal
{
    char *av_val;
    int av_len;
} AVal;

// 初使化串
#define AVC(str)        {str,sizeof(str)-1}
// 比較串
#define AVMATCH(a1,a2)  ((a1)->av_len == (a2)->av_len && !memcmp((a1)->av_val,(a2)->av_val,(a1)->av_len))

對(duì)象屬性定義:

typedef struct AMFObjectProperty
{
    AVal p_name;
    AMFDataType p_type;
    union
    {
      double p_number;
      AVal p_aval;
      AMFObject p_object;
    } p_vu;
    int16_t p_UTCoffset;
} AMFObjectProperty;

對(duì)象定義:

typedef struct AMFObject
{
    int o_num;
    struct AMFObjectProperty *o_props;
} AMFObject;

函數(shù)定義:

整數(shù)操作:

注意:由于整數(shù)不屬于AMF的基本類型,序列化時(shí)不會(huì)壓入類型字節(jié)焙矛。

// 16位整數(shù)序列化
char *AMF_EncodeInt16(char *output, char *outend, short nVal);
// 24位整數(shù)序列化
char *AMF_EncodeInt24(char *output, char *outend, int nVal);
// 32位整數(shù)序列化
char *AMF_EncodeInt32(char *output, char *outend, int nVal);

// 16位整數(shù)反序列化
unsigned short AMF_DecodeInt16(const char *data);
// 24位整數(shù)反序列化
unsigned int AMF_DecodeInt24(const char *data);
// 32位整數(shù)反序列化
unsigned int AMF_DecodeInt32(const char *data);
基本類型操作:

注意:基本類型序列化時(shí)會(huì)壓入類型字節(jié)葫盼,但反序列化時(shí)需跳過類型字節(jié)。

// 浮點(diǎn)數(shù)序列化
char *AMF_EncodeNumber(char *output, char *outend, double dVal);
// Bool型序列化
char *AMF_EncodeBoolean(char *output, char *outend, int bVal);
// 字符串序列化村斟,自動(dòng)支持短串和長串贫导,即當(dāng)串長度>=65536時(shí)使用長串
char *AMF_EncodeString(char *output, char *outend, const AVal * str);

// 浮點(diǎn)數(shù)反序列化
double AMF_DecodeNumber(const char *data);
// Bool型反序列化
int AMF_DecodeBoolean(const char *data);
// 短串反序列化
void AMF_DecodeString(const char *data, AVal * str);
// 長串反序列化
void AMF_DecodeLongString(const char *data, AVal * str);
命名的基本類型操作:

注意:命名的基本類型是復(fù)合操作,序列化時(shí)首先壓入不帶類型的字符串蟆盹,然后壓入帶類型的基本類型孩灯。

// 串值KV序列化,用于屬性或數(shù)組元素
char *AMF_EncodeNamedString(char *output, char *outend, const AVal * name, const AVal * value);
// 浮點(diǎn)數(shù)KV序列化逾滥,用于屬性或數(shù)組元素
char *AMF_EncodeNamedNumber(char *output, char *outend, const AVal * name, double dVal);
// Bool型KV序列化峰档,用于屬性或數(shù)組元素
char *AMF_EncodeNamedBoolean(char *output, char *outend, const AVal * name, int bVal);
對(duì)象操作:
// 打印對(duì)象信息
void AMF_Dump(AMFObject * obj);

// 添加新屬性到對(duì)象中,若容量不夠,可能會(huì)申請(qǐng)空間
void AMF_AddProp(AMFObject * obj, const AMFObjectProperty * prop);
// 獲取對(duì)象的屬性個(gè)數(shù)
int AMF_CountProp(AMFObject * obj);

// 對(duì)象序列化
char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd);
// 對(duì)象反序列化讥巡,bDecodeName為FALSE表示不解析屬性名稱
int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize, int bDecodeName);

// 重置對(duì)象掀亩,釋放屬性空間
void AMF_Reset(AMFObject * obj);
對(duì)象屬性操作:
// 打印對(duì)象屬性
void AMFProp_Dump(AMFObjectProperty * prop);

// 對(duì)象屬性序列化
char *AMFProp_Encode(AMFObjectProperty * prop, char *pBuffer, char *pBufEnd);
// 對(duì)象屬性反序列化,bDecodeName為FALSE表示不解析屬性名稱
int AMFProp_Decode(AMFObjectProperty * prop, const char *pBuffer, int nSize, int bDecodeName);

// 重置對(duì)象屬性
void AMFProp_Reset(AMFObjectProperty * prop);
對(duì)象屬性訪問:
// 根據(jù)索引位置或Key名稱找屬性尚卫,當(dāng)nIndex>=0時(shí)归榕,表示按索引查找,否則按名稱查找
AMFObjectProperty *AMF_GetProp(AMFObject * obj, const AVal * name, int nIndex);

// 檢查是否無效屬性
int AMFProp_IsValid(AMFObjectProperty * prop);

// 設(shè)置屬性名稱
void AMFProp_SetName(AMFObjectProperty * prop, AVal * name);
// 獲取屬性名稱
void AMFProp_GetName(AMFObjectProperty * prop, AVal * name);

// 獲取屬性的值類型
AMFDataType AMFProp_GetType(AMFObjectProperty * prop);

// 獲取浮點(diǎn)數(shù)屬性值
double AMFProp_GetNumber(AMFObjectProperty * prop);
// 獲取Bool型屬性值
int AMFProp_GetBoolean(AMFObjectProperty * prop);
// 獲取字符串屬性值
void AMFProp_GetString(AMFObjectProperty * prop, AVal * str);
// 獲取對(duì)象屬性值
void AMFProp_GetObject(AMFObjectProperty * prop, AMFObject * obj);
數(shù)組操作:

注意:librtmp的實(shí)現(xiàn)中沒有數(shù)組的序列化操作接口吱涉,我查閱了源代碼刹泄,發(fā)現(xiàn)AMF_DecodeArray()是可以反序列化對(duì)象的,但是首先必須先獲得數(shù)組元素的個(gè)數(shù)怎爵。

// 數(shù)組反序列化
int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize, int nArrayLen, int bDecodeName);

源碼解析:

為了加深對(duì)實(shí)現(xiàn)的理解特石,下面我摘錄一部分源碼,代碼中加了一些注冊(cè)鳖链,與諸位一起分享姆蘸。

整數(shù)的序列化與反序列化:

char *
AMF_EncodeInt32(char *output, char *outend, int nVal)
{
  // 預(yù)判長度溢出
  if (output+4 > outend)
    return NULL;

  // 小端轉(zhuǎn)大端
  output[3] = nVal & 0xff;
  output[2] = nVal >> 8;
  output[1] = nVal >> 16;
  output[0] = nVal >> 24;
  return output+4;
}

unsigned int
AMF_DecodeInt32(const char *data)
{
  unsigned char *c = (unsigned char *)data;
  unsigned int val;
  // 大端轉(zhuǎn)小端
  val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
  return val;
}

浮點(diǎn)數(shù)的序列化與反序列化:

char *
AMF_EncodeNumber(char *output, char *outend, double dVal)
{
  // 預(yù)判長度溢出,需要1字節(jié)類型+8字節(jié)浮點(diǎn)
  if (output+1+8 > outend)
    return NULL;

  // 壓入類型
  *output++ = AMF_NUMBER;       /* type: Number */

  // 小端轉(zhuǎn)大端
  {
    unsigned char *ci, *co;
    ci = (unsigned char *)&dVal;
    co = (unsigned char *)output;
    co[0] = ci[7];
    co[1] = ci[6];
    co[2] = ci[5];
    co[3] = ci[4];
    co[4] = ci[3];
    co[5] = ci[2];
    co[6] = ci[1];
    co[7] = ci[0];
  }

  return output+8;
}

double
AMF_DecodeNumber(const char *data)
{
  double dVal;
  unsigned char *ci, *co; 
  ci = (unsigned char *)data;
  co = (unsigned char *)&dVal;
  // 大端轉(zhuǎn)小端
  co[0] = ci[7];
  co[1] = ci[6];
  co[2] = ci[5];
  co[3] = ci[4];
  co[4] = ci[3];
  co[5] = ci[2];
  co[6] = ci[1];
  co[7] = ci[0];
  return dVal;
}

Bool型的序列化與反序列化:

char *
AMF_EncodeBoolean(char *output, char *outend, int bVal)
{
  // 預(yù)判長度溢出芙委,需要1字節(jié)類型+1字節(jié)Bool
  if (output+2 > outend)
    return NULL;

  // 壓入類型
  *output++ = AMF_BOOLEAN;

  *output++ = bVal ? 0x01 : 0x00;

  return output;
}

int
AMF_DecodeBoolean(const char *data)
{
  return *data != 0;
}

字符串的序列化與反序列化:

char *
AMF_EncodeString(char *output, char *outend, const AVal *bv)
{
  // 預(yù)判長度溢出逞敷,短串需要1字節(jié)類型+2字節(jié)短長度+字符串長度,或長串需要1字節(jié)類型+4字節(jié)長長度+字符串長度
  if ((bv->av_len < 65536 && output + 1 + 2 + bv->av_len > outend) ||
        output + 1 + 4 + bv->av_len > outend)
    return NULL;

  if (bv->av_len < 65536)
    {
      // 壓入類型
      *output++ = AMF_STRING;

      // 壓入2字節(jié)長度
      output = AMF_EncodeInt16(output, outend, bv->av_len);
    }
  else
    {
      // 壓入類型
      *output++ = AMF_LONG_STRING;

      // 壓入4字節(jié)長度
      output = AMF_EncodeInt32(output, outend, bv->av_len);
    }

  // 拷貝bv指向的字符中到序列化緩沖
  memcpy(output, bv->av_val, bv->av_len);
  output += bv->av_len;

  return output;
}

void
AMF_DecodeString(const char *data, AVal *bv)
{
  // 解2字節(jié)大小
  bv->av_len = AMF_DecodeInt16(data);
  // 設(shè)置bv串指向
  bv->av_val = (bv->av_len > 0) ? (char *)data + 2 : NULL;
}

void
AMF_DecodeLongString(const char *data, AVal *bv)
{
  // 解4字節(jié)大小
  bv->av_len = AMF_DecodeInt32(data);
  // 設(shè)置bv串指向
  bv->av_val = (bv->av_len > 0) ? (char *)data + 4 : NULL;
}

命名的基本類型的序列化與反序列化:

char *
AMF_EncodeNamedString(char *output, char *outend, const AVal *strName, const AVal *strValue)
{
  // 預(yù)判長度溢出灌侣,需要2字節(jié)串長度+命名串長度
  if (output+2+strName->av_len > outend)
    return NULL;

  // 壓入2字節(jié)長度
  output = AMF_EncodeInt16(output, outend, strName->av_len);

  // 拷貝串
  memcpy(output, strName->av_val, strName->av_len);
  output += strName->av_len;

  // 繼續(xù)壓入基本串
  return AMF_EncodeString(output, outend, strValue);
}

char *
AMF_EncodeNamedNumber(char *output, char *outend, const AVal *strName, double dVal)
{
  // 預(yù)判長度溢出推捐,需要2字節(jié)串長度+命名串長度
  if (output+2+strName->av_len > outend)
    return NULL;

  // 壓入2字節(jié)長度
  output = AMF_EncodeInt16(output, outend, strName->av_len);

  // 拷貝串
  memcpy(output, strName->av_val, strName->av_len);
  output += strName->av_len;

  // 繼續(xù)壓入基本浮點(diǎn)數(shù)
  return AMF_EncodeNumber(output, outend, dVal);
}

char *
AMF_EncodeNamedBoolean(char *output, char *outend, const AVal *strName, int bVal)
{
  // 預(yù)判長度溢出,需要2字節(jié)串長度+命名串長度
  if (output+2+strName->av_len > outend)
    return NULL;

  // 壓入2字節(jié)長度
  output = AMF_EncodeInt16(output, outend, strName->av_len);

  // 拷貝串
  memcpy(output, strName->av_val, strName->av_len);
  output += strName->av_len;

  // 繼續(xù)壓入基本Bool
  return AMF_EncodeBoolean(output, outend, bVal);
}

屬性的操作侧啼,這些都屬于普通的結(jié)構(gòu)體操作:

void
AMFProp_GetName(AMFObjectProperty *prop, AVal *name)
{
  *name = prop->p_name;
}

void
AMFProp_SetName(AMFObjectProperty *prop, AVal *name)
{
  prop->p_name = *name;
}

AMFDataType
AMFProp_GetType(AMFObjectProperty *prop)
{
  return prop->p_type;
}

double
AMFProp_GetNumber(AMFObjectProperty *prop)
{
  return prop->p_vu.p_number;
}

int
AMFProp_GetBoolean(AMFObjectProperty *prop)
{
  return prop->p_vu.p_number != 0;
}

void
AMFProp_GetString(AMFObjectProperty *prop, AVal *str)
{
  *str = prop->p_vu.p_aval;
}

void
AMFProp_GetObject(AMFObjectProperty *prop, AMFObject *obj)
{
  *obj = prop->p_vu.p_object;
}

int
AMFProp_IsValid(AMFObjectProperty *prop)
{
  return prop->p_type != AMF_INVALID;
}

void
AMF_AddProp(AMFObject *obj, const AMFObjectProperty *prop)
{
  if (!(obj->o_num & 0x0f))
    obj->o_props =
      realloc(obj->o_props, (obj->o_num + 16) * sizeof(AMFObjectProperty));
  obj->o_props[obj->o_num++] = *prop;
}

int
AMF_CountProp(AMFObject *obj)
{
  return obj->o_num;
}

AMFObjectProperty *
AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex)
{
  // 索引有效時(shí)牛柒,按索引取屬性
  if (nIndex >= 0)
    {
      if (nIndex <= obj->o_num)
        return &obj->o_props[nIndex];
    }
  // 否則按名稱取屬性
  else
    {
      int n;
      for (n = 0; n < obj->o_num; n++)
        {
          if (AVMATCH(&obj->o_props[n].p_name, name))
            return &obj->o_props[n];
        }
    }

  return (AMFObjectProperty *)&AMFProp_Invalid;
}

屬性的序列化與反序列化:

char *
AMFProp_Encode(AMFObjectProperty *prop, char *pBuffer, char *pBufEnd)
{
  // 無效屬性不處理
  if (prop->p_type == AMF_INVALID)
    return NULL;

  // 預(yù)判長度溢出,需要2字節(jié)串長度+屬性名稱長度+1字節(jié)類型
  if (prop->p_type != AMF_NULL && pBuffer + prop->p_name.av_len + 2 + 1 >= pBufEnd)
    return NULL;

  // 屬性名稱非空痊乾,壓入屬性名稱
  if (prop->p_type != AMF_NULL && prop->p_name.av_len)
    {
      *pBuffer++ = prop->p_name.av_len >> 8;
      *pBuffer++ = prop->p_name.av_len & 0xff;
      memcpy(pBuffer, prop->p_name.av_val, prop->p_name.av_len);
      pBuffer += prop->p_name.av_len;
    }

  // 繼續(xù)壓入實(shí)際基本類型
  switch (prop->p_type)
    {
    case AMF_NUMBER:
      pBuffer = AMF_EncodeNumber(pBuffer, pBufEnd, prop->p_vu.p_number);
      break;

    case AMF_BOOLEAN:
      pBuffer = AMF_EncodeBoolean(pBuffer, pBufEnd, prop->p_vu.p_number != 0);
      break;

    case AMF_STRING:
      pBuffer = AMF_EncodeString(pBuffer, pBufEnd, &prop->p_vu.p_aval);
      break;

    // 壓入NULL
    case AMF_NULL:
      if (pBuffer+1 >= pBufEnd)
        return NULL;
      *pBuffer++ = AMF_NULL;
      break;

    // 壓入子對(duì)象
    case AMF_OBJECT:
      pBuffer = AMF_Encode(&prop->p_vu.p_object, pBuffer, pBufEnd);
      break;

    default:
      RTMP_Log(RTMP_LOGERROR, "%s, invalid type. %d", __FUNCTION__, prop->p_type);
      pBuffer = NULL;
    };

  return pBuffer;
}

int
AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
               int bDecodeName)
{
  int nOriginalSize = nSize;
  int nRes;

  prop->p_name.av_len = 0;
  prop->p_name.av_val = NULL;

  if (nSize == 0 || !pBuffer)
    {
      RTMP_Log(RTMP_LOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__);
      return -1;
    }

  // 包含屬性名稱時(shí)皮壁,最小長度為2字節(jié)屬性名稱長度指示+至少1字節(jié)屬性+至少1字節(jié)值的類型
  if (bDecodeName && nSize < 4)
    {                           /* at least name (length + at least 1 byte) and 1 byte of data */
      RTMP_Log(RTMP_LOGDEBUG,
          "%s: Not enough data for decoding with name, less than 4 bytes!",
          __FUNCTION__);
      return -1;
    }

  // 解屬性名稱
  if (bDecodeName)
    {
      unsigned short nNameSize = AMF_DecodeInt16(pBuffer);
      if (nNameSize > nSize - 2)
        {
          RTMP_Log(RTMP_LOGDEBUG,
              "%s: Name size out of range: namesize (%d) > len (%d) - 2",
              __FUNCTION__, nNameSize, nSize);
          return -1;
        }

      AMF_DecodeString(pBuffer, &prop->p_name);
      nSize -= 2 + nNameSize;
      pBuffer += 2 + nNameSize;
    }

  if (nSize == 0)
    {
      return -1;
    }

  nSize--;

  // 解屬性值
  prop->p_type = *pBuffer++;
  switch (prop->p_type)
    {
    case AMF_NUMBER:
      if (nSize < 8)
        return -1;
      prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
      nSize -= 8;
      break;
    case AMF_BOOLEAN:
      if (nSize < 1)
        return -1;
      prop->p_vu.p_number = (double)AMF_DecodeBoolean(pBuffer);
      nSize--;
      break;
    case AMF_STRING:
      {
        unsigned short nStringSize = AMF_DecodeInt16(pBuffer);

        if (nSize < (long)nStringSize + 2)
          return -1;
        AMF_DecodeString(pBuffer, &prop->p_vu.p_aval);
        nSize -= (2 + nStringSize);
        break;
      }
    case AMF_OBJECT:
      {
        int nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
        if (nRes == -1)
          return -1;
        nSize -= nRes;
        break;
      }
    case AMF_MOVIECLIP:
      {
        RTMP_Log(RTMP_LOGERROR, "AMF_MOVIECLIP reserved!");
        return -1;
        break;
      }
    case AMF_NULL:
    case AMF_UNDEFINED:
    case AMF_UNSUPPORTED:
      prop->p_type = AMF_NULL;
      break;
    case AMF_REFERENCE:
      {
        RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!");
        return -1;
        break;
      }
    case AMF_ECMA_ARRAY:
      {
        nSize -= 4;

        // 如果是數(shù)組,按對(duì)象方式反序列化
        /* next comes the rest, mixed array has a final 0x000009 mark and names, so its an object */
        nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer + 4, nSize, TRUE);
        if (nRes == -1)
          return -1;
        nSize -= nRes;
        prop->p_type = AMF_OBJECT;
        break;
      }
    case AMF_OBJECT_END:
      {
        return -1;
        break;
      }
    case AMF_STRICT_ARRAY:
      {
        unsigned int nArrayLen = AMF_DecodeInt32(pBuffer);
        nSize -= 4;

        nRes = AMF_DecodeArray(&prop->p_vu.p_object, pBuffer + 4, nSize,
                                   nArrayLen, FALSE);
        if (nRes == -1)
          return -1;
        nSize -= nRes;
        prop->p_type = AMF_OBJECT;
        break;
      }
    case AMF_DATE:
      {
        RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE");

        if (nSize < 10)
          return -1;

        prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
        prop->p_UTCoffset = AMF_DecodeInt16(pBuffer + 8);

        nSize -= 10;
        break;
      }
    case AMF_LONG_STRING:
      {
        unsigned int nStringSize = AMF_DecodeInt32(pBuffer);
        if (nSize < (long)nStringSize + 4)
          return -1;
        AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval);
        nSize -= (4 + nStringSize);
        prop->p_type = AMF_STRING;
        break;
      }
    case AMF_RECORDSET:
      {
        RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!");
        return -1;
        break;
      }
    case AMF_XML_DOC:
      {
        RTMP_Log(RTMP_LOGERROR, "AMF_XML_DOC not supported!");
        return -1;
        break;
      }
    case AMF_TYPED_OBJECT:
      {
        RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!");
        return -1;
        break;
      }
    case AMF_AVMPLUS:
      {
        int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
        if (nRes == -1)
          return -1;
        nSize -= nRes;
        prop->p_type = AMF_OBJECT;
        break;
      }
    default:
      RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @0x%08X", __FUNCTION__,
          prop->p_type, pBuffer - 1);
      return -1;
    }

  return nOriginalSize - nSize;
}

對(duì)象的序列化與反序列化:

char *
AMF_Encode(AMFObject *obj, char *pBuffer, char *pBufEnd)
{
  int i;

  // 預(yù)判長度溢出哪审,需要5個(gè)字節(jié)
  if (pBuffer+4 >= pBufEnd)
    return NULL;

  // 壓入類型
  *pBuffer++ = AMF_OBJECT;

  // 遍歷屬性并壓入
  for (i = 0; i < obj->o_num; i++)
    {
      char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
      if (res == NULL)
        {
          RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
              i);
          break;
        }
      else
        {
          pBuffer = res;
        }
    }

  if (pBuffer + 3 >= pBufEnd)
    return NULL;                        /* no room for the end marker */

  // 壓入對(duì)象結(jié)束標(biāo)記
  pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);

  return pBuffer;
}

int
AMF_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bDecodeName)
{
  int nOriginalSize = nSize;
  int bError = FALSE;           /* if there is an error while decoding - try to at least find the end mark AMF_OBJECT_END */

  obj->o_num = 0;
  obj->o_props = NULL;
  while (nSize > 0)
    {
      AMFObjectProperty prop;
      int nRes;

      // 判斷結(jié)束標(biāo)志
      if (nSize >=3 && AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END)
        {
          nSize -= 3;
          bError = FALSE;
          break;
        }

      if (bError)
        {
          RTMP_Log(RTMP_LOGERROR,
              "DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!");
          nSize--;
          pBuffer++;
          continue;
        }

      // 解屬性
      nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
      if (nRes == -1)
        bError = TRUE;
      else
        {
          nSize -= nRes;
          pBuffer += nRes;
          AMF_AddProp(obj, &prop);
        }
    }

  if (bError)
    return -1;

  return nOriginalSize - nSize;
}

數(shù)組的反序列化:

int
AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize,
                int nArrayLen, int bDecodeName)
{
  int nOriginalSize = nSize;
  int bError = FALSE;

  obj->o_num = 0;
  obj->o_props = NULL;
  while (nArrayLen > 0)
    {
      AMFObjectProperty prop;
      int nRes;
      nArrayLen--;

      // 解屬性
      nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
      if (nRes == -1)
        bError = TRUE;
      else
        {
          nSize -= nRes;
          pBuffer += nRes;
          AMF_AddProp(obj, &prop);
        }
    }
  if (bError)
    return -1;

  return nOriginalSize - nSize;
}

編碼格式:

AMF的編碼基本遵循TLV范式蛾魄,但是為了進(jìn)一步節(jié)省空間,比如對(duì)象的屬性湿滓,在序列化屬性名稱的時(shí)候畏腕,即使屬性名是字符串,也并不壓入類型茉稠。我想這有兩個(gè)原因描馅,一是壓入類型會(huì)增加序列化的長度,二是開放了TLV而线,就等于支持屬性名稱可以是其它的類型铭污。這是一個(gè)駁論恋日,即然不能做到完美,那就做到安全嘹狞。當(dāng)然岂膳,類似這樣的約束加入,等于在算法中加了很多特殊條件磅网,對(duì)使用者來說也無法形成統(tǒng)一的規(guī)范思維谈截,這也是無耐之舉吧。

關(guān)于編碼格式涧偷,如果您讀懂了AMF的源碼實(shí)現(xiàn)簸喂,基本上也就明白了序列化后的數(shù)據(jù)排隊(duì)。百度上可以搜索到很多關(guān)于編碼格式的精彩說明和圖解燎潮,出于文檔的完整性喻鳄,所以我這里只簡單地描述幾個(gè)結(jié)構(gòu)。如下:

浮點(diǎn)數(shù):
0x00 + 8字節(jié)浮點(diǎn)數(shù)

Bool型:
0x01 + 1字節(jié)Bool值

短字符串:
0x02 + 2字節(jié)長度 + 字符串
長字符串
0x02 + 4字節(jié)長度 + 字符串

對(duì)象:
0x03 + 屬性1名稱長度 + 屬性1名稱 + 1字節(jié)屬性1類型 + n字節(jié)屬性1值 + 屬性2名稱長度 + 屬性2名稱 + 1字節(jié)屬性2類型 + n字節(jié)屬性2值 + 3字節(jié)結(jié)尾標(biāo)志

使用舉例:

#include <stdio.h>
#include <stdlib.h>
#include <stdint.h>

#include <librtmp/amf.h>
#include <librtmp/log.h>

int main(int argc, char* argv[])
{
    char sBuf[128] = {0};

    char* pStart = sBuf;
    char* pEnd = sBuf + sizeof(sBuf);

    // 整數(shù)操作:
    {
        char* pPos = AMF_EncodeInt16(pStart, pEnd, 1);
        pPos = AMF_EncodeInt24(pPos, pEnd, 2);
        pPos = AMF_EncodeInt32(pPos, pEnd, 3);

        int n1 = AMF_DecodeInt16(pStart);
        int n2 = AMF_DecodeInt24(pStart + 2);
        int n3 = AMF_DecodeInt32(pStart + 2 + 3);
        printf("n1=%d, n2=%d, n3=%d \n", n1, n2, n3);
    }

    // 基本類型操作:
    {
        char* pPos = AMF_EncodeNumber(pStart, pEnd, 1);
        pPos = AMF_EncodeBoolean(pPos, pEnd, TRUE);
        AVal str = AVC("hello");
        pPos = AMF_EncodeString(pPos, pEnd, &str);

        double f = AMF_DecodeNumber(pStart + 1);
        int b = AMF_DecodeBoolean(pStart + 1 + 8);
        AVal str2 = {NULL, 0};
        AMF_DecodeString(pStart + 1 + 8 + 1 + 1 + 1, &str2);
        printf("f:%.02f b:%d str:%s \n", f, b, str2.av_val);
    }

    // 對(duì)象操作:
    {
        AMFObject obj = {0, NULL};

        AMFObjectProperty prop1, prop2;
        AVal key1 = AVC("key1");
        AMFProp_SetName(&prop1, &key1);
        prop1.p_type = AMF_NUMBER;
        prop1.p_vu.p_number = 1;
        AVal key2 = AVC("key2");
        AMFProp_SetName(&prop2, &key2);
        prop2.p_type = AMF_STRING;
        prop2.p_vu.p_aval = AVC("test");
        AMF_AddProp(&obj, &prop1);
        AMF_AddProp(&obj, &prop2);

        char* pPos = AMF_Encode(&obj, pStart, pEnd);

        AMFObject obj2;
        AMF_Decode(&obj2, pStart + 1, pPos - pStart - 1, TRUE);

        RTMP_LogSetLevel(RTMP_LOGDEBUG);
        AMF_Dump(&obj2);

        AMF_Reset(&obj);
        AMF_Reset(&obj2);
    }

    return 0;
}

運(yùn)行輸出:

n1=1, n2=2, n3=3 
f:1.00 b:1 str:hello 
DEBUG: (object begin)
DEBUG: Property: <Name:               key1, NUMBER:     1.00>
DEBUG: Property: <Name:               key2, STRING:     test>
DEBUG: (object end)

最后展示下代碼中obj的二進(jìn)制內(nèi)容确封,我用線段分開了除呵,具體交給大家解讀吧。


image.png
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末爪喘,一起剝皮案震驚了整個(gè)濱河市颜曾,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌秉剑,老刑警劉巖泛豪,帶你破解...
    沈念sama閱讀 217,185評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異秃症,居然都是意外死亡,警方通過查閱死者的電腦和手機(jī)吕粹,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門种柑,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人匹耕,你說我怎么就攤上這事聚请。” “怎么了稳其?”我有些...
    開封第一講書人閱讀 163,524評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵驶赏,是天一觀的道長。 經(jīng)常有香客問我既鞠,道長煤傍,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 58,339評(píng)論 1 293
  • 正文 為了忘掉前任嘱蛋,我火速辦了婚禮蚯姆,結(jié)果婚禮上五续,老公的妹妹穿的比我還像新娘。我一直安慰自己龄恋,他們只是感情好疙驾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,387評(píng)論 6 391
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著郭毕,像睡著了一般它碎。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上显押,一...
    開封第一講書人閱讀 51,287評(píng)論 1 301
  • 那天扳肛,我揣著相機(jī)與錄音,去河邊找鬼煮落。 笑死敞峭,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的蝉仇。 我是一名探鬼主播旋讹,決...
    沈念sama閱讀 40,130評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼轿衔!你這毒婦竟也來了沉迹?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評(píng)論 0 275
  • 序言:老撾萬榮一對(duì)情侶失蹤害驹,失蹤者是張志新(化名)和其女友劉穎鞭呕,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體宛官,經(jīng)...
    沈念sama閱讀 45,420評(píng)論 1 313
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡葫松,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,617評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了底洗。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片腋么。...
    茶點(diǎn)故事閱讀 39,779評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖亥揖,靈堂內(nèi)的尸體忽然破棺而出珊擂,到底是詐尸還是另有隱情,我是刑警寧澤费变,帶...
    沈念sama閱讀 35,477評(píng)論 5 345
  • 正文 年R本政府宣布摧扇,位于F島的核電站,受9級(jí)特大地震影響挚歧,放射性物質(zhì)發(fā)生泄漏扛稽。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,088評(píng)論 3 328
  • 文/蒙蒙 一滑负、第九天 我趴在偏房一處隱蔽的房頂上張望庇绽。 院中可真熱鬧锡搜,春花似錦、人聲如沸瞧掺。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽辟狈。三九已至肠缔,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間哼转,已是汗流浹背明未。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評(píng)論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留壹蔓,地道東北人趟妥。 一個(gè)月前我還...
    沈念sama閱讀 47,876評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像佣蓉,于是被迫代替她去往敵國和親披摄。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,700評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容