diff --git a/trunk/src/core/srs_core_amf0.cpp b/trunk/src/core/srs_core_amf0.cpp index 5bd40e1cf..4dcb242de 100755 --- a/trunk/src/core/srs_core_amf0.cpp +++ b/trunk/src/core/srs_core_amf0.cpp @@ -54,6 +54,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define RTMP_AMF0_Invalid 0x3F int srs_amf0_get_object_eof_size(); +int srs_amf0_get_any_size(SrsAmf0Any* value); int srs_amf0_read_object_eof(SrsStream* stream, SrsAmf0ObjectEOF*&); int srs_amf0_write_object_eof(SrsStream* stream, SrsAmf0ObjectEOF*); int srs_amf0_read_any(SrsStream* stream, SrsAmf0Any*& value); @@ -88,43 +89,51 @@ bool SrsAmf0Any::is_object() return marker == RTMP_AMF0_Object; } +bool SrsAmf0Any::is_ecma_array() +{ + return marker == RTMP_AMF0_EcmaArray; +} + bool SrsAmf0Any::is_object_eof() { return marker == RTMP_AMF0_ObjectEnd; } -SrsAmf0String::SrsAmf0String() +SrsAmf0String::SrsAmf0String(const char* _value) { marker = RTMP_AMF0_String; + if (_value) { + value = _value; + } } SrsAmf0String::~SrsAmf0String() { } -SrsAmf0Boolean::SrsAmf0Boolean() +SrsAmf0Boolean::SrsAmf0Boolean(bool _value) { marker = RTMP_AMF0_Boolean; - value = false; + value = _value; } SrsAmf0Boolean::~SrsAmf0Boolean() { } -SrsAmf0Number::SrsAmf0Number() +SrsAmf0Number::SrsAmf0Number(double _value) { marker = RTMP_AMF0_Number; - value = 0; + value = _value; } SrsAmf0Number::~SrsAmf0Number() { - marker = RTMP_AMF0_ObjectEnd; } SrsAmf0ObjectEOF::SrsAmf0ObjectEOF() { + marker = RTMP_AMF0_ObjectEnd; utf8_empty = 0x00; } @@ -173,6 +182,47 @@ SrsAmf0Any* SrsAmf0Object::ensure_property_string(std::string name) return prop; } +SrsASrsAmf0EcmaArray::SrsASrsAmf0EcmaArray() +{ + marker = RTMP_AMF0_EcmaArray; +} + +SrsASrsAmf0EcmaArray::~SrsASrsAmf0EcmaArray() +{ + std::map::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + SrsAmf0Any* any = it->second; + delete any; + } + properties.clear(); +} + +SrsAmf0Any* SrsASrsAmf0EcmaArray::get_property(std::string name) +{ + std::map::iterator it; + + if ((it = properties.find(name)) == properties.end()) { + return NULL; + } + + return it->second; +} + +SrsAmf0Any* SrsASrsAmf0EcmaArray::ensure_property_string(std::string name) +{ + SrsAmf0Any* prop = get_property(name); + + if (!prop) { + return NULL; + } + + if (!prop->is_string()) { + return NULL; + } + + return prop; +} + int srs_amf0_read_utf8(SrsStream* stream, std::string& value) { int ret = ERROR_SUCCESS; @@ -481,6 +531,14 @@ int srs_amf0_read_any(SrsStream* stream, SrsAmf0Any*& value) value = p; return ret; } + case RTMP_AMF0_EcmaArray: { + SrsASrsAmf0EcmaArray* p = NULL; + if ((ret = srs_amf0_read_ecma_array(stream, p)) != ERROR_SUCCESS) { + return ret; + } + value = p; + return ret; + } case RTMP_AMF0_Invalid: default: { ret = ERROR_RTMP_AMF0_INVALID; @@ -518,6 +576,10 @@ int srs_amf0_write_any(SrsStream* stream, SrsAmf0Any* value) SrsAmf0Object* p = srs_amf0_convert(value); return srs_amf0_write_object(stream, p); } + case RTMP_AMF0_EcmaArray: { + SrsASrsAmf0EcmaArray* p = srs_amf0_convert(value); + return srs_amf0_write_ecma_array(stream, p); + } case RTMP_AMF0_Invalid: default: { ret = ERROR_RTMP_AMF0_INVALID; @@ -529,6 +591,52 @@ int srs_amf0_write_any(SrsStream* stream, SrsAmf0Any* value) return ret; } +int srs_amf0_get_any_size(SrsAmf0Any* value) +{ + if (!value) { + return 0; + } + + int size = 0; + + switch (value->marker) { + case RTMP_AMF0_String: { + SrsAmf0String* p = srs_amf0_convert(value); + size += srs_amf0_get_string_size(p->value); + break; + } + case RTMP_AMF0_Boolean: { + size += srs_amf0_get_boolean_size(); + break; + } + case RTMP_AMF0_Number: { + size += srs_amf0_get_number_size(); + break; + } + case RTMP_AMF0_ObjectEnd: { + size += srs_amf0_get_object_eof_size(); + break; + } + case RTMP_AMF0_Object: { + SrsAmf0Object* p = srs_amf0_convert(value); + size += srs_amf0_get_object_size(p); + break; + } + case RTMP_AMF0_EcmaArray: { + SrsASrsAmf0EcmaArray* p = srs_amf0_convert(value); + size += srs_amf0_get_ecma_array_size(p); + break; + } + default: { + // TOOD: other AMF0 types. + srs_warn("ignore unkown AMF0 type size."); + break; + } + } + + return size; +} + int srs_amf0_read_object_eof(SrsStream* stream, SrsAmf0ObjectEOF*& value) { int ret = ERROR_SUCCESS; @@ -699,6 +807,125 @@ int srs_amf0_write_object(SrsStream* stream, SrsAmf0Object* value) return ret; } +int srs_amf0_read_ecma_array(SrsStream* stream, SrsASrsAmf0EcmaArray*& value) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read ecma_array marker failed. ret=%d", ret); + return ret; + } + + char marker = stream->read_1bytes(); + if (marker != RTMP_AMF0_EcmaArray) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 check ecma_array marker failed. " + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Object, ret); + return ret; + } + srs_verbose("amf0 read ecma_array marker success"); + + // count + if (!stream->require(4)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read ecma_array count failed. ret=%d", ret); + return ret; + } + + int32_t count = stream->read_4bytes(); + srs_verbose("amf0 read ecma_array count success. count=%d", count); + + // value + value = new SrsASrsAmf0EcmaArray(); + value->count = count; + + while (!stream->empty()) { + // property-name: utf8 string + std::string property_name; + if ((ret =srs_amf0_read_utf8(stream, property_name)) != ERROR_SUCCESS) { + srs_error("amf0 ecma_array read property name failed. ret=%d", ret); + return ret; + } + // property-value: any + SrsAmf0Any* property_value = NULL; + if ((ret = srs_amf0_read_any(stream, property_value)) != ERROR_SUCCESS) { + srs_error("amf0 ecma_array read property_value failed. " + "name=%s, ret=%d", property_name.c_str(), ret); + return ret; + } + + // AMF0 Object EOF. + if (property_name.empty() || !property_value || property_value->is_object_eof()) { + if (property_value) { + delete property_value; + } + srs_info("amf0 read ecma_array EOF."); + break; + } + + // add property + value->properties[property_name] = property_value; + } + + return ret; +} +int srs_amf0_write_ecma_array(SrsStream* stream, SrsASrsAmf0EcmaArray* value) +{ + int ret = ERROR_SUCCESS; + + srs_assert(value != NULL); + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write ecma_array marker failed. ret=%d", ret); + return ret; + } + + stream->write_1bytes(RTMP_AMF0_EcmaArray); + srs_verbose("amf0 write ecma_array marker success"); + + // count + if (!stream->require(4)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write ecma_array count failed. ret=%d", ret); + return ret; + } + + stream->write_4bytes(value->count); + srs_verbose("amf0 write ecma_array count success. count=%d", value->count); + + // value + std::map::iterator it; + for (it = value->properties.begin(); it != value->properties.end(); ++it) { + std::string name = it->first; + SrsAmf0Any* any = it->second; + + if ((ret = srs_amf0_write_utf8(stream, name)) != ERROR_SUCCESS) { + srs_error("write ecma_array property name failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_write_any(stream, any)) != ERROR_SUCCESS) { + srs_error("write ecma_array property value failed. ret=%d", ret); + return ret; + } + + srs_verbose("write amf0 property success. name=%s", name.c_str()); + } + + if ((ret = srs_amf0_write_object_eof(stream, &value->eof)) != ERROR_SUCCESS) { + srs_error("write ecma_array eof failed. ret=%d", ret); + return ret; + } + + srs_verbose("write ecma_array object success."); + + return ret; +} + int srs_amf0_get_utf8_size(std::string value) { return 2 + value.length(); @@ -733,21 +960,29 @@ int srs_amf0_get_object_size(SrsAmf0Object* obj) SrsAmf0Any* value = it->second; size += srs_amf0_get_utf8_size(name); + size += srs_amf0_get_any_size(value); + } + + size += srs_amf0_get_object_eof_size(); + + return size; +} + +int srs_amf0_get_ecma_array_size(SrsASrsAmf0EcmaArray* arr) +{ + if (!arr) { + return 0; + } + + int size = 1 + 4; + + std::map::iterator it; + for (it = arr->properties.begin(); it != arr->properties.end(); ++it) { + std::string name = it->first; + SrsAmf0Any* value = it->second; - if (value->is_boolean()) { - size += srs_amf0_get_boolean_size(); - } else if (value->is_number()) { - size += srs_amf0_get_number_size(); - } else if (value->is_string()) { - SrsAmf0String* p = srs_amf0_convert(value); - size += srs_amf0_get_string_size(p->value); - } else if (value->is_object()) { - SrsAmf0Object* p = srs_amf0_convert(value); - size += srs_amf0_get_object_size(p); - } else { - // TOOD: other AMF0 types. - srs_warn("ignore unkown AMF0 type size."); - } + size += srs_amf0_get_utf8_size(name); + size += srs_amf0_get_any_size(value); } size += srs_amf0_get_object_eof_size(); diff --git a/trunk/src/core/srs_core_amf0.hpp b/trunk/src/core/srs_core_amf0.hpp index b8e82fdfa..1d272e6be 100755 --- a/trunk/src/core/srs_core_amf0.hpp +++ b/trunk/src/core/srs_core_amf0.hpp @@ -56,6 +56,7 @@ struct SrsAmf0Any virtual bool is_number(); virtual bool is_object(); virtual bool is_object_eof(); + virtual bool is_ecma_array(); }; /** @@ -68,7 +69,7 @@ struct SrsAmf0String : public SrsAmf0Any { std::string value; - SrsAmf0String(); + SrsAmf0String(const char* _value = NULL); virtual ~SrsAmf0String(); }; @@ -83,7 +84,7 @@ struct SrsAmf0Boolean : public SrsAmf0Any { bool value; - SrsAmf0Boolean(); + SrsAmf0Boolean(bool _value = false); virtual ~SrsAmf0Boolean(); }; @@ -97,7 +98,7 @@ struct SrsAmf0Number : public SrsAmf0Any { double value; - SrsAmf0Number(); + SrsAmf0Number(double _value = 0.0); virtual ~SrsAmf0Number(); }; @@ -131,6 +132,25 @@ struct SrsAmf0Object : public SrsAmf0Any virtual SrsAmf0Any* ensure_property_string(std::string name); }; +/** +* 2.10 ECMA Array Type +* ecma-array-type = associative-count *(object-property) +* associative-count = U32 +* object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker) +*/ +struct SrsASrsAmf0EcmaArray : public SrsAmf0Any +{ + int32_t count; + std::map properties; + SrsAmf0ObjectEOF eof; + + SrsASrsAmf0EcmaArray(); + virtual ~SrsASrsAmf0EcmaArray(); + + virtual SrsAmf0Any* get_property(std::string name); + virtual SrsAmf0Any* ensure_property_string(std::string name); +}; + /** * read amf0 utf8 string from stream. * 1.3.1 Strings and UTF-8 @@ -176,6 +196,16 @@ extern int srs_amf0_write_number(SrsStream* stream, double value); extern int srs_amf0_read_object(SrsStream* stream, SrsAmf0Object*& value); extern int srs_amf0_write_object(SrsStream* stream, SrsAmf0Object* value); +/** +* read amf0 object from stream. +* 2.10 ECMA Array Type +* ecma-array-type = associative-count *(object-property) +* associative-count = U32 +* object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker) +*/ +extern int srs_amf0_read_ecma_array(SrsStream* stream, SrsASrsAmf0EcmaArray*& value); +extern int srs_amf0_write_ecma_array(SrsStream* stream, SrsASrsAmf0EcmaArray* value); + /** * get amf0 objects size. */ @@ -184,7 +214,8 @@ extern int srs_amf0_get_string_size(std::string value); extern int srs_amf0_get_number_size(); extern int srs_amf0_get_boolean_size(); extern int srs_amf0_get_object_size(SrsAmf0Object* obj); - +extern int srs_amf0_get_ecma_array_size(SrsASrsAmf0EcmaArray* arr); + /** * convert the any to specified object. * @return T*, the converted object. never NULL. diff --git a/trunk/src/core/srs_core_client.cpp b/trunk/src/core/srs_core_client.cpp index 327286f8b..aba988b71 100755 --- a/trunk/src/core/srs_core_client.cpp +++ b/trunk/src/core/srs_core_client.cpp @@ -92,6 +92,12 @@ int SrsClient::do_cycle() return ret; } srs_verbose("set peer bandwidth success"); + + if ((ret = rtmp->response_connect_app()) != ERROR_SUCCESS) { + srs_error("response connect app failed. ret=%d", ret); + return ret; + } + srs_verbose("response connect app success"); return ret; } diff --git a/trunk/src/core/srs_core_protocol.cpp b/trunk/src/core/srs_core_protocol.cpp index 7c98aa797..1065c2447 100755 --- a/trunk/src/core/srs_core_protocol.cpp +++ b/trunk/src/core/srs_core_protocol.cpp @@ -1061,8 +1061,8 @@ SrsConnectAppResPacket::SrsConnectAppResPacket() { command_name = RTMP_AMF0_COMMAND_CONNECT; transaction_id = 1; - props = NULL; - info = NULL; + props = new SrsAmf0Object(); + info = new SrsAmf0Object(); } SrsConnectAppResPacket::~SrsConnectAppResPacket() diff --git a/trunk/src/core/srs_core_rtmp.cpp b/trunk/src/core/srs_core_rtmp.cpp index 65226ddac..a23cda2d2 100755 --- a/trunk/src/core/srs_core_rtmp.cpp +++ b/trunk/src/core/srs_core_rtmp.cpp @@ -30,6 +30,15 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +/** +* the signature for packets to client. +*/ +#define RTMP_SIG_FMS_VER "3,5,3,888" +#define RTMP_SIG_AMF0_VER 3 +#define RTMP_SIG_SRS_NAME "srs(simple rtmp server)" +#define RTMP_SIG_SRS_URL "https://github.com/winlinvip/simple-rtmp-server" +#define RTMP_SIG_SRS_VERSION "0.1" + int SrsRequest::discovery_app() { int ret = ERROR_SUCCESS; @@ -205,3 +214,39 @@ int SrsRtmp::set_peer_bandwidth(int bandwidth, int type) return ret; } +int SrsRtmp::response_connect_app() +{ + int ret = ERROR_SUCCESS; + + SrsMessage* msg = new SrsMessage(); + SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); + + pkt->command_name = "_result"; + + pkt->props->properties["fmsVer"] = new SrsAmf0String("FMS/"RTMP_SIG_FMS_VER); + pkt->props->properties["capabilities"] = new SrsAmf0Number(123); + pkt->props->properties["mode"] = new SrsAmf0Number(1); + + pkt->info->properties["level"] = new SrsAmf0String("status"); + pkt->info->properties["code"] = new SrsAmf0String("NetConnection.Connect.Success"); + pkt->info->properties["description"] = new SrsAmf0String("Connection succeeded"); + pkt->info->properties["objectEncoding"] = new SrsAmf0Number(RTMP_SIG_AMF0_VER); + SrsASrsAmf0EcmaArray* data = new SrsASrsAmf0EcmaArray(); + pkt->info->properties["data"] = data; + + data->properties["version"] = new SrsAmf0String(RTMP_SIG_FMS_VER); + data->properties["server"] = new SrsAmf0String(RTMP_SIG_SRS_NAME); + data->properties["srs_url"] = new SrsAmf0String(RTMP_SIG_SRS_URL); + data->properties["srs_version"] = new SrsAmf0String(RTMP_SIG_SRS_VERSION); + + msg->set_packet(pkt); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send connect app response message failed. ret=%d", ret); + return ret; + } + srs_info("send connect app response message success."); + + return ret; +} + diff --git a/trunk/src/core/srs_core_rtmp.hpp b/trunk/src/core/srs_core_rtmp.hpp index 846aedbca..10223f191 100755 --- a/trunk/src/core/srs_core_rtmp.hpp +++ b/trunk/src/core/srs_core_rtmp.hpp @@ -79,6 +79,7 @@ public: * using the Limit type field. */ virtual int set_peer_bandwidth(int bandwidth, int type); + virtual int response_connect_app(); }; #endif \ No newline at end of file