Fix upper bound check in AMF_GetProp
[rtmpdump.git] / librtmp / amf.c
blobce84f8121bfcbba04e3dc47b923a3db79749a15b
1 /*
2 * Copyright (C) 2005-2008 Team XBMC
3 * http://www.xbmc.org
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
26 #include <string.h>
27 #include <assert.h>
28 #include <stdlib.h>
30 #include "rtmp_sys.h"
31 #include "amf.h"
32 #include "log.h"
33 #include "bytes.h"
35 static const AMFObjectProperty AMFProp_Invalid = { {0, 0}, AMF_INVALID };
36 static const AVal AV_empty = { 0, 0 };
38 /* Data is Big-Endian */
39 unsigned short
40 AMF_DecodeInt16(const char *data)
42 unsigned char *c = (unsigned char *) data;
43 unsigned short val;
44 val = (c[0] << 8) | c[1];
45 return val;
48 unsigned int
49 AMF_DecodeInt24(const char *data)
51 unsigned char *c = (unsigned char *) data;
52 unsigned int val;
53 val = (c[0] << 16) | (c[1] << 8) | c[2];
54 return val;
57 unsigned int
58 AMF_DecodeInt32(const char *data)
60 unsigned char *c = (unsigned char *)data;
61 unsigned int val;
62 val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
63 return val;
66 void
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;
73 void
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;
80 double
81 AMF_DecodeNumber(const char *data)
83 double dVal;
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;
91 co[0] = ci[7];
92 co[1] = ci[6];
93 co[2] = ci[5];
94 co[3] = ci[4];
95 co[4] = ci[3];
96 co[5] = ci[2];
97 co[6] = ci[1];
98 co[7] = ci[0];
99 #endif
100 #else
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;
105 co[0] = ci[3];
106 co[1] = ci[2];
107 co[2] = ci[1];
108 co[3] = ci[0];
109 co[4] = ci[7];
110 co[5] = ci[6];
111 co[6] = ci[5];
112 co[7] = ci[4];
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;
117 co[0] = ci[4];
118 co[1] = ci[5];
119 co[2] = ci[6];
120 co[3] = ci[7];
121 co[4] = ci[0];
122 co[5] = ci[1];
123 co[6] = ci[2];
124 co[7] = ci[3];
125 #endif
126 #endif
127 return dVal;
131 AMF_DecodeBoolean(const char *data)
133 return *data != 0;
136 char *
137 AMF_EncodeInt16(char *output, char *outend, short nVal)
139 if (output+2 > outend)
140 return NULL;
142 output[1] = nVal & 0xff;
143 output[0] = nVal >> 8;
144 return output+2;
147 char *
148 AMF_EncodeInt24(char *output, char *outend, int nVal)
150 if (output+3 > outend)
151 return NULL;
153 output[2] = nVal & 0xff;
154 output[1] = nVal >> 8;
155 output[0] = nVal >> 16;
156 return output+3;
159 char *
160 AMF_EncodeInt32(char *output, char *outend, int nVal)
162 if (output+4 > outend)
163 return NULL;
165 output[3] = nVal & 0xff;
166 output[2] = nVal >> 8;
167 output[1] = nVal >> 16;
168 output[0] = nVal >> 24;
169 return output+4;
172 char *
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)
177 return NULL;
179 if (bv->av_len < 65536)
181 *output++ = AMF_STRING;
183 output = AMF_EncodeInt16(output, outend, bv->av_len);
185 else
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;
194 return output;
197 char *
198 AMF_EncodeNumber(char *output, char *outend, double dVal)
200 if (output+1+8 > outend)
201 return NULL;
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;
213 co[0] = ci[7];
214 co[1] = ci[6];
215 co[2] = ci[5];
216 co[3] = ci[4];
217 co[4] = ci[3];
218 co[5] = ci[2];
219 co[6] = ci[1];
220 co[7] = ci[0];
222 #endif
223 #else
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;
229 co[0] = ci[3];
230 co[1] = ci[2];
231 co[2] = ci[1];
232 co[3] = ci[0];
233 co[4] = ci[7];
234 co[5] = ci[6];
235 co[6] = ci[5];
236 co[7] = ci[4];
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;
243 co[0] = ci[4];
244 co[1] = ci[5];
245 co[2] = ci[6];
246 co[3] = ci[7];
247 co[4] = ci[0];
248 co[5] = ci[1];
249 co[6] = ci[2];
250 co[7] = ci[3];
252 #endif
253 #endif
255 return output+8;
258 char *
259 AMF_EncodeBoolean(char *output, char *outend, int bVal)
261 if (output+2 > outend)
262 return NULL;
264 *output++ = AMF_BOOLEAN;
266 *output++ = bVal ? 0x01 : 0x00;
268 return output;
271 char *
272 AMF_EncodeNamedString(char *output, char *outend, const AVal *strName, const AVal *strValue)
274 if (output+2+strName->av_len > outend)
275 return NULL;
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);
284 char *
285 AMF_EncodeNamedNumber(char *output, char *outend, const AVal *strName, double dVal)
287 if (output+2+strName->av_len > outend)
288 return NULL;
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);
297 char *
298 AMF_EncodeNamedBoolean(char *output, char *outend, const AVal *strName, int bVal)
300 if (output+2+strName->av_len > outend)
301 return NULL;
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);
310 void
311 AMFProp_GetName(AMFObjectProperty *prop, AVal *name)
313 *name = prop->p_name;
316 void
317 AMFProp_SetName(AMFObjectProperty *prop, AVal *name)
319 prop->p_name = *name;
322 AMFDataType
323 AMFProp_GetType(AMFObjectProperty *prop)
325 return prop->p_type;
328 double
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;
340 void
341 AMFProp_GetString(AMFObjectProperty *prop, AVal *str)
343 *str = prop->p_vu.p_aval;
346 void
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;
358 char *
359 AMFProp_Encode(AMFObjectProperty *prop, char *pBuffer, char *pBufEnd)
361 if (prop->p_type == AMF_INVALID)
362 return NULL;
364 if (prop->p_type != AMF_NULL && pBuffer + prop->p_name.av_len + 2 + 1 >= pBufEnd)
365 return NULL;
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)
377 case AMF_NUMBER:
378 pBuffer = AMF_EncodeNumber(pBuffer, pBufEnd, prop->p_vu.p_number);
379 break;
381 case AMF_BOOLEAN:
382 pBuffer = AMF_EncodeBoolean(pBuffer, pBufEnd, prop->p_vu.p_number != 0);
383 break;
385 case AMF_STRING:
386 pBuffer = AMF_EncodeString(pBuffer, pBufEnd, &prop->p_vu.p_aval);
387 break;
389 case AMF_NULL:
390 if (pBuffer+1 >= pBufEnd)
391 return NULL;
392 *pBuffer++ = AMF_NULL;
393 break;
395 case AMF_OBJECT:
396 pBuffer = AMF_Encode(&prop->p_vu.p_object, pBuffer, pBufEnd);
397 break;
399 default:
400 RTMP_Log(RTMP_LOGERROR, "%s, invalid type. %d", __FUNCTION__, prop->p_type);
401 pBuffer = NULL;
404 return pBuffer;
407 #define AMF3_INTEGER_MAX 268435455
408 #define AMF3_INTEGER_MIN -268435456
411 AMF3ReadInteger(const char *data, int32_t *valp)
413 int i = 0;
414 int32_t val = 0;
416 while (i <= 2)
417 { /* handle first 3 bytes */
418 if (data[i] & 0x80)
419 { /* byte used */
420 val <<= 7; /* shift up */
421 val |= (data[i] & 0x7f); /* add bits */
422 i++;
424 else
426 break;
430 if (i > 2)
431 { /* use 4th byte, all 8bits */
432 val <<= 8;
433 val |= data[3];
435 /* range check */
436 if (val > AMF3_INTEGER_MAX)
437 val -= (1 << 29);
439 else
440 { /* use 7bits of last unparsed byte (0xxxxxxx) */
441 val <<= 7;
442 val |= data[i];
445 *valp = val;
447 return i > 2 ? 4 : i + 1;
451 AMF3ReadString(const char *data, AVal *str)
453 int32_t ref = 0;
454 int len;
455 assert(str != 0);
457 len = AMF3ReadInteger(data, &ref);
458 data += len;
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);
466 return len;
468 else
470 uint32_t nSize = (ref >> 1);
472 str->av_val = (char *)data;
473 str->av_len = nSize;
475 return len + nSize;
477 return len;
481 AMF3Prop_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
482 int bDecodeName)
484 int nOriginalSize = nSize;
485 AMF3DataType type;
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!");
493 return -1;
496 /* decode name */
497 if (bDecodeName)
499 AVal name;
500 int nRes = AMF3ReadString(pBuffer, &name);
502 if (name.av_len <= 0)
503 return nRes;
505 prop->p_name = name;
506 pBuffer += nRes;
507 nSize -= nRes;
510 /* decode */
511 type = *pBuffer++;
512 nSize--;
514 switch (type)
516 case AMF3_UNDEFINED:
517 case AMF3_NULL:
518 prop->p_type = AMF_NULL;
519 break;
520 case AMF3_FALSE:
521 prop->p_type = AMF_BOOLEAN;
522 prop->p_vu.p_number = 0.0;
523 break;
524 case AMF3_TRUE:
525 prop->p_type = AMF_BOOLEAN;
526 prop->p_vu.p_number = 1.0;
527 break;
528 case AMF3_INTEGER:
530 int32_t res = 0;
531 int len = AMF3ReadInteger(pBuffer, &res);
532 prop->p_vu.p_number = (double)res;
533 prop->p_type = AMF_NUMBER;
534 nSize -= len;
535 break;
537 case AMF3_DOUBLE:
538 if (nSize < 8)
539 return -1;
540 prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
541 prop->p_type = AMF_NUMBER;
542 nSize -= 8;
543 break;
544 case AMF3_STRING:
545 case AMF3_XML_DOC:
546 case AMF3_XML:
548 int len = AMF3ReadString(pBuffer, &prop->p_vu.p_aval);
549 prop->p_type = AMF_STRING;
550 nSize -= len;
551 break;
553 case AMF3_DATE:
555 int32_t res = 0;
556 int len = AMF3ReadInteger(pBuffer, &res);
558 nSize -= len;
559 pBuffer += len;
561 if ((res & 0x1) == 0)
562 { /* reference */
563 uint32_t nIndex = (res >> 1);
564 RTMP_Log(RTMP_LOGDEBUG, "AMF3_DATE reference: %d, not supported!", nIndex);
566 else
568 if (nSize < 8)
569 return -1;
571 prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
572 nSize -= 8;
573 prop->p_type = AMF_NUMBER;
575 break;
577 case AMF3_OBJECT:
579 int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
580 if (nRes == -1)
581 return -1;
582 nSize -= nRes;
583 prop->p_type = AMF_OBJECT;
584 break;
586 case AMF3_ARRAY:
587 case AMF3_BYTE_ARRAY:
588 default:
589 RTMP_Log(RTMP_LOGDEBUG, "%s - AMF3 unknown/unsupported datatype 0x%02x, @%p",
590 __FUNCTION__, (unsigned char)(*pBuffer), pBuffer);
591 return -1;
594 return nOriginalSize - nSize;
598 AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
599 int bDecodeName)
601 int nOriginalSize = nSize;
602 int nRes;
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__);
610 return -1;
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!",
617 __FUNCTION__);
618 return -1;
621 if (bDecodeName)
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);
629 return -1;
632 AMF_DecodeString(pBuffer, &prop->p_name);
633 nSize -= 2 + nNameSize;
634 pBuffer += 2 + nNameSize;
637 if (nSize == 0)
639 return -1;
642 nSize--;
644 prop->p_type = *pBuffer++;
645 switch (prop->p_type)
647 case AMF_NUMBER:
648 if (nSize < 8)
649 return -1;
650 prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
651 nSize -= 8;
652 break;
653 case AMF_BOOLEAN:
654 if (nSize < 1)
655 return -1;
656 prop->p_vu.p_number = (double)AMF_DecodeBoolean(pBuffer);
657 nSize--;
658 break;
659 case AMF_STRING:
661 unsigned short nStringSize = AMF_DecodeInt16(pBuffer);
663 if (nSize < (long)nStringSize + 2)
664 return -1;
665 AMF_DecodeString(pBuffer, &prop->p_vu.p_aval);
666 nSize -= (2 + nStringSize);
667 break;
669 case AMF_OBJECT:
671 int nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
672 if (nRes == -1)
673 return -1;
674 nSize -= nRes;
675 break;
677 case AMF_MOVIECLIP:
679 RTMP_Log(RTMP_LOGERROR, "AMF_MOVIECLIP reserved!");
680 return -1;
681 break;
683 case AMF_NULL:
684 case AMF_UNDEFINED:
685 case AMF_UNSUPPORTED:
686 prop->p_type = AMF_NULL;
687 break;
688 case AMF_REFERENCE:
690 RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!");
691 return -1;
692 break;
694 case AMF_ECMA_ARRAY:
696 nSize -= 4;
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);
700 if (nRes == -1)
701 return -1;
702 nSize -= nRes;
703 prop->p_type = AMF_OBJECT;
704 break;
706 case AMF_OBJECT_END:
708 return -1;
709 break;
711 case AMF_STRICT_ARRAY:
713 unsigned int nArrayLen = AMF_DecodeInt32(pBuffer);
714 nSize -= 4;
716 nRes = AMF_DecodeArray(&prop->p_vu.p_object, pBuffer + 4, nSize,
717 nArrayLen, FALSE);
718 if (nRes == -1)
719 return -1;
720 nSize -= nRes;
721 prop->p_type = AMF_OBJECT;
722 break;
724 case AMF_DATE:
726 RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE");
728 if (nSize < 10)
729 return -1;
731 prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
732 prop->p_UTCoffset = AMF_DecodeInt16(pBuffer + 8);
734 nSize -= 10;
735 break;
737 case AMF_LONG_STRING:
738 case AMF_XML_DOC:
740 unsigned int nStringSize = AMF_DecodeInt32(pBuffer);
741 if (nSize < (long)nStringSize + 4)
742 return -1;
743 AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval);
744 nSize -= (4 + nStringSize);
745 if (prop->p_type == AMF_LONG_STRING)
746 prop->p_type = AMF_STRING;
747 break;
749 case AMF_RECORDSET:
751 RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!");
752 return -1;
753 break;
755 case AMF_TYPED_OBJECT:
757 RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!");
758 return -1;
759 break;
761 case AMF_AVMPLUS:
763 int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
764 if (nRes == -1)
765 return -1;
766 nSize -= nRes;
767 prop->p_type = AMF_OBJECT;
768 break;
770 default:
771 RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__,
772 prop->p_type, pBuffer - 1);
773 return -1;
776 return nOriginalSize - nSize;
779 void
780 AMFProp_Dump(AMFObjectProperty *prop)
782 char strRes[256];
783 char str[256];
784 AVal name;
786 if (prop->p_type == AMF_INVALID)
788 RTMP_Log(RTMP_LOGDEBUG, "Property: INVALID");
789 return;
792 if (prop->p_type == AMF_NULL)
794 RTMP_Log(RTMP_LOGDEBUG, "Property: NULL");
795 return;
798 if (prop->p_name.av_len)
800 name = prop->p_name;
802 else
804 name.av_val = "no-name.";
805 name.av_len = sizeof("no-name.") - 1;
807 if (name.av_len > 18)
808 name.av_len = 18;
810 snprintf(strRes, 255, "Name: %18.*s, ", name.av_len, name.av_val);
812 if (prop->p_type == AMF_OBJECT)
814 RTMP_Log(RTMP_LOGDEBUG, "Property: <%sOBJECT>", strRes);
815 AMF_Dump(&prop->p_vu.p_object);
816 return;
819 switch (prop->p_type)
821 case AMF_NUMBER:
822 snprintf(str, 255, "NUMBER:\t%.2f", prop->p_vu.p_number);
823 break;
824 case AMF_BOOLEAN:
825 snprintf(str, 255, "BOOLEAN:\t%s",
826 prop->p_vu.p_number != 0.0 ? "TRUE" : "FALSE");
827 break;
828 case AMF_STRING:
829 snprintf(str, 255, "STRING:\t%.*s", prop->p_vu.p_aval.av_len,
830 prop->p_vu.p_aval.av_val);
831 break;
832 case AMF_DATE:
833 snprintf(str, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d",
834 prop->p_vu.p_number, prop->p_UTCoffset);
835 break;
836 default:
837 snprintf(str, 255, "INVALID TYPE 0x%02x", (unsigned char)prop->p_type);
840 RTMP_Log(RTMP_LOGDEBUG, "Property: <%s%s>", strRes, str);
843 void
844 AMFProp_Reset(AMFObjectProperty *prop)
846 if (prop->p_type == AMF_OBJECT)
847 AMF_Reset(&prop->p_vu.p_object);
848 else
850 prop->p_vu.p_aval.av_len = 0;
851 prop->p_vu.p_aval.av_val = NULL;
853 prop->p_type = AMF_INVALID;
856 /* AMFObject */
858 char *
859 AMF_Encode(AMFObject *obj, char *pBuffer, char *pBufEnd)
861 int i;
863 if (pBuffer+4 >= pBufEnd)
864 return NULL;
866 *pBuffer++ = AMF_OBJECT;
868 for (i = 0; i < obj->o_num; i++)
870 char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
871 if (res == NULL)
873 RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
875 break;
877 else
879 pBuffer = res;
883 if (pBuffer + 3 >= pBufEnd)
884 return NULL; /* no room for the end marker */
886 pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
888 return pBuffer;
892 AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize,
893 int nArrayLen, int bDecodeName)
895 int nOriginalSize = nSize;
896 int bError = FALSE;
898 obj->o_num = 0;
899 obj->o_props = NULL;
900 while (nArrayLen > 0)
902 AMFObjectProperty prop;
903 int nRes;
904 nArrayLen--;
906 nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
907 if (nRes == -1)
908 bError = TRUE;
909 else
911 nSize -= nRes;
912 pBuffer += nRes;
913 AMF_AddProp(obj, &prop);
916 if (bError)
917 return -1;
919 return nOriginalSize - nSize;
923 AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bAMFData)
925 int nOriginalSize = nSize;
926 int32_t ref;
927 int len;
929 obj->o_num = 0;
930 obj->o_props = NULL;
931 if (bAMFData)
933 if (*pBuffer != AMF3_OBJECT)
934 RTMP_Log(RTMP_LOGERROR,
935 "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!");
936 pBuffer++;
937 nSize--;
940 ref = 0;
941 len = AMF3ReadInteger(pBuffer, &ref);
942 pBuffer += len;
943 nSize -= len;
945 if ((ref & 1) == 0)
946 { /* object reference, 0xxx */
947 uint32_t objectIndex = (ref >> 1);
949 RTMP_Log(RTMP_LOGDEBUG, "Object reference, index: %d", objectIndex);
951 else /* object instance */
953 int32_t classRef = (ref >> 1);
955 AMF3ClassDef cd = { {0, 0}
957 AMFObjectProperty prop;
959 if ((classRef & 0x1) == 0)
960 { /* class reference */
961 uint32_t classIndex = (classRef >> 1);
962 RTMP_Log(RTMP_LOGDEBUG, "Class reference: %d", classIndex);
964 else
966 int32_t classExtRef = (classRef >> 1);
967 int i;
969 cd.cd_externalizable = (classExtRef & 0x1) == 1;
970 cd.cd_dynamic = ((classExtRef >> 1) & 0x1) == 1;
972 cd.cd_num = classExtRef >> 2;
974 /* class name */
976 len = AMF3ReadString(pBuffer, &cd.cd_name);
977 nSize -= len;
978 pBuffer += len;
980 /*std::string str = className; */
982 RTMP_Log(RTMP_LOGDEBUG,
983 "Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d",
984 cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic,
985 cd.cd_num);
987 for (i = 0; i < cd.cd_num; i++)
989 AVal memberName;
990 len = AMF3ReadString(pBuffer, &memberName);
991 RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val);
992 AMF3CD_AddProp(&cd, &memberName);
993 nSize -= len;
994 pBuffer += len;
998 /* add as referencable object */
1000 if (cd.cd_externalizable)
1002 int nRes;
1003 AVal name = AVC("DEFAULT_ATTRIBUTE");
1005 RTMP_Log(RTMP_LOGDEBUG, "Externalizable, TODO check");
1007 nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
1008 if (nRes == -1)
1009 RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
1010 __FUNCTION__);
1011 else
1013 nSize -= nRes;
1014 pBuffer += nRes;
1017 AMFProp_SetName(&prop, &name);
1018 AMF_AddProp(obj, &prop);
1020 else
1022 int nRes, i;
1023 for (i = 0; i < cd.cd_num; i++) /* non-dynamic */
1025 nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
1026 if (nRes == -1)
1027 RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
1028 __FUNCTION__);
1030 AMFProp_SetName(&prop, AMF3CD_GetProp(&cd, i));
1031 AMF_AddProp(obj, &prop);
1033 pBuffer += nRes;
1034 nSize -= nRes;
1036 if (cd.cd_dynamic)
1038 int len = 0;
1042 nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, TRUE);
1043 AMF_AddProp(obj, &prop);
1045 pBuffer += nRes;
1046 nSize -= nRes;
1048 len = prop.p_name.av_len;
1050 while (len > 0);
1053 RTMP_Log(RTMP_LOGDEBUG, "class object!");
1055 return nOriginalSize - nSize;
1059 AMF_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bDecodeName)
1061 int nOriginalSize = nSize;
1062 int bError = FALSE; /* if there is an error while decoding - try to at least find the end mark AMF_OBJECT_END */
1064 obj->o_num = 0;
1065 obj->o_props = NULL;
1066 while (nSize > 0)
1068 AMFObjectProperty prop;
1069 int nRes;
1071 if (nSize >=3 && AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END)
1073 nSize -= 3;
1074 bError = FALSE;
1075 break;
1078 if (bError)
1080 RTMP_Log(RTMP_LOGERROR,
1081 "DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!");
1082 nSize--;
1083 pBuffer++;
1084 continue;
1087 nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
1088 if (nRes == -1)
1089 bError = TRUE;
1090 else
1092 nSize -= nRes;
1093 pBuffer += nRes;
1094 AMF_AddProp(obj, &prop);
1098 if (bError)
1099 return -1;
1101 return nOriginalSize - nSize;
1104 void
1105 AMF_AddProp(AMFObject *obj, const AMFObjectProperty *prop)
1107 if (!(obj->o_num & 0x0f))
1108 obj->o_props =
1109 realloc(obj->o_props, (obj->o_num + 16) * sizeof(AMFObjectProperty));
1110 memcpy(&obj->o_props[obj->o_num++], prop, sizeof(AMFObjectProperty));
1114 AMF_CountProp(AMFObject *obj)
1116 return obj->o_num;
1119 AMFObjectProperty *
1120 AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex)
1122 if (nIndex >= 0)
1124 if (nIndex < obj->o_num)
1125 return &obj->o_props[nIndex];
1127 else
1129 int n;
1130 for (n = 0; n < obj->o_num; n++)
1132 if (AVMATCH(&obj->o_props[n].p_name, name))
1133 return &obj->o_props[n];
1137 return (AMFObjectProperty *)&AMFProp_Invalid;
1140 void
1141 AMF_Dump(AMFObject *obj)
1143 int n;
1144 RTMP_Log(RTMP_LOGDEBUG, "(object begin)");
1145 for (n = 0; n < obj->o_num; n++)
1147 AMFProp_Dump(&obj->o_props[n]);
1149 RTMP_Log(RTMP_LOGDEBUG, "(object end)");
1152 void
1153 AMF_Reset(AMFObject *obj)
1155 int n;
1156 for (n = 0; n < obj->o_num; n++)
1158 AMFProp_Reset(&obj->o_props[n]);
1160 free(obj->o_props);
1161 obj->o_props = NULL;
1162 obj->o_num = 0;
1166 /* AMF3ClassDefinition */
1168 void
1169 AMF3CD_AddProp(AMF3ClassDef *cd, AVal *prop)
1171 if (!(cd->cd_num & 0x0f))
1172 cd->cd_props = realloc(cd->cd_props, (cd->cd_num + 16) * sizeof(AVal));
1173 cd->cd_props[cd->cd_num++] = *prop;
1176 AVal *
1177 AMF3CD_GetProp(AMF3ClassDef *cd, int nIndex)
1179 if (nIndex >= cd->cd_num)
1180 return (AVal *)&AV_empty;
1181 return &cd->cd_props[nIndex];