|
- #include "amf0.h"
- #include <stdint.h>
- #include <stddef.h>
- #include <string.h>
- #include <assert.h>
-
- static double s_double = 1.0; // 3ff0 0000 0000 0000
-
- static uint8_t* AMFWriteInt16(uint8_t* ptr, const uint8_t* end, uint16_t value)
- {
- if (ptr + 2 > end) return NULL;
- ptr[0] = value >> 8;
- ptr[1] = value & 0xFF;
- return ptr + 2;
- }
-
- static uint8_t* AMFWriteInt32(uint8_t* ptr, const uint8_t* end, uint32_t value)
- {
- if (ptr + 4 > end) return NULL;
- ptr[0] = (uint8_t)(value >> 24);
- ptr[1] = (uint8_t)(value >> 16);
- ptr[2] = (uint8_t)(value >> 8);
- ptr[3] = (uint8_t)(value & 0xFF);
- return ptr + 4;
- }
-
- static uint8_t* AMFWriteString16(uint8_t* ptr, const uint8_t* end, const char* string, size_t length)
- {
- if (ptr + 2 + length > end) return NULL;
- ptr = AMFWriteInt16(ptr, end, (uint16_t)length);
- memcpy(ptr, string, length);
- return ptr + length;
- }
-
- static uint8_t* AMFWriteString32(uint8_t* ptr, const uint8_t* end, const char* string, size_t length)
- {
- if (ptr + 4 + length > end) return NULL;
- ptr = AMFWriteInt32(ptr, end, (uint32_t)length);
- memcpy(ptr, string, length);
- return ptr + length;
- }
-
- uint8_t* AMFWriteNull(uint8_t* ptr, const uint8_t* end)
- {
- if (!ptr || ptr + 1 > end) return NULL;
-
- *ptr++ = AMF_NULL;
- return ptr;
- }
-
- uint8_t* AMFWriteUndefined(uint8_t* ptr, const uint8_t* end)
- {
- if (!ptr || ptr + 1 > end) return NULL;
-
- *ptr++ = AMF_UNDEFINED;
- return ptr;
- }
-
- uint8_t* AMFWriteObject(uint8_t* ptr, const uint8_t* end)
- {
- if (!ptr || ptr + 1 > end) return NULL;
-
- *ptr++ = AMF_OBJECT;
- return ptr;
- }
-
- uint8_t* AMFWriteObjectEnd(uint8_t* ptr, const uint8_t* end)
- {
- if (!ptr || ptr + 3 > end) return NULL;
-
- /* end of object - 0x00 0x00 0x09 */
- *ptr++ = 0;
- *ptr++ = 0;
- *ptr++ = AMF_OBJECT_END;
- return ptr;
- }
-
- uint8_t* AMFWriteTypedObject(uint8_t* ptr, const uint8_t* end)
- {
- if (!ptr || ptr + 1 > end) return NULL;
-
- *ptr++ = AMF_TYPED_OBJECT;
- return ptr;
- }
-
- uint8_t* AMFWriteECMAArarry(uint8_t* ptr, const uint8_t* end)
- {
- if (!ptr || ptr + 1 > end) return NULL;
-
- *ptr++ = AMF_ECMA_ARRAY;
- return AMFWriteInt32(ptr, end, 0); // U32 associative-count
- }
-
- uint8_t* AMFWriteBoolean(uint8_t* ptr, const uint8_t* end, uint8_t value)
- {
- if (!ptr || ptr + 2 > end) return NULL;
-
- ptr[0] = AMF_BOOLEAN;
- ptr[1] = 0 == value ? 0 : 1;
- return ptr + 2;
- }
-
- uint8_t* AMFWriteDouble(uint8_t* ptr, const uint8_t* end, double value)
- {
- if (!ptr || ptr + 9 > end) return NULL;
-
- assert(8 == sizeof(double));
- *ptr++ = AMF_NUMBER;
-
- // Little-Endian
- if (0x00 == *(char*)&s_double)
- {
- *ptr++ = ((uint8_t*)&value)[7];
- *ptr++ = ((uint8_t*)&value)[6];
- *ptr++ = ((uint8_t*)&value)[5];
- *ptr++ = ((uint8_t*)&value)[4];
- *ptr++ = ((uint8_t*)&value)[3];
- *ptr++ = ((uint8_t*)&value)[2];
- *ptr++ = ((uint8_t*)&value)[1];
- *ptr++ = ((uint8_t*)&value)[0];
- }
- else
- {
- memcpy(ptr, &value, 8);
- }
- return ptr;
- }
-
- uint8_t* AMFWriteString(uint8_t* ptr, const uint8_t* end, const char* string, size_t length)
- {
- if (!ptr || ptr + 1 + (length < 65536 ? 2 : 4) + length > end || length > UINT32_MAX)
- return NULL;
-
- if (length < 65536)
- {
- *ptr++ = AMF_STRING;
- AMFWriteString16(ptr, end, string, length);
- ptr += 2;
- }
- else
- {
- *ptr++ = AMF_LONG_STRING;
- AMFWriteString32(ptr, end, string, length);
- ptr += 4;
- }
- return ptr + length;
- }
-
- uint8_t* AMFWriteDate(uint8_t* ptr, const uint8_t* end, double milliseconds, int16_t timezone)
- {
- if (!ptr || ptr + 11 > end)
- return NULL;
-
- AMFWriteDouble(ptr, end, milliseconds);
- *ptr = AMF_DATE; // rewrite to date
- return AMFWriteInt16(ptr + 8, end, timezone);
- }
-
- uint8_t* AMFWriteNamedBoolean(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, uint8_t value)
- {
- if (ptr + length + 2 + 2 > end)
- return NULL;
-
- ptr = AMFWriteString16(ptr, end, name, length);
- return ptr ? AMFWriteBoolean(ptr, end, value) : NULL;
- }
-
- uint8_t* AMFWriteNamedDouble(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, double value)
- {
- if (ptr + length + 2 + 8 + 1 > end)
- return NULL;
-
- ptr = AMFWriteString16(ptr, end, name, length);
- return ptr ? AMFWriteDouble(ptr, end, value) : NULL;
- }
-
- uint8_t* AMFWriteNamedString(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, const char* value, size_t length2)
- {
- if (ptr + length + 2 + length2 + 3 > end)
- return NULL;
-
- ptr = AMFWriteString16(ptr, end, name, length);
- return ptr ? AMFWriteString(ptr, end, value, length2) : NULL;
- }
-
- static const uint8_t* AMFReadInt16(const uint8_t* ptr, const uint8_t* end, uint32_t* value)
- {
- if (!ptr || ptr + 2 > end)
- return NULL;
-
- if (value)
- {
- *value = ((uint32_t)ptr[0] << 8) | ptr[1];
- }
- return ptr + 2;
- }
-
- static const uint8_t* AMFReadInt32(const uint8_t* ptr, const uint8_t* end, uint32_t* value)
- {
- if (!ptr || ptr + 4 > end)
- return NULL;
-
- if (value)
- {
- *value = ((uint32_t)ptr[0] << 24) | ((uint32_t)ptr[1] << 16) | ((uint32_t)ptr[2] << 8) | ptr[3];
- }
- return ptr + 4;
- }
-
- const uint8_t* AMFReadNull(const uint8_t* ptr, const uint8_t* end)
- {
- (void)end;
- return ptr;
- }
-
- const uint8_t* AMFReadUndefined(const uint8_t* ptr, const uint8_t* end)
- {
- (void)end;
- return ptr;
- }
-
- const uint8_t* AMFReadBoolean(const uint8_t* ptr, const uint8_t* end, uint8_t* value)
- {
- if (!ptr || ptr + 1 > end)
- return NULL;
-
- if (value)
- {
- *value = ptr[0];
- }
- return ptr + 1;
- }
-
- const uint8_t* AMFReadDouble(const uint8_t* ptr, const uint8_t* end, double* value)
- {
- uint8_t* p = (uint8_t*)value;
- if (!ptr || ptr + 8 > end)
- return NULL;
-
- if (value)
- {
- if (0x00 == *(char*)&s_double)
- {// Little-Endian
- *p++ = ptr[7];
- *p++ = ptr[6];
- *p++ = ptr[5];
- *p++ = ptr[4];
- *p++ = ptr[3];
- *p++ = ptr[2];
- *p++ = ptr[1];
- *p++ = ptr[0];
- }
- else
- {
- memcpy(value, ptr, 8);
- }
- }
- return ptr + 8;
- }
-
- const uint8_t* AMFReadString(const uint8_t* ptr, const uint8_t* end, int isLongString, char* string, size_t length)
- {
- uint32_t len = 0;
- if (0 == isLongString)
- ptr = AMFReadInt16(ptr, end, &len);
- else
- ptr = AMFReadInt32(ptr, end, &len);
-
- if (!ptr || ptr + len > end)
- return NULL;
-
- if (string && length > len)
- {
- memcpy(string, ptr, len);
- string[len] = 0;
- }
- return ptr + len;
- }
-
- const uint8_t* AMFReadDate(const uint8_t* ptr, const uint8_t* end, double *milliseconds, int16_t *timezone)
- {
- uint32_t v;
- ptr = AMFReadDouble(ptr, end, milliseconds);
- if (ptr)
- {
- ptr = AMFReadInt16(ptr, end, &v);
- if(timezone)
- *timezone = (int16_t)v;
- }
- return ptr;
- }
-
- static const uint8_t* amf_read_object(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t n);
- static const uint8_t* amf_read_ecma_array(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t n);
- static const uint8_t* amf_read_strict_array(const uint8_t* ptr, const uint8_t* end, struct amf_object_item_t* items, size_t n);
-
- static const uint8_t* amf_read_item(const uint8_t* data, const uint8_t* end, enum AMFDataType type, struct amf_object_item_t* item)
- {
- switch (type)
- {
- case AMF_BOOLEAN:
- return AMFReadBoolean(data, end, (uint8_t*)(item ? item->value : NULL));
-
- case AMF_NUMBER:
- return AMFReadDouble(data, end, (double*)(item ? item->value : NULL));
-
- case AMF_STRING:
- return AMFReadString(data, end, 0, (char*)(item ? item->value : NULL), item ? item->size : 0);
-
- case AMF_LONG_STRING:
- return AMFReadString(data, end, 1, (char*)(item ? item->value : NULL), item ? item->size : 0);
-
- case AMF_DATE:
- return AMFReadDate(data, end, (double*)(item ? item->value : NULL), (int16_t*)(item ? (char*)item->value + 8 : NULL));
-
- case AMF_OBJECT:
- return amf_read_object(data, end, (struct amf_object_item_t*)(item ? item->value : NULL), item ? item->size : 0);
-
- case AMF_NULL:
- return data;
-
- case AMF_UNDEFINED:
- return data;
-
- case AMF_ECMA_ARRAY:
- return amf_read_ecma_array(data, end, (struct amf_object_item_t*)(item ? item->value : NULL), item ? item->size : 0);
-
- case AMF_STRICT_ARRAY:
- return amf_read_strict_array(data, end, (struct amf_object_item_t*)(item ? item->value : NULL), item ? item->size : 0);
-
- default:
- assert(0);
- return NULL;
- }
- }
-
- static inline int amf_read_item_type_check(uint8_t type0, uint8_t itemtype)
- {
- // decode AMF_ECMA_ARRAY as AMF_OBJECT
- return (type0 == itemtype || (AMF_OBJECT == itemtype && (AMF_ECMA_ARRAY == type0 || AMF_NULL == type0))) ? 1 : 0;
- }
-
- static const uint8_t* amf_read_strict_array(const uint8_t* ptr, const uint8_t* end, struct amf_object_item_t* items, size_t n)
- {
- uint8_t type;
- uint32_t i, count;
- if (!ptr || ptr + 4 > end)
- return NULL;
-
- ptr = AMFReadInt32(ptr, end, &count); // U32 array-count
- for (i = 0; i < count && ptr && ptr < end; i++)
- {
- type = *ptr++;
- ptr = amf_read_item(ptr, end, type, (i < n && amf_read_item_type_check(type, items[i].type)) ? &items[i] : NULL);
- }
-
- return ptr;
- }
-
- static const uint8_t* amf_read_ecma_array(const uint8_t* ptr, const uint8_t* end, struct amf_object_item_t* items, size_t n)
- {
- if (!ptr || ptr + 4 > end)
- return NULL;
- ptr += 4; // U32 associative-count
- return amf_read_object(ptr, end, items, n);
- }
-
- static const uint8_t* amf_read_object(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t n)
- {
- uint8_t type;
- uint32_t len;
- size_t i;
-
- while (data && data + 2 <= end)
- {
- len = *data++ << 8;
- len |= *data++;
- if (0 == len)
- break; // last item
-
- if (data + len + 1 > end)
- return NULL; // invalid
-
- for (i = 0; i < n; i++)
- {
- if (strlen(items[i].name) == len && 0 == memcmp(items[i].name, data, len) && amf_read_item_type_check(data[len], items[i].type))
- break;
- }
-
- data += len; // skip name string
- type = *data++; // value type
- data = amf_read_item(data, end, type, i < n ? &items[i] : NULL);
- }
-
- if (data && data < end && AMF_OBJECT_END == *data)
- return data + 1;
- return NULL; // invalid object
- }
-
- const uint8_t* amf_read_items(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t count)
- {
- size_t i;
- uint8_t type;
- for (i = 0; i < count && data && data < end; i++)
- {
- type = *data++;
- if (!amf_read_item_type_check(type, items[i].type))
- return NULL;
-
- data = amf_read_item(data, end, type, &items[i]);
- }
-
- return data;
- }
-
- #if defined(_DEBUG) || defined(DEBUG)
- struct rtmp_amf0_command_t
- {
- char fmsVer[64];
- double capabilities;
- double mode;
- };
- struct rtmp_amf0_data_t
- {
- char version[64];
- };
- struct rtmp_amf0_information_t
- {
- char code[64]; // NetStream.Play.Start
- char level[8]; // warning/status/error
- char description[256];
- double clientid;
- double objectEncoding;
- struct rtmp_amf0_data_t data;
- };
- static void amf0_test_1(void)
- {
- const uint8_t amf0[] = {
- 0x02, 0x00, 0x07, 0x5F, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74,
- 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
-
- 0x03,
- 0x00, 0x06, 0x66, 0x6D, 0x73, 0x56, 0x65, 0x72, 0x02, 0x00, 0x0E, 0x46, 0x4D, 0x53, 0x2F, 0x33, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x32, 0x30, 0x30, 0x34,
- 0x00, 0x0C, 0x63, 0x61, 0x70,0x61, 0x62, 0x69, 0x6C, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x04, 0x6D, 0x6F, 0x64, 0x65, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x09,
-
- 0x03,
- 0x00, 0x05, 0x6C, 0x65, 0x76, 0x65, 0x6C, 0x02, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
- 0x00, 0x04, 0x63, 0x6F, 0x64, 0x65, 0x02, 0x00, 0x1D, 0x4E, 0x65, 0x74, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x2E, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x2E, 0x53, 0x75, 0x63, 0x63, 0x65, 0x73, 0x73,
- 0x00, 0x0B, 0x64, 0x65, 0x73, 0x63, 0x72, 0x69, 0x70, 0x74, 0x69, 0x6F, 0x6E, 0x02, 0x00, 0x15, 0x43, 0x6F, 0x6E, 0x6E, 0x65, 0x63, 0x74, 0x69, 0x6F, 0x6E, 0x20, 0x73, 0x75, 0x63, 0x63, 0x65, 0x65, 0x64, 0x65, 0x64, 0x2E,
- 0x00, 0x04, 0x64, 0x61, 0x74, 0x61,
- 0x08, 0x00, 0x00, 0x00, 0x01,
- 0x00, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x02, 0x00, 0x0A, 0x33, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x32, 0x30, 0x30, 0x34,
- 0x00, 0x00, 0x09,
- 0x00, 0x08, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x69, 0x64, 0x00, 0x41, 0xD7, 0x9B, 0x78, 0x7C, 0xC0, 0x00, 0x00,
- 0x00, 0x0E, 0x6F, 0x62, 0x6A, 0x65, 0x63, 0x74, 0x45, 0x6E, 0x63, 0x6F, 0x64, 0x69, 0x6E, 0x67, 0x00, 0x40, 0x08, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x09,
- };
-
- char reply[8];
- const uint8_t* end;
- double transactionId;
- struct rtmp_amf0_command_t fms;
- struct rtmp_amf0_information_t result;
- struct amf_object_item_t cmd[3];
- struct amf_object_item_t data[1];
- struct amf_object_item_t info[6];
- struct amf_object_item_t items[4];
-
- #define AMF_OBJECT_ITEM_VALUE(v, amf_type, amf_name, amf_value, amf_size) { v.type=amf_type; v.name=amf_name; v.value=amf_value; v.size=amf_size; }
- AMF_OBJECT_ITEM_VALUE(cmd[0], AMF_STRING, "fmsVer", fms.fmsVer, sizeof(fms.fmsVer));
- AMF_OBJECT_ITEM_VALUE(cmd[1], AMF_NUMBER, "capabilities", &fms.capabilities, sizeof(fms.capabilities));
- AMF_OBJECT_ITEM_VALUE(cmd[2], AMF_NUMBER, "mode", &fms.mode, sizeof(fms.mode));
-
- AMF_OBJECT_ITEM_VALUE(data[0], AMF_STRING, "version", result.data.version, sizeof(result.data.version));
-
- AMF_OBJECT_ITEM_VALUE(info[0], AMF_STRING, "code", result.code, sizeof(result.code));
- AMF_OBJECT_ITEM_VALUE(info[1], AMF_STRING, "level", result.level, sizeof(result.level));
- AMF_OBJECT_ITEM_VALUE(info[2], AMF_STRING, "description", result.description, sizeof(result.description));
- AMF_OBJECT_ITEM_VALUE(info[3], AMF_ECMA_ARRAY, "data", data, sizeof(data)/sizeof(data[0]));
- AMF_OBJECT_ITEM_VALUE(info[4], AMF_NUMBER, "clientid", &result.clientid, sizeof(result.clientid));
- AMF_OBJECT_ITEM_VALUE(info[5], AMF_NUMBER, "objectEncoding", &result.objectEncoding, sizeof(result.objectEncoding));
-
- AMF_OBJECT_ITEM_VALUE(items[0], AMF_STRING, "reply", reply, sizeof(reply)); // Command object
- AMF_OBJECT_ITEM_VALUE(items[1], AMF_NUMBER, "transaction", &transactionId, sizeof(transactionId)); // Command object
- AMF_OBJECT_ITEM_VALUE(items[2], AMF_OBJECT, "command", cmd, sizeof(cmd)/sizeof(cmd[0])); // Command object
- AMF_OBJECT_ITEM_VALUE(items[3], AMF_OBJECT, "information", info, sizeof(info) / sizeof(info[0])); // Information object
-
- end = amf0 + sizeof(amf0);
- assert(end == amf_read_items(amf0, end, items, sizeof(items) / sizeof(items[0])));
- assert(0 == strcmp(fms.fmsVer, "FMS/3,5,5,2004"));
- assert(fms.capabilities == 31.0);
- assert(fms.mode == 1.0);
- assert(0 == strcmp(result.code, "NetConnection.Connect.Success"));
- assert(0 == strcmp(result.level, "status"));
- assert(0 == strcmp(result.description, "Connection succeeded."));
- assert(0 == strcmp(result.data.version, "3,5,5,2004"));
- assert(1584259571.0 == result.clientid);
- assert(3.0 == result.objectEncoding);
- }
-
- struct rtmp_amf0_connect_t
- {
- char app[64]; // Server application name, e.g.: testapp
- char flashver[32]; // Flash Player version, FMSc/1.0
- char swfUrl[256]; // URL of the source SWF file
- char tcUrl[256]; // URL of the Server, rtmp://host:1935/testapp/instance1
- uint8_t fpad; // boolean: True if proxy is being used.
- double capabilities; // double default: 15
- double audioCodecs; // double default: 4071
- double videoCodecs; // double default: 252
- double videoFunction; // double default: 1
- double encoding;
- char pageUrl[256]; // http://host/sample.html
- };
-
- static void amf0_test_2(void)
- {
- const uint8_t amf0[] = {
- 0x02, 0x00, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
- 0x03, 0x00,
- 0x03, 0x61, 0x70, 0x70,
- 0x02,
- 0x00, 0x05, 0x6c, 0x69, 0x76, 0x65, 0x2f,
- 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c,
- 0x02, 0x00, 0x1A, 0x72, 0x74, 0x6d, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2e, 0x72, 0x74, 0x6d, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x76, 0x65, 0x2f,
- 0x00, 0x04, 0x74, 0x79, 0x70, 0x65,
- 0x02, 0x00, 0x0a, 0x6e, 0x6f, 0x6e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x00,
- 0x08, 0x66, 0x6c, 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x1f, 0x46, 0x4d, 0x4c, 0x45,
- 0x2f, 0x33, 0x2e, 0x30, 0x20, 0x28, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6c,
- 0x65, 0x3b, 0x20, 0x46, 0x4d, 0x53, 0x63, 0x2f, 0x31, 0x2e, 0x30, 0x29, 0x00, 0x06, 0x73, 0x77,
- 0x66, 0x55, 0x72, 0x6c,
- 0x02, 0x00, 0x1A, 0x72, 0x74, 0x6d, 0x70, 0x3a, 0x2f, 0x2f, 0x70, 0x75, 0x73, 0x68, 0x2e, 0x72, 0x74, 0x6d, 0x70, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x6c, 0x69, 0x76, 0x65, 0x2f,
- 0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c,
- 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
- 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xa8, 0xee, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
- 0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x76, 0x69, 0x64,
- 0x65, 0x6f, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c, 0x06, 0x00, 0x0e, 0x6f,
- 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00,
- 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
- };
-
- char reply[8];
- const uint8_t* end;
- double transactionId;
- struct rtmp_amf0_connect_t connect;
- struct amf_object_item_t commands[11];
- struct amf_object_item_t items[3];
-
- #define AMF_OBJECT_ITEM_VALUE(v, amf_type, amf_name, amf_value, amf_size) { v.type=amf_type; v.name=amf_name; v.value=amf_value; v.size=amf_size; }
- AMF_OBJECT_ITEM_VALUE(commands[0], AMF_STRING, "app", connect.app, sizeof(connect.app));
- AMF_OBJECT_ITEM_VALUE(commands[1], AMF_STRING, "flashVer", connect.flashver, sizeof(connect.flashver));
- AMF_OBJECT_ITEM_VALUE(commands[2], AMF_STRING, "tcUrl", connect.tcUrl, sizeof(connect.tcUrl));
- AMF_OBJECT_ITEM_VALUE(commands[3], AMF_BOOLEAN, "fpad", &connect.fpad, 1);
- AMF_OBJECT_ITEM_VALUE(commands[4], AMF_NUMBER, "audioCodecs", &connect.audioCodecs, 8);
- AMF_OBJECT_ITEM_VALUE(commands[5], AMF_NUMBER, "videoCodecs", &connect.videoCodecs, 8);
- AMF_OBJECT_ITEM_VALUE(commands[6], AMF_NUMBER, "videoFunction", &connect.videoFunction, 8);
- AMF_OBJECT_ITEM_VALUE(commands[7], AMF_NUMBER, "objectEncoding", &connect.encoding, 8);
- AMF_OBJECT_ITEM_VALUE(commands[8], AMF_NUMBER, "capabilities", &connect.capabilities, 8);
- AMF_OBJECT_ITEM_VALUE(commands[9], AMF_STRING, "pageUrl", &connect.pageUrl, sizeof(connect.pageUrl));
- AMF_OBJECT_ITEM_VALUE(commands[10], AMF_STRING, "swfUrl", &connect.swfUrl, sizeof(connect.swfUrl));
-
- AMF_OBJECT_ITEM_VALUE(items[0], AMF_STRING, "reply", reply, sizeof(reply)); // Command object
- AMF_OBJECT_ITEM_VALUE(items[1], AMF_NUMBER, "transaction", &transactionId, sizeof(transactionId)); // Command object
- AMF_OBJECT_ITEM_VALUE(items[2], AMF_OBJECT, "command", commands, sizeof(commands) / sizeof(commands[0])); // Command object
-
- end = amf0 + sizeof(amf0);
- memset(&connect, 0, sizeof(connect));
- assert(end == amf_read_items(amf0, end, items, sizeof(items) / sizeof(items[0])));
- assert(0 == strcmp(connect.app, "live/"));
- assert(0 == strcmp(connect.tcUrl, "rtmp://push.rtmp.com/live/"));
- assert(0 == strcmp(connect.flashver, "FMLE/3.0 (compatible; FMSc/1.0)"));
- assert(0 == strcmp(connect.swfUrl, "rtmp://push.rtmp.com/live/"));
- assert(0 == strcmp(connect.pageUrl, "")); // pageUrl undefined
- assert(connect.fpad == 0);
- assert(connect.capabilities == 15);
- assert(connect.audioCodecs == 3191);
- assert(connect.videoCodecs == 252);
- assert(connect.videoFunction == 1);
- assert(connect.encoding == 0);
- }
-
- void amf0_test(void)
- {
- amf0_test_1();
- amf0_test_2();
- }
- #endif
|