25개 이상의 토픽을 선택하실 수 없습니다. Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

592 lines
19KB

  1. #include "amf0.h"
  2. #include <stdint.h>
  3. #include <stddef.h>
  4. #include <string.h>
  5. #include <assert.h>
  6. static double s_double = 1.0; // 3ff0 0000 0000 0000
  7. static uint8_t* AMFWriteInt16(uint8_t* ptr, const uint8_t* end, uint16_t value)
  8. {
  9. if (ptr + 2 > end) return NULL;
  10. ptr[0] = value >> 8;
  11. ptr[1] = value & 0xFF;
  12. return ptr + 2;
  13. }
  14. static uint8_t* AMFWriteInt32(uint8_t* ptr, const uint8_t* end, uint32_t value)
  15. {
  16. if (ptr + 4 > end) return NULL;
  17. ptr[0] = (uint8_t)(value >> 24);
  18. ptr[1] = (uint8_t)(value >> 16);
  19. ptr[2] = (uint8_t)(value >> 8);
  20. ptr[3] = (uint8_t)(value & 0xFF);
  21. return ptr + 4;
  22. }
  23. static uint8_t* AMFWriteString16(uint8_t* ptr, const uint8_t* end, const char* string, size_t length)
  24. {
  25. if (ptr + 2 + length > end) return NULL;
  26. ptr = AMFWriteInt16(ptr, end, (uint16_t)length);
  27. memcpy(ptr, string, length);
  28. return ptr + length;
  29. }
  30. static uint8_t* AMFWriteString32(uint8_t* ptr, const uint8_t* end, const char* string, size_t length)
  31. {
  32. if (ptr + 4 + length > end) return NULL;
  33. ptr = AMFWriteInt32(ptr, end, (uint32_t)length);
  34. memcpy(ptr, string, length);
  35. return ptr + length;
  36. }
  37. uint8_t* AMFWriteNull(uint8_t* ptr, const uint8_t* end)
  38. {
  39. if (!ptr || ptr + 1 > end) return NULL;
  40. *ptr++ = AMF_NULL;
  41. return ptr;
  42. }
  43. uint8_t* AMFWriteUndefined(uint8_t* ptr, const uint8_t* end)
  44. {
  45. if (!ptr || ptr + 1 > end) return NULL;
  46. *ptr++ = AMF_UNDEFINED;
  47. return ptr;
  48. }
  49. uint8_t* AMFWriteObject(uint8_t* ptr, const uint8_t* end)
  50. {
  51. if (!ptr || ptr + 1 > end) return NULL;
  52. *ptr++ = AMF_OBJECT;
  53. return ptr;
  54. }
  55. uint8_t* AMFWriteObjectEnd(uint8_t* ptr, const uint8_t* end)
  56. {
  57. if (!ptr || ptr + 3 > end) return NULL;
  58. /* end of object - 0x00 0x00 0x09 */
  59. *ptr++ = 0;
  60. *ptr++ = 0;
  61. *ptr++ = AMF_OBJECT_END;
  62. return ptr;
  63. }
  64. uint8_t* AMFWriteTypedObject(uint8_t* ptr, const uint8_t* end)
  65. {
  66. if (!ptr || ptr + 1 > end) return NULL;
  67. *ptr++ = AMF_TYPED_OBJECT;
  68. return ptr;
  69. }
  70. uint8_t* AMFWriteECMAArarry(uint8_t* ptr, const uint8_t* end)
  71. {
  72. if (!ptr || ptr + 1 > end) return NULL;
  73. *ptr++ = AMF_ECMA_ARRAY;
  74. return AMFWriteInt32(ptr, end, 0); // U32 associative-count
  75. }
  76. uint8_t* AMFWriteBoolean(uint8_t* ptr, const uint8_t* end, uint8_t value)
  77. {
  78. if (!ptr || ptr + 2 > end) return NULL;
  79. ptr[0] = AMF_BOOLEAN;
  80. ptr[1] = 0 == value ? 0 : 1;
  81. return ptr + 2;
  82. }
  83. uint8_t* AMFWriteDouble(uint8_t* ptr, const uint8_t* end, double value)
  84. {
  85. if (!ptr || ptr + 9 > end) return NULL;
  86. assert(8 == sizeof(double));
  87. *ptr++ = AMF_NUMBER;
  88. // Little-Endian
  89. if (0x00 == *(char*)&s_double)
  90. {
  91. *ptr++ = ((uint8_t*)&value)[7];
  92. *ptr++ = ((uint8_t*)&value)[6];
  93. *ptr++ = ((uint8_t*)&value)[5];
  94. *ptr++ = ((uint8_t*)&value)[4];
  95. *ptr++ = ((uint8_t*)&value)[3];
  96. *ptr++ = ((uint8_t*)&value)[2];
  97. *ptr++ = ((uint8_t*)&value)[1];
  98. *ptr++ = ((uint8_t*)&value)[0];
  99. }
  100. else
  101. {
  102. memcpy(ptr, &value, 8);
  103. }
  104. return ptr;
  105. }
  106. uint8_t* AMFWriteString(uint8_t* ptr, const uint8_t* end, const char* string, size_t length)
  107. {
  108. if (!ptr || ptr + 1 + (length < 65536 ? 2 : 4) + length > end || length > UINT32_MAX)
  109. return NULL;
  110. if (length < 65536)
  111. {
  112. *ptr++ = AMF_STRING;
  113. AMFWriteString16(ptr, end, string, length);
  114. ptr += 2;
  115. }
  116. else
  117. {
  118. *ptr++ = AMF_LONG_STRING;
  119. AMFWriteString32(ptr, end, string, length);
  120. ptr += 4;
  121. }
  122. return ptr + length;
  123. }
  124. uint8_t* AMFWriteDate(uint8_t* ptr, const uint8_t* end, double milliseconds, int16_t timezone)
  125. {
  126. if (!ptr || ptr + 11 > end)
  127. return NULL;
  128. AMFWriteDouble(ptr, end, milliseconds);
  129. *ptr = AMF_DATE; // rewrite to date
  130. return AMFWriteInt16(ptr + 8, end, timezone);
  131. }
  132. uint8_t* AMFWriteNamedBoolean(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, uint8_t value)
  133. {
  134. if (ptr + length + 2 + 2 > end)
  135. return NULL;
  136. ptr = AMFWriteString16(ptr, end, name, length);
  137. return ptr ? AMFWriteBoolean(ptr, end, value) : NULL;
  138. }
  139. uint8_t* AMFWriteNamedDouble(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, double value)
  140. {
  141. if (ptr + length + 2 + 8 + 1 > end)
  142. return NULL;
  143. ptr = AMFWriteString16(ptr, end, name, length);
  144. return ptr ? AMFWriteDouble(ptr, end, value) : NULL;
  145. }
  146. uint8_t* AMFWriteNamedString(uint8_t* ptr, const uint8_t* end, const char* name, size_t length, const char* value, size_t length2)
  147. {
  148. if (ptr + length + 2 + length2 + 3 > end)
  149. return NULL;
  150. ptr = AMFWriteString16(ptr, end, name, length);
  151. return ptr ? AMFWriteString(ptr, end, value, length2) : NULL;
  152. }
  153. static const uint8_t* AMFReadInt16(const uint8_t* ptr, const uint8_t* end, uint32_t* value)
  154. {
  155. if (!ptr || ptr + 2 > end)
  156. return NULL;
  157. if (value)
  158. {
  159. *value = ((uint32_t)ptr[0] << 8) | ptr[1];
  160. }
  161. return ptr + 2;
  162. }
  163. static const uint8_t* AMFReadInt32(const uint8_t* ptr, const uint8_t* end, uint32_t* value)
  164. {
  165. if (!ptr || ptr + 4 > end)
  166. return NULL;
  167. if (value)
  168. {
  169. *value = ((uint32_t)ptr[0] << 24) | ((uint32_t)ptr[1] << 16) | ((uint32_t)ptr[2] << 8) | ptr[3];
  170. }
  171. return ptr + 4;
  172. }
  173. const uint8_t* AMFReadNull(const uint8_t* ptr, const uint8_t* end)
  174. {
  175. (void)end;
  176. return ptr;
  177. }
  178. const uint8_t* AMFReadUndefined(const uint8_t* ptr, const uint8_t* end)
  179. {
  180. (void)end;
  181. return ptr;
  182. }
  183. const uint8_t* AMFReadBoolean(const uint8_t* ptr, const uint8_t* end, uint8_t* value)
  184. {
  185. if (!ptr || ptr + 1 > end)
  186. return NULL;
  187. if (value)
  188. {
  189. *value = ptr[0];
  190. }
  191. return ptr + 1;
  192. }
  193. const uint8_t* AMFReadDouble(const uint8_t* ptr, const uint8_t* end, double* value)
  194. {
  195. uint8_t* p = (uint8_t*)value;
  196. if (!ptr || ptr + 8 > end)
  197. return NULL;
  198. if (value)
  199. {
  200. if (0x00 == *(char*)&s_double)
  201. {// Little-Endian
  202. *p++ = ptr[7];
  203. *p++ = ptr[6];
  204. *p++ = ptr[5];
  205. *p++ = ptr[4];
  206. *p++ = ptr[3];
  207. *p++ = ptr[2];
  208. *p++ = ptr[1];
  209. *p++ = ptr[0];
  210. }
  211. else
  212. {
  213. memcpy(value, ptr, 8);
  214. }
  215. }
  216. return ptr + 8;
  217. }
  218. const uint8_t* AMFReadString(const uint8_t* ptr, const uint8_t* end, int isLongString, char* string, size_t length)
  219. {
  220. uint32_t len = 0;
  221. if (0 == isLongString)
  222. ptr = AMFReadInt16(ptr, end, &len);
  223. else
  224. ptr = AMFReadInt32(ptr, end, &len);
  225. if (!ptr || ptr + len > end)
  226. return NULL;
  227. if (string && length > len)
  228. {
  229. memcpy(string, ptr, len);
  230. string[len] = 0;
  231. }
  232. return ptr + len;
  233. }
  234. const uint8_t* AMFReadDate(const uint8_t* ptr, const uint8_t* end, double *milliseconds, int16_t *timezone)
  235. {
  236. uint32_t v;
  237. ptr = AMFReadDouble(ptr, end, milliseconds);
  238. if (ptr)
  239. {
  240. ptr = AMFReadInt16(ptr, end, &v);
  241. if(timezone)
  242. *timezone = (int16_t)v;
  243. }
  244. return ptr;
  245. }
  246. static const uint8_t* amf_read_object(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t n);
  247. 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);
  248. 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);
  249. static const uint8_t* amf_read_item(const uint8_t* data, const uint8_t* end, enum AMFDataType type, struct amf_object_item_t* item)
  250. {
  251. switch (type)
  252. {
  253. case AMF_BOOLEAN:
  254. return AMFReadBoolean(data, end, (uint8_t*)(item ? item->value : NULL));
  255. case AMF_NUMBER:
  256. return AMFReadDouble(data, end, (double*)(item ? item->value : NULL));
  257. case AMF_STRING:
  258. return AMFReadString(data, end, 0, (char*)(item ? item->value : NULL), item ? item->size : 0);
  259. case AMF_LONG_STRING:
  260. return AMFReadString(data, end, 1, (char*)(item ? item->value : NULL), item ? item->size : 0);
  261. case AMF_DATE:
  262. return AMFReadDate(data, end, (double*)(item ? item->value : NULL), (int16_t*)(item ? (char*)item->value + 8 : NULL));
  263. case AMF_OBJECT:
  264. return amf_read_object(data, end, (struct amf_object_item_t*)(item ? item->value : NULL), item ? item->size : 0);
  265. case AMF_NULL:
  266. return data;
  267. case AMF_UNDEFINED:
  268. return data;
  269. case AMF_ECMA_ARRAY:
  270. return amf_read_ecma_array(data, end, (struct amf_object_item_t*)(item ? item->value : NULL), item ? item->size : 0);
  271. case AMF_STRICT_ARRAY:
  272. return amf_read_strict_array(data, end, (struct amf_object_item_t*)(item ? item->value : NULL), item ? item->size : 0);
  273. default:
  274. assert(0);
  275. return NULL;
  276. }
  277. }
  278. static inline int amf_read_item_type_check(uint8_t type0, uint8_t itemtype)
  279. {
  280. // decode AMF_ECMA_ARRAY as AMF_OBJECT
  281. return (type0 == itemtype || (AMF_OBJECT == itemtype && (AMF_ECMA_ARRAY == type0 || AMF_NULL == type0))) ? 1 : 0;
  282. }
  283. 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)
  284. {
  285. uint8_t type;
  286. uint32_t i, count;
  287. if (!ptr || ptr + 4 > end)
  288. return NULL;
  289. ptr = AMFReadInt32(ptr, end, &count); // U32 array-count
  290. for (i = 0; i < count && ptr && ptr < end; i++)
  291. {
  292. type = *ptr++;
  293. ptr = amf_read_item(ptr, end, type, (i < n && amf_read_item_type_check(type, items[i].type)) ? &items[i] : NULL);
  294. }
  295. return ptr;
  296. }
  297. 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)
  298. {
  299. if (!ptr || ptr + 4 > end)
  300. return NULL;
  301. ptr += 4; // U32 associative-count
  302. return amf_read_object(ptr, end, items, n);
  303. }
  304. static const uint8_t* amf_read_object(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t n)
  305. {
  306. uint8_t type;
  307. uint32_t len;
  308. size_t i;
  309. while (data && data + 2 <= end)
  310. {
  311. len = *data++ << 8;
  312. len |= *data++;
  313. if (0 == len)
  314. break; // last item
  315. if (data + len + 1 > end)
  316. return NULL; // invalid
  317. for (i = 0; i < n; i++)
  318. {
  319. if (strlen(items[i].name) == len && 0 == memcmp(items[i].name, data, len) && amf_read_item_type_check(data[len], items[i].type))
  320. break;
  321. }
  322. data += len; // skip name string
  323. type = *data++; // value type
  324. data = amf_read_item(data, end, type, i < n ? &items[i] : NULL);
  325. }
  326. if (data && data < end && AMF_OBJECT_END == *data)
  327. return data + 1;
  328. return NULL; // invalid object
  329. }
  330. const uint8_t* amf_read_items(const uint8_t* data, const uint8_t* end, struct amf_object_item_t* items, size_t count)
  331. {
  332. size_t i;
  333. uint8_t type;
  334. for (i = 0; i < count && data && data < end; i++)
  335. {
  336. type = *data++;
  337. if (!amf_read_item_type_check(type, items[i].type))
  338. return NULL;
  339. data = amf_read_item(data, end, type, &items[i]);
  340. }
  341. return data;
  342. }
  343. #if defined(_DEBUG) || defined(DEBUG)
  344. struct rtmp_amf0_command_t
  345. {
  346. char fmsVer[64];
  347. double capabilities;
  348. double mode;
  349. };
  350. struct rtmp_amf0_data_t
  351. {
  352. char version[64];
  353. };
  354. struct rtmp_amf0_information_t
  355. {
  356. char code[64]; // NetStream.Play.Start
  357. char level[8]; // warning/status/error
  358. char description[256];
  359. double clientid;
  360. double objectEncoding;
  361. struct rtmp_amf0_data_t data;
  362. };
  363. static void amf0_test_1(void)
  364. {
  365. const uint8_t amf0[] = {
  366. 0x02, 0x00, 0x07, 0x5F, 0x72, 0x65, 0x73, 0x75, 0x6C, 0x74,
  367. 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  368. 0x03,
  369. 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,
  370. 0x00, 0x0C, 0x63, 0x61, 0x70,0x61, 0x62, 0x69, 0x6C, 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x3F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  371. 0x00, 0x04, 0x6D, 0x6F, 0x64, 0x65, 0x00, 0x3F, 0xF0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  372. 0x00, 0x00, 0x09,
  373. 0x03,
  374. 0x00, 0x05, 0x6C, 0x65, 0x76, 0x65, 0x6C, 0x02, 0x00, 0x06, 0x73, 0x74, 0x61, 0x74, 0x75, 0x73,
  375. 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,
  376. 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,
  377. 0x00, 0x04, 0x64, 0x61, 0x74, 0x61,
  378. 0x08, 0x00, 0x00, 0x00, 0x01,
  379. 0x00, 0x07, 0x76, 0x65, 0x72, 0x73, 0x69, 0x6F, 0x6E, 0x02, 0x00, 0x0A, 0x33, 0x2C, 0x35, 0x2C, 0x35, 0x2C, 0x32, 0x30, 0x30, 0x34,
  380. 0x00, 0x00, 0x09,
  381. 0x00, 0x08, 0x63, 0x6C, 0x69, 0x65, 0x6E, 0x74, 0x69, 0x64, 0x00, 0x41, 0xD7, 0x9B, 0x78, 0x7C, 0xC0, 0x00, 0x00,
  382. 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,
  383. 0x00, 0x00, 0x09,
  384. };
  385. char reply[8];
  386. const uint8_t* end;
  387. double transactionId;
  388. struct rtmp_amf0_command_t fms;
  389. struct rtmp_amf0_information_t result;
  390. struct amf_object_item_t cmd[3];
  391. struct amf_object_item_t data[1];
  392. struct amf_object_item_t info[6];
  393. struct amf_object_item_t items[4];
  394. #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; }
  395. AMF_OBJECT_ITEM_VALUE(cmd[0], AMF_STRING, "fmsVer", fms.fmsVer, sizeof(fms.fmsVer));
  396. AMF_OBJECT_ITEM_VALUE(cmd[1], AMF_NUMBER, "capabilities", &fms.capabilities, sizeof(fms.capabilities));
  397. AMF_OBJECT_ITEM_VALUE(cmd[2], AMF_NUMBER, "mode", &fms.mode, sizeof(fms.mode));
  398. AMF_OBJECT_ITEM_VALUE(data[0], AMF_STRING, "version", result.data.version, sizeof(result.data.version));
  399. AMF_OBJECT_ITEM_VALUE(info[0], AMF_STRING, "code", result.code, sizeof(result.code));
  400. AMF_OBJECT_ITEM_VALUE(info[1], AMF_STRING, "level", result.level, sizeof(result.level));
  401. AMF_OBJECT_ITEM_VALUE(info[2], AMF_STRING, "description", result.description, sizeof(result.description));
  402. AMF_OBJECT_ITEM_VALUE(info[3], AMF_ECMA_ARRAY, "data", data, sizeof(data)/sizeof(data[0]));
  403. AMF_OBJECT_ITEM_VALUE(info[4], AMF_NUMBER, "clientid", &result.clientid, sizeof(result.clientid));
  404. AMF_OBJECT_ITEM_VALUE(info[5], AMF_NUMBER, "objectEncoding", &result.objectEncoding, sizeof(result.objectEncoding));
  405. AMF_OBJECT_ITEM_VALUE(items[0], AMF_STRING, "reply", reply, sizeof(reply)); // Command object
  406. AMF_OBJECT_ITEM_VALUE(items[1], AMF_NUMBER, "transaction", &transactionId, sizeof(transactionId)); // Command object
  407. AMF_OBJECT_ITEM_VALUE(items[2], AMF_OBJECT, "command", cmd, sizeof(cmd)/sizeof(cmd[0])); // Command object
  408. AMF_OBJECT_ITEM_VALUE(items[3], AMF_OBJECT, "information", info, sizeof(info) / sizeof(info[0])); // Information object
  409. end = amf0 + sizeof(amf0);
  410. assert(end == amf_read_items(amf0, end, items, sizeof(items) / sizeof(items[0])));
  411. assert(0 == strcmp(fms.fmsVer, "FMS/3,5,5,2004"));
  412. assert(fms.capabilities == 31.0);
  413. assert(fms.mode == 1.0);
  414. assert(0 == strcmp(result.code, "NetConnection.Connect.Success"));
  415. assert(0 == strcmp(result.level, "status"));
  416. assert(0 == strcmp(result.description, "Connection succeeded."));
  417. assert(0 == strcmp(result.data.version, "3,5,5,2004"));
  418. assert(1584259571.0 == result.clientid);
  419. assert(3.0 == result.objectEncoding);
  420. }
  421. struct rtmp_amf0_connect_t
  422. {
  423. char app[64]; // Server application name, e.g.: testapp
  424. char flashver[32]; // Flash Player version, FMSc/1.0
  425. char swfUrl[256]; // URL of the source SWF file
  426. char tcUrl[256]; // URL of the Server, rtmp://host:1935/testapp/instance1
  427. uint8_t fpad; // boolean: True if proxy is being used.
  428. double capabilities; // double default: 15
  429. double audioCodecs; // double default: 4071
  430. double videoCodecs; // double default: 252
  431. double videoFunction; // double default: 1
  432. double encoding;
  433. char pageUrl[256]; // http://host/sample.html
  434. };
  435. static void amf0_test_2(void)
  436. {
  437. const uint8_t amf0[] = {
  438. 0x02, 0x00, 0x07, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
  439. 0x03, 0x00,
  440. 0x03, 0x61, 0x70, 0x70,
  441. 0x02,
  442. 0x00, 0x05, 0x6c, 0x69, 0x76, 0x65, 0x2f,
  443. 0x00, 0x05, 0x74, 0x63, 0x55, 0x72, 0x6c,
  444. 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,
  445. 0x00, 0x04, 0x74, 0x79, 0x70, 0x65,
  446. 0x02, 0x00, 0x0a, 0x6e, 0x6f, 0x6e, 0x70, 0x72, 0x69, 0x76, 0x61, 0x74, 0x65, 0x00,
  447. 0x08, 0x66, 0x6c, 0x61, 0x73, 0x68, 0x56, 0x65, 0x72, 0x02, 0x00, 0x1f, 0x46, 0x4d, 0x4c, 0x45,
  448. 0x2f, 0x33, 0x2e, 0x30, 0x20, 0x28, 0x63, 0x6f, 0x6d, 0x70, 0x61, 0x74, 0x69, 0x62, 0x6c,
  449. 0x65, 0x3b, 0x20, 0x46, 0x4d, 0x53, 0x63, 0x2f, 0x31, 0x2e, 0x30, 0x29, 0x00, 0x06, 0x73, 0x77,
  450. 0x66, 0x55, 0x72, 0x6c,
  451. 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,
  452. 0x00, 0x04, 0x66, 0x70, 0x61, 0x64, 0x01, 0x00, 0x00, 0x0c, 0x63, 0x61, 0x70, 0x61, 0x62, 0x69, 0x6c,
  453. 0x69, 0x74, 0x69, 0x65, 0x73, 0x00, 0x40, 0x2e, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b,
  454. 0x61, 0x75, 0x64, 0x69, 0x6f, 0x43, 0x6f, 0x64, 0x65, 0x63, 0x73, 0x00, 0x40, 0xa8, 0xee, 0x00,
  455. 0x00, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x76, 0x69, 0x64, 0x65, 0x6f, 0x43, 0x6f, 0x64, 0x65,
  456. 0x63, 0x73, 0x00, 0x40, 0x6f, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x76, 0x69, 0x64,
  457. 0x65, 0x6f, 0x46, 0x75, 0x6e, 0x63, 0x74, 0x69, 0x6f, 0x6e, 0x00, 0x3f, 0xf0, 0x00, 0x00, 0x00,
  458. 0x00, 0x00, 0x00, 0x00, 0x07, 0x70, 0x61, 0x67, 0x65, 0x55, 0x72, 0x6c, 0x06, 0x00, 0x0e, 0x6f,
  459. 0x62, 0x6a, 0x65, 0x63, 0x74, 0x45, 0x6e, 0x63, 0x6f, 0x64, 0x69, 0x6e, 0x67, 0x00, 0x00, 0x00,
  460. 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x09,
  461. };
  462. char reply[8];
  463. const uint8_t* end;
  464. double transactionId;
  465. struct rtmp_amf0_connect_t connect;
  466. struct amf_object_item_t commands[11];
  467. struct amf_object_item_t items[3];
  468. #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; }
  469. AMF_OBJECT_ITEM_VALUE(commands[0], AMF_STRING, "app", connect.app, sizeof(connect.app));
  470. AMF_OBJECT_ITEM_VALUE(commands[1], AMF_STRING, "flashVer", connect.flashver, sizeof(connect.flashver));
  471. AMF_OBJECT_ITEM_VALUE(commands[2], AMF_STRING, "tcUrl", connect.tcUrl, sizeof(connect.tcUrl));
  472. AMF_OBJECT_ITEM_VALUE(commands[3], AMF_BOOLEAN, "fpad", &connect.fpad, 1);
  473. AMF_OBJECT_ITEM_VALUE(commands[4], AMF_NUMBER, "audioCodecs", &connect.audioCodecs, 8);
  474. AMF_OBJECT_ITEM_VALUE(commands[5], AMF_NUMBER, "videoCodecs", &connect.videoCodecs, 8);
  475. AMF_OBJECT_ITEM_VALUE(commands[6], AMF_NUMBER, "videoFunction", &connect.videoFunction, 8);
  476. AMF_OBJECT_ITEM_VALUE(commands[7], AMF_NUMBER, "objectEncoding", &connect.encoding, 8);
  477. AMF_OBJECT_ITEM_VALUE(commands[8], AMF_NUMBER, "capabilities", &connect.capabilities, 8);
  478. AMF_OBJECT_ITEM_VALUE(commands[9], AMF_STRING, "pageUrl", &connect.pageUrl, sizeof(connect.pageUrl));
  479. AMF_OBJECT_ITEM_VALUE(commands[10], AMF_STRING, "swfUrl", &connect.swfUrl, sizeof(connect.swfUrl));
  480. AMF_OBJECT_ITEM_VALUE(items[0], AMF_STRING, "reply", reply, sizeof(reply)); // Command object
  481. AMF_OBJECT_ITEM_VALUE(items[1], AMF_NUMBER, "transaction", &transactionId, sizeof(transactionId)); // Command object
  482. AMF_OBJECT_ITEM_VALUE(items[2], AMF_OBJECT, "command", commands, sizeof(commands) / sizeof(commands[0])); // Command object
  483. end = amf0 + sizeof(amf0);
  484. memset(&connect, 0, sizeof(connect));
  485. assert(end == amf_read_items(amf0, end, items, sizeof(items) / sizeof(items[0])));
  486. assert(0 == strcmp(connect.app, "live/"));
  487. assert(0 == strcmp(connect.tcUrl, "rtmp://push.rtmp.com/live/"));
  488. assert(0 == strcmp(connect.flashver, "FMLE/3.0 (compatible; FMSc/1.0)"));
  489. assert(0 == strcmp(connect.swfUrl, "rtmp://push.rtmp.com/live/"));
  490. assert(0 == strcmp(connect.pageUrl, "")); // pageUrl undefined
  491. assert(connect.fpad == 0);
  492. assert(connect.capabilities == 15);
  493. assert(connect.audioCodecs == 3191);
  494. assert(connect.videoCodecs == 252);
  495. assert(connect.videoFunction == 1);
  496. assert(connect.encoding == 0);
  497. }
  498. void amf0_test(void)
  499. {
  500. amf0_test_1();
  501. amf0_test_2();
  502. }
  503. #endif