2 * Copyright (C) 2005-2008 Team XBMC
4 * Copyright (C) 2008-2009 Andrej Stepanchuk
5 * Copyright (C) 2009-2010 Howard Chu
7 * This file is part of librtmp.
9 * librtmp is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU Lesser General Public License as
11 * published by the Free Software Foundation; either version 2.1,
12 * or (at your option) any later version.
14 * librtmp is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with librtmp see the file COPYING. If not, write to
21 * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301, USA.
23 * http://www.gnu.org/copyleft/lgpl.html
35 static const AMFObjectProperty AMFProp_Invalid
= { {0, 0}, AMF_INVALID
};
36 static const AVal AV_empty
= { 0, 0 };
38 /* Data is Big-Endian */
40 AMF_DecodeInt16(const char *data
)
42 unsigned char *c
= (unsigned char *) data
;
44 val
= (c
[0] << 8) | c
[1];
49 AMF_DecodeInt24(const char *data
)
51 unsigned char *c
= (unsigned char *) data
;
53 val
= (c
[0] << 16) | (c
[1] << 8) | c
[2];
58 AMF_DecodeInt32(const char *data
)
60 unsigned char *c
= (unsigned char *)data
;
62 val
= (c
[0] << 24) | (c
[1] << 16) | (c
[2] << 8) | c
[3];
67 AMF_DecodeString(const char *data
, AVal
*bv
)
69 bv
->av_len
= AMF_DecodeInt16(data
);
70 bv
->av_val
= (bv
->av_len
> 0) ? (char *)data
+ 2 : NULL
;
74 AMF_DecodeLongString(const char *data
, AVal
*bv
)
76 bv
->av_len
= AMF_DecodeInt32(data
);
77 bv
->av_val
= (bv
->av_len
> 0) ? (char *)data
+ 4 : NULL
;
81 AMF_DecodeNumber(const char *data
)
84 #if __FLOAT_WORD_ORDER == __BYTE_ORDER
85 #if __BYTE_ORDER == __BIG_ENDIAN
86 memcpy(&dVal
, data
, 8);
87 #elif __BYTE_ORDER == __LITTLE_ENDIAN
88 unsigned char *ci
, *co
;
89 ci
= (unsigned char *)data
;
90 co
= (unsigned char *)&dVal
;
101 #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */
102 unsigned char *ci
, *co
;
103 ci
= (unsigned char *)data
;
104 co
= (unsigned char *)&dVal
;
113 #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
114 unsigned char *ci
, *co
;
115 ci
= (unsigned char *)data
;
116 co
= (unsigned char *)&dVal
;
131 AMF_DecodeBoolean(const char *data
)
137 AMF_EncodeInt16(char *output
, char *outend
, short nVal
)
139 if (output
+2 > outend
)
142 output
[1] = nVal
& 0xff;
143 output
[0] = nVal
>> 8;
148 AMF_EncodeInt24(char *output
, char *outend
, int nVal
)
150 if (output
+3 > outend
)
153 output
[2] = nVal
& 0xff;
154 output
[1] = nVal
>> 8;
155 output
[0] = nVal
>> 16;
160 AMF_EncodeInt32(char *output
, char *outend
, int nVal
)
162 if (output
+4 > outend
)
165 output
[3] = nVal
& 0xff;
166 output
[2] = nVal
>> 8;
167 output
[1] = nVal
>> 16;
168 output
[0] = nVal
>> 24;
173 AMF_EncodeString(char *output
, char *outend
, const AVal
*bv
)
175 if ((bv
->av_len
< 65536 && output
+ 1 + 2 + bv
->av_len
> outend
) ||
176 output
+ 1 + 4 + bv
->av_len
> outend
)
179 if (bv
->av_len
< 65536)
181 *output
++ = AMF_STRING
;
183 output
= AMF_EncodeInt16(output
, outend
, bv
->av_len
);
187 *output
++ = AMF_LONG_STRING
;
189 output
= AMF_EncodeInt32(output
, outend
, bv
->av_len
);
191 memcpy(output
, bv
->av_val
, bv
->av_len
);
192 output
+= bv
->av_len
;
198 AMF_EncodeNumber(char *output
, char *outend
, double dVal
)
200 if (output
+1+8 > outend
)
203 *output
++ = AMF_NUMBER
; /* type: Number */
205 #if __FLOAT_WORD_ORDER == __BYTE_ORDER
206 #if __BYTE_ORDER == __BIG_ENDIAN
207 memcpy(output
, &dVal
, 8);
208 #elif __BYTE_ORDER == __LITTLE_ENDIAN
210 unsigned char *ci
, *co
;
211 ci
= (unsigned char *)&dVal
;
212 co
= (unsigned char *)output
;
224 #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */
226 unsigned char *ci
, *co
;
227 ci
= (unsigned char *)&dVal
;
228 co
= (unsigned char *)output
;
238 #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
240 unsigned char *ci
, *co
;
241 ci
= (unsigned char *)&dVal
;
242 co
= (unsigned char *)output
;
259 AMF_EncodeBoolean(char *output
, char *outend
, int bVal
)
261 if (output
+2 > outend
)
264 *output
++ = AMF_BOOLEAN
;
266 *output
++ = bVal
? 0x01 : 0x00;
272 AMF_EncodeNamedString(char *output
, char *outend
, const AVal
*strName
, const AVal
*strValue
)
274 if (output
+2+strName
->av_len
> outend
)
276 output
= AMF_EncodeInt16(output
, outend
, strName
->av_len
);
278 memcpy(output
, strName
->av_val
, strName
->av_len
);
279 output
+= strName
->av_len
;
281 return AMF_EncodeString(output
, outend
, strValue
);
285 AMF_EncodeNamedNumber(char *output
, char *outend
, const AVal
*strName
, double dVal
)
287 if (output
+2+strName
->av_len
> outend
)
289 output
= AMF_EncodeInt16(output
, outend
, strName
->av_len
);
291 memcpy(output
, strName
->av_val
, strName
->av_len
);
292 output
+= strName
->av_len
;
294 return AMF_EncodeNumber(output
, outend
, dVal
);
298 AMF_EncodeNamedBoolean(char *output
, char *outend
, const AVal
*strName
, int bVal
)
300 if (output
+2+strName
->av_len
> outend
)
302 output
= AMF_EncodeInt16(output
, outend
, strName
->av_len
);
304 memcpy(output
, strName
->av_val
, strName
->av_len
);
305 output
+= strName
->av_len
;
307 return AMF_EncodeBoolean(output
, outend
, bVal
);
311 AMFProp_GetName(AMFObjectProperty
*prop
, AVal
*name
)
313 *name
= prop
->p_name
;
317 AMFProp_SetName(AMFObjectProperty
*prop
, AVal
*name
)
319 prop
->p_name
= *name
;
323 AMFProp_GetType(AMFObjectProperty
*prop
)
329 AMFProp_GetNumber(AMFObjectProperty
*prop
)
331 return prop
->p_vu
.p_number
;
335 AMFProp_GetBoolean(AMFObjectProperty
*prop
)
337 return prop
->p_vu
.p_number
!= 0;
341 AMFProp_GetString(AMFObjectProperty
*prop
, AVal
*str
)
343 *str
= prop
->p_vu
.p_aval
;
347 AMFProp_GetObject(AMFObjectProperty
*prop
, AMFObject
*obj
)
349 *obj
= prop
->p_vu
.p_object
;
353 AMFProp_IsValid(AMFObjectProperty
*prop
)
355 return prop
->p_type
!= AMF_INVALID
;
359 AMFProp_Encode(AMFObjectProperty
*prop
, char *pBuffer
, char *pBufEnd
)
361 if (prop
->p_type
== AMF_INVALID
)
364 if (prop
->p_type
!= AMF_NULL
&& pBuffer
+ prop
->p_name
.av_len
+ 2 + 1 >= pBufEnd
)
367 if (prop
->p_type
!= AMF_NULL
&& prop
->p_name
.av_len
)
369 *pBuffer
++ = prop
->p_name
.av_len
>> 8;
370 *pBuffer
++ = prop
->p_name
.av_len
& 0xff;
371 memcpy(pBuffer
, prop
->p_name
.av_val
, prop
->p_name
.av_len
);
372 pBuffer
+= prop
->p_name
.av_len
;
375 switch (prop
->p_type
)
378 pBuffer
= AMF_EncodeNumber(pBuffer
, pBufEnd
, prop
->p_vu
.p_number
);
382 pBuffer
= AMF_EncodeBoolean(pBuffer
, pBufEnd
, prop
->p_vu
.p_number
!= 0);
386 pBuffer
= AMF_EncodeString(pBuffer
, pBufEnd
, &prop
->p_vu
.p_aval
);
390 if (pBuffer
+1 >= pBufEnd
)
392 *pBuffer
++ = AMF_NULL
;
396 pBuffer
= AMF_Encode(&prop
->p_vu
.p_object
, pBuffer
, pBufEnd
);
400 RTMP_Log(RTMP_LOGERROR
, "%s, invalid type. %d", __FUNCTION__
, prop
->p_type
);
407 #define AMF3_INTEGER_MAX 268435455
408 #define AMF3_INTEGER_MIN -268435456
411 AMF3ReadInteger(const char *data
, int32_t *valp
)
417 { /* handle first 3 bytes */
420 val
<<= 7; /* shift up */
421 val
|= (data
[i
] & 0x7f); /* add bits */
431 { /* use 4th byte, all 8bits */
436 if (val
> AMF3_INTEGER_MAX
)
440 { /* use 7bits of last unparsed byte (0xxxxxxx) */
447 return i
> 2 ? 4 : i
+ 1;
451 AMF3ReadString(const char *data
, AVal
*str
)
457 len
= AMF3ReadInteger(data
, &ref
);
460 if ((ref
& 0x1) == 0)
461 { /* reference: 0xxx */
462 uint32_t refIndex
= (ref
>> 1);
463 RTMP_Log(RTMP_LOGDEBUG
,
464 "%s, string reference, index: %d, not supported, ignoring!",
465 __FUNCTION__
, refIndex
);
470 uint32_t nSize
= (ref
>> 1);
472 str
->av_val
= (char *)data
;
481 AMF3Prop_Decode(AMFObjectProperty
*prop
, const char *pBuffer
, int nSize
,
484 int nOriginalSize
= nSize
;
487 prop
->p_name
.av_len
= 0;
488 prop
->p_name
.av_val
= NULL
;
490 if (nSize
== 0 || !pBuffer
)
492 RTMP_Log(RTMP_LOGDEBUG
, "empty buffer/no buffer pointer!");
500 int nRes
= AMF3ReadString(pBuffer
, &name
);
502 if (name
.av_len
<= 0)
518 prop
->p_type
= AMF_NULL
;
521 prop
->p_type
= AMF_BOOLEAN
;
522 prop
->p_vu
.p_number
= 0.0;
525 prop
->p_type
= AMF_BOOLEAN
;
526 prop
->p_vu
.p_number
= 1.0;
531 int len
= AMF3ReadInteger(pBuffer
, &res
);
532 prop
->p_vu
.p_number
= (double)res
;
533 prop
->p_type
= AMF_NUMBER
;
540 prop
->p_vu
.p_number
= AMF_DecodeNumber(pBuffer
);
541 prop
->p_type
= AMF_NUMBER
;
548 int len
= AMF3ReadString(pBuffer
, &prop
->p_vu
.p_aval
);
549 prop
->p_type
= AMF_STRING
;
556 int len
= AMF3ReadInteger(pBuffer
, &res
);
561 if ((res
& 0x1) == 0)
563 uint32_t nIndex
= (res
>> 1);
564 RTMP_Log(RTMP_LOGDEBUG
, "AMF3_DATE reference: %d, not supported!", nIndex
);
571 prop
->p_vu
.p_number
= AMF_DecodeNumber(pBuffer
);
573 prop
->p_type
= AMF_NUMBER
;
579 int nRes
= AMF3_Decode(&prop
->p_vu
.p_object
, pBuffer
, nSize
, TRUE
);
583 prop
->p_type
= AMF_OBJECT
;
587 case AMF3_BYTE_ARRAY
:
589 RTMP_Log(RTMP_LOGDEBUG
, "%s - AMF3 unknown/unsupported datatype 0x%02x, @%p",
590 __FUNCTION__
, (unsigned char)(*pBuffer
), pBuffer
);
594 return nOriginalSize
- nSize
;
598 AMFProp_Decode(AMFObjectProperty
*prop
, const char *pBuffer
, int nSize
,
601 int nOriginalSize
= nSize
;
604 prop
->p_name
.av_len
= 0;
605 prop
->p_name
.av_val
= NULL
;
607 if (nSize
== 0 || !pBuffer
)
609 RTMP_Log(RTMP_LOGDEBUG
, "%s: Empty buffer/no buffer pointer!", __FUNCTION__
);
613 if (bDecodeName
&& nSize
< 4)
614 { /* at least name (length + at least 1 byte) and 1 byte of data */
615 RTMP_Log(RTMP_LOGDEBUG
,
616 "%s: Not enough data for decoding with name, less than 4 bytes!",
623 unsigned short nNameSize
= AMF_DecodeInt16(pBuffer
);
624 if (nNameSize
> nSize
- 2)
626 RTMP_Log(RTMP_LOGDEBUG
,
627 "%s: Name size out of range: namesize (%d) > len (%d) - 2",
628 __FUNCTION__
, nNameSize
, nSize
);
632 AMF_DecodeString(pBuffer
, &prop
->p_name
);
633 nSize
-= 2 + nNameSize
;
634 pBuffer
+= 2 + nNameSize
;
644 prop
->p_type
= *pBuffer
++;
645 switch (prop
->p_type
)
650 prop
->p_vu
.p_number
= AMF_DecodeNumber(pBuffer
);
656 prop
->p_vu
.p_number
= (double)AMF_DecodeBoolean(pBuffer
);
661 unsigned short nStringSize
= AMF_DecodeInt16(pBuffer
);
663 if (nSize
< (long)nStringSize
+ 2)
665 AMF_DecodeString(pBuffer
, &prop
->p_vu
.p_aval
);
666 nSize
-= (2 + nStringSize
);
671 int nRes
= AMF_Decode(&prop
->p_vu
.p_object
, pBuffer
, nSize
, TRUE
);
679 RTMP_Log(RTMP_LOGERROR
, "AMF_MOVIECLIP reserved!");
685 case AMF_UNSUPPORTED
:
686 prop
->p_type
= AMF_NULL
;
690 RTMP_Log(RTMP_LOGERROR
, "AMF_REFERENCE not supported!");
698 /* next comes the rest, mixed array has a final 0x000009 mark and names, so its an object */
699 nRes
= AMF_Decode(&prop
->p_vu
.p_object
, pBuffer
+ 4, nSize
, TRUE
);
703 prop
->p_type
= AMF_OBJECT
;
711 case AMF_STRICT_ARRAY
:
713 unsigned int nArrayLen
= AMF_DecodeInt32(pBuffer
);
716 nRes
= AMF_DecodeArray(&prop
->p_vu
.p_object
, pBuffer
+ 4, nSize
,
721 prop
->p_type
= AMF_OBJECT
;
726 RTMP_Log(RTMP_LOGDEBUG
, "AMF_DATE");
731 prop
->p_vu
.p_number
= AMF_DecodeNumber(pBuffer
);
732 prop
->p_UTCoffset
= AMF_DecodeInt16(pBuffer
+ 8);
737 case AMF_LONG_STRING
:
739 unsigned int nStringSize
= AMF_DecodeInt32(pBuffer
);
740 if (nSize
< (long)nStringSize
+ 4)
742 AMF_DecodeLongString(pBuffer
, &prop
->p_vu
.p_aval
);
743 nSize
-= (4 + nStringSize
);
744 prop
->p_type
= AMF_STRING
;
749 RTMP_Log(RTMP_LOGERROR
, "AMF_RECORDSET reserved!");
755 RTMP_Log(RTMP_LOGERROR
, "AMF_XML_DOC not supported!");
759 case AMF_TYPED_OBJECT
:
761 RTMP_Log(RTMP_LOGERROR
, "AMF_TYPED_OBJECT not supported!");
767 int nRes
= AMF3_Decode(&prop
->p_vu
.p_object
, pBuffer
, nSize
, TRUE
);
771 prop
->p_type
= AMF_OBJECT
;
775 RTMP_Log(RTMP_LOGDEBUG
, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__
,
776 prop
->p_type
, pBuffer
- 1);
780 return nOriginalSize
- nSize
;
784 AMFProp_Dump(AMFObjectProperty
*prop
)
790 if (prop
->p_type
== AMF_INVALID
)
792 RTMP_Log(RTMP_LOGDEBUG
, "Property: INVALID");
796 if (prop
->p_type
== AMF_NULL
)
798 RTMP_Log(RTMP_LOGDEBUG
, "Property: NULL");
802 if (prop
->p_name
.av_len
)
808 name
.av_val
= "no-name.";
809 name
.av_len
= sizeof("no-name.") - 1;
811 if (name
.av_len
> 18)
814 snprintf(strRes
, 255, "Name: %18.*s, ", name
.av_len
, name
.av_val
);
816 if (prop
->p_type
== AMF_OBJECT
)
818 RTMP_Log(RTMP_LOGDEBUG
, "Property: <%sOBJECT>", strRes
);
819 AMF_Dump(&prop
->p_vu
.p_object
);
823 switch (prop
->p_type
)
826 snprintf(str
, 255, "NUMBER:\t%.2f", prop
->p_vu
.p_number
);
829 snprintf(str
, 255, "BOOLEAN:\t%s",
830 prop
->p_vu
.p_number
!= 0.0 ? "TRUE" : "FALSE");
833 snprintf(str
, 255, "STRING:\t%.*s", prop
->p_vu
.p_aval
.av_len
,
834 prop
->p_vu
.p_aval
.av_val
);
837 snprintf(str
, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d",
838 prop
->p_vu
.p_number
, prop
->p_UTCoffset
);
841 snprintf(str
, 255, "INVALID TYPE 0x%02x", (unsigned char)prop
->p_type
);
844 RTMP_Log(RTMP_LOGDEBUG
, "Property: <%s%s>", strRes
, str
);
848 AMFProp_Reset(AMFObjectProperty
*prop
)
850 if (prop
->p_type
== AMF_OBJECT
)
851 AMF_Reset(&prop
->p_vu
.p_object
);
854 prop
->p_vu
.p_aval
.av_len
= 0;
855 prop
->p_vu
.p_aval
.av_val
= NULL
;
857 prop
->p_type
= AMF_INVALID
;
863 AMF_Encode(AMFObject
*obj
, char *pBuffer
, char *pBufEnd
)
867 if (pBuffer
+4 >= pBufEnd
)
870 *pBuffer
++ = AMF_OBJECT
;
872 for (i
= 0; i
< obj
->o_num
; i
++)
874 char *res
= AMFProp_Encode(&obj
->o_props
[i
], pBuffer
, pBufEnd
);
877 RTMP_Log(RTMP_LOGERROR
, "AMF_Encode - failed to encode property in index %d",
887 if (pBuffer
+ 3 >= pBufEnd
)
888 return NULL
; /* no room for the end marker */
890 pBuffer
= AMF_EncodeInt24(pBuffer
, pBufEnd
, AMF_OBJECT_END
);
896 AMF_DecodeArray(AMFObject
*obj
, const char *pBuffer
, int nSize
,
897 int nArrayLen
, int bDecodeName
)
899 int nOriginalSize
= nSize
;
904 while (nArrayLen
> 0)
906 AMFObjectProperty prop
;
910 nRes
= AMFProp_Decode(&prop
, pBuffer
, nSize
, bDecodeName
);
917 AMF_AddProp(obj
, &prop
);
923 return nOriginalSize
- nSize
;
927 AMF3_Decode(AMFObject
*obj
, const char *pBuffer
, int nSize
, int bAMFData
)
929 int nOriginalSize
= nSize
;
937 if (*pBuffer
!= AMF3_OBJECT
)
938 RTMP_Log(RTMP_LOGERROR
,
939 "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!");
945 len
= AMF3ReadInteger(pBuffer
, &ref
);
950 { /* object reference, 0xxx */
951 uint32_t objectIndex
= (ref
>> 1);
953 RTMP_Log(RTMP_LOGDEBUG
, "Object reference, index: %d", objectIndex
);
955 else /* object instance */
957 int32_t classRef
= (ref
>> 1);
959 AMF3ClassDef cd
= { {0, 0}
961 AMFObjectProperty prop
;
963 if ((classRef
& 0x1) == 0)
964 { /* class reference */
965 uint32_t classIndex
= (classRef
>> 1);
966 RTMP_Log(RTMP_LOGDEBUG
, "Class reference: %d", classIndex
);
970 int32_t classExtRef
= (classRef
>> 1);
973 cd
.cd_externalizable
= (classExtRef
& 0x1) == 1;
974 cd
.cd_dynamic
= ((classExtRef
>> 1) & 0x1) == 1;
976 cd
.cd_num
= classExtRef
>> 2;
980 len
= AMF3ReadString(pBuffer
, &cd
.cd_name
);
984 /*std::string str = className; */
986 RTMP_Log(RTMP_LOGDEBUG
,
987 "Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d",
988 cd
.cd_name
.av_val
, cd
.cd_externalizable
, cd
.cd_dynamic
,
991 for (i
= 0; i
< cd
.cd_num
; i
++)
994 len
= AMF3ReadString(pBuffer
, &memberName
);
995 RTMP_Log(RTMP_LOGDEBUG
, "Member: %s", memberName
.av_val
);
996 AMF3CD_AddProp(&cd
, &memberName
);
1002 /* add as referencable object */
1004 if (cd
.cd_externalizable
)
1007 AVal name
= AVC("DEFAULT_ATTRIBUTE");
1009 RTMP_Log(RTMP_LOGDEBUG
, "Externalizable, TODO check");
1011 nRes
= AMF3Prop_Decode(&prop
, pBuffer
, nSize
, FALSE
);
1013 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to decode AMF3 property!",
1021 AMFProp_SetName(&prop
, &name
);
1022 AMF_AddProp(obj
, &prop
);
1027 for (i
= 0; i
< cd
.cd_num
; i
++) /* non-dynamic */
1029 nRes
= AMF3Prop_Decode(&prop
, pBuffer
, nSize
, FALSE
);
1031 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to decode AMF3 property!",
1034 AMFProp_SetName(&prop
, AMF3CD_GetProp(&cd
, i
));
1035 AMF_AddProp(obj
, &prop
);
1046 nRes
= AMF3Prop_Decode(&prop
, pBuffer
, nSize
, TRUE
);
1047 AMF_AddProp(obj
, &prop
);
1052 len
= prop
.p_name
.av_len
;
1057 RTMP_Log(RTMP_LOGDEBUG
, "class object!");
1059 return nOriginalSize
- nSize
;
1063 AMF_Decode(AMFObject
*obj
, const char *pBuffer
, int nSize
, int bDecodeName
)
1065 int nOriginalSize
= nSize
;
1066 int bError
= FALSE
; /* if there is an error while decoding - try to at least find the end mark AMF_OBJECT_END */
1069 obj
->o_props
= NULL
;
1072 AMFObjectProperty prop
;
1075 if (nSize
>=3 && AMF_DecodeInt24(pBuffer
) == AMF_OBJECT_END
)
1084 RTMP_Log(RTMP_LOGERROR
,
1085 "DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!");
1091 nRes
= AMFProp_Decode(&prop
, pBuffer
, nSize
, bDecodeName
);
1098 AMF_AddProp(obj
, &prop
);
1105 return nOriginalSize
- nSize
;
1109 AMF_AddProp(AMFObject
*obj
, const AMFObjectProperty
*prop
)
1111 if (!(obj
->o_num
& 0x0f))
1113 realloc(obj
->o_props
, (obj
->o_num
+ 16) * sizeof(AMFObjectProperty
));
1114 memcpy(&obj
->o_props
[obj
->o_num
++], prop
, sizeof(AMFObjectProperty
));
1118 AMF_CountProp(AMFObject
*obj
)
1124 AMF_GetProp(AMFObject
*obj
, const AVal
*name
, int nIndex
)
1128 if (nIndex
<= obj
->o_num
)
1129 return &obj
->o_props
[nIndex
];
1134 for (n
= 0; n
< obj
->o_num
; n
++)
1136 if (AVMATCH(&obj
->o_props
[n
].p_name
, name
))
1137 return &obj
->o_props
[n
];
1141 return (AMFObjectProperty
*)&AMFProp_Invalid
;
1145 AMF_Dump(AMFObject
*obj
)
1148 RTMP_Log(RTMP_LOGDEBUG
, "(object begin)");
1149 for (n
= 0; n
< obj
->o_num
; n
++)
1151 AMFProp_Dump(&obj
->o_props
[n
]);
1153 RTMP_Log(RTMP_LOGDEBUG
, "(object end)");
1157 AMF_Reset(AMFObject
*obj
)
1160 for (n
= 0; n
< obj
->o_num
; n
++)
1162 AMFProp_Reset(&obj
->o_props
[n
]);
1165 obj
->o_props
= NULL
;
1170 /* AMF3ClassDefinition */
1173 AMF3CD_AddProp(AMF3ClassDef
*cd
, AVal
*prop
)
1175 if (!(cd
->cd_num
& 0x0f))
1176 cd
->cd_props
= realloc(cd
->cd_props
, (cd
->cd_num
+ 16) * sizeof(AVal
));
1177 cd
->cd_props
[cd
->cd_num
++] = *prop
;
1181 AMF3CD_GetProp(AMF3ClassDef
*cd
, int nIndex
)
1183 if (nIndex
>= cd
->cd_num
)
1184 return (AVal
*)&AV_empty
;
1185 return &cd
->cd_props
[nIndex
];