/* read an array */ static amf0_data * amf0_array_read(read_proc_t read_proc, void * user_data) { size_t i; amf0_data * element; amf0_data * data = amf0_array_new(); if (data != NULL) { uint32_t array_size; if (read_proc(&array_size, sizeof(uint32_t), user_data) == sizeof(uint32_t)) { array_size = swap_uint32(array_size); for (i = 0; i < array_size; ++i) { element = amf0_data_read(read_proc, user_data); if (element != NULL) { if (amf0_array_push(data, element) == NULL) { amf0_data_free(element); amf0_data_free(data); return NULL; } } else { amf0_data_free(data); return NULL; } } } else { amf0_data_free(data); return NULL; } } return data; }
/* read an object */ static amf0_data * amf0_object_read(read_proc_t read_proc, void * user_data) { amf0_data * data = amf0_object_new(); if (data != NULL) { amf0_data * name; amf0_data * element; while (1) { name = amf0_string_read(read_proc, user_data); if (name != NULL) { element = amf0_data_read(read_proc, user_data); if (element != NULL) { if (amf0_object_add(data, (char *)amf0_string_get_uint8_ts(name), element) == NULL) { amf0_data_free(name); amf0_data_free(element); amf0_data_free(data); return NULL; } } else { amf0_data_free(name); break; } } else { /* invalid name: error */ amf0_data_free(data); return NULL; } } } return data; }
/* static */ Message Message::CreateStream() { uint8_t buf[4096]; size_t len; Message msg; amf0_data *data; msg.write_string("createStream"); data = amf0_number_new(2); len = amf0_data_buffer_write(data, buf, 4096); msg.body_.insert(msg.body_.end(), buf, buf + len); amf0_data_free(data); data = amf0_null_new(); len = amf0_data_buffer_write(data, buf, 4096); msg.body_.insert(msg.body_.end(), buf, buf + len); amf0_data_free(data); msg.csid_ = 3; msg.timestamp_ = 0; msg.type_ = COMMAND_AMF0; /* Command AMF0 */ msg.stream_id_ = 0; msg.length_ = msg.body_.size(); return msg; }
Message Message::Play(std::string key) { uint8_t buf[4096]; size_t len; Message msg; amf0_data *data; msg.write_string("play"); data = amf0_number_new(3); len = amf0_data_buffer_write(data, buf, 4096); msg.body_.insert(msg.body_.end(), buf, buf + len); amf0_data_free(data); data = amf0_null_new(); len = amf0_data_buffer_write(data, buf, 4096); msg.body_.insert(msg.body_.end(), buf, buf + len); amf0_data_free(data); msg.write_string(key); data = amf0_number_new(-2000); len = amf0_data_buffer_write(data, buf, 4096); msg.body_.insert(msg.body_.end(), buf, buf + len); amf0_data_free(data); msg.csid_ = 8; msg.timestamp_ = 0; msg.type_ = COMMAND_AMF0; /* AMF 0 */ msg.stream_id_ = 1; msg.length_ = msg.body_.size(); return msg; }
/*static*/ Message Message::Connect(std::string app, int rpc_number) { uint8_t buf[4096]; size_t len; Message msg; amf0_data *data; msg.write_string("connect"); data = amf0_number_new(rpc_number); len = amf0_data_buffer_write(data, buf, 4096); msg.body_.insert(msg.body_.end(), buf, buf + len); amf0_data_free(data); data = amf0_object_new(); amf0_object_add(data, "app", amf0_str(app.c_str())); amf0_object_add(data, "type", amf0_str("nonprivate")); amf0_object_add(data, "flashVer", amf0_str("FMLE/3.0 (compatible; obs-studio/0.14.2; FMSc/1.0)")); amf0_object_add(data, "tcUrl", amf0_str("rtmp://live.hkstv.hk.lxdns.com:1935/live")); amf0_object_add(data, "capabilities", amf0_number_new(15)); amf0_object_add(data, "audioCodecs", amf0_number_new(4071)); amf0_object_add(data, "videoCodecs", amf0_number_new(252)); amf0_object_add(data, "videoFunction", amf0_number_new(1)); len = amf0_data_buffer_write(data, buf, 4096); msg.body_.insert(msg.body_.end(), buf, buf + len); amf0_data_free(data); msg.csid_ = 3; msg.type_ = COMMAND_AMF0; /* Command AMF0 */ msg.stream_id_ = 0; msg.length_ = msg.body_.size(); return msg; }
amf0_data * amf0_object_add(amf0_data * data, const char * name, amf0_data * element) { if (data != NULL) { amf0_data *str_name = amf0_str(name); if (amf0_list_push(&data->u.list_data, str_name) != NULL) { if (amf0_list_push(&data->u.list_data, element) != NULL) { return element; } else { amf0_data_free(amf0_list_pop(&data->u.list_data)); } } amf0_data_free(str_name); } return NULL; }
size_t Message::write_string(std::string str) { uint8_t buf[4096]; size_t len; amf0_data *data = amf0_str(str.c_str()); len = amf0_data_buffer_write(data, buf, 4096); body_.insert(body_.end(), buf, buf + len); amf0_data_free(data); return len; }
static void amf0_list_clear(amf0_list * list) { amf0_node * tmp; amf0_node * node = list->first_element; while (node != NULL) { amf0_data_free(node->data); tmp = node; node = node->next; free(tmp); } list->size = 0; }
/* read an array */ static amf0_data * amf0_array_read(read_proc_t read_proc, void * user_data) { size_t i; amf0_data * element; uint8_t error_code; amf0_data * data; uint32_t array_size; data = amf0_array_new(); if (data == NULL) { return NULL; } if (read_proc(&array_size, sizeof(uint32_t), user_data) < sizeof(uint32_t)) { amf0_data_free(data); return amf0_data_error(AMF0_ERROR_EOF); } array_size = swap_uint32(array_size); for (i = 0; i < array_size; ++i) { element = amf0_data_read(read_proc, user_data); error_code = amf0_data_get_error(element); if (error_code != AMF0_ERROR_OK) { amf0_data_free(element); amf0_data_free(data); return amf0_data_error(error_code); } if (amf0_array_push(data, element) == NULL) { amf0_data_free(element); amf0_data_free(data); return NULL; } } return data; }
amf0_data * amf0_object_delete(amf0_data * data, const char * name) { if (data != NULL) { amf0_node * node = amf0_list_first(&data->list_data); while (node != NULL) { node = node->next; if (strncmp((char*)(node->data->string_data.mbstr), name, (size_t)(node->data->string_data.size)) == 0) { amf0_node * data_node = node->next; amf0_data_free(amf0_list_delete(&data->list_data, node)); return amf0_list_delete(&data->list_data, data_node); } else { node = node->next; } } } return NULL; }
/* string functions */ amf0_data * amf0_string_new(uint8_t * str, uint16_t size) { amf0_data * data = amf0_data_new(AMF0_TYPE_STRING); if (data != NULL) { data->string_data.size = size; data->string_data.mbstr = (uint8_t*)calloc(size+1, sizeof(uint8_t)); if (data->string_data.mbstr != NULL) { if (size > 0) { memcpy(data->string_data.mbstr, str, size); } } else { amf0_data_free(data); return NULL; } } return data; }
amf0_data * amf0_object_set(amf0_data * data, const char * name, amf0_data * element) { if (data != NULL) { amf0_node * node = amf0_list_first(&(data->list_data)); while (node != NULL) { if (strncmp((char*)(node->data->string_data.mbstr), name, (size_t)(node->data->string_data.size)) == 0) { node = node->next; if (node != NULL && node->data != NULL) { amf0_data_free(node->data); node->data = element; return element; } } /* we have to skip the element data to reach the next name */ node = node->next->next; } } return NULL; }
/* read an associative array */ static amf0_data * amf0_associative_array_read(read_proc_t read_proc, void * user_data) { amf0_data * data = amf0_associative_array_new(); if (data != NULL) { amf0_data * name; amf0_data * element; uint32_t size; if (read_proc(&size, sizeof(uint32_t), user_data) == sizeof(uint32_t)) { /* we ignore the 32 bits array size marker */ while(1) { name = amf0_string_read(read_proc, user_data); if (name != NULL) { element = amf0_data_read(read_proc, user_data); if (element != NULL) { if (amf0_associative_array_add(data, (char *)amf0_string_get_uint8_ts(name), element) == NULL) { amf0_data_free(name); amf0_data_free(element); amf0_data_free(data); return NULL; } } else { amf0_data_free(name); break; } } else { /* invalid name: error */ amf0_data_free(data); return NULL; } } } else { amf0_data_free(data); return NULL; } } return data; }
/* read an associative array */ static amf0_data * amf0_associative_array_read(read_proc_t read_proc, void * user_data) { amf0_data * name; amf0_data * element; uint32_t size; uint8_t error_code; amf0_data * data; data = amf0_associative_array_new(); if (data == NULL) { return NULL; } /* we ignore the 32 bits array size marker */ if (read_proc(&size, sizeof(uint32_t), user_data) < sizeof(uint32_t)) { amf0_data_free(data); return amf0_data_error(AMF0_ERROR_EOF); } while(1) { name = amf0_string_read(read_proc, user_data); error_code = amf0_data_get_error(name); if (error_code != AMF0_ERROR_OK) { /* invalid name: error */ amf0_data_free(name); amf0_data_free(data); return amf0_data_error(error_code); } element = amf0_data_read(read_proc, user_data); error_code = amf0_data_get_error(element); if (amf0_string_get_size(name) == 0 || error_code == AMF0_ERROR_END_TAG || error_code == AMF0_ERROR_UNKNOWN_TYPE) { /* end tag or unknown element: end of data, exit loop */ amf0_data_free(name); amf0_data_free(element); break; } else if (error_code != AMF0_ERROR_OK) { amf0_data_free(name); amf0_data_free(data); amf0_data_free(element); return amf0_data_error(error_code); } if (amf0_associative_array_add(data, (char *)amf0_string_get_bytes(name), element) == NULL) { amf0_data_free(name); amf0_data_free(element); amf0_data_free(data); return NULL; } else { amf0_data_free(name); } } return data; }
/* read an object */ static amf0_data * amf0_object_read(read_proc_t read_proc, void * user_data) { amf0_data * name; amf0_data * element; uint8_t error_code; amf0_data * data; data = amf0_object_new(); if (data == NULL) { return NULL; } while (1) { name = amf0_string_read(read_proc, user_data); error_code = amf0_data_get_error(name); if (error_code != AMF0_ERROR_OK) { /* invalid name: error */ amf0_data_free(name); amf0_data_free(data); return amf0_data_error(error_code); } element = amf0_data_read(read_proc, user_data); error_code = amf0_data_get_error(element); if (error_code == AMF0_ERROR_END_TAG || error_code == AMF0_ERROR_UNKNOWN_TYPE) { /* end tag or unknown element: end of data, exit loop */ amf0_data_free(name); amf0_data_free(element); break; } else if (error_code != AMF0_ERROR_OK) { amf0_data_free(name); amf0_data_free(data); amf0_data_free(element); return amf0_data_error(error_code); } if (amf0_object_add(data, (char *)amf0_string_get_bytes(name), element) == NULL) { amf0_data_free(name); amf0_data_free(element); amf0_data_free(data); return NULL; } else { amf0_data_free(name); } } return data; }