diff --git a/README.md b/README.md index 9a598a34e..e92db1b50 100755 --- a/README.md +++ b/README.md @@ -231,6 +231,7 @@ Supported operating systems and hardware: * 2013-10-17, Created.
## History +* v1.0, 2014-05-29, support flv inject and flv http streaming with start=bytes. 0.9.122 * v1.0, 2014-05-28, [1.0 mainline4(0.9.120)](https://github.com/winlinvip/simple-rtmp-server/releases/tag/1.0.mainline4) released. 39200 lines. * v1.0, 2014-05-27, fix [#87](https://github.com/winlinvip/simple-rtmp-server/issues/87), add source id for full trackable log. 0.9.120 * v1.0, 2014-05-27, fix [#84](https://github.com/winlinvip/simple-rtmp-server/issues/84), unpublish when edge disconnect. 0.9.119 diff --git a/trunk/research/librtmp/srs_flv_injecter.c b/trunk/research/librtmp/srs_flv_injecter.c index 8a500b445..9779081d6 100644 --- a/trunk/research/librtmp/srs_flv_injecter.c +++ b/trunk/research/librtmp/srs_flv_injecter.c @@ -38,7 +38,6 @@ gcc srs_flv_injecter.c ../../objs/lib/srs_librtmp.a -g -O0 -lstdc++ -o srs_flv_i #define ERROR_INJECTED 10000 int process(const char* in_flv_file, const char* out_flv_file, srs_flv_t* pic, srs_flv_t* poc); -int inject_flv(srs_flv_t ic, srs_flv_t oc); int main(int argc, char** argv) { int ret = 0; @@ -108,22 +107,52 @@ int process(const char* in_flv_file, const char* out_flv_file, srs_flv_t* pic, s { int ret = 0; - if ((*pic = srs_flv_open_read(in_flv_file)) == NULL) { + srs_flv_t ic; + srs_flv_t oc; + + // to adjust metadata. + // the ic metadata end offset, the next tag start offset. + // all oc metadata must adjust according to: + // adjust = new_metadata_end_offset - metadata_end_offset + int64_t metadata_end_offset = 0; + + // metadata + srs_amf0_t amf0_name = NULL; + srs_amf0_t amf0_data = NULL; + srs_amf0_t filepositions = NULL; + + if ((ic = srs_flv_open_read(in_flv_file)) == NULL) { ret = 2; trace("open input flv file failed. ret=%d", ret); return ret; } + *pic = ic; - if ((*poc = srs_flv_open_write(out_flv_file)) == NULL) { + if ((oc = srs_flv_open_write(out_flv_file)) == NULL) { ret = 2; trace("open output flv file failed. ret=%d", ret); return ret; } + *poc = oc; + + /** + * we use two roundtrip to avoid the paddings of metadata, + * to support large keyframes videos without padding fields. + */ + // build keyframes offset to metadata. + if ((ret = build_keyframes(ic, &amf0_name, &amf0_data, &filepositions, &metadata_end_offset)) != 0) { + return ret; + } - if ((ret = inject_flv(*pic, *poc)) != 0) { + // inject the metadata to oc. + if ((ret = do_inject_flv(ic, oc, amf0_name, amf0_data, filepositions, metadata_end_offset)) != 0) { return ret; } + // TODO: FIXME: mem leak when error. + srs_amf0_free(amf0_name); + srs_amf0_free(amf0_data); + return ret; } @@ -148,12 +177,13 @@ int parse_metadata(char* data, int size, srs_amf0_t* pname, srs_amf0_t* pdata) return ret; } -int inject_flv(srs_flv_t ic, srs_flv_t oc) +int build_keyframes(srs_flv_t ic, srs_amf0_t *pname, srs_amf0_t* pdata, srs_amf0_t* pfilepositions, int64_t* pmetadata_end_offset) { int ret = 0; // flv header char header[13]; + // packet data char type; u_int32_t timestamp = 0; @@ -163,8 +193,8 @@ int inject_flv(srs_flv_t ic, srs_flv_t oc) // metadata srs_amf0_t amf0_name = NULL; - int amf0_name_size = 0; srs_amf0_t amf0_data = NULL; + srs_amf0_t keyframes = NULL; srs_amf0_t filepositions = NULL; srs_amf0_t times = NULL; @@ -184,7 +214,7 @@ int inject_flv(srs_flv_t ic, srs_flv_t oc) if ((ret = srs_flv_read_tag_header(ic, &type, &size, ×tamp)) != 0) { if (srs_flv_is_eof(ret)) { trace("parse completed."); - break; + return 0; } trace("flv get packet failed. ret=%d", ret); return ret; @@ -192,7 +222,7 @@ int inject_flv(srs_flv_t ic, srs_flv_t oc) if (size <= 0) { trace("invalid size=%d", size); - break; + return ret; } // TODO: FIXME: mem leak when error. @@ -208,30 +238,40 @@ int inject_flv(srs_flv_t ic, srs_flv_t oc) srs_amf0_strict_array_append(times, srs_amf0_create_number(((double)timestamp)/ 1000)); } } else if (type == SRS_RTMP_TYPE_SCRIPT) { + *pmetadata_end_offset = srs_flv_tellg(ic); if ((ret = parse_metadata(data, size, &amf0_name, &amf0_data)) != 0) { return ret; } + *pname = amf0_name; + *pdata = amf0_data; + if (srs_amf0_is_object(amf0_data)) { keyframes = srs_amf0_object_property(amf0_data, "keyframes"); - if (keyframes != NULL) { - return 0; + if (keyframes == NULL) { + keyframes = srs_amf0_create_ecma_array(); } - keyframes = srs_amf0_create_ecma_array(); srs_amf0_object_property_set(amf0_data, "keyframes", keyframes); - filepositions = srs_amf0_create_strict_array(); + // always clear the old keyframes. + srs_amf0_ecma_array_clear(keyframes); + + *pfilepositions = filepositions = srs_amf0_create_strict_array(); srs_amf0_object_property_set(keyframes, "filepositions", filepositions); + times = srs_amf0_create_strict_array(); srs_amf0_object_property_set(keyframes, "times", times); } else if (srs_amf0_is_ecma_array(amf0_data)) { keyframes = srs_amf0_ecma_array_property(amf0_data, "keyframes"); - if (keyframes != NULL) { - return 0; + if (keyframes == NULL) { + keyframes = srs_amf0_create_ecma_array(); } - keyframes = srs_amf0_create_ecma_array(); srs_amf0_ecma_array_property_set(amf0_data, "keyframes", keyframes); - filepositions = srs_amf0_create_strict_array(); + // always clear the old keyframes. + srs_amf0_ecma_array_clear(keyframes); + + *pfilepositions = filepositions = srs_amf0_create_strict_array(); srs_amf0_ecma_array_property_set(keyframes, "filepositions", filepositions); + times = srs_amf0_create_strict_array(); srs_amf0_ecma_array_property_set(keyframes, "times", times); } @@ -240,6 +280,30 @@ int inject_flv(srs_flv_t ic, srs_flv_t oc) free(data); } + return ret; +} + +int do_inject_flv(srs_flv_t ic, srs_flv_t oc, srs_amf0_t amf0_name, srs_amf0_t amf0_data, srs_amf0_t filepositions, int64_t metadata_end_offset) +{ + int ret = 0; + + // flv header + char header[13]; + // packet data + char type; + u_int32_t timestamp = 0; + char* data = NULL; + int32_t size; + + // metadata + srs_amf0_t fileposition = NULL; + int amf0_name_size = 0; + int i; + + // the metadata end offset, the next tag start offset. + int64_t new_metadata_end_offset = 0; + int offset_adjust = 0; + // reset to write injected file srs_flv_lseek(ic, 0); @@ -255,7 +319,18 @@ int inject_flv(srs_flv_t ic, srs_flv_t oc) if (amf0_name != NULL && amf0_data != NULL) { amf0_name_size = srs_amf0_size(amf0_name); size = amf0_name_size + srs_amf0_size(amf0_data); + + // adjust all offset of keyframes. + new_metadata_end_offset = srs_flv_tellg(oc) + srs_flv_size_tag(size); + // the adjust is new offset sub the old offset of metadata end. + offset_adjust = new_metadata_end_offset - metadata_end_offset; + for (i = 0; i < srs_amf0_strict_array_property_count(filepositions); i++) { + fileposition = srs_amf0_strict_array_property_at(filepositions, i); + srs_amf0_set_number(fileposition, srs_amf0_to_number(fileposition) + offset_adjust); + } + data = (char*)malloc(size); + memset(data, 0, size); if ((ret = srs_amf0_serialize(amf0_name, data, amf0_name_size)) != 0) { return ret; } @@ -267,6 +342,7 @@ int inject_flv(srs_flv_t ic, srs_flv_t oc) } free(data); } + trace("build keyframe infos from flv"); for (;;) { // tag header @@ -303,8 +379,5 @@ int inject_flv(srs_flv_t ic, srs_flv_t oc) free(data); } - srs_amf0_free(amf0_name); - srs_amf0_free(amf0_data); - return ret; } diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index ed3275a94..20bdac12d 100644 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -31,7 +31,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR "0" #define VERSION_MINOR "9" -#define VERSION_REVISION "121" +#define VERSION_REVISION "122" #define RTMP_SIG_SRS_VERSION VERSION_MAJOR"."VERSION_MINOR"."VERSION_REVISION // server info. #define RTMP_SIG_SRS_KEY "SRS" diff --git a/trunk/src/kernel/srs_kernel_flv.cpp b/trunk/src/kernel/srs_kernel_flv.cpp index 60c7e313f..d5c25b8be 100644 --- a/trunk/src/kernel/srs_kernel_flv.cpp +++ b/trunk/src/kernel/srs_kernel_flv.cpp @@ -330,6 +330,11 @@ int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size) return ret; } +int SrsFlvEncoder::size_tag(int data_size) +{ + return SRS_FLV_TAG_HEADER_SIZE + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; +} + int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_size) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/kernel/srs_kernel_flv.hpp b/trunk/src/kernel/srs_kernel_flv.hpp index 8efd3b800..f5f282f3e 100644 --- a/trunk/src/kernel/srs_kernel_flv.hpp +++ b/trunk/src/kernel/srs_kernel_flv.hpp @@ -105,6 +105,11 @@ public: */ virtual int write_audio(int64_t timestamp, char* data, int size); virtual int write_video(int64_t timestamp, char* data, int size); + /** + * get the tag size, + * including the tag header, body, and 4bytes previous tag size. + */ + static int size_tag(int data_size); private: virtual int write_tag(char* header, int header_size, char* tag, int tag_size); }; diff --git a/trunk/src/libs/srs_librtmp.cpp b/trunk/src/libs/srs_librtmp.cpp index 7648d1f70..f1d2d5451 100644 --- a/trunk/src/libs/srs_librtmp.cpp +++ b/trunk/src/libs/srs_librtmp.cpp @@ -519,6 +519,11 @@ int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int si return ret; } +int srs_flv_size_tag(int data_size) +{ + return SrsFlvEncoder::size_tag(data_size); +} + int64_t srs_flv_tellg(srs_flv_t flv) { FlvContext* context = (FlvContext*)flv; @@ -684,6 +689,12 @@ amf0_number srs_amf0_to_number(srs_amf0_t amf0) return any->to_number(); } +void srs_amf0_set_number(srs_amf0_t amf0, amf0_number value) +{ + SrsAmf0Any* any = (SrsAmf0Any*)amf0; + any->set_number(value); +} + int srs_amf0_object_property_count(srs_amf0_t amf0) { SrsAmf0Object* obj = (SrsAmf0Object*)amf0; @@ -746,6 +757,12 @@ void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf obj->set(name, any); } +void srs_amf0_ecma_array_clear(srs_amf0_t amf0) +{ + SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0; + obj->clear(); +} + int srs_amf0_strict_array_property_count(srs_amf0_t amf0) { SrsAmf0StrictArray * obj = (SrsAmf0StrictArray*)amf0; diff --git a/trunk/src/libs/srs_librtmp.hpp b/trunk/src/libs/srs_librtmp.hpp index d3bd5fde8..c236ea710 100644 --- a/trunk/src/libs/srs_librtmp.hpp +++ b/trunk/src/libs/srs_librtmp.hpp @@ -169,6 +169,8 @@ int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size); int srs_flv_write_header(srs_flv_t flv, char header[9]); /* write flv tag to file, auto write the 4bytes previous tag size */ int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size); +/* get the tag size, for flv injecter to adjust offset, size=tag_header+data+previous_tag */ +int srs_flv_size_tag(int data_size); /* file stream */ /* file stream tellg to get offset */ int64_t srs_flv_tellg(srs_flv_t flv); @@ -211,6 +213,8 @@ amf0_bool srs_amf0_is_strict_array(srs_amf0_t amf0); const char* srs_amf0_to_string(srs_amf0_t amf0); amf0_bool srs_amf0_to_boolean(srs_amf0_t amf0); amf0_number srs_amf0_to_number(srs_amf0_t amf0); +/* value setter */ +void srs_amf0_set_number(srs_amf0_t amf0, amf0_number value); /* object value converter */ int srs_amf0_object_property_count(srs_amf0_t amf0); const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index); @@ -223,6 +227,7 @@ 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); srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name); void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value); +void srs_amf0_ecma_array_clear(srs_amf0_t amf0); /* 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); diff --git a/trunk/src/rtmp/srs_protocol_amf0.cpp b/trunk/src/rtmp/srs_protocol_amf0.cpp index 716f4ce16..00c5bd22e 100644 --- a/trunk/src/rtmp/srs_protocol_amf0.cpp +++ b/trunk/src/rtmp/srs_protocol_amf0.cpp @@ -75,6 +75,7 @@ public: virtual int total_size(); virtual int read(SrsStream* stream); virtual int write(SrsStream* stream); + virtual SrsAmf0Any* copy(); }; /** @@ -95,6 +96,7 @@ public: virtual int total_size(); virtual int read(SrsStream* stream); virtual int write(SrsStream* stream); + virtual SrsAmf0Any* copy(); }; /** @@ -114,6 +116,7 @@ public: virtual int total_size(); virtual int read(SrsStream* stream); virtual int write(SrsStream* stream); + virtual SrsAmf0Any* copy(); }; /** @@ -130,6 +133,7 @@ public: virtual int total_size(); virtual int read(SrsStream* stream); virtual int write(SrsStream* stream); + virtual SrsAmf0Any* copy(); }; /** @@ -146,6 +150,7 @@ public: virtual int total_size(); virtual int read(SrsStream* stream); virtual int write(SrsStream* stream); + virtual SrsAmf0Any* copy(); }; /** @@ -173,6 +178,8 @@ public: virtual SrsAmf0Any* get_property(std::string name); virtual SrsAmf0Any* ensure_property_string(std::string name); virtual SrsAmf0Any* ensure_property_number(std::string name); + + virtual void copy(__SrsUnSortedHashtable* src); }; /** @@ -191,6 +198,7 @@ public: virtual int total_size(); virtual int read(SrsStream* stream); virtual int write(SrsStream* stream); + virtual SrsAmf0Any* copy(); }; /** @@ -310,6 +318,13 @@ SrsAmf0StrictArray* SrsAmf0Any::to_strict_array() return p; } +void SrsAmf0Any::set_number(double value) +{ + __SrsAmf0Number* p = dynamic_cast<__SrsAmf0Number*>(this); + srs_assert(p != NULL); + p->value = value; +} + bool SrsAmf0Any::is_object_eof() { return marker == RTMP_AMF0_ObjectEnd; @@ -433,13 +448,7 @@ __SrsUnSortedHashtable::__SrsUnSortedHashtable() __SrsUnSortedHashtable::~__SrsUnSortedHashtable() { - std::vector::iterator it; - for (it = properties.begin(); it != properties.end(); ++it) { - SrsAmf0ObjectPropertyType& elem = *it; - SrsAmf0Any* any = elem.second; - srs_freep(any); - } - properties.clear(); + clear(); } int __SrsUnSortedHashtable::count() @@ -449,6 +458,12 @@ int __SrsUnSortedHashtable::count() void __SrsUnSortedHashtable::clear() { + std::vector::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + SrsAmf0ObjectPropertyType& elem = *it; + SrsAmf0Any* any = elem.second; + srs_freep(any); + } properties.clear(); } @@ -543,6 +558,17 @@ SrsAmf0Any* __SrsUnSortedHashtable::ensure_property_number(string name) return prop; } +void __SrsUnSortedHashtable::copy(__SrsUnSortedHashtable* src) +{ + std::vector::iterator it; + for (it = src->properties.begin(); it != src->properties.end(); ++it) { + SrsAmf0ObjectPropertyType& elem = *it; + std::string key = elem.first; + SrsAmf0Any* any = elem.second; + set(key, any->copy()); + } +} + __SrsAmf0ObjectEOF::__SrsAmf0ObjectEOF() { marker = RTMP_AMF0_ObjectEnd; @@ -623,6 +649,11 @@ int __SrsAmf0ObjectEOF::write(SrsStream* stream) return ret; } +SrsAmf0Any* __SrsAmf0ObjectEOF::copy() +{ + return new __SrsAmf0ObjectEOF(); +} + SrsAmf0Object::SrsAmf0Object() { properties = new __SrsUnSortedHashtable(); @@ -750,6 +781,13 @@ int SrsAmf0Object::write(SrsStream* stream) return ret; } +SrsAmf0Any* SrsAmf0Object::copy() +{ + SrsAmf0Object* copy = new SrsAmf0Object(); + copy->properties->copy(properties); + return copy; +} + int SrsAmf0Object::count() { return properties->count(); @@ -792,6 +830,7 @@ SrsAmf0Any* SrsAmf0Object::ensure_property_number(string name) SrsAmf0EcmaArray::SrsAmf0EcmaArray() { + _count = 0; properties = new __SrsUnSortedHashtable(); eof = new __SrsAmf0ObjectEOF(); marker = RTMP_AMF0_EcmaArray; @@ -937,6 +976,14 @@ int SrsAmf0EcmaArray::write(SrsStream* stream) return ret; } +SrsAmf0Any* SrsAmf0EcmaArray::copy() +{ + SrsAmf0EcmaArray* copy = new SrsAmf0EcmaArray(); + copy->properties->copy(properties); + copy->_count = _count; + return copy; +} + void SrsAmf0EcmaArray::clear() { properties->clear(); @@ -1097,6 +1144,20 @@ int SrsAmf0StrictArray::write(SrsStream* stream) return ret; } +SrsAmf0Any* SrsAmf0StrictArray::copy() +{ + SrsAmf0StrictArray* copy = new SrsAmf0StrictArray(); + + std::vector::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + SrsAmf0Any* any = *it; + copy->append(any->copy()); + } + + copy->_count = _count; + return copy; +} + void SrsAmf0StrictArray::clear() { properties.clear(); @@ -1116,6 +1177,7 @@ SrsAmf0Any* SrsAmf0StrictArray::at(int index) void SrsAmf0StrictArray::append(SrsAmf0Any* any) { properties.push_back(any); + _count = (int32_t)properties.size(); } int SrsAmf0Size::utf8(string value) @@ -1216,6 +1278,12 @@ int __SrsAmf0String::write(SrsStream* stream) return srs_amf0_write_string(stream, value); } +SrsAmf0Any* __SrsAmf0String::copy() +{ + __SrsAmf0String* copy = new __SrsAmf0String(value.c_str()); + return copy; +} + __SrsAmf0Boolean::__SrsAmf0Boolean(bool _value) { marker = RTMP_AMF0_Boolean; @@ -1241,6 +1309,12 @@ int __SrsAmf0Boolean::write(SrsStream* stream) return srs_amf0_write_boolean(stream, value); } +SrsAmf0Any* __SrsAmf0Boolean::copy() +{ + __SrsAmf0Boolean* copy = new __SrsAmf0Boolean(value); + return copy; +} + __SrsAmf0Number::__SrsAmf0Number(double _value) { marker = RTMP_AMF0_Number; @@ -1266,6 +1340,12 @@ int __SrsAmf0Number::write(SrsStream* stream) return srs_amf0_write_number(stream, value); } +SrsAmf0Any* __SrsAmf0Number::copy() +{ + __SrsAmf0Number* copy = new __SrsAmf0Number(value); + return copy; +} + __SrsAmf0Null::__SrsAmf0Null() { marker = RTMP_AMF0_Null; @@ -1290,6 +1370,12 @@ int __SrsAmf0Null::write(SrsStream* stream) return srs_amf0_write_null(stream); } +SrsAmf0Any* __SrsAmf0Null::copy() +{ + __SrsAmf0Null* copy = new __SrsAmf0Null(); + return copy; +} + __SrsAmf0Undefined::__SrsAmf0Undefined() { marker = RTMP_AMF0_Undefined; @@ -1314,6 +1400,12 @@ int __SrsAmf0Undefined::write(SrsStream* stream) return srs_amf0_write_undefined(stream); } +SrsAmf0Any* __SrsAmf0Undefined::copy() +{ + __SrsAmf0Undefined* copy = new __SrsAmf0Undefined(); + return copy; +} + int srs_amf0_read_any(SrsStream* stream, SrsAmf0Any** ppvalue) { int ret = ERROR_SUCCESS; diff --git a/trunk/src/rtmp/srs_protocol_amf0.hpp b/trunk/src/rtmp/srs_protocol_amf0.hpp index df8401958..0b751dc9a 100644 --- a/trunk/src/rtmp/srs_protocol_amf0.hpp +++ b/trunk/src/rtmp/srs_protocol_amf0.hpp @@ -63,6 +63,11 @@ class __SrsAmf0ObjectEOF; // SrsAmf0Object* obj = SrsAmf0Any::object(); // 5. SrsAmf0EcmaArray: create the amf0 ecma array. // SrsAmf0EcmaArray* arr = SrsAmf0Any::ecma_array(); +// 6. SrsAmf0Any: set basic type value +// SrsAmf0Any* pany = ... +// if (pany->is_number()) { +// pany->set_number(100.1); +// } // // please carefully the size and count of amf0 any: // 1. total_size(): the total memory size the object wrote to buffer. @@ -129,6 +134,12 @@ public: */ virtual SrsAmf0EcmaArray* to_ecma_array(); virtual SrsAmf0StrictArray* to_strict_array(); +public: + /** + * set the number of any when is_number() indicates true. + * user must ensure the type is a number, or assert failed. + */ + virtual void set_number(double value); public: /** * get the size of amf0 any, including the marker size. @@ -139,6 +150,7 @@ public: */ virtual int read(SrsStream* stream) = 0; virtual int write(SrsStream* stream) = 0; + virtual SrsAmf0Any* copy() = 0; public: static SrsAmf0Any* str(const char* value = NULL); static SrsAmf0Any* boolean(bool value = false); @@ -175,6 +187,7 @@ public: virtual int total_size(); virtual int read(SrsStream* stream); virtual int write(SrsStream* stream); + virtual SrsAmf0Any* copy(); public: virtual int count(); @@ -215,6 +228,7 @@ public: virtual int total_size(); virtual int read(SrsStream* stream); virtual int write(SrsStream* stream); + virtual SrsAmf0Any* copy(); public: virtual void clear(); @@ -254,6 +268,7 @@ public: virtual int total_size(); virtual int read(SrsStream* stream); virtual int write(SrsStream* stream); + virtual SrsAmf0Any* copy(); public: virtual void clear(); diff --git a/trunk/src/rtmp/srs_protocol_rtmp_stack.cpp b/trunk/src/rtmp/srs_protocol_rtmp_stack.cpp index 37c0aaac2..786a32ade 100644 --- a/trunk/src/rtmp/srs_protocol_rtmp_stack.cpp +++ b/trunk/src/rtmp/srs_protocol_rtmp_stack.cpp @@ -3173,10 +3173,9 @@ int SrsOnMetaDataPacket::decode(SrsStream* stream) // if ecma array, copy to object. for (int i = 0; i < arr->count(); i++) { - metadata->set(arr->key_at(i), arr->value_at(i)); + metadata->set(arr->key_at(i), arr->value_at(i)->copy()); } - arr->clear(); srs_info("decode metadata array success"); }