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 pBuffer
= AMF_EncodeEcmaArray(&prop
->p_vu
.p_object
, pBuffer
, pBufEnd
);
403 case AMF_STRICT_ARRAY
:
404 pBuffer
= AMF_EncodeArray(&prop
->p_vu
.p_object
, pBuffer
, pBufEnd
);
408 RTMP_Log(RTMP_LOGERROR
, "%s, invalid type. %d", __FUNCTION__
, prop
->p_type
);
415 #define AMF3_INTEGER_MAX 268435455
416 #define AMF3_INTEGER_MIN -268435456
419 AMF3ReadInteger(const char *data
, int32_t *valp
)
425 { /* handle first 3 bytes */
428 val
<<= 7; /* shift up */
429 val
|= (data
[i
] & 0x7f); /* add bits */
439 { /* use 4th byte, all 8bits */
444 if (val
> AMF3_INTEGER_MAX
)
448 { /* use 7bits of last unparsed byte (0xxxxxxx) */
455 return i
> 2 ? 4 : i
+ 1;
459 AMF3ReadString(const char *data
, AVal
*str
)
465 len
= AMF3ReadInteger(data
, &ref
);
468 if ((ref
& 0x1) == 0)
469 { /* reference: 0xxx */
470 uint32_t refIndex
= (ref
>> 1);
471 RTMP_Log(RTMP_LOGDEBUG
,
472 "%s, string reference, index: %d, not supported, ignoring!",
473 __FUNCTION__
, refIndex
);
480 uint32_t nSize
= (ref
>> 1);
482 str
->av_val
= (char *)data
;
491 AMF3Prop_Decode(AMFObjectProperty
*prop
, const char *pBuffer
, int nSize
,
494 int nOriginalSize
= nSize
;
497 prop
->p_name
.av_len
= 0;
498 prop
->p_name
.av_val
= NULL
;
500 if (nSize
== 0 || !pBuffer
)
502 RTMP_Log(RTMP_LOGDEBUG
, "empty buffer/no buffer pointer!");
510 int nRes
= AMF3ReadString(pBuffer
, &name
);
512 if (name
.av_len
<= 0)
528 prop
->p_type
= AMF_NULL
;
531 prop
->p_type
= AMF_BOOLEAN
;
532 prop
->p_vu
.p_number
= 0.0;
535 prop
->p_type
= AMF_BOOLEAN
;
536 prop
->p_vu
.p_number
= 1.0;
541 int len
= AMF3ReadInteger(pBuffer
, &res
);
542 prop
->p_vu
.p_number
= (double)res
;
543 prop
->p_type
= AMF_NUMBER
;
550 prop
->p_vu
.p_number
= AMF_DecodeNumber(pBuffer
);
551 prop
->p_type
= AMF_NUMBER
;
558 int len
= AMF3ReadString(pBuffer
, &prop
->p_vu
.p_aval
);
559 prop
->p_type
= AMF_STRING
;
566 int len
= AMF3ReadInteger(pBuffer
, &res
);
571 if ((res
& 0x1) == 0)
573 uint32_t nIndex
= (res
>> 1);
574 RTMP_Log(RTMP_LOGDEBUG
, "AMF3_DATE reference: %d, not supported!", nIndex
);
581 prop
->p_vu
.p_number
= AMF_DecodeNumber(pBuffer
);
583 prop
->p_type
= AMF_NUMBER
;
589 int nRes
= AMF3_Decode(&prop
->p_vu
.p_object
, pBuffer
, nSize
, TRUE
);
593 prop
->p_type
= AMF_OBJECT
;
597 case AMF3_BYTE_ARRAY
:
599 RTMP_Log(RTMP_LOGDEBUG
, "%s - AMF3 unknown/unsupported datatype 0x%02x, @%p",
600 __FUNCTION__
, (unsigned char)(*pBuffer
), pBuffer
);
604 return nOriginalSize
- nSize
;
608 AMFProp_Decode(AMFObjectProperty
*prop
, const char *pBuffer
, int nSize
,
611 int nOriginalSize
= nSize
;
614 prop
->p_name
.av_len
= 0;
615 prop
->p_name
.av_val
= NULL
;
617 if (nSize
== 0 || !pBuffer
)
619 RTMP_Log(RTMP_LOGDEBUG
, "%s: Empty buffer/no buffer pointer!", __FUNCTION__
);
623 if (bDecodeName
&& nSize
< 4)
624 { /* at least name (length + at least 1 byte) and 1 byte of data */
625 RTMP_Log(RTMP_LOGDEBUG
,
626 "%s: Not enough data for decoding with name, less than 4 bytes!",
633 unsigned short nNameSize
= AMF_DecodeInt16(pBuffer
);
634 if (nNameSize
> nSize
- 2)
636 RTMP_Log(RTMP_LOGDEBUG
,
637 "%s: Name size out of range: namesize (%d) > len (%d) - 2",
638 __FUNCTION__
, nNameSize
, nSize
);
642 AMF_DecodeString(pBuffer
, &prop
->p_name
);
643 nSize
-= 2 + nNameSize
;
644 pBuffer
+= 2 + nNameSize
;
654 prop
->p_type
= *pBuffer
++;
655 switch (prop
->p_type
)
660 prop
->p_vu
.p_number
= AMF_DecodeNumber(pBuffer
);
666 prop
->p_vu
.p_number
= (double)AMF_DecodeBoolean(pBuffer
);
671 unsigned short nStringSize
= AMF_DecodeInt16(pBuffer
);
673 if (nSize
< (long)nStringSize
+ 2)
675 AMF_DecodeString(pBuffer
, &prop
->p_vu
.p_aval
);
676 nSize
-= (2 + nStringSize
);
681 int nRes
= AMF_Decode(&prop
->p_vu
.p_object
, pBuffer
, nSize
, TRUE
);
689 RTMP_Log(RTMP_LOGERROR
, "AMF_MOVIECLIP reserved!");
695 case AMF_UNSUPPORTED
:
696 prop
->p_type
= AMF_NULL
;
700 RTMP_Log(RTMP_LOGERROR
, "AMF_REFERENCE not supported!");
708 /* next comes the rest, mixed array has a final 0x000009 mark and names, so its an object */
709 nRes
= AMF_Decode(&prop
->p_vu
.p_object
, pBuffer
+ 4, nSize
, TRUE
);
720 case AMF_STRICT_ARRAY
:
722 unsigned int nArrayLen
= AMF_DecodeInt32(pBuffer
);
725 nRes
= AMF_DecodeArray(&prop
->p_vu
.p_object
, pBuffer
+ 4, nSize
,
734 RTMP_Log(RTMP_LOGDEBUG
, "AMF_DATE");
739 prop
->p_vu
.p_number
= AMF_DecodeNumber(pBuffer
);
740 prop
->p_UTCoffset
= AMF_DecodeInt16(pBuffer
+ 8);
745 case AMF_LONG_STRING
:
748 unsigned int nStringSize
= AMF_DecodeInt32(pBuffer
);
749 if (nSize
< (long)nStringSize
+ 4)
751 AMF_DecodeLongString(pBuffer
, &prop
->p_vu
.p_aval
);
752 nSize
-= (4 + nStringSize
);
753 if (prop
->p_type
== AMF_LONG_STRING
)
754 prop
->p_type
= AMF_STRING
;
759 RTMP_Log(RTMP_LOGERROR
, "AMF_RECORDSET reserved!");
763 case AMF_TYPED_OBJECT
:
765 RTMP_Log(RTMP_LOGERROR
, "AMF_TYPED_OBJECT not supported!");
771 int nRes
= AMF3_Decode(&prop
->p_vu
.p_object
, pBuffer
, nSize
, TRUE
);
775 prop
->p_type
= AMF_OBJECT
;
779 RTMP_Log(RTMP_LOGDEBUG
, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__
,
780 prop
->p_type
, pBuffer
- 1);
784 return nOriginalSize
- nSize
;
788 AMFProp_Dump(AMFObjectProperty
*prop
)
794 if (prop
->p_type
== AMF_INVALID
)
796 RTMP_Log(RTMP_LOGDEBUG
, "Property: INVALID");
800 if (prop
->p_type
== AMF_NULL
)
802 RTMP_Log(RTMP_LOGDEBUG
, "Property: NULL");
806 if (prop
->p_name
.av_len
)
812 name
.av_val
= "no-name.";
813 name
.av_len
= sizeof("no-name.") - 1;
815 if (name
.av_len
> 18)
818 snprintf(strRes
, 255, "Name: %18.*s, ", name
.av_len
, name
.av_val
);
820 if (prop
->p_type
== AMF_OBJECT
)
822 RTMP_Log(RTMP_LOGDEBUG
, "Property: <%sOBJECT>", strRes
);
823 AMF_Dump(&prop
->p_vu
.p_object
);
826 else if (prop
->p_type
== AMF_ECMA_ARRAY
)
828 RTMP_Log(RTMP_LOGDEBUG
, "Property: <%sECMA_ARRAY>", strRes
);
829 AMF_Dump(&prop
->p_vu
.p_object
);
832 else if (prop
->p_type
== AMF_STRICT_ARRAY
)
834 RTMP_Log(RTMP_LOGDEBUG
, "Property: <%sSTRICT_ARRAY>", strRes
);
835 AMF_Dump(&prop
->p_vu
.p_object
);
839 switch (prop
->p_type
)
842 snprintf(str
, 255, "NUMBER:\t%.2f", prop
->p_vu
.p_number
);
845 snprintf(str
, 255, "BOOLEAN:\t%s",
846 prop
->p_vu
.p_number
!= 0.0 ? "TRUE" : "FALSE");
849 snprintf(str
, 255, "STRING:\t%.*s", prop
->p_vu
.p_aval
.av_len
,
850 prop
->p_vu
.p_aval
.av_val
);
853 snprintf(str
, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d",
854 prop
->p_vu
.p_number
, prop
->p_UTCoffset
);
857 snprintf(str
, 255, "INVALID TYPE 0x%02x", (unsigned char)prop
->p_type
);
860 RTMP_Log(RTMP_LOGDEBUG
, "Property: <%s%s>", strRes
, str
);
864 AMFProp_Reset(AMFObjectProperty
*prop
)
866 if (prop
->p_type
== AMF_OBJECT
|| prop
->p_type
== AMF_ECMA_ARRAY
||
867 prop
->p_type
== AMF_STRICT_ARRAY
)
868 AMF_Reset(&prop
->p_vu
.p_object
);
871 prop
->p_vu
.p_aval
.av_len
= 0;
872 prop
->p_vu
.p_aval
.av_val
= NULL
;
874 prop
->p_type
= AMF_INVALID
;
880 AMF_Encode(AMFObject
*obj
, char *pBuffer
, char *pBufEnd
)
884 if (pBuffer
+4 >= pBufEnd
)
887 *pBuffer
++ = AMF_OBJECT
;
889 for (i
= 0; i
< obj
->o_num
; i
++)
891 char *res
= AMFProp_Encode(&obj
->o_props
[i
], pBuffer
, pBufEnd
);
894 RTMP_Log(RTMP_LOGERROR
, "AMF_Encode - failed to encode property in index %d",
904 if (pBuffer
+ 3 >= pBufEnd
)
905 return NULL
; /* no room for the end marker */
907 pBuffer
= AMF_EncodeInt24(pBuffer
, pBufEnd
, AMF_OBJECT_END
);
913 AMF_EncodeEcmaArray(AMFObject
*obj
, char *pBuffer
, char *pBufEnd
)
917 if (pBuffer
+4 >= pBufEnd
)
920 *pBuffer
++ = AMF_ECMA_ARRAY
;
922 pBuffer
= AMF_EncodeInt32(pBuffer
, pBufEnd
, obj
->o_num
);
924 for (i
= 0; i
< obj
->o_num
; i
++)
926 char *res
= AMFProp_Encode(&obj
->o_props
[i
], pBuffer
, pBufEnd
);
929 RTMP_Log(RTMP_LOGERROR
, "AMF_Encode - failed to encode property in index %d",
939 if (pBuffer
+ 3 >= pBufEnd
)
940 return NULL
; /* no room for the end marker */
942 pBuffer
= AMF_EncodeInt24(pBuffer
, pBufEnd
, AMF_OBJECT_END
);
948 AMF_EncodeArray(AMFObject
*obj
, char *pBuffer
, char *pBufEnd
)
952 if (pBuffer
+4 >= pBufEnd
)
955 *pBuffer
++ = AMF_STRICT_ARRAY
;
957 pBuffer
= AMF_EncodeInt32(pBuffer
, pBufEnd
, obj
->o_num
);
959 for (i
= 0; i
< obj
->o_num
; i
++)
961 char *res
= AMFProp_Encode(&obj
->o_props
[i
], pBuffer
, pBufEnd
);
964 RTMP_Log(RTMP_LOGERROR
, "AMF_Encode - failed to encode property in index %d",
974 //if (pBuffer + 3 >= pBufEnd)
975 // return NULL; /* no room for the end marker */
977 //pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
983 AMF_DecodeArray(AMFObject
*obj
, const char *pBuffer
, int nSize
,
984 int nArrayLen
, int bDecodeName
)
986 int nOriginalSize
= nSize
;
991 while (nArrayLen
> 0)
993 AMFObjectProperty prop
;
997 nRes
= AMFProp_Decode(&prop
, pBuffer
, nSize
, bDecodeName
);
1004 AMF_AddProp(obj
, &prop
);
1010 return nOriginalSize
- nSize
;
1014 AMF3_Decode(AMFObject
*obj
, const char *pBuffer
, int nSize
, int bAMFData
)
1016 int nOriginalSize
= nSize
;
1021 obj
->o_props
= NULL
;
1024 if (*pBuffer
!= AMF3_OBJECT
)
1025 RTMP_Log(RTMP_LOGERROR
,
1026 "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!");
1032 len
= AMF3ReadInteger(pBuffer
, &ref
);
1037 { /* object reference, 0xxx */
1038 uint32_t objectIndex
= (ref
>> 1);
1040 RTMP_Log(RTMP_LOGDEBUG
, "Object reference, index: %d", objectIndex
);
1042 else /* object instance */
1044 int32_t classRef
= (ref
>> 1);
1046 AMF3ClassDef cd
= { {0, 0}
1048 AMFObjectProperty prop
;
1050 if ((classRef
& 0x1) == 0)
1051 { /* class reference */
1052 uint32_t classIndex
= (classRef
>> 1);
1053 RTMP_Log(RTMP_LOGDEBUG
, "Class reference: %d", classIndex
);
1057 int32_t classExtRef
= (classRef
>> 1);
1060 cd
.cd_externalizable
= (classExtRef
& 0x1) == 1;
1061 cd
.cd_dynamic
= ((classExtRef
>> 1) & 0x1) == 1;
1063 cdnum
= classExtRef
>> 2;
1067 len
= AMF3ReadString(pBuffer
, &cd
.cd_name
);
1071 /*std::string str = className; */
1073 RTMP_Log(RTMP_LOGDEBUG
,
1074 "Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d",
1075 cd
.cd_name
.av_val
, cd
.cd_externalizable
, cd
.cd_dynamic
,
1078 for (i
= 0; i
< cdnum
; i
++)
1084 RTMP_Log(RTMP_LOGDEBUG
, "%s, invalid class encoding!",
1086 return nOriginalSize
;
1088 len
= AMF3ReadString(pBuffer
, &memberName
);
1089 RTMP_Log(RTMP_LOGDEBUG
, "Member: %s", memberName
.av_val
);
1090 AMF3CD_AddProp(&cd
, &memberName
);
1096 /* add as referencable object */
1098 if (cd
.cd_externalizable
)
1101 AVal name
= AVC("DEFAULT_ATTRIBUTE");
1103 RTMP_Log(RTMP_LOGDEBUG
, "Externalizable, TODO check");
1105 nRes
= AMF3Prop_Decode(&prop
, pBuffer
, nSize
, FALSE
);
1107 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to decode AMF3 property!",
1115 AMFProp_SetName(&prop
, &name
);
1116 AMF_AddProp(obj
, &prop
);
1121 for (i
= 0; i
< cd
.cd_num
; i
++) /* non-dynamic */
1125 nRes
= AMF3Prop_Decode(&prop
, pBuffer
, nSize
, FALSE
);
1127 RTMP_Log(RTMP_LOGDEBUG
, "%s, failed to decode AMF3 property!",
1130 AMFProp_SetName(&prop
, AMF3CD_GetProp(&cd
, i
));
1131 AMF_AddProp(obj
, &prop
);
1144 nRes
= AMF3Prop_Decode(&prop
, pBuffer
, nSize
, TRUE
);
1145 AMF_AddProp(obj
, &prop
);
1150 len
= prop
.p_name
.av_len
;
1155 RTMP_Log(RTMP_LOGDEBUG
, "class object!");
1157 return nOriginalSize
- nSize
;
1161 AMF_Decode(AMFObject
*obj
, const char *pBuffer
, int nSize
, int bDecodeName
)
1163 int nOriginalSize
= nSize
;
1164 int bError
= FALSE
; /* if there is an error while decoding - try to at least find the end mark AMF_OBJECT_END */
1167 obj
->o_props
= NULL
;
1170 AMFObjectProperty prop
;
1173 if (nSize
>=3 && AMF_DecodeInt24(pBuffer
) == AMF_OBJECT_END
)
1182 RTMP_Log(RTMP_LOGERROR
,
1183 "DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!");
1189 nRes
= AMFProp_Decode(&prop
, pBuffer
, nSize
, bDecodeName
);
1196 AMF_AddProp(obj
, &prop
);
1203 return nOriginalSize
- nSize
;
1207 AMF_AddProp(AMFObject
*obj
, const AMFObjectProperty
*prop
)
1209 if (!(obj
->o_num
& 0x0f))
1211 realloc(obj
->o_props
, (obj
->o_num
+ 16) * sizeof(AMFObjectProperty
));
1212 memcpy(&obj
->o_props
[obj
->o_num
++], prop
, sizeof(AMFObjectProperty
));
1216 AMF_CountProp(AMFObject
*obj
)
1222 AMF_GetProp(AMFObject
*obj
, const AVal
*name
, int nIndex
)
1226 if (nIndex
< obj
->o_num
)
1227 return &obj
->o_props
[nIndex
];
1232 for (n
= 0; n
< obj
->o_num
; n
++)
1234 if (AVMATCH(&obj
->o_props
[n
].p_name
, name
))
1235 return &obj
->o_props
[n
];
1239 return (AMFObjectProperty
*)&AMFProp_Invalid
;
1243 AMF_Dump(AMFObject
*obj
)
1246 RTMP_Log(RTMP_LOGDEBUG
, "(object begin)");
1247 for (n
= 0; n
< obj
->o_num
; n
++)
1249 AMFProp_Dump(&obj
->o_props
[n
]);
1251 RTMP_Log(RTMP_LOGDEBUG
, "(object end)");
1255 AMF_Reset(AMFObject
*obj
)
1258 for (n
= 0; n
< obj
->o_num
; n
++)
1260 AMFProp_Reset(&obj
->o_props
[n
]);
1263 obj
->o_props
= NULL
;
1268 /* AMF3ClassDefinition */
1271 AMF3CD_AddProp(AMF3ClassDef
*cd
, AVal
*prop
)
1273 if (!(cd
->cd_num
& 0x0f))
1274 cd
->cd_props
= realloc(cd
->cd_props
, (cd
->cd_num
+ 16) * sizeof(AVal
));
1275 cd
->cd_props
[cd
->cd_num
++] = *prop
;
1279 AMF3CD_GetProp(AMF3ClassDef
*cd
, int nIndex
)
1281 if (nIndex
>= cd
->cd_num
)
1282 return (AVal
*)&AV_empty
;
1283 return &cd
->cd_props
[nIndex
];