v2.6 changes
[rtmpdump.git] / librtmp / amf.c
blob79541447d926fa4d58e19be0c6889dc861b0b5d5
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 AMFObject AMFObj_Invalid = { 0, 0 };
37 static const AVal AV_empty = { 0, 0 };
39 /* Data is Big-Endian */
40 unsigned short
41 AMF_DecodeInt16(const char *data)
43 unsigned char *c = (unsigned char *) data;
44 unsigned short val;
45 val = (c[0] << 8) | c[1];
46 return val;
49 unsigned int
50 AMF_DecodeInt24(const char *data)
52 unsigned char *c = (unsigned char *) data;
53 unsigned int val;
54 val = (c[0] << 16) | (c[1] << 8) | c[2];
55 return val;
58 unsigned int
59 AMF_DecodeInt32(const char *data)
61 unsigned char *c = (unsigned char *)data;
62 unsigned int val;
63 val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
64 return val;
67 void
68 AMF_DecodeString(const char *data, AVal *bv)
70 bv->av_len = AMF_DecodeInt16(data);
71 bv->av_val = (bv->av_len > 0) ? (char *)data + 2 : NULL;
74 void
75 AMF_DecodeLongString(const char *data, AVal *bv)
77 bv->av_len = AMF_DecodeInt32(data);
78 bv->av_val = (bv->av_len > 0) ? (char *)data + 4 : NULL;
81 double
82 AMF_DecodeNumber(const char *data)
84 double dVal;
85 #if __FLOAT_WORD_ORDER == __BYTE_ORDER
86 #if __BYTE_ORDER == __BIG_ENDIAN
87 memcpy(&dVal, data, 8);
88 #elif __BYTE_ORDER == __LITTLE_ENDIAN
89 unsigned char *ci, *co;
90 ci = (unsigned char *)data;
91 co = (unsigned char *)&dVal;
92 co[0] = ci[7];
93 co[1] = ci[6];
94 co[2] = ci[5];
95 co[3] = ci[4];
96 co[4] = ci[3];
97 co[5] = ci[2];
98 co[6] = ci[1];
99 co[7] = ci[0];
100 #endif
101 #else
102 #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */
103 unsigned char *ci, *co;
104 ci = (unsigned char *)data;
105 co = (unsigned char *)&dVal;
106 co[0] = ci[3];
107 co[1] = ci[2];
108 co[2] = ci[1];
109 co[3] = ci[0];
110 co[4] = ci[7];
111 co[5] = ci[6];
112 co[6] = ci[5];
113 co[7] = ci[4];
114 #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
115 unsigned char *ci, *co;
116 ci = (unsigned char *)data;
117 co = (unsigned char *)&dVal;
118 co[0] = ci[4];
119 co[1] = ci[5];
120 co[2] = ci[6];
121 co[3] = ci[7];
122 co[4] = ci[0];
123 co[5] = ci[1];
124 co[6] = ci[2];
125 co[7] = ci[3];
126 #endif
127 #endif
128 return dVal;
132 AMF_DecodeBoolean(const char *data)
134 return *data != 0;
137 char *
138 AMF_EncodeInt16(char *output, char *outend, short nVal)
140 if (output+2 > outend)
141 return NULL;
143 output[1] = nVal & 0xff;
144 output[0] = nVal >> 8;
145 return output+2;
148 char *
149 AMF_EncodeInt24(char *output, char *outend, int nVal)
151 if (output+3 > outend)
152 return NULL;
154 output[2] = nVal & 0xff;
155 output[1] = nVal >> 8;
156 output[0] = nVal >> 16;
157 return output+3;
160 char *
161 AMF_EncodeInt32(char *output, char *outend, int nVal)
163 if (output+4 > outend)
164 return NULL;
166 output[3] = nVal & 0xff;
167 output[2] = nVal >> 8;
168 output[1] = nVal >> 16;
169 output[0] = nVal >> 24;
170 return output+4;
173 char *
174 AMF_EncodeString(char *output, char *outend, const AVal *bv)
176 if ((bv->av_len < 65536 && output + 1 + 2 + bv->av_len > outend) ||
177 output + 1 + 4 + bv->av_len > outend)
178 return NULL;
180 if (bv->av_len < 65536)
182 *output++ = AMF_STRING;
184 output = AMF_EncodeInt16(output, outend, bv->av_len);
186 else
188 *output++ = AMF_LONG_STRING;
190 output = AMF_EncodeInt32(output, outend, bv->av_len);
192 memcpy(output, bv->av_val, bv->av_len);
193 output += bv->av_len;
195 return output;
198 char *
199 AMF_EncodeNumber(char *output, char *outend, double dVal)
201 if (output+1+8 > outend)
202 return NULL;
204 *output++ = AMF_NUMBER; /* type: Number */
206 #if __FLOAT_WORD_ORDER == __BYTE_ORDER
207 #if __BYTE_ORDER == __BIG_ENDIAN
208 memcpy(output, &dVal, 8);
209 #elif __BYTE_ORDER == __LITTLE_ENDIAN
211 unsigned char *ci, *co;
212 ci = (unsigned char *)&dVal;
213 co = (unsigned char *)output;
214 co[0] = ci[7];
215 co[1] = ci[6];
216 co[2] = ci[5];
217 co[3] = ci[4];
218 co[4] = ci[3];
219 co[5] = ci[2];
220 co[6] = ci[1];
221 co[7] = ci[0];
223 #endif
224 #else
225 #if __BYTE_ORDER == __LITTLE_ENDIAN /* __FLOAT_WORD_ORER == __BIG_ENDIAN */
227 unsigned char *ci, *co;
228 ci = (unsigned char *)&dVal;
229 co = (unsigned char *)output;
230 co[0] = ci[3];
231 co[1] = ci[2];
232 co[2] = ci[1];
233 co[3] = ci[0];
234 co[4] = ci[7];
235 co[5] = ci[6];
236 co[6] = ci[5];
237 co[7] = ci[4];
239 #else /* __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN */
241 unsigned char *ci, *co;
242 ci = (unsigned char *)&dVal;
243 co = (unsigned char *)output;
244 co[0] = ci[4];
245 co[1] = ci[5];
246 co[2] = ci[6];
247 co[3] = ci[7];
248 co[4] = ci[0];
249 co[5] = ci[1];
250 co[6] = ci[2];
251 co[7] = ci[3];
253 #endif
254 #endif
256 return output+8;
259 char *
260 AMF_EncodeBoolean(char *output, char *outend, int bVal)
262 if (output+2 > outend)
263 return NULL;
265 *output++ = AMF_BOOLEAN;
267 *output++ = bVal ? 0x01 : 0x00;
269 return output;
272 char *
273 AMF_EncodeNamedString(char *output, char *outend, const AVal *strName, const AVal *strValue)
275 if (output+2+strName->av_len > outend)
276 return NULL;
277 output = AMF_EncodeInt16(output, outend, strName->av_len);
279 memcpy(output, strName->av_val, strName->av_len);
280 output += strName->av_len;
282 return AMF_EncodeString(output, outend, strValue);
285 char *
286 AMF_EncodeNamedNumber(char *output, char *outend, const AVal *strName, double dVal)
288 if (output+2+strName->av_len > outend)
289 return NULL;
290 output = AMF_EncodeInt16(output, outend, strName->av_len);
292 memcpy(output, strName->av_val, strName->av_len);
293 output += strName->av_len;
295 return AMF_EncodeNumber(output, outend, dVal);
298 char *
299 AMF_EncodeNamedBoolean(char *output, char *outend, const AVal *strName, int bVal)
301 if (output+2+strName->av_len > outend)
302 return NULL;
303 output = AMF_EncodeInt16(output, outend, strName->av_len);
305 memcpy(output, strName->av_val, strName->av_len);
306 output += strName->av_len;
308 return AMF_EncodeBoolean(output, outend, bVal);
311 void
312 AMFProp_GetName(AMFObjectProperty *prop, AVal *name)
314 *name = prop->p_name;
317 void
318 AMFProp_SetName(AMFObjectProperty *prop, AVal *name)
320 prop->p_name = *name;
323 AMFDataType
324 AMFProp_GetType(AMFObjectProperty *prop)
326 return prop->p_type;
329 double
330 AMFProp_GetNumber(AMFObjectProperty *prop)
332 return prop->p_vu.p_number;
336 AMFProp_GetBoolean(AMFObjectProperty *prop)
338 return prop->p_vu.p_number != 0;
341 void
342 AMFProp_GetString(AMFObjectProperty *prop, AVal *str)
344 if (prop->p_type == AMF_STRING)
345 *str = prop->p_vu.p_aval;
346 else
347 *str = AV_empty;
350 void
351 AMFProp_GetObject(AMFObjectProperty *prop, AMFObject *obj)
353 if (prop->p_type == AMF_OBJECT)
354 *obj = prop->p_vu.p_object;
355 else
356 *obj = AMFObj_Invalid;
360 AMFProp_IsValid(AMFObjectProperty *prop)
362 return prop->p_type != AMF_INVALID;
365 char *
366 AMFProp_Encode(AMFObjectProperty *prop, char *pBuffer, char *pBufEnd)
368 if (prop->p_type == AMF_INVALID)
369 return NULL;
371 if (prop->p_type != AMF_NULL && pBuffer + prop->p_name.av_len + 2 + 1 >= pBufEnd)
372 return NULL;
374 if (prop->p_type != AMF_NULL && prop->p_name.av_len)
376 *pBuffer++ = prop->p_name.av_len >> 8;
377 *pBuffer++ = prop->p_name.av_len & 0xff;
378 memcpy(pBuffer, prop->p_name.av_val, prop->p_name.av_len);
379 pBuffer += prop->p_name.av_len;
382 switch (prop->p_type)
384 case AMF_NUMBER:
385 pBuffer = AMF_EncodeNumber(pBuffer, pBufEnd, prop->p_vu.p_number);
386 break;
388 case AMF_BOOLEAN:
389 pBuffer = AMF_EncodeBoolean(pBuffer, pBufEnd, prop->p_vu.p_number != 0);
390 break;
392 case AMF_STRING:
393 pBuffer = AMF_EncodeString(pBuffer, pBufEnd, &prop->p_vu.p_aval);
394 break;
396 case AMF_NULL:
397 if (pBuffer+1 >= pBufEnd)
398 return NULL;
399 *pBuffer++ = AMF_NULL;
400 break;
402 case AMF_OBJECT:
403 pBuffer = AMF_Encode(&prop->p_vu.p_object, pBuffer, pBufEnd);
404 break;
406 case AMF_ECMA_ARRAY:
407 pBuffer = AMF_EncodeEcmaArray(&prop->p_vu.p_object, pBuffer, pBufEnd);
408 break;
410 case AMF_STRICT_ARRAY:
411 pBuffer = AMF_EncodeArray(&prop->p_vu.p_object, pBuffer, pBufEnd);
412 break;
414 default:
415 RTMP_Log(RTMP_LOGERROR, "%s, invalid type. %d", __FUNCTION__, prop->p_type);
416 pBuffer = NULL;
419 return pBuffer;
422 #define AMF3_INTEGER_MAX 268435455
423 #define AMF3_INTEGER_MIN -268435456
426 AMF3ReadInteger(const char *data, int32_t *valp)
428 int i = 0;
429 int32_t val = 0;
431 while (i <= 2)
432 { /* handle first 3 bytes */
433 if (data[i] & 0x80)
434 { /* byte used */
435 val <<= 7; /* shift up */
436 val |= (data[i] & 0x7f); /* add bits */
437 i++;
439 else
441 break;
445 if (i > 2)
446 { /* use 4th byte, all 8bits */
447 val <<= 8;
448 val |= data[3];
450 /* range check */
451 if (val > AMF3_INTEGER_MAX)
452 val -= (1 << 29);
454 else
455 { /* use 7bits of last unparsed byte (0xxxxxxx) */
456 val <<= 7;
457 val |= data[i];
460 *valp = val;
462 return i > 2 ? 4 : i + 1;
466 AMF3ReadString(const char *data, AVal *str)
468 int32_t ref = 0;
469 int len;
470 assert(str != 0);
472 len = AMF3ReadInteger(data, &ref);
473 data += len;
475 if ((ref & 0x1) == 0)
476 { /* reference: 0xxx */
477 uint32_t refIndex = (ref >> 1);
478 RTMP_Log(RTMP_LOGDEBUG,
479 "%s, string reference, index: %d, not supported, ignoring!",
480 __FUNCTION__, refIndex);
481 str->av_val = NULL;
482 str->av_len = 0;
483 return len;
485 else
487 uint32_t nSize = (ref >> 1);
489 str->av_val = (char *)data;
490 str->av_len = nSize;
492 return len + nSize;
494 return len;
498 AMF3Prop_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
499 int bDecodeName)
501 int nOriginalSize = nSize;
502 AMF3DataType type;
504 prop->p_name.av_len = 0;
505 prop->p_name.av_val = NULL;
507 if (nSize == 0 || !pBuffer)
509 RTMP_Log(RTMP_LOGDEBUG, "empty buffer/no buffer pointer!");
510 return -1;
513 /* decode name */
514 if (bDecodeName)
516 AVal name;
517 int nRes = AMF3ReadString(pBuffer, &name);
519 if (name.av_len <= 0)
520 return nRes;
522 nSize -= nRes;
523 if (nSize <= 0)
524 return -1;
525 prop->p_name = name;
526 pBuffer += nRes;
529 /* decode */
530 type = *pBuffer++;
531 nSize--;
533 switch (type)
535 case AMF3_UNDEFINED:
536 case AMF3_NULL:
537 prop->p_type = AMF_NULL;
538 break;
539 case AMF3_FALSE:
540 prop->p_type = AMF_BOOLEAN;
541 prop->p_vu.p_number = 0.0;
542 break;
543 case AMF3_TRUE:
544 prop->p_type = AMF_BOOLEAN;
545 prop->p_vu.p_number = 1.0;
546 break;
547 case AMF3_INTEGER:
549 int32_t res = 0;
550 int len = AMF3ReadInteger(pBuffer, &res);
551 prop->p_vu.p_number = (double)res;
552 prop->p_type = AMF_NUMBER;
553 nSize -= len;
554 break;
556 case AMF3_DOUBLE:
557 if (nSize < 8)
558 return -1;
559 prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
560 prop->p_type = AMF_NUMBER;
561 nSize -= 8;
562 break;
563 case AMF3_STRING:
564 case AMF3_XML_DOC:
565 case AMF3_XML:
567 int len = AMF3ReadString(pBuffer, &prop->p_vu.p_aval);
568 prop->p_type = AMF_STRING;
569 nSize -= len;
570 break;
572 case AMF3_DATE:
574 int32_t res = 0;
575 int len = AMF3ReadInteger(pBuffer, &res);
577 nSize -= len;
578 pBuffer += len;
580 if ((res & 0x1) == 0)
581 { /* reference */
582 uint32_t nIndex = (res >> 1);
583 RTMP_Log(RTMP_LOGDEBUG, "AMF3_DATE reference: %d, not supported!", nIndex);
585 else
587 if (nSize < 8)
588 return -1;
590 prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
591 nSize -= 8;
592 prop->p_type = AMF_NUMBER;
594 break;
596 case AMF3_OBJECT:
598 int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
599 if (nRes == -1)
600 return -1;
601 nSize -= nRes;
602 prop->p_type = AMF_OBJECT;
603 break;
605 case AMF3_ARRAY:
606 case AMF3_BYTE_ARRAY:
607 default:
608 RTMP_Log(RTMP_LOGDEBUG, "%s - AMF3 unknown/unsupported datatype 0x%02x, @%p",
609 __FUNCTION__, (unsigned char)(*pBuffer), pBuffer);
610 return -1;
612 if (nSize < 0)
613 return -1;
615 return nOriginalSize - nSize;
619 AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
620 int bDecodeName)
622 int nOriginalSize = nSize;
623 int nRes;
625 prop->p_name.av_len = 0;
626 prop->p_name.av_val = NULL;
628 if (nSize == 0 || !pBuffer)
630 RTMP_Log(RTMP_LOGDEBUG, "%s: Empty buffer/no buffer pointer!", __FUNCTION__);
631 return -1;
634 if (bDecodeName && nSize < 4)
635 { /* at least name (length + at least 1 byte) and 1 byte of data */
636 RTMP_Log(RTMP_LOGDEBUG,
637 "%s: Not enough data for decoding with name, less than 4 bytes!",
638 __FUNCTION__);
639 return -1;
642 if (bDecodeName)
644 unsigned short nNameSize = AMF_DecodeInt16(pBuffer);
645 if (nNameSize > nSize - 2)
647 RTMP_Log(RTMP_LOGDEBUG,
648 "%s: Name size out of range: namesize (%d) > len (%d) - 2",
649 __FUNCTION__, nNameSize, nSize);
650 return -1;
653 AMF_DecodeString(pBuffer, &prop->p_name);
654 nSize -= 2 + nNameSize;
655 pBuffer += 2 + nNameSize;
658 if (nSize == 0)
660 return -1;
663 nSize--;
665 prop->p_type = *pBuffer++;
666 switch (prop->p_type)
668 case AMF_NUMBER:
669 if (nSize < 8)
670 return -1;
671 prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
672 nSize -= 8;
673 break;
674 case AMF_BOOLEAN:
675 if (nSize < 1)
676 return -1;
677 prop->p_vu.p_number = (double)AMF_DecodeBoolean(pBuffer);
678 nSize--;
679 break;
680 case AMF_STRING:
682 unsigned short nStringSize = AMF_DecodeInt16(pBuffer);
684 if (nSize < (long)nStringSize + 2)
685 return -1;
686 AMF_DecodeString(pBuffer, &prop->p_vu.p_aval);
687 nSize -= (2 + nStringSize);
688 break;
690 case AMF_OBJECT:
692 int nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
693 if (nRes == -1)
694 return -1;
695 nSize -= nRes;
696 break;
698 case AMF_MOVIECLIP:
700 RTMP_Log(RTMP_LOGERROR, "AMF_MOVIECLIP reserved!");
701 return -1;
702 break;
704 case AMF_NULL:
705 case AMF_UNDEFINED:
706 case AMF_UNSUPPORTED:
707 prop->p_type = AMF_NULL;
708 break;
709 case AMF_REFERENCE:
711 RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!");
712 return -1;
713 break;
715 case AMF_ECMA_ARRAY:
717 nSize -= 4;
719 /* next comes the rest, mixed array has a final 0x000009 mark and names, so its an object */
720 nRes = AMF_Decode(&prop->p_vu.p_object, pBuffer + 4, nSize, TRUE);
721 if (nRes == -1)
722 return -1;
723 nSize -= nRes;
724 break;
726 case AMF_OBJECT_END:
728 return -1;
729 break;
731 case AMF_STRICT_ARRAY:
733 unsigned int nArrayLen = AMF_DecodeInt32(pBuffer);
734 nSize -= 4;
736 nRes = AMF_DecodeArray(&prop->p_vu.p_object, pBuffer + 4, nSize,
737 nArrayLen, FALSE);
738 if (nRes == -1)
739 return -1;
740 nSize -= nRes;
741 break;
743 case AMF_DATE:
745 RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE");
747 if (nSize < 10)
748 return -1;
750 prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
751 prop->p_UTCoffset = AMF_DecodeInt16(pBuffer + 8);
753 nSize -= 10;
754 break;
756 case AMF_LONG_STRING:
757 case AMF_XML_DOC:
759 unsigned int nStringSize = AMF_DecodeInt32(pBuffer);
760 if (nSize < (long)nStringSize + 4)
761 return -1;
762 AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval);
763 nSize -= (4 + nStringSize);
764 if (prop->p_type == AMF_LONG_STRING)
765 prop->p_type = AMF_STRING;
766 break;
768 case AMF_RECORDSET:
770 RTMP_Log(RTMP_LOGERROR, "AMF_RECORDSET reserved!");
771 return -1;
772 break;
774 case AMF_TYPED_OBJECT:
776 RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!");
777 return -1;
778 break;
780 case AMF_AVMPLUS:
782 int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
783 if (nRes == -1)
784 return -1;
785 nSize -= nRes;
786 prop->p_type = AMF_OBJECT;
787 break;
789 default:
790 RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__,
791 prop->p_type, pBuffer - 1);
792 return -1;
795 return nOriginalSize - nSize;
798 void
799 AMFProp_Dump(AMFObjectProperty *prop)
801 char strRes[256];
802 char str[256];
803 AVal name;
805 if (prop->p_type == AMF_INVALID)
807 RTMP_Log(RTMP_LOGDEBUG, "Property: INVALID");
808 return;
811 if (prop->p_type == AMF_NULL)
813 RTMP_Log(RTMP_LOGDEBUG, "Property: NULL");
814 return;
817 if (prop->p_name.av_len)
819 name = prop->p_name;
821 else
823 name.av_val = "no-name.";
824 name.av_len = sizeof("no-name.") - 1;
826 if (name.av_len > 18)
827 name.av_len = 18;
829 snprintf(strRes, 255, "Name: %18.*s, ", name.av_len, name.av_val);
831 if (prop->p_type == AMF_OBJECT)
833 RTMP_Log(RTMP_LOGDEBUG, "Property: <%sOBJECT>", strRes);
834 AMF_Dump(&prop->p_vu.p_object);
835 return;
837 else if (prop->p_type == AMF_ECMA_ARRAY)
839 RTMP_Log(RTMP_LOGDEBUG, "Property: <%sECMA_ARRAY>", strRes);
840 AMF_Dump(&prop->p_vu.p_object);
841 return;
843 else if (prop->p_type == AMF_STRICT_ARRAY)
845 RTMP_Log(RTMP_LOGDEBUG, "Property: <%sSTRICT_ARRAY>", strRes);
846 AMF_Dump(&prop->p_vu.p_object);
847 return;
850 switch (prop->p_type)
852 case AMF_NUMBER:
853 snprintf(str, 255, "NUMBER:\t%.2f", prop->p_vu.p_number);
854 break;
855 case AMF_BOOLEAN:
856 snprintf(str, 255, "BOOLEAN:\t%s",
857 prop->p_vu.p_number != 0.0 ? "TRUE" : "FALSE");
858 break;
859 case AMF_STRING:
860 snprintf(str, 255, "STRING:\t%.*s", prop->p_vu.p_aval.av_len,
861 prop->p_vu.p_aval.av_val);
862 break;
863 case AMF_DATE:
864 snprintf(str, 255, "DATE:\ttimestamp: %.2f, UTC offset: %d",
865 prop->p_vu.p_number, prop->p_UTCoffset);
866 break;
867 default:
868 snprintf(str, 255, "INVALID TYPE 0x%02x", (unsigned char)prop->p_type);
871 RTMP_Log(RTMP_LOGDEBUG, "Property: <%s%s>", strRes, str);
874 void
875 AMFProp_Reset(AMFObjectProperty *prop)
877 if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY ||
878 prop->p_type == AMF_STRICT_ARRAY)
879 AMF_Reset(&prop->p_vu.p_object);
880 else
882 prop->p_vu.p_aval.av_len = 0;
883 prop->p_vu.p_aval.av_val = NULL;
885 prop->p_type = AMF_INVALID;
888 /* AMFObject */
890 char *
891 AMF_Encode(AMFObject *obj, char *pBuffer, char *pBufEnd)
893 int i;
895 if (pBuffer+4 >= pBufEnd)
896 return NULL;
898 *pBuffer++ = AMF_OBJECT;
900 for (i = 0; i < obj->o_num; i++)
902 char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
903 if (res == NULL)
905 RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
907 break;
909 else
911 pBuffer = res;
915 if (pBuffer + 3 >= pBufEnd)
916 return NULL; /* no room for the end marker */
918 pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
920 return pBuffer;
923 char *
924 AMF_EncodeEcmaArray(AMFObject *obj, char *pBuffer, char *pBufEnd)
926 int i;
928 if (pBuffer+4 >= pBufEnd)
929 return NULL;
931 *pBuffer++ = AMF_ECMA_ARRAY;
933 pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num);
935 for (i = 0; i < obj->o_num; i++)
937 char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
938 if (res == NULL)
940 RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
942 break;
944 else
946 pBuffer = res;
950 if (pBuffer + 3 >= pBufEnd)
951 return NULL; /* no room for the end marker */
953 pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
955 return pBuffer;
958 char *
959 AMF_EncodeArray(AMFObject *obj, char *pBuffer, char *pBufEnd)
961 int i;
963 if (pBuffer+4 >= pBufEnd)
964 return NULL;
966 *pBuffer++ = AMF_STRICT_ARRAY;
968 pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num);
970 for (i = 0; i < obj->o_num; i++)
972 char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
973 if (res == NULL)
975 RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
977 break;
979 else
981 pBuffer = res;
985 //if (pBuffer + 3 >= pBufEnd)
986 // return NULL; /* no room for the end marker */
988 //pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
990 return pBuffer;
994 AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize,
995 int nArrayLen, int bDecodeName)
997 int nOriginalSize = nSize;
998 int bError = FALSE;
1000 obj->o_num = 0;
1001 obj->o_props = NULL;
1002 while (nArrayLen > 0)
1004 AMFObjectProperty prop;
1005 int nRes;
1006 nArrayLen--;
1008 if (nSize <= 0)
1010 bError = TRUE;
1011 break;
1013 nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
1014 if (nRes == -1)
1016 bError = TRUE;
1017 break;
1019 else
1021 nSize -= nRes;
1022 pBuffer += nRes;
1023 AMF_AddProp(obj, &prop);
1026 if (bError)
1027 return -1;
1029 return nOriginalSize - nSize;
1033 AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bAMFData)
1035 int nOriginalSize = nSize;
1036 int32_t ref;
1037 int len;
1039 obj->o_num = 0;
1040 obj->o_props = NULL;
1041 if (bAMFData)
1043 if (*pBuffer != AMF3_OBJECT)
1044 RTMP_Log(RTMP_LOGERROR,
1045 "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!");
1046 pBuffer++;
1047 nSize--;
1050 ref = 0;
1051 len = AMF3ReadInteger(pBuffer, &ref);
1052 pBuffer += len;
1053 nSize -= len;
1055 if ((ref & 1) == 0)
1056 { /* object reference, 0xxx */
1057 uint32_t objectIndex = (ref >> 1);
1059 RTMP_Log(RTMP_LOGDEBUG, "Object reference, index: %d", objectIndex);
1061 else /* object instance */
1063 int32_t classRef = (ref >> 1);
1065 AMF3ClassDef cd = { {0, 0}
1067 AMFObjectProperty prop;
1069 if ((classRef & 0x1) == 0)
1070 { /* class reference */
1071 uint32_t classIndex = (classRef >> 1);
1072 RTMP_Log(RTMP_LOGDEBUG, "Class reference: %d", classIndex);
1074 else
1076 int32_t classExtRef = (classRef >> 1);
1077 int i, cdnum;
1079 cd.cd_externalizable = (classExtRef & 0x1) == 1;
1080 cd.cd_dynamic = ((classExtRef >> 1) & 0x1) == 1;
1082 cdnum = classExtRef >> 2;
1084 /* class name */
1086 len = AMF3ReadString(pBuffer, &cd.cd_name);
1087 nSize -= len;
1088 pBuffer += len;
1090 /*std::string str = className; */
1092 RTMP_Log(RTMP_LOGDEBUG,
1093 "Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d",
1094 cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic,
1095 cd.cd_num);
1097 for (i = 0; i < cdnum; i++)
1099 AVal memberName;
1100 if (nSize <=0)
1102 invalid:
1103 RTMP_Log(RTMP_LOGDEBUG, "%s, invalid class encoding!",
1104 __FUNCTION__);
1105 return nOriginalSize;
1107 len = AMF3ReadString(pBuffer, &memberName);
1108 RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val);
1109 AMF3CD_AddProp(&cd, &memberName);
1110 nSize -= len;
1111 pBuffer += len;
1115 /* add as referencable object */
1117 if (cd.cd_externalizable)
1119 int nRes;
1120 AVal name = AVC("DEFAULT_ATTRIBUTE");
1122 RTMP_Log(RTMP_LOGDEBUG, "Externalizable, TODO check");
1124 nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
1125 if (nRes == -1)
1126 RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
1127 __FUNCTION__);
1128 else
1130 nSize -= nRes;
1131 pBuffer += nRes;
1134 AMFProp_SetName(&prop, &name);
1135 AMF_AddProp(obj, &prop);
1137 else
1139 int nRes, i;
1140 for (i = 0; i < cd.cd_num; i++) /* non-dynamic */
1142 if (nSize <=0)
1143 goto invalid;
1144 nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
1145 if (nRes == -1)
1146 RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
1147 __FUNCTION__);
1149 AMFProp_SetName(&prop, AMF3CD_GetProp(&cd, i));
1150 AMF_AddProp(obj, &prop);
1152 pBuffer += nRes;
1153 nSize -= nRes;
1155 if (cd.cd_dynamic)
1157 int len = 0;
1161 if (nSize <=0)
1162 goto invalid;
1163 nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, TRUE);
1164 AMF_AddProp(obj, &prop);
1166 pBuffer += nRes;
1167 nSize -= nRes;
1169 len = prop.p_name.av_len;
1171 while (len > 0);
1174 RTMP_Log(RTMP_LOGDEBUG, "class object!");
1176 return nOriginalSize - nSize;
1180 AMF_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bDecodeName)
1182 int nOriginalSize = nSize;
1183 int bError = FALSE; /* if there is an error while decoding - try to at least find the end mark AMF_OBJECT_END */
1185 obj->o_num = 0;
1186 obj->o_props = NULL;
1187 while (nSize > 0)
1189 AMFObjectProperty prop;
1190 int nRes;
1192 if (nSize >=3 && AMF_DecodeInt24(pBuffer) == AMF_OBJECT_END)
1194 nSize -= 3;
1195 bError = FALSE;
1196 break;
1199 if (bError)
1201 RTMP_Log(RTMP_LOGERROR,
1202 "DECODING ERROR, IGNORING BYTES UNTIL NEXT KNOWN PATTERN!");
1203 nSize--;
1204 pBuffer++;
1205 continue;
1208 nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
1209 if (nRes == -1)
1211 bError = TRUE;
1212 break;
1214 else
1216 nSize -= nRes;
1217 if (nSize < 0)
1219 bError = TRUE;
1220 break;
1222 pBuffer += nRes;
1223 AMF_AddProp(obj, &prop);
1227 if (bError)
1228 return -1;
1230 return nOriginalSize - nSize;
1233 void
1234 AMF_AddProp(AMFObject *obj, const AMFObjectProperty *prop)
1236 if (!(obj->o_num & 0x0f))
1237 obj->o_props =
1238 realloc(obj->o_props, (obj->o_num + 16) * sizeof(AMFObjectProperty));
1239 memcpy(&obj->o_props[obj->o_num++], prop, sizeof(AMFObjectProperty));
1243 AMF_CountProp(AMFObject *obj)
1245 return obj->o_num;
1248 AMFObjectProperty *
1249 AMF_GetProp(AMFObject *obj, const AVal *name, int nIndex)
1251 if (nIndex >= 0)
1253 if (nIndex < obj->o_num)
1254 return &obj->o_props[nIndex];
1256 else
1258 int n;
1259 for (n = 0; n < obj->o_num; n++)
1261 if (AVMATCH(&obj->o_props[n].p_name, name))
1262 return &obj->o_props[n];
1266 return (AMFObjectProperty *)&AMFProp_Invalid;
1269 void
1270 AMF_Dump(AMFObject *obj)
1272 int n;
1273 RTMP_Log(RTMP_LOGDEBUG, "(object begin)");
1274 for (n = 0; n < obj->o_num; n++)
1276 AMFProp_Dump(&obj->o_props[n]);
1278 RTMP_Log(RTMP_LOGDEBUG, "(object end)");
1281 void
1282 AMF_Reset(AMFObject *obj)
1284 int n;
1285 for (n = 0; n < obj->o_num; n++)
1287 AMFProp_Reset(&obj->o_props[n]);
1289 free(obj->o_props);
1290 obj->o_props = NULL;
1291 obj->o_num = 0;
1295 /* AMF3ClassDefinition */
1297 void
1298 AMF3CD_AddProp(AMF3ClassDef *cd, AVal *prop)
1300 if (!(cd->cd_num & 0x0f))
1301 cd->cd_props = realloc(cd->cd_props, (cd->cd_num + 16) * sizeof(AVal));
1302 cd->cd_props[cd->cd_num++] = *prop;
1305 AVal *
1306 AMF3CD_GetProp(AMF3ClassDef *cd, int nIndex)
1308 if (nIndex >= cd->cd_num)
1309 return (AVal *)&AV_empty;
1310 return &cd->cd_props[nIndex];