diff --git a/README.md b/README.md index 16ab122b5..02344bf80 100755 --- a/README.md +++ b/README.md @@ -230,6 +230,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v1.0, 2014-05-22, support amf0 StrictArray(0x0a). 0.9.111. * v1.0, 2014-05-22, support flv parser, add amf0 to librtmp. 0.9.110 * v1.0, 2014-05-22, fix [#74](https://github.com/winlinvip/simple-rtmp-server/issues/74), add tcUrl for http callback on_connect, 0.9.109 * v1.0, 2014-05-19, support http heartbeat, 0.9.107 diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 041819ddd..044166455 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -484,6 +484,12 @@ amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0) return any->is_ecma_array(); } +amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + return any->is_strict_array(); +} + const char* srs_amf0_to_string(srs_amf0_t amf0) { SrsAmf0Any* any = (SrsAmf0Any*)amf0; @@ -520,32 +526,44 @@ srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index) return (srs_amf0_t)obj->value_at(index); } -int srs_amf0_array_property_count(srs_amf0_t amf0) +int srs_amf0_ecma_array_property_count(srs_amf0_t amf0) { SrsAmf0EcmaArray * obj = (SrsAmf0EcmaArray*)amf0; return obj->count(); } -const char* srs_amf0_array_property_name_at(srs_amf0_t amf0, int index) +const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index) { SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; return obj->key_raw_at(index); } -srs_amf0_t srs_amf0_array_property_value_at(srs_amf0_t amf0, int index) +srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index) { SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; return (srs_amf0_t)obj->value_at(index); } -void __srs_amf0_do_print(SrsAmf0Any* any, stringstream& ss, int& level) +int srs_amf0_strict_array_property_count(srs_amf0_t amf0) { - if (true) { - for (int i = 0; i < level; i++) { - ss << " "; - } + SrsAmf0EcmaArray * obj = (SrsAmf0EcmaArray*)amf0; + return obj->count(); +} + +srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index) +{ + SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; + return (srs_amf0_t)obj->value_at(index); +} + +void __srs_fill_level_spaces(stringstream& ss, int level) +{ + for (int i = 0; i < level; i++) { + ss << " "; } - +} +void __srs_amf0_do_print(SrsAmf0Any* any, stringstream& ss, int level) +{ if (any->is_boolean()) { ss << "Boolean " << (any->to_boolean()? "true":"false") << endl; } else if (any->is_number()) { @@ -558,26 +576,36 @@ void __srs_amf0_do_print(SrsAmf0Any* any, stringstream& ss, int& level) SrsAmf0EcmaArray* obj = any->to_ecma_array(); ss << "EcmaArray " << "(" << obj->count() << " items)" << endl; for (int i = 0; i < obj->count(); i++) { - ss << " Property '" << obj->key_at(i) << "' "; - if (obj->value_at(i)->is_object() || obj->value_at(i)->is_ecma_array()) { - int next_level = level + 1; - __srs_amf0_do_print(obj->value_at(i), ss, next_level); + __srs_fill_level_spaces(ss, level + 1); + ss << "Elem '" << obj->key_at(i) << "' "; + if (obj->value_at(i)->is_complex_object()) { + __srs_amf0_do_print(obj->value_at(i), ss, level + 1); } else { - int next_level = 0; - __srs_amf0_do_print(obj->value_at(i), ss, next_level); + __srs_amf0_do_print(obj->value_at(i), ss, 0); + } + } + } else if (any->is_strict_array()) { + SrsAmf0StrictArray* obj = any->to_strict_array(); + ss << "StrictArray " << "(" << obj->count() << " items)" << endl; + for (int i = 0; i < obj->count(); i++) { + __srs_fill_level_spaces(ss, level + 1); + ss << "Elem "; + if (obj->at(i)->is_complex_object()) { + __srs_amf0_do_print(obj->at(i), ss, level + 1); + } else { + __srs_amf0_do_print(obj->at(i), ss, 0); } } } else if (any->is_object()) { SrsAmf0Object* obj = any->to_object(); ss << "Object " << "(" << obj->count() << " items)" << endl; for (int i = 0; i < obj->count(); i++) { - ss << " Property '" << obj->key_at(i) << "' "; - if (obj->value_at(i)->is_object() || obj->value_at(i)->is_ecma_array()) { - int next_level = level + 1; - __srs_amf0_do_print(obj->value_at(i), ss, next_level); + __srs_fill_level_spaces(ss, level + 1); + ss << "Property '" << obj->key_at(i) << "' "; + if (obj->value_at(i)->is_complex_object()) { + __srs_amf0_do_print(obj->value_at(i), ss, level + 1); } else { - int next_level = 0; - __srs_amf0_do_print(obj->value_at(i), ss, next_level); + __srs_amf0_do_print(obj->value_at(i), ss, 0); } } } else { @@ -587,23 +615,29 @@ void __srs_amf0_do_print(SrsAmf0Any* any, stringstream& ss, int& level) char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize) { + if (!amf0) { + return NULL; + } + stringstream ss; ss.precision(1); SrsAmf0Any* any = (SrsAmf0Any*)amf0; - int level = 0; - __srs_amf0_do_print(any, ss, level); + __srs_amf0_do_print(any, ss, 0); string str = ss.str(); if (str.empty()) { return NULL; } - *pdata = new char[str.length()]; + char* data = new char[str.length() + 1]; + memcpy(data, str.data(), str.length()); + data[str.length()] = 0; + + *pdata = data; *psize = str.length(); - memcpy(*pdata, str.data(), str.length()); return *pdata; } diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index e57971b0f..e5eb37d6d 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -176,6 +176,7 @@ amf0_bool srs_amf0_is_number(srs_amf0_t amf0); amf0_bool srs_amf0_is_null(srs_amf0_t amf0); amf0_bool srs_amf0_is_object(srs_amf0_t amf0); amf0_bool srs_amf0_is_ecma_array(srs_amf0_t amf0); +amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); /* value converter */ const char* srs_amf0_to_string(srs_amf0_t amf0); amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); @@ -184,10 +185,13 @@ amf0_number srs_amf0_to_number(srs_amf0_t amf0); int srs_amf0_object_property_count(srs_amf0_t amf0); const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index); -/* array value converter */ -int srs_amf0_array_property_count(srs_amf0_t amf0); -const char* srs_amf0_array_property_name_at(srs_amf0_t amf0, int index); -srs_amf0_t srs_amf0_array_property_value_at(srs_amf0_t amf0, int index); +/* ecma array value converter */ +int srs_amf0_ecma_array_property_count(srs_amf0_t amf0); +const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index); +srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index); +/* strict array value converter */ +int srs_amf0_strict_array_property_count(srs_amf0_t amf0); +srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index); /* human readable print */ char* srs_amf0_human_print(srs_amf0_t amf0, char** pdata, int* psize); diff --git a/trunk/src/rtmp/srs_protocol_amf0.cpp b/trunk/src/rtmp/srs_protocol_amf0.cpp index 6342fd6da..74d664f1b 100644 --- a/trunk/src/rtmp/srs_protocol_amf0.cpp +++ b/trunk/src/rtmp/srs_protocol_amf0.cpp @@ -251,6 +251,16 @@ bool SrsAmf0Any::is_ecma_array() return marker == RTMP_AMF0_EcmaArray; } +bool SrsAmf0Any::is_strict_array() +{ + return marker == RTMP_AMF0_StrictArray; +} + +bool SrsAmf0Any::is_complex_object() +{ + return is_object() || is_object_eof() || is_ecma_array() || is_strict_array(); +} + string SrsAmf0Any::to_str() { __SrsAmf0String* p = dynamic_cast<__SrsAmf0String*>(this); @@ -293,6 +303,13 @@ SrsAmf0EcmaArray* SrsAmf0Any::to_ecma_array() return p; } +SrsAmf0StrictArray* SrsAmf0Any::to_strict_array() +{ + SrsAmf0StrictArray* p = dynamic_cast(this); + srs_assert(p != NULL); + return p; +} + bool SrsAmf0Any::is_object_eof() { return marker == RTMP_AMF0_ObjectEnd; @@ -338,6 +355,11 @@ SrsAmf0EcmaArray* SrsAmf0Any::ecma_array() return new SrsAmf0EcmaArray(); } +SrsAmf0StrictArray* SrsAmf0Any::strict_array() +{ + return new SrsAmf0StrictArray(); +} + int SrsAmf0Any::discovery(SrsStream* stream, SrsAmf0Any** ppvalue) { int ret = ERROR_SUCCESS; @@ -390,6 +412,10 @@ int SrsAmf0Any::discovery(SrsStream* stream, SrsAmf0Any** ppvalue) *ppvalue = SrsAmf0Any::ecma_array(); return ret; } + case RTMP_AMF0_StrictArray: { + *ppvalue = SrsAmf0Any::strict_array(); + return ret; + } case RTMP_AMF0_Invalid: default: { ret = ERROR_RTMP_AMF0_INVALID; @@ -956,6 +982,137 @@ SrsAmf0Any* SrsAmf0EcmaArray::ensure_property_number(string name) return properties->ensure_property_number(name); } +SrsAmf0StrictArray::SrsAmf0StrictArray() +{ + marker = RTMP_AMF0_StrictArray; +} + +SrsAmf0StrictArray::~SrsAmf0StrictArray() +{ + std::vector::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + SrsAmf0Any* any = *it; + srs_freep(any); + } + properties.clear(); +} + +int SrsAmf0StrictArray::total_size() +{ + int size = 1 + 4; + + for (int i = 0; i < (int)properties.size(); i++){ + SrsAmf0Any* any = properties[i]; + size += any->total_size(); + } + + return size; +} + +int SrsAmf0StrictArray::read(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read strict_array marker failed. ret=%d", ret); + return ret; + } + + char marker = stream->read_1bytes(); + if (marker != RTMP_AMF0_StrictArray) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 check strict_array marker failed. " + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Object, ret); + return ret; + } + srs_verbose("amf0 read strict_array marker success"); + + // count + if (!stream->require(4)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read strict_array count failed. ret=%d", ret); + return ret; + } + + int32_t count = stream->read_4bytes(); + srs_verbose("amf0 read strict_array count success. count=%d", count); + + // value + this->_count = count; + + for (int i = 0; i < count && !stream->empty(); i++) { + // property-value: any + SrsAmf0Any* elem = NULL; + if ((ret = srs_amf0_read_any(stream, &elem)) != ERROR_SUCCESS) { + srs_error("amf0 strict_array read value failed. ret=%d", ret); + return ret; + } + + // add property + properties.push_back(elem); + } + + return ret; +} +int SrsAmf0StrictArray::write(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write strict_array marker failed. ret=%d", ret); + return ret; + } + + stream->write_1bytes(RTMP_AMF0_StrictArray); + srs_verbose("amf0 write strict_array marker success"); + + // count + if (!stream->require(4)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write strict_array count failed. ret=%d", ret); + return ret; + } + + stream->write_4bytes(this->_count); + srs_verbose("amf0 write strict_array count success. count=%d", _count); + + // value + for (int i = 0; i < (int)properties.size(); i++) { + SrsAmf0Any* any = properties[i]; + + if ((ret = srs_amf0_write_any(stream, any)) != ERROR_SUCCESS) { + srs_error("write strict_array property value failed. ret=%d", ret); + return ret; + } + + srs_verbose("write amf0 property success. name=%s", name.c_str()); + } + + srs_verbose("write strict_array object success."); + + return ret; +} + +void SrsAmf0StrictArray::clear() +{ + properties.clear(); +} + +int SrsAmf0StrictArray::count() +{ + return properties.size(); +} + +SrsAmf0Any* SrsAmf0StrictArray::at(int index) +{ + srs_assert(index < (int)properties.size()); + return properties.at(index); +} + int SrsAmf0Size::utf8(string value) { return 2 + value.length(); @@ -1009,6 +1166,15 @@ int SrsAmf0Size::ecma_array(SrsAmf0EcmaArray* arr) return arr->total_size(); } +int SrsAmf0Size::strict_array(SrsAmf0StrictArray* arr) +{ + if (!arr) { + return 0; + } + + return arr->total_size(); +} + int SrsAmf0Size::any(SrsAmf0Any* o) { if (!o) { diff --git a/trunk/src/rtmp/srs_protocol_amf0.hpp b/trunk/src/rtmp/srs_protocol_amf0.hpp index da2d4c06c..0ad88e2cf 100644 --- a/trunk/src/rtmp/srs_protocol_amf0.hpp +++ b/trunk/src/rtmp/srs_protocol_amf0.hpp @@ -31,10 +31,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include +#include class SrsStream; class SrsAmf0Object; class SrsAmf0EcmaArray; +class SrsAmf0StrictArray; class __SrsUnSortedHashtable; class __SrsAmf0ObjectEOF; @@ -96,6 +98,9 @@ public: virtual bool is_object(); virtual bool is_object_eof(); virtual bool is_ecma_array(); + virtual bool is_strict_array(); +public: + virtual bool is_complex_object(); public: /** * get the string of any when is_string() indicates true. @@ -123,6 +128,7 @@ public: * user must ensure the type is a ecma array, or assert failed. */ virtual SrsAmf0EcmaArray* to_ecma_array(); + virtual SrsAmf0StrictArray* to_strict_array(); public: /** * get the size of amf0 any, including the marker size. @@ -142,6 +148,7 @@ public: static SrsAmf0Object* object(); static SrsAmf0Any* object_eof(); static SrsAmf0EcmaArray* ecma_array(); + static SrsAmf0StrictArray* strict_array(); public: static int discovery(SrsStream* stream, SrsAmf0Any** ppvalue); }; @@ -225,6 +232,36 @@ public: virtual SrsAmf0Any* ensure_property_number(std::string name); }; +/** +* 2.12 Strict Array Type +* array-count = U32 +* strict-array-type = array-count *(value-type) +*/ +class SrsAmf0StrictArray : public SrsAmf0Any +{ +private: + std::vector properties; + int32_t _count; + +private: + // use SrsAmf0Any::strict_array() to create it. + friend class SrsAmf0Any; + SrsAmf0StrictArray(); +public: + virtual ~SrsAmf0StrictArray(); + +public: + virtual int total_size(); + virtual int read(SrsStream* stream); + virtual int write(SrsStream* stream); + +public: + virtual void clear(); + virtual int count(); + // @remark: max index is count(). + virtual SrsAmf0Any* at(int index); +}; + /** * the class to get amf0 object size */ @@ -240,6 +277,7 @@ public: static int object(SrsAmf0Object* obj); static int object_eof(); static int ecma_array(SrsAmf0EcmaArray* arr); + static int strict_array(SrsAmf0StrictArray* arr); static int any(SrsAmf0Any* o); };