Fix mismatched format string conversions
[rtmpdump.git] / librtmp / amf.c
blobf9ecf216c95a07f88535c6fa541789f2c85f5596
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:
739 unsigned int nStringSize = AMF_DecodeInt32(pBuffer);
740 if (nSize < (long)nStringSize + 4)
741 return -1;
742 AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval);
743 nSize -= (4 + nStringSize);
744 prop->p_type = AMF_STRING;
745 break;
747 case AMF_RECORDSET:
749 RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!");
750 return -1;
751 break;
753 case AMF_XML_DOC:
755 RTMP_Log(RTMP_LOGERROR, "AMF_XML_DOC not supported!");
756 return -1;
757 break;
759 case AMF_TYPED_OBJECT:
761 RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!");
762 return -1;
763 break;
765 case AMF_AVMPLUS:
767 int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
768 if (nRes == -1)
769 return -1;
770 nSize -= nRes;
771 prop->p_type = AMF_OBJECT;
772 break;
774 default:
775 RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__,
776 prop->p_type, pBuffer - 1);
777 return -1;
780 return nOriginalSize - nSize;
783 void
784 AMFProp_Dump(AMFObjectProperty *prop)
786 char strRes[256];
787 char str[256];
788 AVal name;
790 if (prop->p_type == AMF_INVALID)
792 RTMP_Log(RTMP_LOGDEBUG, "Property: INVALID");
793 return;
796 if (prop->p_type == AMF_NULL)
798 RTMP_Log(RTMP_LOGDEBUG, "Property: NULL");
799 return;
802 if (prop->p_name.av_len)
804 name = prop->p_name;
806 else
808 name.av_val = "no-name.";
809 name.av_len = sizeof("no-name.") - 1;
811 if (name.av_len > 18)
812 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);
820 return;
823 switch (prop->p_type)
825 case AMF_NUMBER:
826 snprintf(str, 255, "NUMBER:\t%.2f", prop->p_vu.p_number);
827 break;
828 case AMF_BOOLEAN:
829 snprintf(str, 255, "BOOLEAN:\t%s",
830 prop->p_vu.p_number != 0.0 ? "TRUE" : "FALSE");
831 break;
832 case AMF_STRING:
833 snprintf(str, 255, "STRING:\t%.*s", prop->p_vu.p_aval.av_len,
834 prop->p_vu.p_aval.av_val);
835 break;
836 case AMF_DATE:
837 snprintf(str, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d",
838 prop->p_vu.p_number, prop->p_UTCoffset);
839 break;
840 default:
841 snprintf(str, 255, "INVALID TYPE 0x%02x", (unsigned char)prop->p_type);
844 RTMP_Log(RTMP_LOGDEBUG, "Property: <%s%s>", strRes, str);
847 void
848 AMFProp_Reset(AMFObjectProperty *prop)
850 if (prop->p_type == AMF_OBJECT)
851 AMF_Reset(&prop->p_vu.p_object);
852 else
854 prop->p_vu.p_aval.av_len = 0;
855 prop->p_vu.p_aval.av_val = NULL;
857 prop->p_type = AMF_INVALID;
860 /* AMFObject */
862 char *
863 AMF_Encode(AMFObject *obj, char *pBuffer, char *pBufEnd)
865 int i;
867 if (pBuffer+4 >= pBufEnd)
868 return NULL;
870 *pBuffer++ = AMF_OBJECT;
872 for (i = 0; i < obj->o_num; i++)
874 char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
875 if (res == NULL)
877 RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
879 break;
881 else
883 pBuffer = res;
887 if (pBuffer + 3 >= pBufEnd)
888 return NULL; /* no room for the end marker */
890 pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
892 return pBuffer;
896 AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize,
897 int nArrayLen, int bDecodeName)
899 int nOriginalSize = nSize;
900 int bError = FALSE;
902 obj->o_num = 0;
903 obj->o_props = NULL;
904 while (nArrayLen > 0)
906 AMFObjectProperty prop;
907 int nRes;
908 nArrayLen--;
910 nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
911 if (nRes == -1)
912 bError = TRUE;
913 else
915 nSize -= nRes;
916 pBuffer += nRes;
917 AMF_AddProp(obj, &prop);
920 if (bError)
921 return -1;
923 return nOriginalSize - nSize;
927 AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bAMFData)
929 int nOriginalSize = nSize;
930 int32_t ref;
931 int len;
933 obj->o_num = 0;
934 obj->o_props = NULL;
935 if (bAMFData)
937 if (*pBuffer != AMF3_OBJECT)
938 RTMP_Log(RTMP_LOGERROR,
939 "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!");
940 pBuffer++;
941 nSize--;
944 ref = 0;
945 len = AMF3ReadInteger(pBuffer, &ref);
946 pBuffer += len;
947 nSize -= len;
949 if ((ref & 1) == 0)
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);
968 else
970 int32_t classExtRef = (classRef >> 1);
971 int i;
973 cd.cd_externalizable = (classExtRef & 0x1) == 1;
974 cd.cd_dynamic = ((classExtRef >> 1) & 0x1) == 1;
976 cd.cd_num = classExtRef >> 2;
978 /* class name */
980 len = AMF3ReadString(pBuffer, &cd.cd_name);
981 nSize -= len;
982 pBuffer += len;
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,
989 cd.cd_num);
991 for (i = 0; i < cd.cd_num; i++)
993 AVal memberName;
994 len = AMF3ReadString(pBuffer, &memberName);
995 RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val);
996 AMF3CD_AddProp(&cd, &memberName);
997 nSize -= len;
998 pBuffer += len;
1002 /* add as referencable object */
1004 if (cd.cd_externalizable)
1006 int nRes;
1007 AVal name = AVC("DEFAULT_ATTRIBUTE");
1009 RTMP_Log(RTMP_LOGDEBUG, "Externalizable, TODO check");
1011 nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
1012 if (nRes == -1)
1013 RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
1014 __FUNCTION__);
1015 else
1017 nSize -= nRes;
1018 pBuffer += nRes;
1021 AMFProp_SetName(&prop, &name);
1022 AMF_AddProp(obj, &prop);
1024 else
1026 int nRes, i;
1027 for (i = 0; i < cd.cd_num; i++) /* non-dynamic */
1029 nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
1030 if (nRes == -1)
1031 RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
1032 __FUNCTION__);
1034 AMFProp_SetName(&prop, AMF3CD_GetProp(&cd, i));
1035 AMF_AddProp(obj, &prop);
1037 pBuffer += nRes;
1038 nSize -= nRes;
1040 if (cd.cd_dynamic)
1042 int len = 0;
1046 nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, TRUE);
1047 AMF_AddProp(obj, &prop);
1049 pBuffer += nRes;
1050 nSize -= nRes;
1052 len = prop.p_name.av_len;
1054 while (len > 0);
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 */
1068 obj->o_num = 0;
1069 obj->o_props = NULL;
1070 while (nSize > 0)
1072 AMFObjectProperty prop;
1073 int nRes;
1075 if (nSize >=3 && AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END)
1077 nSize -= 3;
1078 bError = FALSE;
1079 break;
1082 if (bError)
1084 RTMP_Log(RTMP_LOGERROR,
1085 "DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!");
1086 nSize--;
1087 pBuffer++;
1088 continue;
1091 nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
1092 if (nRes == -1)
1093 bError = TRUE;
1094 else
1096 nSize -= nRes;
1097 pBuffer += nRes;
1098 AMF_AddProp(obj, &prop);
1102 if (bError)
1103 return -1;
1105 return nOriginalSize - nSize;
1108 void
1109 AMF_AddProp(AMFObject *obj, const AMFObjectProperty *prop)
1111 if (!(obj->o_num & 0x0f))
1112 obj->o_props =
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)
1120 return obj->o_num;
1123 AMFObjectProperty *
1124 AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex)
1126 if (nIndex >= 0)
1128 if (nIndex <= obj->o_num)
1129 return &obj->o_props[nIndex];
1131 else
1133 int n;
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;
1144 void
1145 AMF_Dump(AMFObject *obj)
1147 int n;
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)");
1156 void
1157 AMF_Reset(AMFObject *obj)
1159 int n;
1160 for (n = 0; n < obj->o_num; n++)
1162 AMFProp_Reset(&obj->o_props[n]);
1164 free(obj->o_props);
1165 obj->o_props = NULL;
1166 obj->o_num = 0;
1170 /* AMF3ClassDefinition */
1172 void
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;
1180 AVal *
1181 AMF3CD_GetProp(AMF3ClassDef *cd, int nIndex)
1183 if (nIndex >= cd->cd_num)
1184 return (AVal *)&AV_empty;
1185 return &cd->cd_props[nIndex];