diff --git a/trunk/src/core/srs_core.cpp b/trunk/src/core/srs_core.cpp index e73c19b71..88da2ef58 100755 --- a/trunk/src/core/srs_core.cpp +++ b/trunk/src/core/srs_core.cpp @@ -1,45 +1,45 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include - -static int64_t _srs_system_time_us_cache = 0; - -int64_t srs_get_system_time_ms() -{ - return _srs_system_time_us_cache / 1000; -} - -void srs_update_system_time_ms() -{ - timeval now; - - gettimeofday(&now, NULL); - - // we must convert the tv_sec/tv_usec to int64_t. - _srs_system_time_us_cache = now.tv_sec * 1000 * 1000 + now.tv_usec; - - _srs_system_time_us_cache = srs_max(0, _srs_system_time_us_cache); -} +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include + +static int64_t _srs_system_time_us_cache = 0; + +int64_t srs_get_system_time_ms() +{ + return _srs_system_time_us_cache / 1000; +} + +void srs_update_system_time_ms() +{ + timeval now; + + gettimeofday(&now, NULL); + + // we must convert the tv_sec/tv_usec to int64_t. + _srs_system_time_us_cache = now.tv_sec * 1000 * 1000 + now.tv_usec; + + _srs_system_time_us_cache = srs_max(0, _srs_system_time_us_cache); +} diff --git a/trunk/src/core/srs_core.hpp b/trunk/src/core/srs_core.hpp index 973dc201f..51382bc7d 100755 --- a/trunk/src/core/srs_core.hpp +++ b/trunk/src/core/srs_core.hpp @@ -1,90 +1,90 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_HPP -#define SRS_CORE_HPP - -/* -#include -*/ - -/** -* the core provides the common defined macros, utilities, -* user must include the srs_core.hpp before any header, or maybe -* build failed. -*/ - -// for int64_t print using PRId64 format. -#ifndef __STDC_FORMAT_MACROS - #define __STDC_FORMAT_MACROS -#endif -#include - -#include -#define srs_assert(expression) assert(expression) - -#include -#include - -// free the p and set to NULL. -// p must be a T*. -#define srs_freep(p) \ - if (p) { \ - delete p; \ - p = NULL; \ - } \ - (void)0 -// free the p which represents a array -#define srs_freepa(p) \ - if (p) { \ - delete[] p; \ - p = NULL; \ - } \ - (void)0 - -// current release version -#define RTMP_SIG_SRS_VERSION "0.4.0" -// server info. -#define RTMP_SIG_SRS_KEY "srs" -#define RTMP_SIG_SRS_ROLE "origin server" -#define RTMP_SIG_SRS_NAME RTMP_SIG_SRS_KEY"(simple rtmp server)" -#define RTMP_SIG_SRS_URL "https://"RTMP_SIG_SRS_URL_SHORT -#define RTMP_SIG_SRS_URL_SHORT "github.com/winlinvip/simple-rtmp-server" -#define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin" -#define RTMP_SIG_SRS_EMAIL "winterserver@126.com" -#define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" -#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013 winlin" - -// compare -#define srs_min(a, b) (((a) < (b))? (a) : (b)) -#define srs_max(a, b) (((a) < (b))? (b) : (a)) - -// get current system time in ms, use cache to avoid performance problem -extern int64_t srs_get_system_time_ms(); -// the deamon st-thread will update it. -extern void srs_update_system_time_ms(); - -// signal defines. -#define SIGNAL_RELOAD SIGHUP - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_HPP +#define SRS_CORE_HPP + +/* +#include +*/ + +/** +* the core provides the common defined macros, utilities, +* user must include the srs_core.hpp before any header, or maybe +* build failed. +*/ + +// for int64_t print using PRId64 format. +#ifndef __STDC_FORMAT_MACROS + #define __STDC_FORMAT_MACROS +#endif +#include + +#include +#define srs_assert(expression) assert(expression) + +#include +#include + +// free the p and set to NULL. +// p must be a T*. +#define srs_freep(p) \ + if (p) { \ + delete p; \ + p = NULL; \ + } \ + (void)0 +// free the p which represents a array +#define srs_freepa(p) \ + if (p) { \ + delete[] p; \ + p = NULL; \ + } \ + (void)0 + +// current release version +#define RTMP_SIG_SRS_VERSION "0.4.0" +// server info. +#define RTMP_SIG_SRS_KEY "srs" +#define RTMP_SIG_SRS_ROLE "origin server" +#define RTMP_SIG_SRS_NAME RTMP_SIG_SRS_KEY"(simple rtmp server)" +#define RTMP_SIG_SRS_URL "https://"RTMP_SIG_SRS_URL_SHORT +#define RTMP_SIG_SRS_URL_SHORT "github.com/winlinvip/simple-rtmp-server" +#define RTMP_SIG_SRS_WEB "http://blog.csdn.net/win_lin" +#define RTMP_SIG_SRS_EMAIL "winterserver@126.com" +#define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" +#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013 winlin" + +// compare +#define srs_min(a, b) (((a) < (b))? (a) : (b)) +#define srs_max(a, b) (((a) < (b))? (b) : (a)) + +// get current system time in ms, use cache to avoid performance problem +extern int64_t srs_get_system_time_ms(); +// the deamon st-thread will update it. +extern void srs_update_system_time_ms(); + +// signal defines. +#define SIGNAL_RELOAD SIGHUP + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_amf0.cpp b/trunk/src/core/srs_core_amf0.cpp index a778dbf06..b6b8a2dc5 100755 --- a/trunk/src/core/srs_core_amf0.cpp +++ b/trunk/src/core/srs_core_amf0.cpp @@ -1,1243 +1,1243 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include - -#include -#include -#include - -// AMF0 marker -#define RTMP_AMF0_Number 0x00 -#define RTMP_AMF0_Boolean 0x01 -#define RTMP_AMF0_String 0x02 -#define RTMP_AMF0_Object 0x03 -#define RTMP_AMF0_MovieClip 0x04 // reserved, not supported -#define RTMP_AMF0_Null 0x05 -#define RTMP_AMF0_Undefined 0x06 -#define RTMP_AMF0_Reference 0x07 -#define RTMP_AMF0_EcmaArray 0x08 -#define RTMP_AMF0_ObjectEnd 0x09 -#define RTMP_AMF0_StrictArray 0x0A -#define RTMP_AMF0_Date 0x0B -#define RTMP_AMF0_LongString 0x0C -#define RTMP_AMF0_UnSupported 0x0D -#define RTMP_AMF0_RecordSet 0x0E // reserved, not supported -#define RTMP_AMF0_XmlDocument 0x0F -#define RTMP_AMF0_TypedObject 0x10 -// AVM+ object is the AMF3 object. -#define RTMP_AMF0_AVMplusObject 0x11 -// origin array whos data takes the same form as LengthValueBytes -#define RTMP_AMF0_OriginStrictArray 0x20 - -// User defined -#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); -int srs_amf0_write_any(SrsStream* stream, SrsAmf0Any* value); - -SrsAmf0Any::SrsAmf0Any() -{ - marker = RTMP_AMF0_Invalid; -} - -SrsAmf0Any::~SrsAmf0Any() -{ -} - -bool SrsAmf0Any::is_string() -{ - return marker == RTMP_AMF0_String; -} - -bool SrsAmf0Any::is_boolean() -{ - return marker == RTMP_AMF0_Boolean; -} - -bool SrsAmf0Any::is_number() -{ - return marker == RTMP_AMF0_Number; -} - -bool SrsAmf0Any::is_null() -{ - return marker == RTMP_AMF0_Null; -} - -bool SrsAmf0Any::is_undefined() -{ - return marker == RTMP_AMF0_Undefined; -} - -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(const char* _value) -{ - marker = RTMP_AMF0_String; - if (_value) { - value = _value; - } -} - -SrsAmf0String::~SrsAmf0String() -{ -} - -SrsAmf0Boolean::SrsAmf0Boolean(bool _value) -{ - marker = RTMP_AMF0_Boolean; - value = _value; -} - -SrsAmf0Boolean::~SrsAmf0Boolean() -{ -} - -SrsAmf0Number::SrsAmf0Number(double _value) -{ - marker = RTMP_AMF0_Number; - value = _value; -} - -SrsAmf0Number::~SrsAmf0Number() -{ -} - -SrsAmf0Null::SrsAmf0Null() -{ - marker = RTMP_AMF0_Null; -} - -SrsAmf0Null::~SrsAmf0Null() -{ -} - -SrsAmf0Undefined::SrsAmf0Undefined() -{ - marker = RTMP_AMF0_Undefined; -} - -SrsAmf0Undefined::~SrsAmf0Undefined() -{ -} - -SrsAmf0ObjectEOF::SrsAmf0ObjectEOF() -{ - marker = RTMP_AMF0_ObjectEnd; - utf8_empty = 0x00; -} - -SrsAmf0ObjectEOF::~SrsAmf0ObjectEOF() -{ -} - -SrsUnSortedHashtable::SrsUnSortedHashtable() -{ -} - -SrsUnSortedHashtable::~SrsUnSortedHashtable() -{ - std::vector::iterator it; - for (it = properties.begin(); it != properties.end(); ++it) { - SrsObjectPropertyType& elem = *it; - SrsAmf0Any* any = elem.second; - srs_freep(any); - } - properties.clear(); -} - -int SrsUnSortedHashtable::size() -{ - return (int)properties.size(); -} - -void SrsUnSortedHashtable::clear() -{ - properties.clear(); -} - -std::string SrsUnSortedHashtable::key_at(int index) -{ - srs_assert(index < size()); - SrsObjectPropertyType& elem = properties[index]; - return elem.first; -} - -SrsAmf0Any* SrsUnSortedHashtable::value_at(int index) -{ - srs_assert(index < size()); - SrsObjectPropertyType& elem = properties[index]; - return elem.second; -} - -void SrsUnSortedHashtable::set(std::string key, SrsAmf0Any* value) -{ - std::vector::iterator it; - - for (it = properties.begin(); it != properties.end(); ++it) { - SrsObjectPropertyType& elem = *it; - std::string name = elem.first; - SrsAmf0Any* any = elem.second; - - if (key == name) { - srs_freep(any); - properties.erase(it); - break; - } - } - - properties.push_back(std::make_pair(key, value)); -} - -SrsAmf0Any* SrsUnSortedHashtable::get_property(std::string name) -{ - std::vector::iterator it; - - for (it = properties.begin(); it != properties.end(); ++it) { - SrsObjectPropertyType& elem = *it; - std::string key = elem.first; - SrsAmf0Any* any = elem.second; - if (key == name) { - return any; - } - } - - return NULL; -} - -SrsAmf0Any* SrsUnSortedHashtable::ensure_property_string(std::string name) -{ - SrsAmf0Any* prop = get_property(name); - - if (!prop) { - return NULL; - } - - if (!prop->is_string()) { - return NULL; - } - - return prop; -} - -SrsAmf0Any* SrsUnSortedHashtable::ensure_property_number(std::string name) -{ - SrsAmf0Any* prop = get_property(name); - - if (!prop) { - return NULL; - } - - if (!prop->is_number()) { - return NULL; - } - - return prop; -} - -SrsAmf0Object::SrsAmf0Object() -{ - marker = RTMP_AMF0_Object; -} - -SrsAmf0Object::~SrsAmf0Object() -{ -} - -int SrsAmf0Object::size() -{ - return properties.size(); -} - -std::string SrsAmf0Object::key_at(int index) -{ - return properties.key_at(index); -} - -SrsAmf0Any* SrsAmf0Object::value_at(int index) -{ - return properties.value_at(index); -} - -void SrsAmf0Object::set(std::string key, SrsAmf0Any* value) -{ - properties.set(key, value); -} - -SrsAmf0Any* SrsAmf0Object::get_property(std::string name) -{ - return properties.get_property(name); -} - -SrsAmf0Any* SrsAmf0Object::ensure_property_string(std::string name) -{ - return properties.ensure_property_string(name); -} - -SrsAmf0Any* SrsAmf0Object::ensure_property_number(std::string name) -{ - return properties.ensure_property_number(name); -} - -SrsASrsAmf0EcmaArray::SrsASrsAmf0EcmaArray() -{ - marker = RTMP_AMF0_EcmaArray; -} - -SrsASrsAmf0EcmaArray::~SrsASrsAmf0EcmaArray() -{ -} - -int SrsASrsAmf0EcmaArray::size() -{ - return properties.size(); -} - -void SrsASrsAmf0EcmaArray::clear() -{ - properties.clear(); -} - -std::string SrsASrsAmf0EcmaArray::key_at(int index) -{ - return properties.key_at(index); -} - -SrsAmf0Any* SrsASrsAmf0EcmaArray::value_at(int index) -{ - return properties.value_at(index); -} - -void SrsASrsAmf0EcmaArray::set(std::string key, SrsAmf0Any* value) -{ - properties.set(key, value); -} - -SrsAmf0Any* SrsASrsAmf0EcmaArray::get_property(std::string name) -{ - return properties.get_property(name); -} - -SrsAmf0Any* SrsASrsAmf0EcmaArray::ensure_property_string(std::string name) -{ - return properties.ensure_property_string(name); -} - -int srs_amf0_read_utf8(SrsStream* stream, std::string& value) -{ - int ret = ERROR_SUCCESS; - - // len - if (!stream->require(2)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read string length failed. ret=%d", ret); - return ret; - } - int16_t len = stream->read_2bytes(); - srs_verbose("amf0 read string length success. len=%d", len); - - // empty string - if (len <= 0) { - srs_verbose("amf0 read empty string. ret=%d", ret); - return ret; - } - - // data - if (!stream->require(len)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read string data failed. ret=%d", ret); - return ret; - } - std::string str = stream->read_string(len); - - // support utf8-1 only - // 1.3.1 Strings and UTF-8 - // UTF8-1 = %x00-7F - for (int i = 0; i < len; i++) { - char ch = *(str.data() + i); - if ((ch & 0x80) != 0) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("ignored. only support utf8-1, 0x00-0x7F, actual is %#x. ret=%d", (int)ch, ret); - ret = ERROR_SUCCESS; - } - } - - value = str; - srs_verbose("amf0 read string data success. str=%s", str.c_str()); - - return ret; -} -int srs_amf0_write_utf8(SrsStream* stream, std::string value) -{ - int ret = ERROR_SUCCESS; - - // len - if (!stream->require(2)) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write string length failed. ret=%d", ret); - return ret; - } - stream->write_2bytes(value.length()); - srs_verbose("amf0 write string length success. len=%d", (int)value.length()); - - // empty string - if (value.length() <= 0) { - srs_verbose("amf0 write empty string. ret=%d", ret); - return ret; - } - - // data - if (!stream->require(value.length())) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write string data failed. ret=%d", ret); - return ret; - } - stream->write_string(value); - srs_verbose("amf0 write string data success. str=%s", value.c_str()); - - return ret; -} - -int srs_amf0_read_string(SrsStream* stream, std::string& value) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read string marker failed. ret=%d", ret); - return ret; - } - - char marker = stream->read_1bytes(); - if (marker != RTMP_AMF0_String) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 check string marker failed. " - "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_String, ret); - return ret; - } - srs_verbose("amf0 read string marker success"); - - return srs_amf0_read_utf8(stream, value); -} - -int srs_amf0_write_string(SrsStream* stream, std::string value) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write string marker failed. ret=%d", ret); - return ret; - } - - stream->write_1bytes(RTMP_AMF0_String); - srs_verbose("amf0 write string marker success"); - - return srs_amf0_write_utf8(stream, value); -} - -int srs_amf0_read_boolean(SrsStream* stream, bool& value) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read bool marker failed. ret=%d", ret); - return ret; - } - - char marker = stream->read_1bytes(); - if (marker != RTMP_AMF0_Boolean) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 check bool marker failed. " - "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Boolean, ret); - return ret; - } - srs_verbose("amf0 read bool marker success"); - - // value - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read bool value failed. ret=%d", ret); - return ret; - } - - if (stream->read_1bytes() == 0) { - value = false; - } else { - value = true; - } - - srs_verbose("amf0 read bool value success. value=%d", value); - - return ret; -} -int srs_amf0_write_boolean(SrsStream* stream, bool value) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write bool marker failed. ret=%d", ret); - return ret; - } - stream->write_1bytes(RTMP_AMF0_Boolean); - srs_verbose("amf0 write bool marker success"); - - // value - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write bool value failed. ret=%d", ret); - return ret; - } - - if (value) { - stream->write_1bytes(0x01); - } else { - stream->write_1bytes(0x00); - } - - srs_verbose("amf0 write bool value success. value=%d", value); - - return ret; -} - -int srs_amf0_read_number(SrsStream* stream, double& value) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read number marker failed. ret=%d", ret); - return ret; - } - - char marker = stream->read_1bytes(); - if (marker != RTMP_AMF0_Number) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 check number marker failed. " - "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Number, ret); - return ret; - } - srs_verbose("amf0 read number marker success"); - - // value - if (!stream->require(8)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read number value failed. ret=%d", ret); - return ret; - } - - int64_t temp = stream->read_8bytes(); - memcpy(&value, &temp, 8); - - srs_verbose("amf0 read number value success. value=%.2f", value); - - return ret; -} -int srs_amf0_write_number(SrsStream* stream, double value) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write number marker failed. ret=%d", ret); - return ret; - } - - stream->write_1bytes(RTMP_AMF0_Number); - srs_verbose("amf0 write number marker success"); - - // value - if (!stream->require(8)) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write number value failed. ret=%d", ret); - return ret; - } - - int64_t temp = 0x00; - memcpy(&temp, &value, 8); - stream->write_8bytes(temp); - - srs_verbose("amf0 write number value success. value=%.2f", value); - - return ret; -} - -int srs_amf0_read_null(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read null marker failed. ret=%d", ret); - return ret; - } - - char marker = stream->read_1bytes(); - if (marker != RTMP_AMF0_Null) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 check null marker failed. " - "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Null, ret); - return ret; - } - srs_verbose("amf0 read null success"); - - return ret; -} -int srs_amf0_write_null(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write null marker failed. ret=%d", ret); - return ret; - } - - stream->write_1bytes(RTMP_AMF0_Null); - srs_verbose("amf0 write null marker success"); - - return ret; -} - -int srs_amf0_read_undefined(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read undefined marker failed. ret=%d", ret); - return ret; - } - - char marker = stream->read_1bytes(); - if (marker != RTMP_AMF0_Undefined) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 check undefined marker failed. " - "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Undefined, ret); - return ret; - } - srs_verbose("amf0 read undefined success"); - - return ret; -} -int srs_amf0_write_undefined(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write undefined marker failed. ret=%d", ret); - return ret; - } - - stream->write_1bytes(RTMP_AMF0_Undefined); - srs_verbose("amf0 write undefined marker success"); - - return ret; -} - -int srs_amf0_read_any(SrsStream* stream, SrsAmf0Any*& value) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read any marker failed. ret=%d", ret); - return ret; - } - - char marker = stream->read_1bytes(); - srs_verbose("amf0 any marker success"); - - // backward the 1byte marker. - stream->skip(-1); - - switch (marker) { - case RTMP_AMF0_String: { - std::string data; - if ((ret = srs_amf0_read_string(stream, data)) != ERROR_SUCCESS) { - return ret; - } - value = new SrsAmf0String(); - srs_amf0_convert(value)->value = data; - return ret; - } - case RTMP_AMF0_Boolean: { - bool data; - if ((ret = srs_amf0_read_boolean(stream, data)) != ERROR_SUCCESS) { - return ret; - } - value = new SrsAmf0Boolean(); - srs_amf0_convert(value)->value = data; - return ret; - } - case RTMP_AMF0_Number: { - double data; - if ((ret = srs_amf0_read_number(stream, data)) != ERROR_SUCCESS) { - return ret; - } - value = new SrsAmf0Number(); - srs_amf0_convert(value)->value = data; - return ret; - } - case RTMP_AMF0_Null: { - stream->skip(1); - value = new SrsAmf0Null(); - return ret; - } - case RTMP_AMF0_Undefined: { - stream->skip(1); - value = new SrsAmf0Undefined(); - return ret; - } - case RTMP_AMF0_ObjectEnd: { - SrsAmf0ObjectEOF* p = NULL; - if ((ret = srs_amf0_read_object_eof(stream, p)) != ERROR_SUCCESS) { - return ret; - } - value = p; - return ret; - } - case RTMP_AMF0_Object: { - SrsAmf0Object* p = NULL; - if ((ret = srs_amf0_read_object(stream, p)) != ERROR_SUCCESS) { - return ret; - } - 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; - srs_error("invalid amf0 message type. marker=%#x, ret=%d", marker, ret); - return ret; - } - } - - return ret; -} -int srs_amf0_write_any(SrsStream* stream, SrsAmf0Any* value) -{ - int ret = ERROR_SUCCESS; - - srs_assert(value != NULL); - - switch (value->marker) { - case RTMP_AMF0_String: { - std::string data = srs_amf0_convert(value)->value; - return srs_amf0_write_string(stream, data); - } - case RTMP_AMF0_Boolean: { - bool data = srs_amf0_convert(value)->value; - return srs_amf0_write_boolean(stream, data); - } - case RTMP_AMF0_Number: { - double data = srs_amf0_convert(value)->value; - return srs_amf0_write_number(stream, data); - } - case RTMP_AMF0_Null: { - return srs_amf0_write_null(stream); - } - case RTMP_AMF0_Undefined: { - return srs_amf0_write_undefined(stream); - } - case RTMP_AMF0_ObjectEnd: { - SrsAmf0ObjectEOF* p = srs_amf0_convert(value); - return srs_amf0_write_object_eof(stream, p); - } - case RTMP_AMF0_Object: { - 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; - srs_error("invalid amf0 message type. marker=%#x, ret=%d", value->marker, ret); - return ret; - } - } - - 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_Null: { - size += srs_amf0_get_null_size(); - break; - } - case RTMP_AMF0_Undefined: { - size += srs_amf0_get_undefined_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; - - // auto skip -2 to read the object eof. - srs_assert(stream->pos() >= 2); - stream->skip(-2); - - // value - if (!stream->require(2)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read object eof value failed. ret=%d", ret); - return ret; - } - int16_t temp = stream->read_2bytes(); - if (temp != 0x00) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read object eof value check failed. " - "must be 0x00, actual is %#x, ret=%d", temp, ret); - return ret; - } - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read object eof marker failed. ret=%d", ret); - return ret; - } - - char marker = stream->read_1bytes(); - if (marker != RTMP_AMF0_ObjectEnd) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 check object eof marker failed. " - "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_ObjectEnd, ret); - return ret; - } - srs_verbose("amf0 read object eof marker success"); - - value = new SrsAmf0ObjectEOF(); - srs_verbose("amf0 read object eof success"); - - return ret; -} -int srs_amf0_write_object_eof(SrsStream* stream, SrsAmf0ObjectEOF* value) -{ - int ret = ERROR_SUCCESS; - - srs_assert(value != NULL); - - // value - if (!stream->require(2)) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write object eof value failed. ret=%d", ret); - return ret; - } - stream->write_2bytes(0x00); - srs_verbose("amf0 write object eof value success"); - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write object eof marker failed. ret=%d", ret); - return ret; - } - - stream->write_1bytes(RTMP_AMF0_ObjectEnd); - - srs_verbose("amf0 read object eof success"); - - return ret; -} - -int srs_amf0_read_object(SrsStream* stream, SrsAmf0Object*& value) -{ - int ret = ERROR_SUCCESS; - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read object marker failed. ret=%d", ret); - return ret; - } - - char marker = stream->read_1bytes(); - if (marker != RTMP_AMF0_Object) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 check object marker failed. " - "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Object, ret); - return ret; - } - srs_verbose("amf0 read object marker success"); - - // value - value = new SrsAmf0Object(); - - 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 object 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 object 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) { - srs_freep(property_value); - } - srs_info("amf0 read object EOF."); - break; - } - - // add property - value->set(property_name, property_value); - } - - return ret; -} -int srs_amf0_write_object(SrsStream* stream, SrsAmf0Object* value) -{ - int ret = ERROR_SUCCESS; - - srs_assert(value != NULL); - - // marker - if (!stream->require(1)) { - ret = ERROR_RTMP_AMF0_ENCODE; - srs_error("amf0 write object marker failed. ret=%d", ret); - return ret; - } - - stream->write_1bytes(RTMP_AMF0_Object); - srs_verbose("amf0 write object marker success"); - - // value - for (int i = 0; i < value->size(); i++) { - std::string name = value->key_at(i); - SrsAmf0Any* any = value->value_at(i); - - if ((ret = srs_amf0_write_utf8(stream, name)) != ERROR_SUCCESS) { - srs_error("write object property name failed. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_write_any(stream, any)) != ERROR_SUCCESS) { - srs_error("write object 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 object eof failed. ret=%d", ret); - return ret; - } - - srs_verbose("write amf0 object success."); - - 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) { - srs_freep(property_value); - } - srs_info("amf0 read ecma_array EOF."); - break; - } - - // add property - value->set(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 - for (int i = 0; i < value->size(); i++) { - std::string name = value->key_at(i); - SrsAmf0Any* any = value->value_at(i); - - 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(); -} - -int srs_amf0_get_string_size(std::string value) -{ - return 1 + srs_amf0_get_utf8_size(value); -} - -int srs_amf0_get_number_size() -{ - return 1 + 8; -} - -int srs_amf0_get_null_size() -{ - return 1; -} - -int srs_amf0_get_undefined_size() -{ - return 1; -} - -int srs_amf0_get_boolean_size() -{ - return 1 + 1; -} - -int srs_amf0_get_object_size(SrsAmf0Object* obj) -{ - if (!obj) { - return 0; - } - - int size = 1; - - for (int i = 0; i < obj->size(); i++){ - std::string name = obj->key_at(i); - SrsAmf0Any* value = obj->value_at(i); - - 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; - - for (int i = 0; i < arr->size(); i++){ - std::string name = arr->key_at(i); - SrsAmf0Any* value = arr->value_at(i); - - 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_object_eof_size() -{ - return 2 + 1; -} +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include + +#include +#include +#include + +// AMF0 marker +#define RTMP_AMF0_Number 0x00 +#define RTMP_AMF0_Boolean 0x01 +#define RTMP_AMF0_String 0x02 +#define RTMP_AMF0_Object 0x03 +#define RTMP_AMF0_MovieClip 0x04 // reserved, not supported +#define RTMP_AMF0_Null 0x05 +#define RTMP_AMF0_Undefined 0x06 +#define RTMP_AMF0_Reference 0x07 +#define RTMP_AMF0_EcmaArray 0x08 +#define RTMP_AMF0_ObjectEnd 0x09 +#define RTMP_AMF0_StrictArray 0x0A +#define RTMP_AMF0_Date 0x0B +#define RTMP_AMF0_LongString 0x0C +#define RTMP_AMF0_UnSupported 0x0D +#define RTMP_AMF0_RecordSet 0x0E // reserved, not supported +#define RTMP_AMF0_XmlDocument 0x0F +#define RTMP_AMF0_TypedObject 0x10 +// AVM+ object is the AMF3 object. +#define RTMP_AMF0_AVMplusObject 0x11 +// origin array whos data takes the same form as LengthValueBytes +#define RTMP_AMF0_OriginStrictArray 0x20 + +// User defined +#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); +int srs_amf0_write_any(SrsStream* stream, SrsAmf0Any* value); + +SrsAmf0Any::SrsAmf0Any() +{ + marker = RTMP_AMF0_Invalid; +} + +SrsAmf0Any::~SrsAmf0Any() +{ +} + +bool SrsAmf0Any::is_string() +{ + return marker == RTMP_AMF0_String; +} + +bool SrsAmf0Any::is_boolean() +{ + return marker == RTMP_AMF0_Boolean; +} + +bool SrsAmf0Any::is_number() +{ + return marker == RTMP_AMF0_Number; +} + +bool SrsAmf0Any::is_null() +{ + return marker == RTMP_AMF0_Null; +} + +bool SrsAmf0Any::is_undefined() +{ + return marker == RTMP_AMF0_Undefined; +} + +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(const char* _value) +{ + marker = RTMP_AMF0_String; + if (_value) { + value = _value; + } +} + +SrsAmf0String::~SrsAmf0String() +{ +} + +SrsAmf0Boolean::SrsAmf0Boolean(bool _value) +{ + marker = RTMP_AMF0_Boolean; + value = _value; +} + +SrsAmf0Boolean::~SrsAmf0Boolean() +{ +} + +SrsAmf0Number::SrsAmf0Number(double _value) +{ + marker = RTMP_AMF0_Number; + value = _value; +} + +SrsAmf0Number::~SrsAmf0Number() +{ +} + +SrsAmf0Null::SrsAmf0Null() +{ + marker = RTMP_AMF0_Null; +} + +SrsAmf0Null::~SrsAmf0Null() +{ +} + +SrsAmf0Undefined::SrsAmf0Undefined() +{ + marker = RTMP_AMF0_Undefined; +} + +SrsAmf0Undefined::~SrsAmf0Undefined() +{ +} + +SrsAmf0ObjectEOF::SrsAmf0ObjectEOF() +{ + marker = RTMP_AMF0_ObjectEnd; + utf8_empty = 0x00; +} + +SrsAmf0ObjectEOF::~SrsAmf0ObjectEOF() +{ +} + +SrsUnSortedHashtable::SrsUnSortedHashtable() +{ +} + +SrsUnSortedHashtable::~SrsUnSortedHashtable() +{ + std::vector::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + SrsObjectPropertyType& elem = *it; + SrsAmf0Any* any = elem.second; + srs_freep(any); + } + properties.clear(); +} + +int SrsUnSortedHashtable::size() +{ + return (int)properties.size(); +} + +void SrsUnSortedHashtable::clear() +{ + properties.clear(); +} + +std::string SrsUnSortedHashtable::key_at(int index) +{ + srs_assert(index < size()); + SrsObjectPropertyType& elem = properties[index]; + return elem.first; +} + +SrsAmf0Any* SrsUnSortedHashtable::value_at(int index) +{ + srs_assert(index < size()); + SrsObjectPropertyType& elem = properties[index]; + return elem.second; +} + +void SrsUnSortedHashtable::set(std::string key, SrsAmf0Any* value) +{ + std::vector::iterator it; + + for (it = properties.begin(); it != properties.end(); ++it) { + SrsObjectPropertyType& elem = *it; + std::string name = elem.first; + SrsAmf0Any* any = elem.second; + + if (key == name) { + srs_freep(any); + properties.erase(it); + break; + } + } + + properties.push_back(std::make_pair(key, value)); +} + +SrsAmf0Any* SrsUnSortedHashtable::get_property(std::string name) +{ + std::vector::iterator it; + + for (it = properties.begin(); it != properties.end(); ++it) { + SrsObjectPropertyType& elem = *it; + std::string key = elem.first; + SrsAmf0Any* any = elem.second; + if (key == name) { + return any; + } + } + + return NULL; +} + +SrsAmf0Any* SrsUnSortedHashtable::ensure_property_string(std::string name) +{ + SrsAmf0Any* prop = get_property(name); + + if (!prop) { + return NULL; + } + + if (!prop->is_string()) { + return NULL; + } + + return prop; +} + +SrsAmf0Any* SrsUnSortedHashtable::ensure_property_number(std::string name) +{ + SrsAmf0Any* prop = get_property(name); + + if (!prop) { + return NULL; + } + + if (!prop->is_number()) { + return NULL; + } + + return prop; +} + +SrsAmf0Object::SrsAmf0Object() +{ + marker = RTMP_AMF0_Object; +} + +SrsAmf0Object::~SrsAmf0Object() +{ +} + +int SrsAmf0Object::size() +{ + return properties.size(); +} + +std::string SrsAmf0Object::key_at(int index) +{ + return properties.key_at(index); +} + +SrsAmf0Any* SrsAmf0Object::value_at(int index) +{ + return properties.value_at(index); +} + +void SrsAmf0Object::set(std::string key, SrsAmf0Any* value) +{ + properties.set(key, value); +} + +SrsAmf0Any* SrsAmf0Object::get_property(std::string name) +{ + return properties.get_property(name); +} + +SrsAmf0Any* SrsAmf0Object::ensure_property_string(std::string name) +{ + return properties.ensure_property_string(name); +} + +SrsAmf0Any* SrsAmf0Object::ensure_property_number(std::string name) +{ + return properties.ensure_property_number(name); +} + +SrsASrsAmf0EcmaArray::SrsASrsAmf0EcmaArray() +{ + marker = RTMP_AMF0_EcmaArray; +} + +SrsASrsAmf0EcmaArray::~SrsASrsAmf0EcmaArray() +{ +} + +int SrsASrsAmf0EcmaArray::size() +{ + return properties.size(); +} + +void SrsASrsAmf0EcmaArray::clear() +{ + properties.clear(); +} + +std::string SrsASrsAmf0EcmaArray::key_at(int index) +{ + return properties.key_at(index); +} + +SrsAmf0Any* SrsASrsAmf0EcmaArray::value_at(int index) +{ + return properties.value_at(index); +} + +void SrsASrsAmf0EcmaArray::set(std::string key, SrsAmf0Any* value) +{ + properties.set(key, value); +} + +SrsAmf0Any* SrsASrsAmf0EcmaArray::get_property(std::string name) +{ + return properties.get_property(name); +} + +SrsAmf0Any* SrsASrsAmf0EcmaArray::ensure_property_string(std::string name) +{ + return properties.ensure_property_string(name); +} + +int srs_amf0_read_utf8(SrsStream* stream, std::string& value) +{ + int ret = ERROR_SUCCESS; + + // len + if (!stream->require(2)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read string length failed. ret=%d", ret); + return ret; + } + int16_t len = stream->read_2bytes(); + srs_verbose("amf0 read string length success. len=%d", len); + + // empty string + if (len <= 0) { + srs_verbose("amf0 read empty string. ret=%d", ret); + return ret; + } + + // data + if (!stream->require(len)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read string data failed. ret=%d", ret); + return ret; + } + std::string str = stream->read_string(len); + + // support utf8-1 only + // 1.3.1 Strings and UTF-8 + // UTF8-1 = %x00-7F + for (int i = 0; i < len; i++) { + char ch = *(str.data() + i); + if ((ch & 0x80) != 0) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("ignored. only support utf8-1, 0x00-0x7F, actual is %#x. ret=%d", (int)ch, ret); + ret = ERROR_SUCCESS; + } + } + + value = str; + srs_verbose("amf0 read string data success. str=%s", str.c_str()); + + return ret; +} +int srs_amf0_write_utf8(SrsStream* stream, std::string value) +{ + int ret = ERROR_SUCCESS; + + // len + if (!stream->require(2)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write string length failed. ret=%d", ret); + return ret; + } + stream->write_2bytes(value.length()); + srs_verbose("amf0 write string length success. len=%d", (int)value.length()); + + // empty string + if (value.length() <= 0) { + srs_verbose("amf0 write empty string. ret=%d", ret); + return ret; + } + + // data + if (!stream->require(value.length())) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write string data failed. ret=%d", ret); + return ret; + } + stream->write_string(value); + srs_verbose("amf0 write string data success. str=%s", value.c_str()); + + return ret; +} + +int srs_amf0_read_string(SrsStream* stream, std::string& value) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read string marker failed. ret=%d", ret); + return ret; + } + + char marker = stream->read_1bytes(); + if (marker != RTMP_AMF0_String) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 check string marker failed. " + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_String, ret); + return ret; + } + srs_verbose("amf0 read string marker success"); + + return srs_amf0_read_utf8(stream, value); +} + +int srs_amf0_write_string(SrsStream* stream, std::string value) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write string marker failed. ret=%d", ret); + return ret; + } + + stream->write_1bytes(RTMP_AMF0_String); + srs_verbose("amf0 write string marker success"); + + return srs_amf0_write_utf8(stream, value); +} + +int srs_amf0_read_boolean(SrsStream* stream, bool& value) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read bool marker failed. ret=%d", ret); + return ret; + } + + char marker = stream->read_1bytes(); + if (marker != RTMP_AMF0_Boolean) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 check bool marker failed. " + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Boolean, ret); + return ret; + } + srs_verbose("amf0 read bool marker success"); + + // value + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read bool value failed. ret=%d", ret); + return ret; + } + + if (stream->read_1bytes() == 0) { + value = false; + } else { + value = true; + } + + srs_verbose("amf0 read bool value success. value=%d", value); + + return ret; +} +int srs_amf0_write_boolean(SrsStream* stream, bool value) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write bool marker failed. ret=%d", ret); + return ret; + } + stream->write_1bytes(RTMP_AMF0_Boolean); + srs_verbose("amf0 write bool marker success"); + + // value + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write bool value failed. ret=%d", ret); + return ret; + } + + if (value) { + stream->write_1bytes(0x01); + } else { + stream->write_1bytes(0x00); + } + + srs_verbose("amf0 write bool value success. value=%d", value); + + return ret; +} + +int srs_amf0_read_number(SrsStream* stream, double& value) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read number marker failed. ret=%d", ret); + return ret; + } + + char marker = stream->read_1bytes(); + if (marker != RTMP_AMF0_Number) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 check number marker failed. " + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Number, ret); + return ret; + } + srs_verbose("amf0 read number marker success"); + + // value + if (!stream->require(8)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read number value failed. ret=%d", ret); + return ret; + } + + int64_t temp = stream->read_8bytes(); + memcpy(&value, &temp, 8); + + srs_verbose("amf0 read number value success. value=%.2f", value); + + return ret; +} +int srs_amf0_write_number(SrsStream* stream, double value) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write number marker failed. ret=%d", ret); + return ret; + } + + stream->write_1bytes(RTMP_AMF0_Number); + srs_verbose("amf0 write number marker success"); + + // value + if (!stream->require(8)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write number value failed. ret=%d", ret); + return ret; + } + + int64_t temp = 0x00; + memcpy(&temp, &value, 8); + stream->write_8bytes(temp); + + srs_verbose("amf0 write number value success. value=%.2f", value); + + return ret; +} + +int srs_amf0_read_null(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read null marker failed. ret=%d", ret); + return ret; + } + + char marker = stream->read_1bytes(); + if (marker != RTMP_AMF0_Null) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 check null marker failed. " + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Null, ret); + return ret; + } + srs_verbose("amf0 read null success"); + + return ret; +} +int srs_amf0_write_null(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write null marker failed. ret=%d", ret); + return ret; + } + + stream->write_1bytes(RTMP_AMF0_Null); + srs_verbose("amf0 write null marker success"); + + return ret; +} + +int srs_amf0_read_undefined(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read undefined marker failed. ret=%d", ret); + return ret; + } + + char marker = stream->read_1bytes(); + if (marker != RTMP_AMF0_Undefined) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 check undefined marker failed. " + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Undefined, ret); + return ret; + } + srs_verbose("amf0 read undefined success"); + + return ret; +} +int srs_amf0_write_undefined(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write undefined marker failed. ret=%d", ret); + return ret; + } + + stream->write_1bytes(RTMP_AMF0_Undefined); + srs_verbose("amf0 write undefined marker success"); + + return ret; +} + +int srs_amf0_read_any(SrsStream* stream, SrsAmf0Any*& value) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read any marker failed. ret=%d", ret); + return ret; + } + + char marker = stream->read_1bytes(); + srs_verbose("amf0 any marker success"); + + // backward the 1byte marker. + stream->skip(-1); + + switch (marker) { + case RTMP_AMF0_String: { + std::string data; + if ((ret = srs_amf0_read_string(stream, data)) != ERROR_SUCCESS) { + return ret; + } + value = new SrsAmf0String(); + srs_amf0_convert(value)->value = data; + return ret; + } + case RTMP_AMF0_Boolean: { + bool data; + if ((ret = srs_amf0_read_boolean(stream, data)) != ERROR_SUCCESS) { + return ret; + } + value = new SrsAmf0Boolean(); + srs_amf0_convert(value)->value = data; + return ret; + } + case RTMP_AMF0_Number: { + double data; + if ((ret = srs_amf0_read_number(stream, data)) != ERROR_SUCCESS) { + return ret; + } + value = new SrsAmf0Number(); + srs_amf0_convert(value)->value = data; + return ret; + } + case RTMP_AMF0_Null: { + stream->skip(1); + value = new SrsAmf0Null(); + return ret; + } + case RTMP_AMF0_Undefined: { + stream->skip(1); + value = new SrsAmf0Undefined(); + return ret; + } + case RTMP_AMF0_ObjectEnd: { + SrsAmf0ObjectEOF* p = NULL; + if ((ret = srs_amf0_read_object_eof(stream, p)) != ERROR_SUCCESS) { + return ret; + } + value = p; + return ret; + } + case RTMP_AMF0_Object: { + SrsAmf0Object* p = NULL; + if ((ret = srs_amf0_read_object(stream, p)) != ERROR_SUCCESS) { + return ret; + } + 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; + srs_error("invalid amf0 message type. marker=%#x, ret=%d", marker, ret); + return ret; + } + } + + return ret; +} +int srs_amf0_write_any(SrsStream* stream, SrsAmf0Any* value) +{ + int ret = ERROR_SUCCESS; + + srs_assert(value != NULL); + + switch (value->marker) { + case RTMP_AMF0_String: { + std::string data = srs_amf0_convert(value)->value; + return srs_amf0_write_string(stream, data); + } + case RTMP_AMF0_Boolean: { + bool data = srs_amf0_convert(value)->value; + return srs_amf0_write_boolean(stream, data); + } + case RTMP_AMF0_Number: { + double data = srs_amf0_convert(value)->value; + return srs_amf0_write_number(stream, data); + } + case RTMP_AMF0_Null: { + return srs_amf0_write_null(stream); + } + case RTMP_AMF0_Undefined: { + return srs_amf0_write_undefined(stream); + } + case RTMP_AMF0_ObjectEnd: { + SrsAmf0ObjectEOF* p = srs_amf0_convert(value); + return srs_amf0_write_object_eof(stream, p); + } + case RTMP_AMF0_Object: { + 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; + srs_error("invalid amf0 message type. marker=%#x, ret=%d", value->marker, ret); + return ret; + } + } + + 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_Null: { + size += srs_amf0_get_null_size(); + break; + } + case RTMP_AMF0_Undefined: { + size += srs_amf0_get_undefined_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; + + // auto skip -2 to read the object eof. + srs_assert(stream->pos() >= 2); + stream->skip(-2); + + // value + if (!stream->require(2)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read object eof value failed. ret=%d", ret); + return ret; + } + int16_t temp = stream->read_2bytes(); + if (temp != 0x00) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read object eof value check failed. " + "must be 0x00, actual is %#x, ret=%d", temp, ret); + return ret; + } + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read object eof marker failed. ret=%d", ret); + return ret; + } + + char marker = stream->read_1bytes(); + if (marker != RTMP_AMF0_ObjectEnd) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 check object eof marker failed. " + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_ObjectEnd, ret); + return ret; + } + srs_verbose("amf0 read object eof marker success"); + + value = new SrsAmf0ObjectEOF(); + srs_verbose("amf0 read object eof success"); + + return ret; +} +int srs_amf0_write_object_eof(SrsStream* stream, SrsAmf0ObjectEOF* value) +{ + int ret = ERROR_SUCCESS; + + srs_assert(value != NULL); + + // value + if (!stream->require(2)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write object eof value failed. ret=%d", ret); + return ret; + } + stream->write_2bytes(0x00); + srs_verbose("amf0 write object eof value success"); + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write object eof marker failed. ret=%d", ret); + return ret; + } + + stream->write_1bytes(RTMP_AMF0_ObjectEnd); + + srs_verbose("amf0 read object eof success"); + + return ret; +} + +int srs_amf0_read_object(SrsStream* stream, SrsAmf0Object*& value) +{ + int ret = ERROR_SUCCESS; + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read object marker failed. ret=%d", ret); + return ret; + } + + char marker = stream->read_1bytes(); + if (marker != RTMP_AMF0_Object) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 check object marker failed. " + "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Object, ret); + return ret; + } + srs_verbose("amf0 read object marker success"); + + // value + value = new SrsAmf0Object(); + + 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 object 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 object 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) { + srs_freep(property_value); + } + srs_info("amf0 read object EOF."); + break; + } + + // add property + value->set(property_name, property_value); + } + + return ret; +} +int srs_amf0_write_object(SrsStream* stream, SrsAmf0Object* value) +{ + int ret = ERROR_SUCCESS; + + srs_assert(value != NULL); + + // marker + if (!stream->require(1)) { + ret = ERROR_RTMP_AMF0_ENCODE; + srs_error("amf0 write object marker failed. ret=%d", ret); + return ret; + } + + stream->write_1bytes(RTMP_AMF0_Object); + srs_verbose("amf0 write object marker success"); + + // value + for (int i = 0; i < value->size(); i++) { + std::string name = value->key_at(i); + SrsAmf0Any* any = value->value_at(i); + + if ((ret = srs_amf0_write_utf8(stream, name)) != ERROR_SUCCESS) { + srs_error("write object property name failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_write_any(stream, any)) != ERROR_SUCCESS) { + srs_error("write object 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 object eof failed. ret=%d", ret); + return ret; + } + + srs_verbose("write amf0 object success."); + + 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) { + srs_freep(property_value); + } + srs_info("amf0 read ecma_array EOF."); + break; + } + + // add property + value->set(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 + for (int i = 0; i < value->size(); i++) { + std::string name = value->key_at(i); + SrsAmf0Any* any = value->value_at(i); + + 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(); +} + +int srs_amf0_get_string_size(std::string value) +{ + return 1 + srs_amf0_get_utf8_size(value); +} + +int srs_amf0_get_number_size() +{ + return 1 + 8; +} + +int srs_amf0_get_null_size() +{ + return 1; +} + +int srs_amf0_get_undefined_size() +{ + return 1; +} + +int srs_amf0_get_boolean_size() +{ + return 1 + 1; +} + +int srs_amf0_get_object_size(SrsAmf0Object* obj) +{ + if (!obj) { + return 0; + } + + int size = 1; + + for (int i = 0; i < obj->size(); i++){ + std::string name = obj->key_at(i); + SrsAmf0Any* value = obj->value_at(i); + + 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; + + for (int i = 0; i < arr->size(); i++){ + std::string name = arr->key_at(i); + SrsAmf0Any* value = arr->value_at(i); + + 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_object_eof_size() +{ + return 2 + 1; +} diff --git a/trunk/src/core/srs_core_amf0.hpp b/trunk/src/core/srs_core_amf0.hpp index 338632722..68ff63784 100755 --- a/trunk/src/core/srs_core_amf0.hpp +++ b/trunk/src/core/srs_core_amf0.hpp @@ -1,319 +1,319 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_AMF0_HPP -#define SRS_CORE_AMF0_HPP - -/* -#include -*/ - -#include - -#include -#include - -class SrsStream; -class SrsAmf0Object; - -/** -* any amf0 value. -* 2.1 Types Overview -* value-type = number-type | boolean-type | string-type | object-type -* | null-marker | undefined-marker | reference-type | ecma-array-type -* | strict-array-type | date-type | long-string-type | xml-document-type -* | typed-object-type -*/ -struct SrsAmf0Any -{ - char marker; - - SrsAmf0Any(); - virtual ~SrsAmf0Any(); - - virtual bool is_string(); - virtual bool is_boolean(); - virtual bool is_number(); - virtual bool is_null(); - virtual bool is_undefined(); - virtual bool is_object(); - virtual bool is_object_eof(); - virtual bool is_ecma_array(); -}; - -/** -* read amf0 string from stream. -* 2.4 String Type -* string-type = string-marker UTF-8 -* @return default value is empty string. -*/ -struct SrsAmf0String : public SrsAmf0Any -{ - std::string value; - - SrsAmf0String(const char* _value = NULL); - virtual ~SrsAmf0String(); -}; - -/** -* read amf0 boolean from stream. -* 2.4 String Type -* boolean-type = boolean-marker U8 -* 0 is false, <> 0 is true -* @return default value is false. -*/ -struct SrsAmf0Boolean : public SrsAmf0Any -{ - bool value; - - SrsAmf0Boolean(bool _value = false); - virtual ~SrsAmf0Boolean(); -}; - -/** -* read amf0 number from stream. -* 2.2 Number Type -* number-type = number-marker DOUBLE -* @return default value is 0. -*/ -struct SrsAmf0Number : public SrsAmf0Any -{ - double value; - - SrsAmf0Number(double _value = 0.0); - virtual ~SrsAmf0Number(); -}; - -/** -* read amf0 null from stream. -* 2.7 null Type -* null-type = null-marker -*/ -struct SrsAmf0Null : public SrsAmf0Any -{ - SrsAmf0Null(); - virtual ~SrsAmf0Null(); -}; - -/** -* read amf0 undefined from stream. -* 2.8 undefined Type -* undefined-type = undefined-marker -*/ -struct SrsAmf0Undefined : public SrsAmf0Any -{ - SrsAmf0Undefined(); - virtual ~SrsAmf0Undefined(); -}; - -/** -* 2.11 Object End Type -* object-end-type = UTF-8-empty object-end-marker -* 0x00 0x00 0x09 -*/ -struct SrsAmf0ObjectEOF : public SrsAmf0Any -{ - int16_t utf8_empty; - - SrsAmf0ObjectEOF(); - virtual ~SrsAmf0ObjectEOF(); -}; - -/** -* to ensure in inserted order. -* for the FMLE will crash when AMF0Object is not ordered by inserted, -* if ordered in map, the string compare order, the FMLE will creash when -* get the response of connect app. -*/ -struct SrsUnSortedHashtable -{ -private: - typedef std::pair SrsObjectPropertyType; - std::vector properties; -public: - SrsUnSortedHashtable(); - virtual ~SrsUnSortedHashtable(); - - virtual int size(); - virtual void clear(); - virtual std::string key_at(int index); - virtual SrsAmf0Any* value_at(int index); - virtual void set(std::string key, SrsAmf0Any* value); - - virtual SrsAmf0Any* get_property(std::string name); - virtual SrsAmf0Any* ensure_property_string(std::string name); - virtual SrsAmf0Any* ensure_property_number(std::string name); -}; - -/** -* 2.5 Object Type -* anonymous-object-type = object-marker *(object-property) -* object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker) -*/ -struct SrsAmf0Object : public SrsAmf0Any -{ -private: - SrsUnSortedHashtable properties; -public: - SrsAmf0ObjectEOF eof; - - SrsAmf0Object(); - virtual ~SrsAmf0Object(); - - virtual int size(); - virtual std::string key_at(int index); - virtual SrsAmf0Any* value_at(int index); - virtual void set(std::string key, SrsAmf0Any* value); - - virtual SrsAmf0Any* get_property(std::string name); - virtual SrsAmf0Any* ensure_property_string(std::string name); - virtual SrsAmf0Any* ensure_property_number(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 -{ -private: - SrsUnSortedHashtable properties; -public: - int32_t count; - SrsAmf0ObjectEOF eof; - - SrsASrsAmf0EcmaArray(); - virtual ~SrsASrsAmf0EcmaArray(); - - virtual int size(); - virtual void clear(); - virtual std::string key_at(int index); - virtual SrsAmf0Any* value_at(int index); - virtual void set(std::string key, SrsAmf0Any* value); - - 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 -* UTF-8 = U16 *(UTF8-char) -* UTF8-char = UTF8-1 | UTF8-2 | UTF8-3 | UTF8-4 -* UTF8-1 = %x00-7F -* @remark only support UTF8-1 char. -*/ -extern int srs_amf0_read_utf8(SrsStream* stream, std::string& value); -extern int srs_amf0_write_utf8(SrsStream* stream, std::string value); - -/** -* read amf0 string from stream. -* 2.4 String Type -* string-type = string-marker UTF-8 -*/ -extern int srs_amf0_read_string(SrsStream* stream, std::string& value); -extern int srs_amf0_write_string(SrsStream* stream, std::string value); - -/** -* read amf0 boolean from stream. -* 2.4 String Type -* boolean-type = boolean-marker U8 -* 0 is false, <> 0 is true -*/ -extern int srs_amf0_read_boolean(SrsStream* stream, bool& value); -extern int srs_amf0_write_boolean(SrsStream* stream, bool value); - -/** -* read amf0 number from stream. -* 2.2 Number Type -* number-type = number-marker DOUBLE -*/ -extern int srs_amf0_read_number(SrsStream* stream, double& value); -extern int srs_amf0_write_number(SrsStream* stream, double value); - -/** -* read amf0 null from stream. -* 2.7 null Type -* null-type = null-marker -*/ -extern int srs_amf0_read_null(SrsStream* stream); -extern int srs_amf0_write_null(SrsStream* stream); - -/** -* read amf0 undefined from stream. -* 2.8 undefined Type -* undefined-type = undefined-marker -*/ -extern int srs_amf0_read_undefined(SrsStream* stream); -extern int srs_amf0_write_undefined(SrsStream* stream); - -extern int srs_amf0_read_any(SrsStream* stream, SrsAmf0Any*& value); - -/** -* read amf0 object from stream. -* 2.5 Object Type -* anonymous-object-type = object-marker *(object-property) -* object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker) -*/ -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. -*/ -extern int srs_amf0_get_utf8_size(std::string value); -extern int srs_amf0_get_string_size(std::string value); -extern int srs_amf0_get_number_size(); -extern int srs_amf0_get_null_size(); -extern int srs_amf0_get_undefined_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. -* @remark, user must ensure the current object type, -* or the covert will cause assert failed. -*/ -template -T* srs_amf0_convert(SrsAmf0Any* any) -{ - T* p = dynamic_cast(any); - srs_assert(p != NULL); - return p; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_AMF0_HPP +#define SRS_CORE_AMF0_HPP + +/* +#include +*/ + +#include + +#include +#include + +class SrsStream; +class SrsAmf0Object; + +/** +* any amf0 value. +* 2.1 Types Overview +* value-type = number-type | boolean-type | string-type | object-type +* | null-marker | undefined-marker | reference-type | ecma-array-type +* | strict-array-type | date-type | long-string-type | xml-document-type +* | typed-object-type +*/ +struct SrsAmf0Any +{ + char marker; + + SrsAmf0Any(); + virtual ~SrsAmf0Any(); + + virtual bool is_string(); + virtual bool is_boolean(); + virtual bool is_number(); + virtual bool is_null(); + virtual bool is_undefined(); + virtual bool is_object(); + virtual bool is_object_eof(); + virtual bool is_ecma_array(); +}; + +/** +* read amf0 string from stream. +* 2.4 String Type +* string-type = string-marker UTF-8 +* @return default value is empty string. +*/ +struct SrsAmf0String : public SrsAmf0Any +{ + std::string value; + + SrsAmf0String(const char* _value = NULL); + virtual ~SrsAmf0String(); +}; + +/** +* read amf0 boolean from stream. +* 2.4 String Type +* boolean-type = boolean-marker U8 +* 0 is false, <> 0 is true +* @return default value is false. +*/ +struct SrsAmf0Boolean : public SrsAmf0Any +{ + bool value; + + SrsAmf0Boolean(bool _value = false); + virtual ~SrsAmf0Boolean(); +}; + +/** +* read amf0 number from stream. +* 2.2 Number Type +* number-type = number-marker DOUBLE +* @return default value is 0. +*/ +struct SrsAmf0Number : public SrsAmf0Any +{ + double value; + + SrsAmf0Number(double _value = 0.0); + virtual ~SrsAmf0Number(); +}; + +/** +* read amf0 null from stream. +* 2.7 null Type +* null-type = null-marker +*/ +struct SrsAmf0Null : public SrsAmf0Any +{ + SrsAmf0Null(); + virtual ~SrsAmf0Null(); +}; + +/** +* read amf0 undefined from stream. +* 2.8 undefined Type +* undefined-type = undefined-marker +*/ +struct SrsAmf0Undefined : public SrsAmf0Any +{ + SrsAmf0Undefined(); + virtual ~SrsAmf0Undefined(); +}; + +/** +* 2.11 Object End Type +* object-end-type = UTF-8-empty object-end-marker +* 0x00 0x00 0x09 +*/ +struct SrsAmf0ObjectEOF : public SrsAmf0Any +{ + int16_t utf8_empty; + + SrsAmf0ObjectEOF(); + virtual ~SrsAmf0ObjectEOF(); +}; + +/** +* to ensure in inserted order. +* for the FMLE will crash when AMF0Object is not ordered by inserted, +* if ordered in map, the string compare order, the FMLE will creash when +* get the response of connect app. +*/ +struct SrsUnSortedHashtable +{ +private: + typedef std::pair SrsObjectPropertyType; + std::vector properties; +public: + SrsUnSortedHashtable(); + virtual ~SrsUnSortedHashtable(); + + virtual int size(); + virtual void clear(); + virtual std::string key_at(int index); + virtual SrsAmf0Any* value_at(int index); + virtual void set(std::string key, SrsAmf0Any* value); + + virtual SrsAmf0Any* get_property(std::string name); + virtual SrsAmf0Any* ensure_property_string(std::string name); + virtual SrsAmf0Any* ensure_property_number(std::string name); +}; + +/** +* 2.5 Object Type +* anonymous-object-type = object-marker *(object-property) +* object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker) +*/ +struct SrsAmf0Object : public SrsAmf0Any +{ +private: + SrsUnSortedHashtable properties; +public: + SrsAmf0ObjectEOF eof; + + SrsAmf0Object(); + virtual ~SrsAmf0Object(); + + virtual int size(); + virtual std::string key_at(int index); + virtual SrsAmf0Any* value_at(int index); + virtual void set(std::string key, SrsAmf0Any* value); + + virtual SrsAmf0Any* get_property(std::string name); + virtual SrsAmf0Any* ensure_property_string(std::string name); + virtual SrsAmf0Any* ensure_property_number(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 +{ +private: + SrsUnSortedHashtable properties; +public: + int32_t count; + SrsAmf0ObjectEOF eof; + + SrsASrsAmf0EcmaArray(); + virtual ~SrsASrsAmf0EcmaArray(); + + virtual int size(); + virtual void clear(); + virtual std::string key_at(int index); + virtual SrsAmf0Any* value_at(int index); + virtual void set(std::string key, SrsAmf0Any* value); + + 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 +* UTF-8 = U16 *(UTF8-char) +* UTF8-char = UTF8-1 | UTF8-2 | UTF8-3 | UTF8-4 +* UTF8-1 = %x00-7F +* @remark only support UTF8-1 char. +*/ +extern int srs_amf0_read_utf8(SrsStream* stream, std::string& value); +extern int srs_amf0_write_utf8(SrsStream* stream, std::string value); + +/** +* read amf0 string from stream. +* 2.4 String Type +* string-type = string-marker UTF-8 +*/ +extern int srs_amf0_read_string(SrsStream* stream, std::string& value); +extern int srs_amf0_write_string(SrsStream* stream, std::string value); + +/** +* read amf0 boolean from stream. +* 2.4 String Type +* boolean-type = boolean-marker U8 +* 0 is false, <> 0 is true +*/ +extern int srs_amf0_read_boolean(SrsStream* stream, bool& value); +extern int srs_amf0_write_boolean(SrsStream* stream, bool value); + +/** +* read amf0 number from stream. +* 2.2 Number Type +* number-type = number-marker DOUBLE +*/ +extern int srs_amf0_read_number(SrsStream* stream, double& value); +extern int srs_amf0_write_number(SrsStream* stream, double value); + +/** +* read amf0 null from stream. +* 2.7 null Type +* null-type = null-marker +*/ +extern int srs_amf0_read_null(SrsStream* stream); +extern int srs_amf0_write_null(SrsStream* stream); + +/** +* read amf0 undefined from stream. +* 2.8 undefined Type +* undefined-type = undefined-marker +*/ +extern int srs_amf0_read_undefined(SrsStream* stream); +extern int srs_amf0_write_undefined(SrsStream* stream); + +extern int srs_amf0_read_any(SrsStream* stream, SrsAmf0Any*& value); + +/** +* read amf0 object from stream. +* 2.5 Object Type +* anonymous-object-type = object-marker *(object-property) +* object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker) +*/ +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. +*/ +extern int srs_amf0_get_utf8_size(std::string value); +extern int srs_amf0_get_string_size(std::string value); +extern int srs_amf0_get_number_size(); +extern int srs_amf0_get_null_size(); +extern int srs_amf0_get_undefined_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. +* @remark, user must ensure the current object type, +* or the covert will cause assert failed. +*/ +template +T* srs_amf0_convert(SrsAmf0Any* any) +{ + T* p = dynamic_cast(any); + srs_assert(p != NULL); + return p; +} + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_autofree.cpp b/trunk/src/core/srs_core_autofree.cpp index 3e04c6e12..ba391709a 100755 --- a/trunk/src/core/srs_core_autofree.cpp +++ b/trunk/src/core/srs_core_autofree.cpp @@ -1,24 +1,24 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include diff --git a/trunk/src/core/srs_core_autofree.hpp b/trunk/src/core/srs_core_autofree.hpp index 9581bd952..73035b783 100755 --- a/trunk/src/core/srs_core_autofree.hpp +++ b/trunk/src/core/srs_core_autofree.hpp @@ -1,71 +1,71 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_AUTO_FREE_HPP -#define SRS_CORE_AUTO_FREE_HPP - -/* -#include -*/ - -#include - -/** -* auto free the instance in the current scope. -*/ -#define SrsAutoFree(className, instance, is_array) \ - __SrsAutoFree _auto_free_##instance(&instance, is_array) - -template -class __SrsAutoFree -{ -private: - T** ptr; - bool is_array; -public: - /** - * auto delete the ptr. - * @is_array a bool value indicates whether the ptr is a array. - */ - __SrsAutoFree(T** _ptr, bool _is_array){ - ptr = _ptr; - is_array = _is_array; - } - - virtual ~__SrsAutoFree(){ - if (ptr == NULL || *ptr == NULL) { - return; - } - - if (is_array) { - delete[] *ptr; - } else { - delete *ptr; - } - - *ptr = NULL; - } -}; - - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_AUTO_FREE_HPP +#define SRS_CORE_AUTO_FREE_HPP + +/* +#include +*/ + +#include + +/** +* auto free the instance in the current scope. +*/ +#define SrsAutoFree(className, instance, is_array) \ + __SrsAutoFree _auto_free_##instance(&instance, is_array) + +template +class __SrsAutoFree +{ +private: + T** ptr; + bool is_array; +public: + /** + * auto delete the ptr. + * @is_array a bool value indicates whether the ptr is a array. + */ + __SrsAutoFree(T** _ptr, bool _is_array){ + ptr = _ptr; + is_array = _is_array; + } + + virtual ~__SrsAutoFree(){ + if (ptr == NULL || *ptr == NULL) { + return; + } + + if (is_array) { + delete[] *ptr; + } else { + delete *ptr; + } + + *ptr = NULL; + } +}; + + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_buffer.cpp b/trunk/src/core/srs_core_buffer.cpp index 3eebd3b71..11d760a2c 100755 --- a/trunk/src/core/srs_core_buffer.cpp +++ b/trunk/src/core/srs_core_buffer.cpp @@ -1,84 +1,84 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include - -#define SOCKET_READ_SIZE 4096 - -SrsBuffer::SrsBuffer() -{ -} - -SrsBuffer::~SrsBuffer() -{ -} - -int SrsBuffer::size() -{ - return (int)data.size(); -} - -char* SrsBuffer::bytes() -{ - return &data.at(0); -} - -void SrsBuffer::erase(int size) -{ - data.erase(data.begin(), data.begin() + size); -} - -void SrsBuffer::append(char* bytes, int size) -{ - data.insert(data.end(), bytes, bytes + size); -} - -int SrsBuffer::ensure_buffer_bytes(SrsSocket* skt, int required_size) -{ - int ret = ERROR_SUCCESS; - - if (required_size < 0) { - ret = ERROR_SYSTEM_SIZE_NEGATIVE; - srs_error("size is negative. size=%d, ret=%d", required_size, ret); - return ret; - } - - while (size() < required_size) { - char buffer[SOCKET_READ_SIZE]; - - ssize_t nread; - if ((ret = skt->read(buffer, SOCKET_READ_SIZE, &nread)) != ERROR_SUCCESS) { - return ret; - } - - srs_assert((int)nread > 0); - append(buffer, (int)nread); - } - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include + +#define SOCKET_READ_SIZE 4096 + +SrsBuffer::SrsBuffer() +{ +} + +SrsBuffer::~SrsBuffer() +{ +} + +int SrsBuffer::size() +{ + return (int)data.size(); +} + +char* SrsBuffer::bytes() +{ + return &data.at(0); +} + +void SrsBuffer::erase(int size) +{ + data.erase(data.begin(), data.begin() + size); +} + +void SrsBuffer::append(char* bytes, int size) +{ + data.insert(data.end(), bytes, bytes + size); +} + +int SrsBuffer::ensure_buffer_bytes(SrsSocket* skt, int required_size) +{ + int ret = ERROR_SUCCESS; + + if (required_size < 0) { + ret = ERROR_SYSTEM_SIZE_NEGATIVE; + srs_error("size is negative. size=%d, ret=%d", required_size, ret); + return ret; + } + + while (size() < required_size) { + char buffer[SOCKET_READ_SIZE]; + + ssize_t nread; + if ((ret = skt->read(buffer, SOCKET_READ_SIZE, &nread)) != ERROR_SUCCESS) { + return ret; + } + + srs_assert((int)nread > 0); + append(buffer, (int)nread); + } + + return ret; +} + diff --git a/trunk/src/core/srs_core_buffer.hpp b/trunk/src/core/srs_core_buffer.hpp index 53585e290..a36f5f65b 100755 --- a/trunk/src/core/srs_core_buffer.hpp +++ b/trunk/src/core/srs_core_buffer.hpp @@ -1,59 +1,59 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_BUFFER_HPP -#define SRS_CORE_BUFFER_HPP - -/* -#include -*/ - -#include - -#include - -class SrsSocket; - -/** -* the buffer provices bytes cache for protocol. generally, -* protocol recv data from socket, put into buffer, decode to RTMP message. -* protocol encode RTMP message to bytes, put into buffer, send to socket. -*/ -class SrsBuffer -{ -private: - std::vector data; -public: - SrsBuffer(); - virtual ~SrsBuffer(); -public: - virtual int size(); - virtual char* bytes(); - virtual void erase(int size); -private: - virtual void append(char* bytes, int size); -public: - virtual int ensure_buffer_bytes(SrsSocket* skt, int required_size); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_BUFFER_HPP +#define SRS_CORE_BUFFER_HPP + +/* +#include +*/ + +#include + +#include + +class SrsSocket; + +/** +* the buffer provices bytes cache for protocol. generally, +* protocol recv data from socket, put into buffer, decode to RTMP message. +* protocol encode RTMP message to bytes, put into buffer, send to socket. +*/ +class SrsBuffer +{ +private: + std::vector data; +public: + SrsBuffer(); + virtual ~SrsBuffer(); +public: + virtual int size(); + virtual char* bytes(); + virtual void erase(int size); +private: + virtual void append(char* bytes, int size); +public: + virtual int ensure_buffer_bytes(SrsSocket* skt, int required_size); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_client.cpp b/trunk/src/core/srs_core_client.cpp index f7bd72d71..4a35f877f 100755 --- a/trunk/src/core/srs_core_client.cpp +++ b/trunk/src/core/srs_core_client.cpp @@ -1,488 +1,488 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define SRS_PULSE_TIMEOUT_MS 100 -#define SRS_SEND_TIMEOUT_MS 5000000L -#define SRS_RECV_TIMEOUT_MS SRS_SEND_TIMEOUT_MS - -SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd) - : SrsConnection(srs_server, client_stfd) -{ - ip = NULL; - req = new SrsRequest(); - res = new SrsResponse(); - rtmp = new SrsRtmp(client_stfd); - refer = new SrsRefer(); -} - -SrsClient::~SrsClient() -{ - srs_freepa(ip); - srs_freep(req); - srs_freep(res); - srs_freep(rtmp); - srs_freep(refer); -} - -int SrsClient::do_cycle() -{ - int ret = ERROR_SUCCESS; - - if ((ret = get_peer_ip()) != ERROR_SUCCESS) { - srs_error("get peer ip failed. ret=%d", ret); - return ret; - } - srs_trace("get peer ip success. ip=%s, send_to=%"PRId64", recv_to=%"PRId64"", - ip, SRS_SEND_TIMEOUT_MS, SRS_RECV_TIMEOUT_MS); - - rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_MS * 1000); - rtmp->set_send_timeout(SRS_SEND_TIMEOUT_MS * 1000); - - if ((ret = rtmp->handshake()) != ERROR_SUCCESS) { - srs_error("rtmp handshake failed. ret=%d", ret); - return ret; - } - srs_verbose("rtmp handshake success"); - - if ((ret = rtmp->connect_app(req)) != ERROR_SUCCESS) { - srs_error("rtmp connect vhost/app failed. ret=%d", ret); - return ret; - } - srs_verbose("rtmp connect app success"); - - if ((ret = check_vhost()) != ERROR_SUCCESS) { - srs_error("check vhost failed. ret=%d", ret); - return ret; - } - srs_verbose("check vhost success."); - - srs_trace("rtmp connect app success. " - "tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s", - req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(), - req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), - req->app.c_str()); - - if ((ret = refer->check(req->pageUrl, config->get_refer(req->vhost))) != ERROR_SUCCESS) { - srs_error("check refer failed. ret=%d", ret); - return ret; - } - srs_verbose("check refer success."); - - if ((ret = rtmp->set_window_ack_size(2.5 * 1000 * 1000)) != ERROR_SUCCESS) { - srs_error("set window acknowledgement size failed. ret=%d", ret); - return ret; - } - srs_verbose("set window acknowledgement size success"); - - if ((ret = rtmp->set_peer_bandwidth(2.5 * 1000 * 1000, 2)) != ERROR_SUCCESS) { - srs_error("set peer bandwidth failed. ret=%d", ret); - return ret; - } - srs_verbose("set peer bandwidth success"); - - if ((ret = rtmp->response_connect_app(req)) != ERROR_SUCCESS) { - srs_error("response connect app failed. ret=%d", ret); - return ret; - } - srs_verbose("response connect app success"); - - if ((ret = rtmp->on_bw_done()) != ERROR_SUCCESS) { - srs_error("on_bw_done failed. ret=%d", ret); - return ret; - } - srs_verbose("on_bw_done success"); - - SrsClientType type; - if ((ret = rtmp->identify_client(res->stream_id, type, req->stream)) != ERROR_SUCCESS) { - srs_error("identify client failed. ret=%d", ret); - return ret; - } - req->strip(); - srs_trace("identify client success. type=%d, stream_name=%s", type, req->stream.c_str()); - - int chunk_size = 4096; - SrsConfDirective* conf = config->get_chunk_size(); - if (conf && !conf->arg0().empty()) { - chunk_size = ::atoi(conf->arg0().c_str()); - } - if ((ret = rtmp->set_chunk_size(chunk_size)) != ERROR_SUCCESS) { - srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret); - return ret; - } - srs_trace("set chunk_size=%d success", chunk_size); - - // find a source to publish. - SrsSource* source = SrsSource::find(req->get_stream_url()); - srs_assert(source != NULL); - - bool enabled_cache = true; - conf = config->get_gop_cache(req->vhost); - if (conf && conf->arg0() == "off") { - enabled_cache = false; - } - source->set_cache(enabled_cache); - - srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache); - - switch (type) { - case SrsClientPlay: { - srs_verbose("start to play stream %s.", req->stream.c_str()); - - if ((ret = rtmp->start_play(res->stream_id)) != ERROR_SUCCESS) { - srs_error("start to play stream failed. ret=%d", ret); - return ret; - } - srs_info("start to play stream %s success", req->stream.c_str()); - return playing(source); - } - case SrsClientFMLEPublish: { - srs_verbose("FMLE start to publish stream %s.", req->stream.c_str()); - - if ((ret = rtmp->start_fmle_publish(res->stream_id)) != ERROR_SUCCESS) { - srs_error("start to publish stream failed. ret=%d", ret); - return ret; - } - srs_info("start to publish stream %s success", req->stream.c_str()); - ret = publish(source, true); - source->on_unpublish(); - return ret; - } - case SrsClientFlashPublish: { - srs_verbose("flash start to publish stream %s.", req->stream.c_str()); - - if ((ret = rtmp->start_flash_publish(res->stream_id)) != ERROR_SUCCESS) { - srs_error("flash start to publish stream failed. ret=%d", ret); - return ret; - } - srs_info("flash start to publish stream %s success", req->stream.c_str()); - ret = publish(source, false); - source->on_unpublish(); - return ret; - } - default: { - ret = ERROR_SYSTEM_CLIENT_INVALID; - srs_info("invalid client type=%d. ret=%d", type, ret); - return ret; - } - } - - return ret; -} - -int SrsClient::check_vhost() -{ - int ret = ERROR_SUCCESS; - - srs_assert(req != NULL); - - SrsConfDirective* vhost = config->get_vhost(req->vhost); - if (vhost == NULL) { - ret = ERROR_RTMP_VHOST_NOT_FOUND; - srs_error("vhost %s not found. ret=%d", req->vhost.c_str(), ret); - return ret; - } - - SrsConfDirective* conf = NULL; - if ((conf = vhost->get(RTMP_VHOST_ENABLED)) != NULL && conf->arg0() != "on") { - ret = ERROR_RTMP_VHOST_NOT_FOUND; - srs_error("vhost %s disabled. ret=%d", req->vhost.c_str(), ret); - return ret; - } - - if (req->vhost != vhost->arg0()) { - srs_trace("vhost change from %s to %s", req->vhost.c_str(), vhost->arg0().c_str()); - req->vhost = vhost->arg0(); - } - - return ret; -} - -int SrsClient::playing(SrsSource* source) -{ - int ret = ERROR_SUCCESS; - - if ((ret = refer->check(req->pageUrl, config->get_refer_play(req->vhost))) != ERROR_SUCCESS) { - srs_error("check play_refer failed. ret=%d", ret); - return ret; - } - srs_verbose("check play_refer success."); - - SrsConsumer* consumer = NULL; - if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) { - srs_error("create consumer failed. ret=%d", ret); - return ret; - } - - srs_assert(consumer != NULL); - SrsAutoFree(SrsConsumer, consumer, false); - srs_verbose("consumer created success."); - - rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000); - - SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER); - - while (true) { - pithy_print.elapse(SRS_PULSE_TIMEOUT_MS); - - // switch to other st-threads. - st_usleep(0); - - // read from client. - int ctl_msg_ret = ERROR_SUCCESS; - if (true) { - SrsCommonMessage* msg = NULL; - ctl_msg_ret = ret = rtmp->recv_message(&msg); - - srs_verbose("play loop recv message. ret=%d", ret); - if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) { - srs_error("recv client control message failed. ret=%d", ret); - return ret; - } - if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { - srs_error("process play control message failed. ret=%d", ret); - return ret; - } - } - - // get messages from consumer. - SrsSharedPtrMessage** msgs = NULL; - int count = 0; - if ((ret = consumer->get_packets(0, msgs, count)) != ERROR_SUCCESS) { - srs_error("get messages from consumer failed. ret=%d", ret); - return ret; - } - - // reportable - if (pithy_print.can_print()) { - srs_trace("-> clock=%u, time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", - (int)(srs_get_system_time_ms()/1000), pithy_print.get_age(), ctl_msg_ret, count, rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps()); - } - - if (count <= 0) { - srs_verbose("no packets in queue."); - continue; - } - SrsAutoFree(SrsSharedPtrMessage*, msgs, true); - - // sendout messages - for (int i = 0; i < count; i++) { - SrsSharedPtrMessage* msg = msgs[i]; - - // the send_message will free the msg, - // so set the msgs[i] to NULL. - msgs[i] = NULL; - - if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send message to client failed. ret=%d", ret); - return ret; - } - } - } - - return ret; -} - -int SrsClient::publish(SrsSource* source, bool is_fmle) -{ - int ret = ERROR_SUCCESS; - - if ((ret = refer->check(req->pageUrl, config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) { - srs_error("check publish_refer failed. ret=%d", ret); - return ret; - } - srs_verbose("check publish_refer success."); - - SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER); - - while (true) { - // switch to other st-threads. - st_usleep(0); - - SrsCommonMessage* msg = NULL; - if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { - srs_error("recv identify client message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsCommonMessage, msg, false); - - pithy_print.set_age(msg->header.timestamp); - - // reportable - if (pithy_print.can_print()) { - srs_trace("<- clock=%u, time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", - (int)(srs_get_system_time_ms()/1000), pithy_print.get_age(), rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps()); - } - - // process audio packet - if (msg->header.is_audio() && ((ret = source->on_audio(msg)) != ERROR_SUCCESS)) { - srs_error("process audio message failed. ret=%d", ret); - return ret; - } - // process video packet - if (msg->header.is_video() && ((ret = source->on_video(msg)) != ERROR_SUCCESS)) { - srs_error("process video message failed. ret=%d", ret); - return ret; - } - - // process onMetaData - if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) { - if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { - srs_error("decode onMetaData message failed. ret=%d", ret); - return ret; - } - - SrsPacket* pkt = msg->get_packet(); - if (dynamic_cast(pkt)) { - SrsOnMetaDataPacket* metadata = dynamic_cast(pkt); - if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) { - srs_error("process onMetaData message failed. ret=%d", ret); - return ret; - } - srs_trace("process onMetaData message success."); - continue; - } - - srs_trace("ignore AMF0/AMF3 data message."); - continue; - } - - // process UnPublish event. - if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) { - if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { - srs_error("decode unpublish message failed. ret=%d", ret); - return ret; - } - - // flash unpublish. - if (!is_fmle) { - srs_trace("flash publish finished."); - return ret; - } - - SrsPacket* pkt = msg->get_packet(); - if (dynamic_cast(pkt)) { - SrsFMLEStartPacket* unpublish = dynamic_cast(pkt); - return rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id); - } - - srs_trace("ignore AMF0/AMF3 command message."); - continue; - } - } - - return ret; -} - -int SrsClient::get_peer_ip() -{ - int ret = ERROR_SUCCESS; - - int fd = st_netfd_fileno(stfd); - - // discovery client information - sockaddr_in addr; - socklen_t addrlen = sizeof(addr); - if (getpeername(fd, (sockaddr*)&addr, &addrlen) == -1) { - ret = ERROR_SOCKET_GET_PEER_NAME; - srs_error("discovery client information failed. ret=%d", ret); - return ret; - } - srs_verbose("get peer name success."); - - // ip v4 or v6 - char buf[INET6_ADDRSTRLEN]; - memset(buf, 0, sizeof(buf)); - - if ((inet_ntop(addr.sin_family, &addr.sin_addr, buf, sizeof(buf))) == NULL) { - ret = ERROR_SOCKET_GET_PEER_IP; - srs_error("convert client information failed. ret=%d", ret); - return ret; - } - srs_verbose("get peer ip of client ip=%s, fd=%d", buf, fd); - - ip = new char[strlen(buf) + 1]; - strcpy(ip, buf); - - srs_verbose("get peer ip success. ip=%s, fd=%d", ip, fd); - - return ret; -} - -int SrsClient::process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg) -{ - int ret = ERROR_SUCCESS; - - if (!msg) { - srs_verbose("ignore all empty message."); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - - if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { - srs_info("ignore all message except amf0/amf3 command."); - return ret; - } - - if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { - srs_error("decode the amf0/amf3 command packet failed. ret=%d", ret); - return ret; - } - srs_info("decode the amf0/amf3 command packet success."); - - SrsPausePacket* pause = dynamic_cast(msg->get_packet()); - if (!pause) { - srs_info("ignore all amf0/amf3 command except pause."); - return ret; - } - - if ((ret = rtmp->on_play_client_pause(res->stream_id, pause->is_pause)) != ERROR_SUCCESS) { - srs_error("rtmp process play client pause failed. ret=%d", ret); - return ret; - } - - if ((ret = consumer->on_play_client_pause(pause->is_pause)) != ERROR_SUCCESS) { - srs_error("consumer process play client pause failed. ret=%d", ret); - return ret; - } - srs_info("process pause success, is_pause=%d, time=%d.", pause->is_pause, pause->time_ms); - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define SRS_PULSE_TIMEOUT_MS 100 +#define SRS_SEND_TIMEOUT_MS 5000000L +#define SRS_RECV_TIMEOUT_MS SRS_SEND_TIMEOUT_MS + +SrsClient::SrsClient(SrsServer* srs_server, st_netfd_t client_stfd) + : SrsConnection(srs_server, client_stfd) +{ + ip = NULL; + req = new SrsRequest(); + res = new SrsResponse(); + rtmp = new SrsRtmp(client_stfd); + refer = new SrsRefer(); +} + +SrsClient::~SrsClient() +{ + srs_freepa(ip); + srs_freep(req); + srs_freep(res); + srs_freep(rtmp); + srs_freep(refer); +} + +int SrsClient::do_cycle() +{ + int ret = ERROR_SUCCESS; + + if ((ret = get_peer_ip()) != ERROR_SUCCESS) { + srs_error("get peer ip failed. ret=%d", ret); + return ret; + } + srs_trace("get peer ip success. ip=%s, send_to=%"PRId64", recv_to=%"PRId64"", + ip, SRS_SEND_TIMEOUT_MS, SRS_RECV_TIMEOUT_MS); + + rtmp->set_recv_timeout(SRS_RECV_TIMEOUT_MS * 1000); + rtmp->set_send_timeout(SRS_SEND_TIMEOUT_MS * 1000); + + if ((ret = rtmp->handshake()) != ERROR_SUCCESS) { + srs_error("rtmp handshake failed. ret=%d", ret); + return ret; + } + srs_verbose("rtmp handshake success"); + + if ((ret = rtmp->connect_app(req)) != ERROR_SUCCESS) { + srs_error("rtmp connect vhost/app failed. ret=%d", ret); + return ret; + } + srs_verbose("rtmp connect app success"); + + if ((ret = check_vhost()) != ERROR_SUCCESS) { + srs_error("check vhost failed. ret=%d", ret); + return ret; + } + srs_verbose("check vhost success."); + + srs_trace("rtmp connect app success. " + "tcUrl=%s, pageUrl=%s, swfUrl=%s, schema=%s, vhost=%s, port=%s, app=%s", + req->tcUrl.c_str(), req->pageUrl.c_str(), req->swfUrl.c_str(), + req->schema.c_str(), req->vhost.c_str(), req->port.c_str(), + req->app.c_str()); + + if ((ret = refer->check(req->pageUrl, config->get_refer(req->vhost))) != ERROR_SUCCESS) { + srs_error("check refer failed. ret=%d", ret); + return ret; + } + srs_verbose("check refer success."); + + if ((ret = rtmp->set_window_ack_size(2.5 * 1000 * 1000)) != ERROR_SUCCESS) { + srs_error("set window acknowledgement size failed. ret=%d", ret); + return ret; + } + srs_verbose("set window acknowledgement size success"); + + if ((ret = rtmp->set_peer_bandwidth(2.5 * 1000 * 1000, 2)) != ERROR_SUCCESS) { + srs_error("set peer bandwidth failed. ret=%d", ret); + return ret; + } + srs_verbose("set peer bandwidth success"); + + if ((ret = rtmp->response_connect_app(req)) != ERROR_SUCCESS) { + srs_error("response connect app failed. ret=%d", ret); + return ret; + } + srs_verbose("response connect app success"); + + if ((ret = rtmp->on_bw_done()) != ERROR_SUCCESS) { + srs_error("on_bw_done failed. ret=%d", ret); + return ret; + } + srs_verbose("on_bw_done success"); + + SrsClientType type; + if ((ret = rtmp->identify_client(res->stream_id, type, req->stream)) != ERROR_SUCCESS) { + srs_error("identify client failed. ret=%d", ret); + return ret; + } + req->strip(); + srs_trace("identify client success. type=%d, stream_name=%s", type, req->stream.c_str()); + + int chunk_size = 4096; + SrsConfDirective* conf = config->get_chunk_size(); + if (conf && !conf->arg0().empty()) { + chunk_size = ::atoi(conf->arg0().c_str()); + } + if ((ret = rtmp->set_chunk_size(chunk_size)) != ERROR_SUCCESS) { + srs_error("set chunk_size=%d failed. ret=%d", chunk_size, ret); + return ret; + } + srs_trace("set chunk_size=%d success", chunk_size); + + // find a source to publish. + SrsSource* source = SrsSource::find(req->get_stream_url()); + srs_assert(source != NULL); + + bool enabled_cache = true; + conf = config->get_gop_cache(req->vhost); + if (conf && conf->arg0() == "off") { + enabled_cache = false; + } + source->set_cache(enabled_cache); + + srs_info("source found, url=%s, enabled_cache=%d", req->get_stream_url().c_str(), enabled_cache); + + switch (type) { + case SrsClientPlay: { + srs_verbose("start to play stream %s.", req->stream.c_str()); + + if ((ret = rtmp->start_play(res->stream_id)) != ERROR_SUCCESS) { + srs_error("start to play stream failed. ret=%d", ret); + return ret; + } + srs_info("start to play stream %s success", req->stream.c_str()); + return playing(source); + } + case SrsClientFMLEPublish: { + srs_verbose("FMLE start to publish stream %s.", req->stream.c_str()); + + if ((ret = rtmp->start_fmle_publish(res->stream_id)) != ERROR_SUCCESS) { + srs_error("start to publish stream failed. ret=%d", ret); + return ret; + } + srs_info("start to publish stream %s success", req->stream.c_str()); + ret = publish(source, true); + source->on_unpublish(); + return ret; + } + case SrsClientFlashPublish: { + srs_verbose("flash start to publish stream %s.", req->stream.c_str()); + + if ((ret = rtmp->start_flash_publish(res->stream_id)) != ERROR_SUCCESS) { + srs_error("flash start to publish stream failed. ret=%d", ret); + return ret; + } + srs_info("flash start to publish stream %s success", req->stream.c_str()); + ret = publish(source, false); + source->on_unpublish(); + return ret; + } + default: { + ret = ERROR_SYSTEM_CLIENT_INVALID; + srs_info("invalid client type=%d. ret=%d", type, ret); + return ret; + } + } + + return ret; +} + +int SrsClient::check_vhost() +{ + int ret = ERROR_SUCCESS; + + srs_assert(req != NULL); + + SrsConfDirective* vhost = config->get_vhost(req->vhost); + if (vhost == NULL) { + ret = ERROR_RTMP_VHOST_NOT_FOUND; + srs_error("vhost %s not found. ret=%d", req->vhost.c_str(), ret); + return ret; + } + + SrsConfDirective* conf = NULL; + if ((conf = vhost->get(RTMP_VHOST_ENABLED)) != NULL && conf->arg0() != "on") { + ret = ERROR_RTMP_VHOST_NOT_FOUND; + srs_error("vhost %s disabled. ret=%d", req->vhost.c_str(), ret); + return ret; + } + + if (req->vhost != vhost->arg0()) { + srs_trace("vhost change from %s to %s", req->vhost.c_str(), vhost->arg0().c_str()); + req->vhost = vhost->arg0(); + } + + return ret; +} + +int SrsClient::playing(SrsSource* source) +{ + int ret = ERROR_SUCCESS; + + if ((ret = refer->check(req->pageUrl, config->get_refer_play(req->vhost))) != ERROR_SUCCESS) { + srs_error("check play_refer failed. ret=%d", ret); + return ret; + } + srs_verbose("check play_refer success."); + + SrsConsumer* consumer = NULL; + if ((ret = source->create_consumer(consumer)) != ERROR_SUCCESS) { + srs_error("create consumer failed. ret=%d", ret); + return ret; + } + + srs_assert(consumer != NULL); + SrsAutoFree(SrsConsumer, consumer, false); + srs_verbose("consumer created success."); + + rtmp->set_recv_timeout(SRS_PULSE_TIMEOUT_MS * 1000); + + SrsPithyPrint pithy_print(SRS_STAGE_PLAY_USER); + + while (true) { + pithy_print.elapse(SRS_PULSE_TIMEOUT_MS); + + // switch to other st-threads. + st_usleep(0); + + // read from client. + int ctl_msg_ret = ERROR_SUCCESS; + if (true) { + SrsCommonMessage* msg = NULL; + ctl_msg_ret = ret = rtmp->recv_message(&msg); + + srs_verbose("play loop recv message. ret=%d", ret); + if (ret != ERROR_SUCCESS && ret != ERROR_SOCKET_TIMEOUT) { + srs_error("recv client control message failed. ret=%d", ret); + return ret; + } + if ((ret = process_play_control_msg(consumer, msg)) != ERROR_SUCCESS) { + srs_error("process play control message failed. ret=%d", ret); + return ret; + } + } + + // get messages from consumer. + SrsSharedPtrMessage** msgs = NULL; + int count = 0; + if ((ret = consumer->get_packets(0, msgs, count)) != ERROR_SUCCESS) { + srs_error("get messages from consumer failed. ret=%d", ret); + return ret; + } + + // reportable + if (pithy_print.can_print()) { + srs_trace("-> clock=%u, time=%"PRId64", cmr=%d, msgs=%d, obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", + (int)(srs_get_system_time_ms()/1000), pithy_print.get_age(), ctl_msg_ret, count, rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps()); + } + + if (count <= 0) { + srs_verbose("no packets in queue."); + continue; + } + SrsAutoFree(SrsSharedPtrMessage*, msgs, true); + + // sendout messages + for (int i = 0; i < count; i++) { + SrsSharedPtrMessage* msg = msgs[i]; + + // the send_message will free the msg, + // so set the msgs[i] to NULL. + msgs[i] = NULL; + + if ((ret = rtmp->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send message to client failed. ret=%d", ret); + return ret; + } + } + } + + return ret; +} + +int SrsClient::publish(SrsSource* source, bool is_fmle) +{ + int ret = ERROR_SUCCESS; + + if ((ret = refer->check(req->pageUrl, config->get_refer_publish(req->vhost))) != ERROR_SUCCESS) { + srs_error("check publish_refer failed. ret=%d", ret); + return ret; + } + srs_verbose("check publish_refer success."); + + SrsPithyPrint pithy_print(SRS_STAGE_PUBLISH_USER); + + while (true) { + // switch to other st-threads. + st_usleep(0); + + SrsCommonMessage* msg = NULL; + if ((ret = rtmp->recv_message(&msg)) != ERROR_SUCCESS) { + srs_error("recv identify client message failed. ret=%d", ret); + return ret; + } + + SrsAutoFree(SrsCommonMessage, msg, false); + + pithy_print.set_age(msg->header.timestamp); + + // reportable + if (pithy_print.can_print()) { + srs_trace("<- clock=%u, time=%"PRId64", obytes=%"PRId64", ibytes=%"PRId64", okbps=%d, ikbps=%d", + (int)(srs_get_system_time_ms()/1000), pithy_print.get_age(), rtmp->get_send_bytes(), rtmp->get_recv_bytes(), rtmp->get_send_kbps(), rtmp->get_recv_kbps()); + } + + // process audio packet + if (msg->header.is_audio() && ((ret = source->on_audio(msg)) != ERROR_SUCCESS)) { + srs_error("process audio message failed. ret=%d", ret); + return ret; + } + // process video packet + if (msg->header.is_video() && ((ret = source->on_video(msg)) != ERROR_SUCCESS)) { + srs_error("process video message failed. ret=%d", ret); + return ret; + } + + // process onMetaData + if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) { + if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { + srs_error("decode onMetaData message failed. ret=%d", ret); + return ret; + } + + SrsPacket* pkt = msg->get_packet(); + if (dynamic_cast(pkt)) { + SrsOnMetaDataPacket* metadata = dynamic_cast(pkt); + if ((ret = source->on_meta_data(msg, metadata)) != ERROR_SUCCESS) { + srs_error("process onMetaData message failed. ret=%d", ret); + return ret; + } + srs_trace("process onMetaData message success."); + continue; + } + + srs_trace("ignore AMF0/AMF3 data message."); + continue; + } + + // process UnPublish event. + if (msg->header.is_amf0_command() || msg->header.is_amf3_command()) { + if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { + srs_error("decode unpublish message failed. ret=%d", ret); + return ret; + } + + // flash unpublish. + if (!is_fmle) { + srs_trace("flash publish finished."); + return ret; + } + + SrsPacket* pkt = msg->get_packet(); + if (dynamic_cast(pkt)) { + SrsFMLEStartPacket* unpublish = dynamic_cast(pkt); + return rtmp->fmle_unpublish(res->stream_id, unpublish->transaction_id); + } + + srs_trace("ignore AMF0/AMF3 command message."); + continue; + } + } + + return ret; +} + +int SrsClient::get_peer_ip() +{ + int ret = ERROR_SUCCESS; + + int fd = st_netfd_fileno(stfd); + + // discovery client information + sockaddr_in addr; + socklen_t addrlen = sizeof(addr); + if (getpeername(fd, (sockaddr*)&addr, &addrlen) == -1) { + ret = ERROR_SOCKET_GET_PEER_NAME; + srs_error("discovery client information failed. ret=%d", ret); + return ret; + } + srs_verbose("get peer name success."); + + // ip v4 or v6 + char buf[INET6_ADDRSTRLEN]; + memset(buf, 0, sizeof(buf)); + + if ((inet_ntop(addr.sin_family, &addr.sin_addr, buf, sizeof(buf))) == NULL) { + ret = ERROR_SOCKET_GET_PEER_IP; + srs_error("convert client information failed. ret=%d", ret); + return ret; + } + srs_verbose("get peer ip of client ip=%s, fd=%d", buf, fd); + + ip = new char[strlen(buf) + 1]; + strcpy(ip, buf); + + srs_verbose("get peer ip success. ip=%s, fd=%d", ip, fd); + + return ret; +} + +int SrsClient::process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg) +{ + int ret = ERROR_SUCCESS; + + if (!msg) { + srs_verbose("ignore all empty message."); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + + if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { + srs_info("ignore all message except amf0/amf3 command."); + return ret; + } + + if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { + srs_error("decode the amf0/amf3 command packet failed. ret=%d", ret); + return ret; + } + srs_info("decode the amf0/amf3 command packet success."); + + SrsPausePacket* pause = dynamic_cast(msg->get_packet()); + if (!pause) { + srs_info("ignore all amf0/amf3 command except pause."); + return ret; + } + + if ((ret = rtmp->on_play_client_pause(res->stream_id, pause->is_pause)) != ERROR_SUCCESS) { + srs_error("rtmp process play client pause failed. ret=%d", ret); + return ret; + } + + if ((ret = consumer->on_play_client_pause(pause->is_pause)) != ERROR_SUCCESS) { + srs_error("consumer process play client pause failed. ret=%d", ret); + return ret; + } + srs_info("process pause success, is_pause=%d, time=%d.", pause->is_pause, pause->time_ms); + + return ret; +} + diff --git a/trunk/src/core/srs_core_client.hpp b/trunk/src/core/srs_core_client.hpp index b8e433da4..05fbd2278 100755 --- a/trunk/src/core/srs_core_client.hpp +++ b/trunk/src/core/srs_core_client.hpp @@ -1,67 +1,67 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_CLIENT_HPP -#define SRS_CORE_CLIENT_HPP - -/* -#include -*/ - -#include - -#include - -class SrsRtmp; -class SrsRequest; -class SrsResponse; -class SrsSource; -class SrsRefer; -class SrsConsumer; -class SrsCommonMessage; - -/** -* the client provides the main logic control for RTMP clients. -*/ -class SrsClient : public SrsConnection -{ -private: - char* ip; - SrsRequest* req; - SrsResponse* res; - SrsRtmp* rtmp; - SrsRefer* refer; -public: - SrsClient(SrsServer* srs_server, st_netfd_t client_stfd); - virtual ~SrsClient(); -protected: - virtual int do_cycle(); -private: - virtual int check_vhost(); - virtual int playing(SrsSource* source); - virtual int publish(SrsSource* source, bool is_fmle); - virtual int get_peer_ip(); - virtual int process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_CLIENT_HPP +#define SRS_CORE_CLIENT_HPP + +/* +#include +*/ + +#include + +#include + +class SrsRtmp; +class SrsRequest; +class SrsResponse; +class SrsSource; +class SrsRefer; +class SrsConsumer; +class SrsCommonMessage; + +/** +* the client provides the main logic control for RTMP clients. +*/ +class SrsClient : public SrsConnection +{ +private: + char* ip; + SrsRequest* req; + SrsResponse* res; + SrsRtmp* rtmp; + SrsRefer* refer; +public: + SrsClient(SrsServer* srs_server, st_netfd_t client_stfd); + virtual ~SrsClient(); +protected: + virtual int do_cycle(); +private: + virtual int check_vhost(); + virtual int playing(SrsSource* source); + virtual int publish(SrsSource* source, bool is_fmle); + virtual int get_peer_ip(); + virtual int process_play_control_msg(SrsConsumer* consumer, SrsCommonMessage* msg); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_codec.cpp b/trunk/src/core/srs_core_codec.cpp index c2d0b0dfd..998904e7f 100755 --- a/trunk/src/core/srs_core_codec.cpp +++ b/trunk/src/core/srs_core_codec.cpp @@ -1,175 +1,175 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -SrsCodec::SrsCodec() -{ -} - -SrsCodec::~SrsCodec() -{ -} - -bool SrsCodec::video_is_keyframe(int8_t* data, int size) -{ - // E.4.3.1 VIDEODATA - // Frame Type UB [4] - // Type of video frame. The following values are defined: - // 1 = key frame (for AVC, a seekable frame) - // 2 = inter frame (for AVC, a non-seekable frame) - // 3 = disposable inter frame (H.263 only) - // 4 = generated key frame (reserved for server use only) - // 5 = video info/command frame - // - // AVCPacketType IF CodecID == 7 UI8 - // The following values are defined: - // 0 = AVC sequence header - // 1 = AVC NALU - // 2 = AVC end of sequence (lower level NALU sequence ender is - // not required or supported) - - // 2bytes required. - if (size < 1) { - return false; - } - - char frame_type = *(char*)data; - frame_type = (frame_type >> 4) & 0x0F; - - return frame_type == 1; -} - -bool SrsCodec::video_is_sequence_header(int8_t* data, int size) -{ - // E.4.3.1 VIDEODATA - // Frame Type UB [4] - // Type of video frame. The following values are defined: - // 1 = key frame (for AVC, a seekable frame) - // 2 = inter frame (for AVC, a non-seekable frame) - // 3 = disposable inter frame (H.263 only) - // 4 = generated key frame (reserved for server use only) - // 5 = video info/command frame - // - // AVCPacketType IF CodecID == 7 UI8 - // The following values are defined: - // 0 = AVC sequence header - // 1 = AVC NALU - // 2 = AVC end of sequence (lower level NALU sequence ender is - // not required or supported) - - // sequence header only for h264 - if (!video_is_h264(data, size)) { - return false; - } - - // 2bytes required. - if (size < 2) { - return false; - } - - char frame_type = *(char*)data; - frame_type = (frame_type >> 4) & 0x0F; - - char avc_packet_type = *(char*)(data + 1); - - return frame_type == 1 && avc_packet_type == 0; -} - -bool SrsCodec::audio_is_sequence_header(int8_t* data, int size) -{ - // AACPacketType IF SoundFormat == 10 UI8 - // The following values are defined: - // 0 = AAC sequence header - // 1 = AAC raw - - // sequence header only for aac - if (!audio_is_aac(data, size)) { - return false; - } - - // 2bytes required. - if (size < 2) { - return false; - } - - char aac_packet_type = *(char*)(data + 1); - - return aac_packet_type == 0; -} - -bool SrsCodec::video_is_h264(int8_t* data, int size) -{ - // E.4.3.1 VIDEODATA - // CodecID UB [4] - // Codec Identifier. The following values are defined: - // 2 = Sorenson H.263 - // 3 = Screen video - // 4 = On2 VP6 - // 5 = On2 VP6 with alpha channel - // 6 = Screen video version 2 - // 7 = AVC - - // 1bytes required. - if (size < 1) { - return false; - } - - char codec_id = *(char*)data; - codec_id = codec_id & 0x0F; - - return codec_id == 7; -} - -bool SrsCodec::audio_is_aac(int8_t* data, int size) -{ - // SoundFormat UB [4] - // Format of SoundData. The following values are defined: - // 0 = Linear PCM, platform endian - // 1 = ADPCM - // 2 = MP3 - // 3 = Linear PCM, little endian - // 4 = Nellymoser 16 kHz mono - // 5 = Nellymoser 8 kHz mono - // 6 = Nellymoser - // 7 = G.711 A-law logarithmic PCM - // 8 = G.711 mu-law logarithmic PCM - // 9 = reserved - // 10 = AAC - // 11 = Speex - // 14 = MP3 8 kHz - // 15 = Device-specific sound - // Formats 7, 8, 14, and 15 are reserved. - // AAC is supported in Flash Player 9,0,115,0 and higher. - // Speex is supported in Flash Player 10 and higher. - - // 1bytes required. - if (size < 1) { - return false; - } - - char sound_format = *(char*)data; - sound_format = (sound_format >> 4) & 0x0F; - - return sound_format == 10; -} +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +SrsCodec::SrsCodec() +{ +} + +SrsCodec::~SrsCodec() +{ +} + +bool SrsCodec::video_is_keyframe(int8_t* data, int size) +{ + // E.4.3.1 VIDEODATA + // Frame Type UB [4] + // Type of video frame. The following values are defined: + // 1 = key frame (for AVC, a seekable frame) + // 2 = inter frame (for AVC, a non-seekable frame) + // 3 = disposable inter frame (H.263 only) + // 4 = generated key frame (reserved for server use only) + // 5 = video info/command frame + // + // AVCPacketType IF CodecID == 7 UI8 + // The following values are defined: + // 0 = AVC sequence header + // 1 = AVC NALU + // 2 = AVC end of sequence (lower level NALU sequence ender is + // not required or supported) + + // 2bytes required. + if (size < 1) { + return false; + } + + char frame_type = *(char*)data; + frame_type = (frame_type >> 4) & 0x0F; + + return frame_type == 1; +} + +bool SrsCodec::video_is_sequence_header(int8_t* data, int size) +{ + // E.4.3.1 VIDEODATA + // Frame Type UB [4] + // Type of video frame. The following values are defined: + // 1 = key frame (for AVC, a seekable frame) + // 2 = inter frame (for AVC, a non-seekable frame) + // 3 = disposable inter frame (H.263 only) + // 4 = generated key frame (reserved for server use only) + // 5 = video info/command frame + // + // AVCPacketType IF CodecID == 7 UI8 + // The following values are defined: + // 0 = AVC sequence header + // 1 = AVC NALU + // 2 = AVC end of sequence (lower level NALU sequence ender is + // not required or supported) + + // sequence header only for h264 + if (!video_is_h264(data, size)) { + return false; + } + + // 2bytes required. + if (size < 2) { + return false; + } + + char frame_type = *(char*)data; + frame_type = (frame_type >> 4) & 0x0F; + + char avc_packet_type = *(char*)(data + 1); + + return frame_type == 1 && avc_packet_type == 0; +} + +bool SrsCodec::audio_is_sequence_header(int8_t* data, int size) +{ + // AACPacketType IF SoundFormat == 10 UI8 + // The following values are defined: + // 0 = AAC sequence header + // 1 = AAC raw + + // sequence header only for aac + if (!audio_is_aac(data, size)) { + return false; + } + + // 2bytes required. + if (size < 2) { + return false; + } + + char aac_packet_type = *(char*)(data + 1); + + return aac_packet_type == 0; +} + +bool SrsCodec::video_is_h264(int8_t* data, int size) +{ + // E.4.3.1 VIDEODATA + // CodecID UB [4] + // Codec Identifier. The following values are defined: + // 2 = Sorenson H.263 + // 3 = Screen video + // 4 = On2 VP6 + // 5 = On2 VP6 with alpha channel + // 6 = Screen video version 2 + // 7 = AVC + + // 1bytes required. + if (size < 1) { + return false; + } + + char codec_id = *(char*)data; + codec_id = codec_id & 0x0F; + + return codec_id == 7; +} + +bool SrsCodec::audio_is_aac(int8_t* data, int size) +{ + // SoundFormat UB [4] + // Format of SoundData. The following values are defined: + // 0 = Linear PCM, platform endian + // 1 = ADPCM + // 2 = MP3 + // 3 = Linear PCM, little endian + // 4 = Nellymoser 16 kHz mono + // 5 = Nellymoser 8 kHz mono + // 6 = Nellymoser + // 7 = G.711 A-law logarithmic PCM + // 8 = G.711 mu-law logarithmic PCM + // 9 = reserved + // 10 = AAC + // 11 = Speex + // 14 = MP3 8 kHz + // 15 = Device-specific sound + // Formats 7, 8, 14, and 15 are reserved. + // AAC is supported in Flash Player 9,0,115,0 and higher. + // Speex is supported in Flash Player 10 and higher. + + // 1bytes required. + if (size < 1) { + return false; + } + + char sound_format = *(char*)data; + sound_format = (sound_format >> 4) & 0x0F; + + return sound_format == 10; +} diff --git a/trunk/src/core/srs_core_codec.hpp b/trunk/src/core/srs_core_codec.hpp index 051dba11c..2e24fd871 100755 --- a/trunk/src/core/srs_core_codec.hpp +++ b/trunk/src/core/srs_core_codec.hpp @@ -1,65 +1,65 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_CODEC_HPP -#define SRS_CORE_CODEC_HPP - -/* -#include -*/ - -#include - -/** -* Annex E. The FLV File Format -*/ -class SrsCodec -{ -public: - SrsCodec(); - virtual ~SrsCodec(); -public: - /** - * only check the frame_type, not check the codec type. - */ - virtual bool video_is_keyframe(int8_t* data, int size); - /** - * check codec h264, keyframe, sequence header - */ - virtual bool video_is_sequence_header(int8_t* data, int size); - /** - * check codec aac, sequence header - */ - virtual bool audio_is_sequence_header(int8_t* data, int size); - /** - * check codec h264. - */ - virtual bool video_is_h264(int8_t* data, int size); -private: - /** - * check codec aac. - */ - virtual bool audio_is_aac(int8_t* data, int size); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_CODEC_HPP +#define SRS_CORE_CODEC_HPP + +/* +#include +*/ + +#include + +/** +* Annex E. The FLV File Format +*/ +class SrsCodec +{ +public: + SrsCodec(); + virtual ~SrsCodec(); +public: + /** + * only check the frame_type, not check the codec type. + */ + virtual bool video_is_keyframe(int8_t* data, int size); + /** + * check codec h264, keyframe, sequence header + */ + virtual bool video_is_sequence_header(int8_t* data, int size); + /** + * check codec aac, sequence header + */ + virtual bool audio_is_sequence_header(int8_t* data, int size); + /** + * check codec h264. + */ + virtual bool video_is_h264(int8_t* data, int size); +private: + /** + * check codec aac. + */ + virtual bool audio_is_aac(int8_t* data, int size); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_config.cpp b/trunk/src/core/srs_core_config.cpp index 415e9f50b..59caa2894 100755 --- a/trunk/src/core/srs_core_config.cpp +++ b/trunk/src/core/srs_core_config.cpp @@ -1,750 +1,750 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include -#include -// file operations. -#include -#include -#include -#include - -#include -#include - -#include -#include -#include - -#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR) - -int64_t FILE_SIZE(int fd) -{ - int64_t pre = FILE_OFFSET(fd); - int64_t pos = lseek(fd, 0, SEEK_END); - lseek(fd, pre, SEEK_SET); - return pos; -} - -#define LF (char)0x0a -#define CR (char)0x0d - -bool is_common_space(char ch) -{ - return (ch == ' ' || ch == '\t' || ch == CR || ch == LF); -} - -#define CONF_BUFFER_SIZE 4096 - -SrsFileBuffer::SrsFileBuffer() -{ - fd = -1; - line = 0; - - pos = last = start = new char[CONF_BUFFER_SIZE]; - end = start + CONF_BUFFER_SIZE; -} - -SrsFileBuffer::~SrsFileBuffer() -{ - if (fd > 0) { - close(fd); - } - srs_freepa(start); -} - -int SrsFileBuffer::open(const char* filename) -{ - assert(fd == -1); - - if ((fd = ::open(filename, O_RDONLY, 0)) < 0) { - srs_error("open conf file error. errno=%d(%s)", errno, strerror(errno)); - return ERROR_SYSTEM_CONFIG_INVALID; - } - - line = 1; - - return ERROR_SUCCESS; -} - -SrsConfDirective::SrsConfDirective() -{ -} - -SrsConfDirective::~SrsConfDirective() -{ - std::vector::iterator it; - for (it = directives.begin(); it != directives.end(); ++it) { - SrsConfDirective* directive = *it; - srs_freep(directive); - } - directives.clear(); -} - -std::string SrsConfDirective::arg0() -{ - if (args.size() > 0) { - return args.at(0); - } - - return ""; -} - -std::string SrsConfDirective::arg1() -{ - if (args.size() > 1) { - return args.at(1); - } - - return ""; -} - -std::string SrsConfDirective::arg2() -{ - if (args.size() > 2) { - return args.at(2); - } - - return ""; -} - -SrsConfDirective* SrsConfDirective::at(int index) -{ - return directives.at(index); -} - -SrsConfDirective* SrsConfDirective::get(std::string _name) -{ - std::vector::iterator it; - for (it = directives.begin(); it != directives.end(); ++it) { - SrsConfDirective* directive = *it; - if (directive->name == _name) { - return directive; - } - } - - return NULL; -} - -int SrsConfDirective::parse(const char* filename) -{ - int ret = ERROR_SUCCESS; - - SrsFileBuffer buffer; - - if ((ret = buffer.open(filename)) != ERROR_SUCCESS) { - return ret; - } - - return parse_conf(&buffer, parse_file); -} - -// see: ngx_conf_parse -int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type) -{ - int ret = ERROR_SUCCESS; - - while (true) { - std::vector args; - ret = read_token(buffer, args); - - /** - * ret maybe: - * ERROR_SYSTEM_CONFIG_INVALID error. - * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found - * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found - * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found - * ERROR_SYSTEM_CONFIG_EOF the config file is done - */ - if (ret == ERROR_SYSTEM_CONFIG_INVALID) { - return ret; - } - if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) { - if (type != parse_block) { - srs_error("line %d: unexpected \"}\"", buffer->line); - return ret; - } - return ERROR_SUCCESS; - } - if (ret == ERROR_SYSTEM_CONFIG_EOF) { - if (type == parse_block) { - srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line); - return ret; - } - return ERROR_SUCCESS; - } - - if (args.empty()) { - srs_error("line %d: empty directive.", buffer->line); - return ret; - } - - // build directive tree. - SrsConfDirective* directive = new SrsConfDirective(); - - directive->conf_line = buffer->line; - directive->name = args[0]; - args.erase(args.begin()); - directive->args.swap(args); - - directives.push_back(directive); - - if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) { - if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) { - return ret; - } - } - } - - return ret; -} - -// see: ngx_conf_read_token -int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args) -{ - int ret = ERROR_SUCCESS; - - char* pstart = buffer->pos; - int startline = buffer->line; - - bool sharp_comment = false; - - bool d_quoted = false; - bool s_quoted = false; - - bool need_space = false; - bool last_space = true; - - while (true) { - if ((ret = refill_buffer(buffer, d_quoted, s_quoted, startline, pstart)) != ERROR_SUCCESS) { - if (!args.empty() || !last_space) { - srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ret; - } - - char ch = *buffer->pos++; - - if (ch == LF) { - buffer->line++; - sharp_comment = false; - } - - if (sharp_comment) { - continue; - } - - if (need_space) { - if (is_common_space(ch)) { - last_space = true; - need_space = false; - continue; - } - if (ch == ';') { - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - } - if (ch == '{') { - return ERROR_SYSTEM_CONFIG_BLOCK_START; - } - srs_error("line %d: unexpected '%c'", buffer->line, ch); - return ERROR_SYSTEM_CONFIG_INVALID; - } - - // last charecter is space. - if (last_space) { - if (is_common_space(ch)) { - continue; - } - pstart = buffer->pos - 1; - startline = buffer->line; - switch (ch) { - case ';': - if (args.size() == 0) { - srs_error("line %d: unexpected ';'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - case '{': - if (args.size() == 0) { - srs_error("line %d: unexpected '{'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_BLOCK_START; - case '}': - if (args.size() != 0) { - srs_error("line %d: unexpected '}'", buffer->line); - return ERROR_SYSTEM_CONFIG_INVALID; - } - return ERROR_SYSTEM_CONFIG_BLOCK_END; - case '#': - sharp_comment = 1; - continue; - case '"': - pstart++; - d_quoted = true; - last_space = 0; - continue; - case '\'': - pstart++; - s_quoted = true; - last_space = 0; - continue; - default: - last_space = 0; - continue; - } - } else { - // last charecter is not space - bool found = false; - if (d_quoted) { - if (ch == '"') { - d_quoted = false; - need_space = true; - found = true; - } - } else if (s_quoted) { - if (ch == '\'') { - s_quoted = false; - need_space = true; - found = true; - } - } else if (is_common_space(ch) || ch == ';' || ch == '{') { - last_space = true; - found = 1; - } - - if (found) { - int len = buffer->pos - pstart; - char* word = new char[len]; - memcpy(word, pstart, len); - word[len - 1] = 0; - - args.push_back(word); - srs_freepa(word); - - if (ch == ';') { - return ERROR_SYSTEM_CONFIG_DIRECTIVE; - } - if (ch == '{') { - return ERROR_SYSTEM_CONFIG_BLOCK_START; - } - } - } - } - - return ret; -} - -int SrsConfDirective::refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart) -{ - int ret = ERROR_SUCCESS; - - if (buffer->pos < buffer->last) { - return ret; - } - - int size = FILE_SIZE(buffer->fd) - FILE_OFFSET(buffer->fd); - - if (size <= 0) { - return ERROR_SYSTEM_CONFIG_EOF; - } - - int len = buffer->pos - buffer->start; - if (len >= CONF_BUFFER_SIZE) { - buffer->line = startline; - - if (!d_quoted && !s_quoted) { - srs_error("line %d: too long parameter \"%*s...\" started", - buffer->line, 10, buffer->start); - - } else { - srs_error("line %d: too long parameter, " - "probably missing terminating '%c' character", buffer->line, d_quoted? '"':'\''); - } - return ERROR_SYSTEM_CONFIG_INVALID; - } - - if (len) { - memmove(buffer->start, pstart, len); - } - - size = srs_min(size, buffer->end - (buffer->start + len)); - int n = read(buffer->fd, buffer->start + len, size); - if (n != size) { - srs_error("read file read error. expect %d, actual %d bytes.", size, n); - return ERROR_SYSTEM_CONFIG_INVALID; - } - - buffer->pos = buffer->start + len; - buffer->last = buffer->pos + n; - pstart = buffer->start; - - return ret; -} - -SrsConfig* config = new SrsConfig(); - -SrsConfig::SrsConfig() -{ - show_help = false; - show_version = false; - - root = new SrsConfDirective(); - root->conf_line = 0; - root->name = "root"; -} - -SrsConfig::~SrsConfig() -{ - srs_freep(root); -} - -int SrsConfig::reload() -{ - int ret = ERROR_SUCCESS; - - SrsConfig conf; - if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) { - srs_error("config reloader parse file failed. ret=%d", ret); - return ret; - } - srs_info("config reloader parse file success."); - - // store current root to old_root, - // and reap the root from conf to current root. - SrsConfDirective* old_root = root; - SrsAutoFree(SrsConfDirective, old_root, false); - - root = conf.root; - conf.root = NULL; - - // merge config. - std::vector::iterator it; - - // merge config: listen - if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - SrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) { - srs_error("notify subscribes reload listen failed. ret=%d", ret); - return ret; - } - } - srs_trace("reload listen success."); - } - // merge config: pithy_print - if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) { - for (it = subscribes.begin(); it != subscribes.end(); ++it) { - SrsReloadHandler* subscribe = *it; - if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) { - srs_error("notify subscribes pithy_print listen failed. ret=%d", ret); - return ret; - } - } - srs_trace("reload pithy_print success."); - } - - return ret; -} - -void SrsConfig::subscribe(SrsReloadHandler* handler) -{ - std::vector::iterator it; - - it = std::find(subscribes.begin(), subscribes.end(), handler); - if (it != subscribes.end()) { - return; - } - - subscribes.push_back(handler); -} - -void SrsConfig::unsubscribe(SrsReloadHandler* handler) -{ - std::vector::iterator it; - - it = std::find(subscribes.begin(), subscribes.end(), handler); - if (it == subscribes.end()) { - return; - } - - subscribes.erase(it); -} - -// see: ngx_get_options -int SrsConfig::parse_options(int argc, char** argv) -{ - int ret = ERROR_SUCCESS; - - for (int i = 1; i < argc; i++) { - if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) { - return ret; - } - } - - if (show_help) { - print_help(argv); - } - - if (show_version) { - printf("%s\n", RTMP_SIG_SRS_VERSION); - } - - if (show_help || show_version) { - exit(0); - } - - if (config_file.empty()) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret); - return ret; - } - - return parse_file(config_file.c_str()); -} - -SrsConfDirective* SrsConfig::get_vhost(std::string vhost) -{ - srs_assert(root); - - for (int i = 0; i < (int)root->directives.size(); i++) { - SrsConfDirective* conf = root->at(i); - - if (conf->name != "vhost") { - continue; - } - - if (conf->arg0() == vhost) { - return conf; - } - } - - if (vhost != RTMP_VHOST_DEFAULT) { - return get_vhost(RTMP_VHOST_DEFAULT); - } - - return NULL; -} - -SrsConfDirective* SrsConfig::get_gop_cache(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("gop_cache"); -} - -SrsConfDirective* SrsConfig::get_refer(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer"); -} - -SrsConfDirective* SrsConfig::get_refer_play(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer_play"); -} - -SrsConfDirective* SrsConfig::get_refer_publish(std::string vhost) -{ - SrsConfDirective* conf = get_vhost(vhost); - - if (!conf) { - return NULL; - } - - return conf->get("refer_publish"); -} - -SrsConfDirective* SrsConfig::get_listen() -{ - return root->get("listen"); -} - -SrsConfDirective* SrsConfig::get_chunk_size() -{ - return root->get("chunk_size"); -} - -SrsConfDirective* SrsConfig::get_pithy_print_publish() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return NULL; - } - - return pithy->get("publish"); -} - -SrsConfDirective* SrsConfig::get_pithy_print_play() -{ - SrsConfDirective* pithy = root->get("pithy_print"); - if (!pithy) { - return NULL; - } - - return pithy->get("play"); -} - -int SrsConfig::parse_file(const char* filename) -{ - int ret = ERROR_SUCCESS; - - config_file = filename; - - if (config_file.empty()) { - return ERROR_SYSTEM_CONFIG_INVALID; - } - - if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) { - return ret; - } - - SrsConfDirective* conf = NULL; - if ((conf = get_listen()) == NULL || conf->args.size() == 0) { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("line %d: conf error, " - "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret); - return ret; - } - - return ret; -} - -int SrsConfig::parse_argv(int& i, char** argv) -{ - int ret = ERROR_SUCCESS; - - char* p = argv[i]; - - if (*p++ != '-') { - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("invalid options(index=%d, value=%s), " - "must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret); - return ret; - } - - while (*p) { - switch (*p++) { - case '?': - case 'h': - show_help = true; - break; - case 'v': - case 'V': - show_version = true; - break; - case 'c': - if (*p) { - config_file = p; - return ret; - } - if (argv[++i]) { - config_file = argv[i]; - return ret; - } - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("option \"-c\" requires parameter, ret=%d", ret); - return ret; - default: - ret = ERROR_SYSTEM_CONFIG_INVALID; - srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret); - return ret; - } - } - - return ret; -} - -void SrsConfig::print_help(char** argv) -{ - printf(RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION - " Copyright (c) 2013 winlin\n" - "Usage: %s [-h?vV] [-c ]\n" - "\n" - "Options:\n" - " -?-h : show help\n" - " -v-V : show version and exit\n" - " -c filename : set configuration file\n" - "\n" - RTMP_SIG_SRS_WEB"\n" - RTMP_SIG_SRS_URL"\n" - "Email: "RTMP_SIG_SRS_EMAIL"\n", - argv[0]); -} - -bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) -{ - if (!a || !b) { - return false; - } - - if (a->name != b->name) { - return false; - } - - if (a->args.size() != b->args.size()) { - return false; - } - - for (int i = 0; i < (int)a->args.size(); i++) { - if (a->args.at(i) != b->args.at(i)) { - return false; - } - } - - if (a->directives.size() != b->directives.size()) { - return false; - } - - for (int i = 0; i < (int)a->directives.size(); i++) { - SrsConfDirective* a0 = a->at(i); - SrsConfDirective* b0 = b->at(i); - - if (!srs_directive_equals(a0, b0)) { - return false; - } - } - - return true; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include +#include +// file operations. +#include +#include +#include +#include + +#include +#include + +#include +#include +#include + +#define FILE_OFFSET(fd) lseek(fd, 0, SEEK_CUR) + +int64_t FILE_SIZE(int fd) +{ + int64_t pre = FILE_OFFSET(fd); + int64_t pos = lseek(fd, 0, SEEK_END); + lseek(fd, pre, SEEK_SET); + return pos; +} + +#define LF (char)0x0a +#define CR (char)0x0d + +bool is_common_space(char ch) +{ + return (ch == ' ' || ch == '\t' || ch == CR || ch == LF); +} + +#define CONF_BUFFER_SIZE 4096 + +SrsFileBuffer::SrsFileBuffer() +{ + fd = -1; + line = 0; + + pos = last = start = new char[CONF_BUFFER_SIZE]; + end = start + CONF_BUFFER_SIZE; +} + +SrsFileBuffer::~SrsFileBuffer() +{ + if (fd > 0) { + close(fd); + } + srs_freepa(start); +} + +int SrsFileBuffer::open(const char* filename) +{ + assert(fd == -1); + + if ((fd = ::open(filename, O_RDONLY, 0)) < 0) { + srs_error("open conf file error. errno=%d(%s)", errno, strerror(errno)); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + line = 1; + + return ERROR_SUCCESS; +} + +SrsConfDirective::SrsConfDirective() +{ +} + +SrsConfDirective::~SrsConfDirective() +{ + std::vector::iterator it; + for (it = directives.begin(); it != directives.end(); ++it) { + SrsConfDirective* directive = *it; + srs_freep(directive); + } + directives.clear(); +} + +std::string SrsConfDirective::arg0() +{ + if (args.size() > 0) { + return args.at(0); + } + + return ""; +} + +std::string SrsConfDirective::arg1() +{ + if (args.size() > 1) { + return args.at(1); + } + + return ""; +} + +std::string SrsConfDirective::arg2() +{ + if (args.size() > 2) { + return args.at(2); + } + + return ""; +} + +SrsConfDirective* SrsConfDirective::at(int index) +{ + return directives.at(index); +} + +SrsConfDirective* SrsConfDirective::get(std::string _name) +{ + std::vector::iterator it; + for (it = directives.begin(); it != directives.end(); ++it) { + SrsConfDirective* directive = *it; + if (directive->name == _name) { + return directive; + } + } + + return NULL; +} + +int SrsConfDirective::parse(const char* filename) +{ + int ret = ERROR_SUCCESS; + + SrsFileBuffer buffer; + + if ((ret = buffer.open(filename)) != ERROR_SUCCESS) { + return ret; + } + + return parse_conf(&buffer, parse_file); +} + +// see: ngx_conf_parse +int SrsConfDirective::parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type) +{ + int ret = ERROR_SUCCESS; + + while (true) { + std::vector args; + ret = read_token(buffer, args); + + /** + * ret maybe: + * ERROR_SYSTEM_CONFIG_INVALID error. + * ERROR_SYSTEM_CONFIG_DIRECTIVE directive terminated by ';' found + * ERROR_SYSTEM_CONFIG_BLOCK_START token terminated by '{' found + * ERROR_SYSTEM_CONFIG_BLOCK_END the '}' found + * ERROR_SYSTEM_CONFIG_EOF the config file is done + */ + if (ret == ERROR_SYSTEM_CONFIG_INVALID) { + return ret; + } + if (ret == ERROR_SYSTEM_CONFIG_BLOCK_END) { + if (type != parse_block) { + srs_error("line %d: unexpected \"}\"", buffer->line); + return ret; + } + return ERROR_SUCCESS; + } + if (ret == ERROR_SYSTEM_CONFIG_EOF) { + if (type == parse_block) { + srs_error("line %d: unexpected end of file, expecting \"}\"", buffer->line); + return ret; + } + return ERROR_SUCCESS; + } + + if (args.empty()) { + srs_error("line %d: empty directive.", buffer->line); + return ret; + } + + // build directive tree. + SrsConfDirective* directive = new SrsConfDirective(); + + directive->conf_line = buffer->line; + directive->name = args[0]; + args.erase(args.begin()); + directive->args.swap(args); + + directives.push_back(directive); + + if (ret == ERROR_SYSTEM_CONFIG_BLOCK_START) { + if ((ret = directive->parse_conf(buffer, parse_block)) != ERROR_SUCCESS) { + return ret; + } + } + } + + return ret; +} + +// see: ngx_conf_read_token +int SrsConfDirective::read_token(SrsFileBuffer* buffer, std::vector& args) +{ + int ret = ERROR_SUCCESS; + + char* pstart = buffer->pos; + int startline = buffer->line; + + bool sharp_comment = false; + + bool d_quoted = false; + bool s_quoted = false; + + bool need_space = false; + bool last_space = true; + + while (true) { + if ((ret = refill_buffer(buffer, d_quoted, s_quoted, startline, pstart)) != ERROR_SUCCESS) { + if (!args.empty() || !last_space) { + srs_error("line %d: unexpected end of file, expecting ; or \"}\"", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ret; + } + + char ch = *buffer->pos++; + + if (ch == LF) { + buffer->line++; + sharp_comment = false; + } + + if (sharp_comment) { + continue; + } + + if (need_space) { + if (is_common_space(ch)) { + last_space = true; + need_space = false; + continue; + } + if (ch == ';') { + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + } + if (ch == '{') { + return ERROR_SYSTEM_CONFIG_BLOCK_START; + } + srs_error("line %d: unexpected '%c'", buffer->line, ch); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + // last charecter is space. + if (last_space) { + if (is_common_space(ch)) { + continue; + } + pstart = buffer->pos - 1; + startline = buffer->line; + switch (ch) { + case ';': + if (args.size() == 0) { + srs_error("line %d: unexpected ';'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + case '{': + if (args.size() == 0) { + srs_error("line %d: unexpected '{'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_BLOCK_START; + case '}': + if (args.size() != 0) { + srs_error("line %d: unexpected '}'", buffer->line); + return ERROR_SYSTEM_CONFIG_INVALID; + } + return ERROR_SYSTEM_CONFIG_BLOCK_END; + case '#': + sharp_comment = 1; + continue; + case '"': + pstart++; + d_quoted = true; + last_space = 0; + continue; + case '\'': + pstart++; + s_quoted = true; + last_space = 0; + continue; + default: + last_space = 0; + continue; + } + } else { + // last charecter is not space + bool found = false; + if (d_quoted) { + if (ch == '"') { + d_quoted = false; + need_space = true; + found = true; + } + } else if (s_quoted) { + if (ch == '\'') { + s_quoted = false; + need_space = true; + found = true; + } + } else if (is_common_space(ch) || ch == ';' || ch == '{') { + last_space = true; + found = 1; + } + + if (found) { + int len = buffer->pos - pstart; + char* word = new char[len]; + memcpy(word, pstart, len); + word[len - 1] = 0; + + args.push_back(word); + srs_freepa(word); + + if (ch == ';') { + return ERROR_SYSTEM_CONFIG_DIRECTIVE; + } + if (ch == '{') { + return ERROR_SYSTEM_CONFIG_BLOCK_START; + } + } + } + } + + return ret; +} + +int SrsConfDirective::refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart) +{ + int ret = ERROR_SUCCESS; + + if (buffer->pos < buffer->last) { + return ret; + } + + int size = FILE_SIZE(buffer->fd) - FILE_OFFSET(buffer->fd); + + if (size <= 0) { + return ERROR_SYSTEM_CONFIG_EOF; + } + + int len = buffer->pos - buffer->start; + if (len >= CONF_BUFFER_SIZE) { + buffer->line = startline; + + if (!d_quoted && !s_quoted) { + srs_error("line %d: too long parameter \"%*s...\" started", + buffer->line, 10, buffer->start); + + } else { + srs_error("line %d: too long parameter, " + "probably missing terminating '%c' character", buffer->line, d_quoted? '"':'\''); + } + return ERROR_SYSTEM_CONFIG_INVALID; + } + + if (len) { + memmove(buffer->start, pstart, len); + } + + size = srs_min(size, buffer->end - (buffer->start + len)); + int n = read(buffer->fd, buffer->start + len, size); + if (n != size) { + srs_error("read file read error. expect %d, actual %d bytes.", size, n); + return ERROR_SYSTEM_CONFIG_INVALID; + } + + buffer->pos = buffer->start + len; + buffer->last = buffer->pos + n; + pstart = buffer->start; + + return ret; +} + +SrsConfig* config = new SrsConfig(); + +SrsConfig::SrsConfig() +{ + show_help = false; + show_version = false; + + root = new SrsConfDirective(); + root->conf_line = 0; + root->name = "root"; +} + +SrsConfig::~SrsConfig() +{ + srs_freep(root); +} + +int SrsConfig::reload() +{ + int ret = ERROR_SUCCESS; + + SrsConfig conf; + if ((ret = conf.parse_file(config_file.c_str())) != ERROR_SUCCESS) { + srs_error("config reloader parse file failed. ret=%d", ret); + return ret; + } + srs_info("config reloader parse file success."); + + // store current root to old_root, + // and reap the root from conf to current root. + SrsConfDirective* old_root = root; + SrsAutoFree(SrsConfDirective, old_root, false); + + root = conf.root; + conf.root = NULL; + + // merge config. + std::vector::iterator it; + + // merge config: listen + if (!srs_directive_equals(root->get("listen"), old_root->get("listen"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + SrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_listen()) != ERROR_SUCCESS) { + srs_error("notify subscribes reload listen failed. ret=%d", ret); + return ret; + } + } + srs_trace("reload listen success."); + } + // merge config: pithy_print + if (!srs_directive_equals(root->get("pithy_print"), old_root->get("pithy_print"))) { + for (it = subscribes.begin(); it != subscribes.end(); ++it) { + SrsReloadHandler* subscribe = *it; + if ((ret = subscribe->on_reload_pithy_print()) != ERROR_SUCCESS) { + srs_error("notify subscribes pithy_print listen failed. ret=%d", ret); + return ret; + } + } + srs_trace("reload pithy_print success."); + } + + return ret; +} + +void SrsConfig::subscribe(SrsReloadHandler* handler) +{ + std::vector::iterator it; + + it = std::find(subscribes.begin(), subscribes.end(), handler); + if (it != subscribes.end()) { + return; + } + + subscribes.push_back(handler); +} + +void SrsConfig::unsubscribe(SrsReloadHandler* handler) +{ + std::vector::iterator it; + + it = std::find(subscribes.begin(), subscribes.end(), handler); + if (it == subscribes.end()) { + return; + } + + subscribes.erase(it); +} + +// see: ngx_get_options +int SrsConfig::parse_options(int argc, char** argv) +{ + int ret = ERROR_SUCCESS; + + for (int i = 1; i < argc; i++) { + if ((ret = parse_argv(i, argv)) != ERROR_SUCCESS) { + return ret; + } + } + + if (show_help) { + print_help(argv); + } + + if (show_version) { + printf("%s\n", RTMP_SIG_SRS_VERSION); + } + + if (show_help || show_version) { + exit(0); + } + + if (config_file.empty()) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("config file not specified, see help: %s -h, ret=%d", argv[0], ret); + return ret; + } + + return parse_file(config_file.c_str()); +} + +SrsConfDirective* SrsConfig::get_vhost(std::string vhost) +{ + srs_assert(root); + + for (int i = 0; i < (int)root->directives.size(); i++) { + SrsConfDirective* conf = root->at(i); + + if (conf->name != "vhost") { + continue; + } + + if (conf->arg0() == vhost) { + return conf; + } + } + + if (vhost != RTMP_VHOST_DEFAULT) { + return get_vhost(RTMP_VHOST_DEFAULT); + } + + return NULL; +} + +SrsConfDirective* SrsConfig::get_gop_cache(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("gop_cache"); +} + +SrsConfDirective* SrsConfig::get_refer(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer"); +} + +SrsConfDirective* SrsConfig::get_refer_play(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer_play"); +} + +SrsConfDirective* SrsConfig::get_refer_publish(std::string vhost) +{ + SrsConfDirective* conf = get_vhost(vhost); + + if (!conf) { + return NULL; + } + + return conf->get("refer_publish"); +} + +SrsConfDirective* SrsConfig::get_listen() +{ + return root->get("listen"); +} + +SrsConfDirective* SrsConfig::get_chunk_size() +{ + return root->get("chunk_size"); +} + +SrsConfDirective* SrsConfig::get_pithy_print_publish() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return NULL; + } + + return pithy->get("publish"); +} + +SrsConfDirective* SrsConfig::get_pithy_print_play() +{ + SrsConfDirective* pithy = root->get("pithy_print"); + if (!pithy) { + return NULL; + } + + return pithy->get("play"); +} + +int SrsConfig::parse_file(const char* filename) +{ + int ret = ERROR_SUCCESS; + + config_file = filename; + + if (config_file.empty()) { + return ERROR_SYSTEM_CONFIG_INVALID; + } + + if ((ret = root->parse(config_file.c_str())) != ERROR_SUCCESS) { + return ret; + } + + SrsConfDirective* conf = NULL; + if ((conf = get_listen()) == NULL || conf->args.size() == 0) { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("line %d: conf error, " + "directive \"listen\" is empty, ret=%d", (conf? conf->conf_line:0), ret); + return ret; + } + + return ret; +} + +int SrsConfig::parse_argv(int& i, char** argv) +{ + int ret = ERROR_SUCCESS; + + char* p = argv[i]; + + if (*p++ != '-') { + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("invalid options(index=%d, value=%s), " + "must starts with -, see help: %s -h, ret=%d", i, argv[i], argv[0], ret); + return ret; + } + + while (*p) { + switch (*p++) { + case '?': + case 'h': + show_help = true; + break; + case 'v': + case 'V': + show_version = true; + break; + case 'c': + if (*p) { + config_file = p; + return ret; + } + if (argv[++i]) { + config_file = argv[i]; + return ret; + } + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("option \"-c\" requires parameter, ret=%d", ret); + return ret; + default: + ret = ERROR_SYSTEM_CONFIG_INVALID; + srs_error("invalid option: \"%c\", see help: %s -h, ret=%d", *(p - 1), argv[0], ret); + return ret; + } + } + + return ret; +} + +void SrsConfig::print_help(char** argv) +{ + printf(RTMP_SIG_SRS_NAME" "RTMP_SIG_SRS_VERSION + " Copyright (c) 2013 winlin\n" + "Usage: %s [-h?vV] [-c ]\n" + "\n" + "Options:\n" + " -?-h : show help\n" + " -v-V : show version and exit\n" + " -c filename : set configuration file\n" + "\n" + RTMP_SIG_SRS_WEB"\n" + RTMP_SIG_SRS_URL"\n" + "Email: "RTMP_SIG_SRS_EMAIL"\n", + argv[0]); +} + +bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b) +{ + if (!a || !b) { + return false; + } + + if (a->name != b->name) { + return false; + } + + if (a->args.size() != b->args.size()) { + return false; + } + + for (int i = 0; i < (int)a->args.size(); i++) { + if (a->args.at(i) != b->args.at(i)) { + return false; + } + } + + if (a->directives.size() != b->directives.size()) { + return false; + } + + for (int i = 0; i < (int)a->directives.size(); i++) { + SrsConfDirective* a0 = a->at(i); + SrsConfDirective* b0 = b->at(i); + + if (!srs_directive_equals(a0, b0)) { + return false; + } + } + + return true; +} + diff --git a/trunk/src/core/srs_core_config.hpp b/trunk/src/core/srs_core_config.hpp index cf48b8766..bc059e1ec 100755 --- a/trunk/src/core/srs_core_config.hpp +++ b/trunk/src/core/srs_core_config.hpp @@ -1,133 +1,133 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_CONIFG_HPP -#define SRS_CORE_CONIFG_HPP - -/* -#include -*/ -#include - -#include -#include - -#include - -// default vhost for rtmp -#define RTMP_VHOST_DEFAULT "__defaultVhost__" - -// conf node: enabled. -#define RTMP_VHOST_ENABLED "enabled" - -class SrsFileBuffer -{ -public: - int fd; - int line; - // start of buffer. - char* start; - // end of buffer. - char* end; - // current consumed position. - char* pos; - // last available position. - char* last; - - SrsFileBuffer(); - virtual ~SrsFileBuffer(); - virtual int open(const char* filename); -}; - -class SrsConfDirective -{ -public: - int conf_line; - std::string name; - std::vector args; - std::vector directives; -public: - SrsConfDirective(); - virtual ~SrsConfDirective(); - std::string arg0(); - std::string arg1(); - std::string arg2(); - SrsConfDirective* at(int index); - SrsConfDirective* get(std::string _name); -public: - virtual int parse(const char* filename); -public: - enum SrsDirectiveType{parse_file, parse_block}; - virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type); - virtual int read_token(SrsFileBuffer* buffer, std::vector& args); - virtual int refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart); -}; - -/** -* the config parser. -* for the config supports reload, so never keep the reference cross st-thread, -* that is, never save the SrsConfDirective* get by any api of config, -* for it maybe free in the reload st-thread cycle. -* you can keep it before st-thread switch, or simply never keep it. -*/ -class SrsConfig -{ -private: - bool show_help; - bool show_version; - std::string config_file; - SrsConfDirective* root; - std::vector subscribes; -public: - SrsConfig(); - virtual ~SrsConfig(); -public: - virtual int reload(); - virtual void subscribe(SrsReloadHandler* handler); - virtual void unsubscribe(SrsReloadHandler* handler); -public: - virtual int parse_options(int argc, char** argv); - virtual SrsConfDirective* get_vhost(std::string vhost); - virtual SrsConfDirective* get_gop_cache(std::string vhost); - virtual SrsConfDirective* get_refer(std::string vhost); - virtual SrsConfDirective* get_refer_play(std::string vhost); - virtual SrsConfDirective* get_refer_publish(std::string vhost); - virtual SrsConfDirective* get_listen(); - virtual SrsConfDirective* get_chunk_size(); - virtual SrsConfDirective* get_pithy_print_publish(); - virtual SrsConfDirective* get_pithy_print_play(); -private: - virtual int parse_file(const char* filename); - virtual int parse_argv(int& i, char** argv); - virtual void print_help(char** argv); -}; - -/** -* deep compare directive. -*/ -bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b); - -// global config -extern SrsConfig* config; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_CONIFG_HPP +#define SRS_CORE_CONIFG_HPP + +/* +#include +*/ +#include + +#include +#include + +#include + +// default vhost for rtmp +#define RTMP_VHOST_DEFAULT "__defaultVhost__" + +// conf node: enabled. +#define RTMP_VHOST_ENABLED "enabled" + +class SrsFileBuffer +{ +public: + int fd; + int line; + // start of buffer. + char* start; + // end of buffer. + char* end; + // current consumed position. + char* pos; + // last available position. + char* last; + + SrsFileBuffer(); + virtual ~SrsFileBuffer(); + virtual int open(const char* filename); +}; + +class SrsConfDirective +{ +public: + int conf_line; + std::string name; + std::vector args; + std::vector directives; +public: + SrsConfDirective(); + virtual ~SrsConfDirective(); + std::string arg0(); + std::string arg1(); + std::string arg2(); + SrsConfDirective* at(int index); + SrsConfDirective* get(std::string _name); +public: + virtual int parse(const char* filename); +public: + enum SrsDirectiveType{parse_file, parse_block}; + virtual int parse_conf(SrsFileBuffer* buffer, SrsDirectiveType type); + virtual int read_token(SrsFileBuffer* buffer, std::vector& args); + virtual int refill_buffer(SrsFileBuffer* buffer, bool d_quoted, bool s_quoted, int startline, char*& pstart); +}; + +/** +* the config parser. +* for the config supports reload, so never keep the reference cross st-thread, +* that is, never save the SrsConfDirective* get by any api of config, +* for it maybe free in the reload st-thread cycle. +* you can keep it before st-thread switch, or simply never keep it. +*/ +class SrsConfig +{ +private: + bool show_help; + bool show_version; + std::string config_file; + SrsConfDirective* root; + std::vector subscribes; +public: + SrsConfig(); + virtual ~SrsConfig(); +public: + virtual int reload(); + virtual void subscribe(SrsReloadHandler* handler); + virtual void unsubscribe(SrsReloadHandler* handler); +public: + virtual int parse_options(int argc, char** argv); + virtual SrsConfDirective* get_vhost(std::string vhost); + virtual SrsConfDirective* get_gop_cache(std::string vhost); + virtual SrsConfDirective* get_refer(std::string vhost); + virtual SrsConfDirective* get_refer_play(std::string vhost); + virtual SrsConfDirective* get_refer_publish(std::string vhost); + virtual SrsConfDirective* get_listen(); + virtual SrsConfDirective* get_chunk_size(); + virtual SrsConfDirective* get_pithy_print_publish(); + virtual SrsConfDirective* get_pithy_print_play(); +private: + virtual int parse_file(const char* filename); + virtual int parse_argv(int& i, char** argv); + virtual void print_help(char** argv); +}; + +/** +* deep compare directive. +*/ +bool srs_directive_equals(SrsConfDirective* a, SrsConfDirective* b); + +// global config +extern SrsConfig* config; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_conn.cpp b/trunk/src/core/srs_core_conn.cpp index 9bae22167..bcb6e4c5e 100755 --- a/trunk/src/core/srs_core_conn.cpp +++ b/trunk/src/core/srs_core_conn.cpp @@ -1,97 +1,97 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include - -SrsConnection::SrsConnection(SrsServer* srs_server, st_netfd_t client_stfd) -{ - server = srs_server; - stfd = client_stfd; -} - -SrsConnection::~SrsConnection() -{ - if (stfd) { - int fd = st_netfd_fileno(stfd); - st_netfd_close(stfd); - stfd = NULL; - - // st does not close it sometimes, - // close it manually. - close(fd); - } -} - -int SrsConnection::start() -{ - int ret = ERROR_SUCCESS; - - if (st_thread_create(cycle_thread, this, 0, 0) == NULL) { - ret = ERROR_ST_CREATE_CYCLE_THREAD; - srs_error("st_thread_create conn cycle thread error. ret=%d", ret); - return ret; - } - srs_verbose("create st conn cycle thread success."); - - return ret; -} - -void SrsConnection::cycle() -{ - int ret = ERROR_SUCCESS; - - log_context->generate_id(); - ret = do_cycle(); - - // if socket io error, set to closed. - if (ret == ERROR_SOCKET_READ || ret == ERROR_SOCKET_READ_FULLY || ret == ERROR_SOCKET_WRITE) { - ret = ERROR_SOCKET_CLOSED; - } - - // success. - if (ret == ERROR_SUCCESS) { - srs_trace("client process normally finished. ret=%d", ret); - } - - // client close peer. - if (ret == ERROR_SOCKET_CLOSED) { - srs_trace("client disconnect peer. ret=%d", ret); - } - - server->remove(this); -} - -void* SrsConnection::cycle_thread(void* arg) -{ - SrsConnection* conn = (SrsConnection*)arg; - srs_assert(conn != NULL); - - conn->cycle(); - - return NULL; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include + +SrsConnection::SrsConnection(SrsServer* srs_server, st_netfd_t client_stfd) +{ + server = srs_server; + stfd = client_stfd; +} + +SrsConnection::~SrsConnection() +{ + if (stfd) { + int fd = st_netfd_fileno(stfd); + st_netfd_close(stfd); + stfd = NULL; + + // st does not close it sometimes, + // close it manually. + close(fd); + } +} + +int SrsConnection::start() +{ + int ret = ERROR_SUCCESS; + + if (st_thread_create(cycle_thread, this, 0, 0) == NULL) { + ret = ERROR_ST_CREATE_CYCLE_THREAD; + srs_error("st_thread_create conn cycle thread error. ret=%d", ret); + return ret; + } + srs_verbose("create st conn cycle thread success."); + + return ret; +} + +void SrsConnection::cycle() +{ + int ret = ERROR_SUCCESS; + + log_context->generate_id(); + ret = do_cycle(); + + // if socket io error, set to closed. + if (ret == ERROR_SOCKET_READ || ret == ERROR_SOCKET_READ_FULLY || ret == ERROR_SOCKET_WRITE) { + ret = ERROR_SOCKET_CLOSED; + } + + // success. + if (ret == ERROR_SUCCESS) { + srs_trace("client process normally finished. ret=%d", ret); + } + + // client close peer. + if (ret == ERROR_SOCKET_CLOSED) { + srs_trace("client disconnect peer. ret=%d", ret); + } + + server->remove(this); +} + +void* SrsConnection::cycle_thread(void* arg) +{ + SrsConnection* conn = (SrsConnection*)arg; + srs_assert(conn != NULL); + + conn->cycle(); + + return NULL; +} + diff --git a/trunk/src/core/srs_core_conn.hpp b/trunk/src/core/srs_core_conn.hpp index 03c47a2a6..502457e0f 100755 --- a/trunk/src/core/srs_core_conn.hpp +++ b/trunk/src/core/srs_core_conn.hpp @@ -1,53 +1,53 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_CONN_HPP -#define SRS_CORE_CONN_HPP - -/* -#include -*/ - -#include - -#include - -class SrsServer; -class SrsConnection -{ -protected: - SrsServer* server; - st_netfd_t stfd; -public: - SrsConnection(SrsServer* srs_server, st_netfd_t client_stfd); - virtual ~SrsConnection(); -public: - virtual int start(); -protected: - virtual int do_cycle() = 0; -private: - virtual void cycle(); - static void* cycle_thread(void* arg); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_CONN_HPP +#define SRS_CORE_CONN_HPP + +/* +#include +*/ + +#include + +#include + +class SrsServer; +class SrsConnection +{ +protected: + SrsServer* server; + st_netfd_t stfd; +public: + SrsConnection(SrsServer* srs_server, st_netfd_t client_stfd); + virtual ~SrsConnection(); +public: + virtual int start(); +protected: + virtual int do_cycle() = 0; +private: + virtual void cycle(); + static void* cycle_thread(void* arg); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_error.cpp b/trunk/src/core/srs_core_error.cpp index ccb03dbce..b9ea1d673 100755 --- a/trunk/src/core/srs_core_error.cpp +++ b/trunk/src/core/srs_core_error.cpp @@ -1,24 +1,24 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include diff --git a/trunk/src/core/srs_core_error.hpp b/trunk/src/core/srs_core_error.hpp index 6dca9bbee..b5afcb6c2 100755 --- a/trunk/src/core/srs_core_error.hpp +++ b/trunk/src/core/srs_core_error.hpp @@ -1,110 +1,110 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_ERROR_HPP -#define SRS_CORE_ERROR_HPP - -/* -#include -*/ - -#include - -#define ERROR_SUCCESS 0 - -#define ERROR_ST_SET_EPOLL 100 -#define ERROR_ST_INITIALIZE 101 -#define ERROR_ST_OPEN_SOCKET 102 -#define ERROR_ST_CREATE_LISTEN_THREAD 103 -#define ERROR_ST_CREATE_CYCLE_THREAD 104 - -#define ERROR_SOCKET_CREATE 200 -#define ERROR_SOCKET_SETREUSE 201 -#define ERROR_SOCKET_BIND 202 -#define ERROR_SOCKET_LISTEN 203 -#define ERROR_SOCKET_CLOSED 204 -#define ERROR_SOCKET_GET_PEER_NAME 205 -#define ERROR_SOCKET_GET_PEER_IP 206 -#define ERROR_SOCKET_READ 207 -#define ERROR_SOCKET_READ_FULLY 208 -#define ERROR_SOCKET_WRITE 209 -#define ERROR_SOCKET_WAIT 210 -#define ERROR_SOCKET_TIMEOUT 211 - -#define ERROR_RTMP_PLAIN_REQUIRED 300 -#define ERROR_RTMP_CHUNK_START 301 -#define ERROR_RTMP_MSG_INVLIAD_SIZE 302 -#define ERROR_RTMP_AMF0_DECODE 303 -#define ERROR_RTMP_AMF0_INVALID 304 -#define ERROR_RTMP_REQ_CONNECT 305 -#define ERROR_RTMP_REQ_TCURL 306 -#define ERROR_RTMP_MESSAGE_DECODE 307 -#define ERROR_RTMP_MESSAGE_ENCODE 308 -#define ERROR_RTMP_AMF0_ENCODE 309 -#define ERROR_RTMP_CHUNK_SIZE 310 -#define ERROR_RTMP_TRY_SIMPLE_HS 311 -#define ERROR_RTMP_CH_SCHEMA 312 -#define ERROR_RTMP_PACKET_SIZE 313 -#define ERROR_RTMP_VHOST_NOT_FOUND 314 -#define ERROR_RTMP_ACCESS_DENIED 315 - -#define ERROR_SYSTEM_STREAM_INIT 400 -#define ERROR_SYSTEM_PACKET_INVALID 401 -#define ERROR_SYSTEM_CLIENT_INVALID 402 -#define ERROR_SYSTEM_ASSERT_FAILED 403 -#define ERROR_SYSTEM_SIZE_NEGATIVE 404 -#define ERROR_SYSTEM_CONFIG_INVALID 405 -#define ERROR_SYSTEM_CONFIG_DIRECTIVE 406 -#define ERROR_SYSTEM_CONFIG_BLOCK_START 407 -#define ERROR_SYSTEM_CONFIG_BLOCK_END 408 -#define ERROR_SYSTEM_CONFIG_EOF 409 - -// see librtmp. -// failed when open ssl create the dh -#define ERROR_OpenSslCreateDH 500 -// failed when open ssl create the Private key. -#define ERROR_OpenSslCreateP 501 -// when open ssl create G. -#define ERROR_OpenSslCreateG 502 -// when open ssl parse P1024 -#define ERROR_OpenSslParseP1024 503 -// when open ssl set G -#define ERROR_OpenSslSetG 504 -// when open ssl generate DHKeys -#define ERROR_OpenSslGenerateDHKeys 505 -// when open ssl share key already computed. -#define ERROR_OpenSslShareKeyComputed 506 -// when open ssl get shared key size. -#define ERROR_OpenSslGetSharedKeySize 507 -// when open ssl get peer public key. -#define ERROR_OpenSslGetPeerPublicKey 508 -// when open ssl compute shared key. -#define ERROR_OpenSslComputeSharedKey 509 -// when open ssl is invalid DH state. -#define ERROR_OpenSslInvalidDHState 510 -// when open ssl copy key -#define ERROR_OpenSslCopyKey 511 -// when open ssl sha256 digest key invalid size. -#define ERROR_OpenSslSha256DigestSize 512 - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_ERROR_HPP +#define SRS_CORE_ERROR_HPP + +/* +#include +*/ + +#include + +#define ERROR_SUCCESS 0 + +#define ERROR_ST_SET_EPOLL 100 +#define ERROR_ST_INITIALIZE 101 +#define ERROR_ST_OPEN_SOCKET 102 +#define ERROR_ST_CREATE_LISTEN_THREAD 103 +#define ERROR_ST_CREATE_CYCLE_THREAD 104 + +#define ERROR_SOCKET_CREATE 200 +#define ERROR_SOCKET_SETREUSE 201 +#define ERROR_SOCKET_BIND 202 +#define ERROR_SOCKET_LISTEN 203 +#define ERROR_SOCKET_CLOSED 204 +#define ERROR_SOCKET_GET_PEER_NAME 205 +#define ERROR_SOCKET_GET_PEER_IP 206 +#define ERROR_SOCKET_READ 207 +#define ERROR_SOCKET_READ_FULLY 208 +#define ERROR_SOCKET_WRITE 209 +#define ERROR_SOCKET_WAIT 210 +#define ERROR_SOCKET_TIMEOUT 211 + +#define ERROR_RTMP_PLAIN_REQUIRED 300 +#define ERROR_RTMP_CHUNK_START 301 +#define ERROR_RTMP_MSG_INVLIAD_SIZE 302 +#define ERROR_RTMP_AMF0_DECODE 303 +#define ERROR_RTMP_AMF0_INVALID 304 +#define ERROR_RTMP_REQ_CONNECT 305 +#define ERROR_RTMP_REQ_TCURL 306 +#define ERROR_RTMP_MESSAGE_DECODE 307 +#define ERROR_RTMP_MESSAGE_ENCODE 308 +#define ERROR_RTMP_AMF0_ENCODE 309 +#define ERROR_RTMP_CHUNK_SIZE 310 +#define ERROR_RTMP_TRY_SIMPLE_HS 311 +#define ERROR_RTMP_CH_SCHEMA 312 +#define ERROR_RTMP_PACKET_SIZE 313 +#define ERROR_RTMP_VHOST_NOT_FOUND 314 +#define ERROR_RTMP_ACCESS_DENIED 315 + +#define ERROR_SYSTEM_STREAM_INIT 400 +#define ERROR_SYSTEM_PACKET_INVALID 401 +#define ERROR_SYSTEM_CLIENT_INVALID 402 +#define ERROR_SYSTEM_ASSERT_FAILED 403 +#define ERROR_SYSTEM_SIZE_NEGATIVE 404 +#define ERROR_SYSTEM_CONFIG_INVALID 405 +#define ERROR_SYSTEM_CONFIG_DIRECTIVE 406 +#define ERROR_SYSTEM_CONFIG_BLOCK_START 407 +#define ERROR_SYSTEM_CONFIG_BLOCK_END 408 +#define ERROR_SYSTEM_CONFIG_EOF 409 + +// see librtmp. +// failed when open ssl create the dh +#define ERROR_OpenSslCreateDH 500 +// failed when open ssl create the Private key. +#define ERROR_OpenSslCreateP 501 +// when open ssl create G. +#define ERROR_OpenSslCreateG 502 +// when open ssl parse P1024 +#define ERROR_OpenSslParseP1024 503 +// when open ssl set G +#define ERROR_OpenSslSetG 504 +// when open ssl generate DHKeys +#define ERROR_OpenSslGenerateDHKeys 505 +// when open ssl share key already computed. +#define ERROR_OpenSslShareKeyComputed 506 +// when open ssl get shared key size. +#define ERROR_OpenSslGetSharedKeySize 507 +// when open ssl get peer public key. +#define ERROR_OpenSslGetPeerPublicKey 508 +// when open ssl compute shared key. +#define ERROR_OpenSslComputeSharedKey 509 +// when open ssl is invalid DH state. +#define ERROR_OpenSslInvalidDHState 510 +// when open ssl copy key +#define ERROR_OpenSslCopyKey 511 +// when open ssl sha256 digest key invalid size. +#define ERROR_OpenSslSha256DigestSize 512 + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_handshake.cpp b/trunk/src/core/srs_core_handshake.cpp index 44b4be416..9877b596d 100755 --- a/trunk/src/core/srs_core_handshake.cpp +++ b/trunk/src/core/srs_core_handshake.cpp @@ -1,1207 +1,1207 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include - -#include -#include -#include -#include - -void srs_random_generate(char* bytes, int size) -{ - static char cdata[] = { - 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2d, 0x72, 0x74, 0x6d, 0x70, 0x2d, 0x73, 0x65, - 0x72, 0x76, 0x65, 0x72, 0x2d, 0x77, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x2d, 0x77, 0x69, - 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x40, 0x31, 0x32, 0x36, - 0x2e, 0x63, 0x6f, 0x6d - }; - for (int i = 0; i < size; i++) { - bytes[i] = cdata[rand() % (sizeof(cdata) - 1)]; - } -} - -// 68bytes FMS key which is used to sign the sever packet. -u_int8_t SrsGenuineFMSKey[] = { - 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, - 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, - 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, - 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, - 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001 - 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, - 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, - 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, - 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae -}; // 68 - -// 62bytes FP key which is used to sign the client packet. -u_int8_t SrsGenuineFPKey[] = { - 0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20, - 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C, - 0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79, - 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001 - 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, - 0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57, - 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, - 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE -}; // 62 - -#include -#include -int openssl_HMACsha256(const void* data, int data_size, const void* key, int key_size, void* digest) { - HMAC_CTX ctx; - - HMAC_CTX_init(&ctx); - HMAC_Init_ex(&ctx, (unsigned char*) key, key_size, EVP_sha256(), NULL); - HMAC_Update(&ctx, (unsigned char *) data, data_size); - - unsigned int digest_size; - HMAC_Final(&ctx, (unsigned char *) digest, &digest_size); - - HMAC_CTX_cleanup(&ctx); - - if (digest_size != 32) { - return ERROR_OpenSslSha256DigestSize; - } - - return ERROR_SUCCESS; -} - -#include -#define RFC2409_PRIME_1024 \ - "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ - "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ - "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ - "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ - "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \ - "FFFFFFFFFFFFFFFF" -int __openssl_generate_key( - u_int8_t*& _private_key, u_int8_t*& _public_key, int32_t& size, - DH*& pdh, int32_t& bits_count, u_int8_t*& shared_key, int32_t& shared_key_length, BIGNUM*& peer_public_key -){ - int ret = ERROR_SUCCESS; - - //1. Create the DH - if ((pdh = DH_new()) == NULL) { - ret = ERROR_OpenSslCreateDH; - return ret; - } - - //2. Create his internal p and g - if ((pdh->p = BN_new()) == NULL) { - ret = ERROR_OpenSslCreateP; - return ret; - } - if ((pdh->g = BN_new()) == NULL) { - ret = ERROR_OpenSslCreateG; - return ret; - } - - //3. initialize p, g and key length - if (BN_hex2bn(&pdh->p, RFC2409_PRIME_1024) == 0) { - ret = ERROR_OpenSslParseP1024; - return ret; - } - if (BN_set_word(pdh->g, 2) != 1) { - ret = ERROR_OpenSslSetG; - return ret; - } - - //4. Set the key length - pdh->length = bits_count; - - //5. Generate private and public key - if (DH_generate_key(pdh) != 1) { - ret = ERROR_OpenSslGenerateDHKeys; - return ret; - } - - // CreateSharedKey - if (pdh == NULL) { - ret = ERROR_OpenSslGenerateDHKeys; - return ret; - } - - if (shared_key_length != 0 || shared_key != NULL) { - ret = ERROR_OpenSslShareKeyComputed; - return ret; - } - - shared_key_length = DH_size(pdh); - if (shared_key_length <= 0 || shared_key_length > 1024) { - ret = ERROR_OpenSslGetSharedKeySize; - return ret; - } - shared_key = new u_int8_t[shared_key_length]; - memset(shared_key, 0, shared_key_length); - - peer_public_key = BN_bin2bn(_private_key, size, 0); - if (peer_public_key == NULL) { - ret = ERROR_OpenSslGetPeerPublicKey; - return ret; - } - - if (DH_compute_key(shared_key, peer_public_key, pdh) == -1) { - ret = ERROR_OpenSslComputeSharedKey; - return ret; - } - - // CopyPublicKey - if (pdh == NULL) { - ret = ERROR_OpenSslComputeSharedKey; - return ret; - } - - int32_t keySize = BN_num_bytes(pdh->pub_key); - if ((keySize <= 0) || (size <= 0) || (keySize > size)) { - //("CopyPublicKey failed due to either invalid DH state or invalid call"); return ret; - ret = ERROR_OpenSslInvalidDHState; - return ret; - } - - if (BN_bn2bin(pdh->pub_key, _public_key) != keySize) { - //("Unable to copy key"); return ret; - ret = ERROR_OpenSslCopyKey; - return ret; - } - - return ret; -} -int openssl_generate_key(char* _private_key, char* _public_key, int32_t size) -{ - int ret = ERROR_SUCCESS; - - // Initialize - DH* pdh = NULL; - int32_t bits_count = 1024; - u_int8_t* shared_key = NULL; - int32_t shared_key_length = 0; - BIGNUM* peer_public_key = NULL; - - ret = __openssl_generate_key( - (u_int8_t*&)_private_key, (u_int8_t*&)_public_key, size, - pdh, bits_count, shared_key, shared_key_length, peer_public_key - ); - - if (pdh != NULL) { - if (pdh->p != NULL) { - BN_free(pdh->p); - pdh->p = NULL; - } - if (pdh->g != NULL) { - BN_free(pdh->g); - pdh->g = NULL; - } - DH_free(pdh); - pdh = NULL; - } - - if (shared_key != NULL) { - delete[] shared_key; - shared_key = NULL; - } - - if (peer_public_key != NULL) { - BN_free(peer_public_key); - peer_public_key = NULL; - } - - return ret; -} - -// the digest key generate size. -#define OpensslHashSize 512 - -/** -* 764bytes key结构 -* random-data: (offset)bytes -* key-data: 128bytes -* random-data: (764-offset-128-4)bytes -* offset: 4bytes -*/ -struct key_block -{ - // (offset)bytes - char* random0; - int random0_size; - - // 128bytes - char key[128]; - - // (764-offset-128-4)bytes - char* random1; - int random1_size; - - // 4bytes - int32_t offset; -}; -// calc the offset of key, -// the key->offset cannot be used as the offset of key. -int srs_key_block_get_offset(key_block* key) -{ - int max_offset_size = 764 - 128 - 4; - - int offset = 0; - u_int8_t* pp = (u_int8_t*)&key->offset; - offset += *pp++; - offset += *pp++; - offset += *pp++; - offset += *pp++; - - return offset % max_offset_size; -} -// create new key block data. -// if created, user must free it by srs_key_block_free -void srs_key_block_init(key_block* key) -{ - key->offset = (int32_t)rand(); - key->random0 = NULL; - key->random1 = NULL; - - int offset = srs_key_block_get_offset(key); - srs_assert(offset >= 0); - - key->random0_size = offset; - if (key->random0_size > 0) { - key->random0 = new char[key->random0_size]; - srs_random_generate(key->random0, key->random0_size); - } - - srs_random_generate(key->key, sizeof(key->key)); - - key->random1_size = 764 - offset - 128 - 4; - if (key->random1_size > 0) { - key->random1 = new char[key->random1_size]; - srs_random_generate(key->random1, key->random1_size); - } -} -// parse key block from c1s1. -// if created, user must free it by srs_key_block_free -// @c1s1_key_bytes the key start bytes, maybe c1s1 or c1s1+764 -int srs_key_block_parse(key_block* key, char* c1s1_key_bytes) -{ - int ret = ERROR_SUCCESS; - - char* pp = c1s1_key_bytes + 764; - - pp -= sizeof(int32_t); - key->offset = *(int32_t*)pp; - - key->random0 = NULL; - key->random1 = NULL; - - int offset = srs_key_block_get_offset(key); - srs_assert(offset >= 0); - - pp = c1s1_key_bytes; - key->random0_size = offset; - if (key->random0_size > 0) { - key->random0 = new char[key->random0_size]; - memcpy(key->random0, pp, key->random0_size); - } - pp += key->random0_size; - - memcpy(key->key, pp, sizeof(key->key)); - pp += sizeof(key->key); - - key->random1_size = 764 - offset - 128 - 4; - if (key->random1_size > 0) { - key->random1 = new char[key->random1_size]; - memcpy(key->random1, pp, key->random1_size); - } - - return ret; -} -// free the block data create by -// srs_key_block_init or srs_key_block_parse -void srs_key_block_free(key_block* key) -{ - if (key->random0) { - srs_freepa(key->random0); - } - if (key->random1) { - srs_freepa(key->random1); - } -} - -/** -* 764bytes digest结构 -* offset: 4bytes -* random-data: (offset)bytes -* digest-data: 32bytes -* random-data: (764-4-offset-32)bytes -*/ -struct digest_block -{ - // 4bytes - int32_t offset; - - // (offset)bytes - char* random0; - int random0_size; - - // 32bytes - char digest[32]; - - // (764-4-offset-32)bytes - char* random1; - int random1_size; -}; -// calc the offset of digest, -// the key->offset cannot be used as the offset of digest. -int srs_digest_block_get_offset(digest_block* digest) -{ - int max_offset_size = 764 - 32 - 4; - - int offset = 0; - u_int8_t* pp = (u_int8_t*)&digest->offset; - offset += *pp++; - offset += *pp++; - offset += *pp++; - offset += *pp++; - - return offset % max_offset_size; -} -// create new digest block data. -// if created, user must free it by srs_digest_block_free -void srs_digest_block_init(digest_block* digest) -{ - digest->offset = (int32_t)rand(); - digest->random0 = NULL; - digest->random1 = NULL; - - int offset = srs_digest_block_get_offset(digest); - srs_assert(offset >= 0); - - digest->random0_size = offset; - if (digest->random0_size > 0) { - digest->random0 = new char[digest->random0_size]; - srs_random_generate(digest->random0, digest->random0_size); - } - - srs_random_generate(digest->digest, sizeof(digest->digest)); - - digest->random1_size = 764 - 4 - offset - 32; - if (digest->random1_size > 0) { - digest->random1 = new char[digest->random1_size]; - srs_random_generate(digest->random1, digest->random1_size); - } -} -// parse digest block from c1s1. -// if created, user must free it by srs_digest_block_free -// @c1s1_digest_bytes the digest start bytes, maybe c1s1 or c1s1+764 -int srs_digest_block_parse(digest_block* digest, char* c1s1_digest_bytes) -{ - int ret = ERROR_SUCCESS; - - char* pp = c1s1_digest_bytes; - - digest->offset = *(int32_t*)pp; - pp += sizeof(int32_t); - - digest->random0 = NULL; - digest->random1 = NULL; - - int offset = srs_digest_block_get_offset(digest); - srs_assert(offset >= 0); - - digest->random0_size = offset; - if (digest->random0_size > 0) { - digest->random0 = new char[digest->random0_size]; - memcpy(digest->random0, pp, digest->random0_size); - } - pp += digest->random0_size; - - memcpy(digest->digest, pp, sizeof(digest->digest)); - pp += sizeof(digest->digest); - - digest->random1_size = 764 - 4 - offset - 32; - if (digest->random1_size > 0) { - digest->random1 = new char[digest->random1_size]; - memcpy(digest->random1, pp, digest->random1_size); - } - - return ret; -} -// free the block data create by -// srs_digest_block_init or srs_digest_block_parse -void srs_digest_block_free(digest_block* digest) -{ - if (digest->random0) { - srs_freepa(digest->random0); - } - if (digest->random1) { - srs_freepa(digest->random1); - } -} - -/** -* the schema type. -*/ -enum srs_schema_type { - srs_schema0 = 0, // key-digest sequence - srs_schema1 = 1, // digest-key sequence - srs_schema_invalid = 2, -}; - -void __srs_time_copy_to(char*& pp, int32_t time) -{ - // 4bytes time - *(int32_t*)pp = time; - pp += 4; -} -void __srs_version_copy_to(char*& pp, int32_t version) -{ - // 4bytes version - *(int32_t*)pp = version; - pp += 4; -} -void __srs_key_copy_to(char*& pp, key_block* key) -{ - // 764bytes key block - if (key->random0_size > 0) { - memcpy(pp, key->random0, key->random0_size); - } - pp += key->random0_size; - - memcpy(pp, key->key, sizeof(key->key)); - pp += sizeof(key->key); - - if (key->random1_size > 0) { - memcpy(pp, key->random1, key->random1_size); - } - pp += key->random1_size; - - *(int32_t*)pp = key->offset; - pp += 4; -} -void __srs_digest_copy_to(char*& pp, digest_block* digest, bool with_digest) -{ - // 732bytes digest block without the 32bytes digest-data - // nbytes digest block part1 - *(int32_t*)pp = digest->offset; - pp += 4; - - if (digest->random0_size > 0) { - memcpy(pp, digest->random0, digest->random0_size); - } - pp += digest->random0_size; - - // digest - if (with_digest) { - memcpy(pp, digest->digest, 32); - pp += 32; - } - - // nbytes digest block part2 - if (digest->random1_size > 0) { - memcpy(pp, digest->random1, digest->random1_size); - } - pp += digest->random1_size; -} - -/** -* copy whole c1s1 to bytes. -*/ -void srs_schema0_copy_to(char* bytes, bool with_digest, - int32_t time, int32_t version, key_block* key, digest_block* digest) -{ - char* pp = bytes; - - __srs_time_copy_to(pp, time); - __srs_version_copy_to(pp, version); - __srs_key_copy_to(pp, key); - __srs_digest_copy_to(pp, digest, with_digest); - - if (with_digest) { - srs_assert(pp - bytes == 1536); - } else { - srs_assert(pp - bytes == 1536 - 32); - } -} -void srs_schema1_copy_to(char* bytes, bool with_digest, - int32_t time, int32_t version, digest_block* digest, key_block* key) -{ - char* pp = bytes; - - __srs_time_copy_to(pp, time); - __srs_version_copy_to(pp, version); - __srs_digest_copy_to(pp, digest, with_digest); - __srs_key_copy_to(pp, key); - - if (with_digest) { - srs_assert(pp - bytes == 1536); - } else { - srs_assert(pp - bytes == 1536 - 32); - } -} -/** -* c1s1 is splited by digest: -* c1s1-part1: n bytes (time, version, key and digest-part1). -* digest-data: 32bytes -* c1s1-part2: (1536-n-32)bytes (digest-part2) -*/ -char* srs_bytes_join_schema0(int32_t time, int32_t version, key_block* key, digest_block* digest) -{ - char* bytes = new char[1536 -32]; - - srs_schema0_copy_to(bytes, false, time, version, key, digest); - - return bytes; -} -/** -* c1s1 is splited by digest: -* c1s1-part1: n bytes (time, version and digest-part1). -* digest-data: 32bytes -* c1s1-part2: (1536-n-32)bytes (digest-part2 and key) -*/ -char* srs_bytes_join_schema1(int32_t time, int32_t version, digest_block* digest, key_block* key) -{ - char* bytes = new char[1536 -32]; - - srs_schema1_copy_to(bytes, false, time, version, digest, key); - - return bytes; -} - -/** -* compare the memory in bytes. -*/ -bool srs_bytes_equals(void* pa, void* pb, int size){ - u_int8_t* a = (u_int8_t*)pa; - u_int8_t* b = (u_int8_t*)pb; - - for(int i = 0; i < size; i++){ - if(a[i] != b[i]){ - return false; - } - } - - return true; -} - -/** -* c1s1 schema0 -* time: 4bytes -* version: 4bytes -* key: 764bytes -* digest: 764bytes -* c1s1 schema1 -* time: 4bytes -* version: 4bytes -* digest: 764bytes -* key: 764bytes -*/ -struct c1s1 -{ - union block { - key_block key; - digest_block digest; - }; - - // 4bytes - int32_t time; - // 4bytes - int32_t version; - // 764bytes - // if schema0, use key - // if schema1, use digest - block block0; - // 764bytes - // if schema0, use digest - // if schema1, use key - block block1; - - // the logic schema - srs_schema_type schema; - - c1s1(); - virtual ~c1s1(); - /** - * get the digest key. - */ - virtual char* get_digest(); - /** - * copy to bytes. - */ - virtual void dump(char* _c1s1); - - /** - * client: create and sign c1 by schema. - * sign the c1, generate the digest. - * calc_c1_digest(c1, schema) { - * get c1s1-joined from c1 by specified schema - * digest-data = HMACsha256(c1s1-joined, FPKey, 30) - * return digest-data; - * } - * random fill 1536bytes c1 // also fill the c1-128bytes-key - * time = time() // c1[0-3] - * version = [0x80, 0x00, 0x07, 0x02] // c1[4-7] - * schema = choose schema0 or schema1 - * digest-data = calc_c1_digest(c1, schema) - * copy digest-data to c1 - */ - virtual int c1_create(srs_schema_type _schema); - /** - * server: parse the c1s1, discovery the key and digest by schema. - * use the c1_validate_digest() to valid the digest of c1. - */ - virtual int c1_parse(char* _c1s1, srs_schema_type _schema); - /** - * server: validate the parsed schema and c1s1 - */ - virtual int c1_validate_digest(bool& is_valid); - /** - * server: create and sign the s1 from c1. - */ - virtual int s1_create(c1s1* c1); -private: - virtual int calc_s1_digest(char*& digest); - virtual int calc_c1_digest(char*& digest); - virtual void destroy_blocks(); -}; - -/** -* the c2s2 complex handshake structure. -* random-data: 1504bytes -* digest-data: 32bytes -*/ -struct c2s2 -{ - char random[1504]; - char digest[32]; - - c2s2(); - virtual ~c2s2(); - - /** - * copy to bytes. - */ - virtual void dump(char* _c2s2); - - /** - * create c2. - * random fill c2s2 1536 bytes - * - * // client generate C2, or server valid C2 - * temp-key = HMACsha256(s1-digest, FPKey, 62) - * c2-digest-data = HMACsha256(c2-random-data, temp-key, 32) - */ - virtual int c2_create(c1s1* s1); - - /** - * create s2. - * random fill c2s2 1536 bytes - * - * // server generate S2, or client valid S2 - * temp-key = HMACsha256(c1-digest, FMSKey, 68) - * s2-digest-data = HMACsha256(s2-random-data, temp-key, 32) - */ - virtual int s2_create(c1s1* c1); -}; - -c2s2::c2s2() -{ - srs_random_generate(random, 1504); - srs_random_generate(digest, 32); -} - -c2s2::~c2s2() -{ -} - -void c2s2::dump(char* _c2s2) -{ - memcpy(_c2s2, random, 1504); - memcpy(_c2s2 + 1504, digest, 32); -} - -int c2s2::c2_create(c1s1* s1) -{ - int ret = ERROR_SUCCESS; - - char temp_key[OpensslHashSize]; - if ((ret = openssl_HMACsha256(s1->get_digest(), 32, SrsGenuineFPKey, 62, temp_key)) != ERROR_SUCCESS) { - srs_error("create c2 temp key failed. ret=%d", ret); - return ret; - } - srs_verbose("generate c2 temp key success."); - - char _digest[OpensslHashSize]; - if ((ret = openssl_HMACsha256(random, 1504, temp_key, 32, _digest)) != ERROR_SUCCESS) { - srs_error("create c2 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("generate c2 digest success."); - - memcpy(digest, _digest, 32); - - return ret; -} - -int c2s2::s2_create(c1s1* c1) -{ - int ret = ERROR_SUCCESS; - - char temp_key[OpensslHashSize]; - if ((ret = openssl_HMACsha256(c1->get_digest(), 32, SrsGenuineFMSKey, 68, temp_key)) != ERROR_SUCCESS) { - srs_error("create s2 temp key failed. ret=%d", ret); - return ret; - } - srs_verbose("generate s2 temp key success."); - - char _digest[OpensslHashSize]; - if ((ret = openssl_HMACsha256(random, 1504, temp_key, 32, _digest)) != ERROR_SUCCESS) { - srs_error("create s2 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("generate s2 digest success."); - - memcpy(digest, _digest, 32); - - return ret; -} - -c1s1::c1s1() -{ - schema = srs_schema_invalid; -} -c1s1::~c1s1() -{ - destroy_blocks(); -} - -char* c1s1::get_digest() -{ - srs_assert(schema != srs_schema_invalid); - - if (schema == srs_schema0) { - return block1.digest.digest; - } else { - return block0.digest.digest; - } -} - -void c1s1::dump(char* _c1s1) -{ - srs_assert(schema != srs_schema_invalid); - - if (schema == srs_schema0) { - srs_schema0_copy_to(_c1s1, true, time, version, &block0.key, &block1.digest); - } else { - srs_schema1_copy_to(_c1s1, true, time, version, &block0.digest, &block1.key); - } -} - -int c1s1::c1_create(srs_schema_type _schema) -{ - int ret = ERROR_SUCCESS; - - if (_schema == srs_schema_invalid) { - ret = ERROR_RTMP_CH_SCHEMA; - srs_error("create c1 failed. invalid schema=%d, ret=%d", _schema, ret); - return ret; - } - - destroy_blocks(); - - time = ::time(NULL); - version = 0x02070080; // client c1 version - - if (_schema == srs_schema0) { - srs_key_block_init(&block0.key); - srs_digest_block_init(&block1.digest); - } else { - srs_digest_block_init(&block0.digest); - srs_key_block_init(&block1.key); - } - - schema = _schema; - - char* digest = NULL; - - if ((ret = calc_c1_digest(digest)) != ERROR_SUCCESS) { - srs_error("sign c1 error, failed to calc digest. ret=%d", ret); - return ret; - } - - srs_assert(digest != NULL); - SrsAutoFree(char, digest, true); - - if (schema == srs_schema0) { - memcpy(block1.digest.digest, digest, 32); - } else { - memcpy(block0.digest.digest, digest, 32); - } - - return ret; -} - -int c1s1::c1_parse(char* _c1s1, srs_schema_type _schema) -{ - int ret = ERROR_SUCCESS; - - if (_schema == srs_schema_invalid) { - ret = ERROR_RTMP_CH_SCHEMA; - srs_error("parse c1 failed. invalid schema=%d, ret=%d", _schema, ret); - return ret; - } - - destroy_blocks(); - - time = *(int32_t*)_c1s1; - version = *(int32_t*)(_c1s1 + 4); // client c1 version - - if (_schema == srs_schema0) { - if ((ret = srs_key_block_parse(&block0.key, _c1s1 + 8)) != ERROR_SUCCESS) { - srs_error("parse the c1 key failed. ret=%d", ret); - return ret; - } - if ((ret = srs_digest_block_parse(&block1.digest, _c1s1 + 8 + 764)) != ERROR_SUCCESS) { - srs_error("parse the c1 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("parse c1 key-digest success"); - } else if (_schema == srs_schema1) { - if ((ret = srs_digest_block_parse(&block0.digest, _c1s1 + 8)) != ERROR_SUCCESS) { - srs_error("parse the c1 key failed. ret=%d", ret); - return ret; - } - if ((ret = srs_key_block_parse(&block1.key, _c1s1 + 8 + 764)) != ERROR_SUCCESS) { - srs_error("parse the c1 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("parse c1 digest-key success"); - } else { - ret = ERROR_RTMP_CH_SCHEMA; - srs_error("parse c1 failed. invalid schema=%d, ret=%d", _schema, ret); - return ret; - } - - schema = _schema; - - return ret; -} - -int c1s1::c1_validate_digest(bool& is_valid) -{ - int ret = ERROR_SUCCESS; - - char* c1_digest = NULL; - - if ((ret = calc_c1_digest(c1_digest)) != ERROR_SUCCESS) { - srs_error("validate c1 error, failed to calc digest. ret=%d", ret); - return ret; - } - - srs_assert(c1_digest != NULL); - SrsAutoFree(char, c1_digest, true); - - if (schema == srs_schema0) { - is_valid = srs_bytes_equals(block1.digest.digest, c1_digest, 32); - } else { - is_valid = srs_bytes_equals(block0.digest.digest, c1_digest, 32); - } - - return ret; -} - -int c1s1::s1_create(c1s1* c1) -{ - int ret = ERROR_SUCCESS; - - if (c1->schema == srs_schema_invalid) { - ret = ERROR_RTMP_CH_SCHEMA; - srs_error("create s1 failed. invalid schema=%d, ret=%d", c1->schema, ret); - return ret; - } - - destroy_blocks(); - schema = c1->schema; - - time = ::time(NULL); - version = 0x01000504; // server s1 version - - if (schema == srs_schema0) { - srs_key_block_init(&block0.key); - srs_digest_block_init(&block1.digest); - } else { - srs_digest_block_init(&block0.digest); - srs_key_block_init(&block1.key); - } - - if (schema == srs_schema0) { - if ((ret = openssl_generate_key(c1->block0.key.key, block0.key.key, 128)) != ERROR_SUCCESS) { - srs_error("calc s1 key failed. ret=%d", ret); - return ret; - } - } else { - if ((ret = openssl_generate_key(c1->block1.key.key, block1.key.key, 128)) != ERROR_SUCCESS) { - srs_error("calc s1 key failed. ret=%d", ret); - return ret; - } - } - srs_verbose("calc s1 key success."); - - char* s1_digest = NULL; - if ((ret = calc_s1_digest(s1_digest)) != ERROR_SUCCESS) { - srs_error("calc s1 digest failed. ret=%d", ret); - return ret; - } - srs_verbose("calc s1 digest success."); - - srs_assert(s1_digest != NULL); - SrsAutoFree(char, s1_digest, true); - - if (schema == srs_schema0) { - memcpy(block1.digest.digest, s1_digest, 32); - } else { - memcpy(block0.digest.digest, s1_digest, 32); - } - srs_verbose("copy s1 key success."); - - return ret; -} - -int c1s1::calc_s1_digest(char*& digest) -{ - int ret = ERROR_SUCCESS; - - srs_assert(schema == srs_schema0 || schema == srs_schema1); - - char* c1s1_joined_bytes = NULL; - - if (schema == srs_schema0) { - c1s1_joined_bytes = srs_bytes_join_schema0(time, version, &block0.key, &block1.digest); - } else { - c1s1_joined_bytes = srs_bytes_join_schema1(time, version, &block0.digest, &block1.key); - } - - srs_assert(c1s1_joined_bytes != NULL); - SrsAutoFree(char, c1s1_joined_bytes, true); - - digest = new char[OpensslHashSize]; - if ((ret = openssl_HMACsha256(c1s1_joined_bytes, 1536 - 32, SrsGenuineFMSKey, 36, digest)) != ERROR_SUCCESS) { - srs_error("calc digest for s1 failed. ret=%d", ret); - return ret; - } - srs_verbose("digest calculated for s1"); - - return ret; -} - -int c1s1::calc_c1_digest(char*& digest) -{ - int ret = ERROR_SUCCESS; - - srs_assert(schema == srs_schema0 || schema == srs_schema1); - - char* c1s1_joined_bytes = NULL; - - if (schema == srs_schema0) { - c1s1_joined_bytes = srs_bytes_join_schema0(time, version, &block0.key, &block1.digest); - } else { - c1s1_joined_bytes = srs_bytes_join_schema1(time, version, &block0.digest, &block1.key); - } - - srs_assert(c1s1_joined_bytes != NULL); - SrsAutoFree(char, c1s1_joined_bytes, true); - - digest = new char[OpensslHashSize]; - if ((ret = openssl_HMACsha256(c1s1_joined_bytes, 1536 - 32, SrsGenuineFPKey, 30, digest)) != ERROR_SUCCESS) { - srs_error("calc digest for c1 failed. ret=%d", ret); - return ret; - } - srs_verbose("digest calculated for c1"); - - return ret; -} - -void c1s1::destroy_blocks() -{ - if (schema == srs_schema_invalid) { - return; - } - - if (schema == srs_schema0) { - srs_key_block_free(&block0.key); - srs_digest_block_free(&block1.digest); - } else { - srs_digest_block_free(&block0.digest); - srs_key_block_free(&block1.key); - } -} - -SrsSimpleHandshake::SrsSimpleHandshake() -{ -} - -SrsSimpleHandshake::~SrsSimpleHandshake() -{ -} - -int SrsSimpleHandshake::handshake(SrsSocket& skt, SrsComplexHandshake& complex_hs) -{ - int ret = ERROR_SUCCESS; - - ssize_t nsize; - - char* c0c1 = new char[1537]; - SrsAutoFree(char, c0c1, true); - if ((ret = skt.read_fully(c0c1, 1537, &nsize)) != ERROR_SUCCESS) { - srs_warn("read c0c1 failed. ret=%d", ret); - return ret; - } - srs_verbose("read c0c1 success."); - - // plain text required. - if (c0c1[0] != 0x03) { - ret = ERROR_RTMP_PLAIN_REQUIRED; - srs_warn("only support rtmp plain text. ret=%d", ret); - return ret; - } - srs_verbose("check c0 success, required plain text."); - - // try complex handshake - ret = complex_hs.handshake(skt, c0c1 + 1); - if (ret == ERROR_SUCCESS) { - srs_trace("complex handshake success."); - return ret; - } - if (ret != ERROR_RTMP_TRY_SIMPLE_HS) { - srs_error("complex handshake failed. ret=%d", ret); - return ret; - } - srs_info("rollback complex to simple handshake. ret=%d", ret); - - char* s0s1s2 = new char[3073]; - srs_random_generate(s0s1s2, 3073); - SrsAutoFree(char, s0s1s2, true); - // plain text required. - s0s1s2[0] = 0x03; - if ((ret = skt.write(s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) { - srs_warn("simple handshake send s0s1s2 failed. ret=%d", ret); - return ret; - } - srs_verbose("simple handshake send s0s1s2 success."); - - char* c2 = new char[1536]; - SrsAutoFree(char, c2, true); - if ((ret = skt.read_fully(c2, 1536, &nsize)) != ERROR_SUCCESS) { - srs_warn("simple handshake read c2 failed. ret=%d", ret); - return ret; - } - srs_verbose("simple handshake read c2 success."); - - srs_trace("simple handshake success."); - - return ret; -} - -SrsComplexHandshake::SrsComplexHandshake() -{ -} - -SrsComplexHandshake::~SrsComplexHandshake() -{ -} - -int SrsComplexHandshake::handshake(SrsSocket& skt, char* _c1) -{ - int ret = ERROR_SUCCESS; - - ssize_t nsize; - - static bool _random_initialized = false; - if (!_random_initialized) { - srand(0); - _random_initialized = true; - srs_trace("srand initialized the random."); - } - - // decode c1 - c1s1 c1; - // try schema0. - if ((ret = c1.c1_parse(_c1, srs_schema0)) != ERROR_SUCCESS) { - srs_error("parse c1 schema%d error. ret=%d", srs_schema0, ret); - return ret; - } - // try schema1 - bool is_valid = false; - if ((ret = c1.c1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) { - if ((ret = c1.c1_parse(_c1, srs_schema1)) != ERROR_SUCCESS) { - srs_error("parse c1 schema%d error. ret=%d", srs_schema1, ret); - return ret; - } - - if ((ret = c1.c1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) { - ret = ERROR_RTMP_TRY_SIMPLE_HS; - srs_info("all schema valid failed, try simple handshake. ret=%d", ret); - return ret; - } - } - srs_verbose("decode c1 success."); - - // encode s1 - c1s1 s1; - if ((ret = s1.s1_create(&c1)) != ERROR_SUCCESS) { - srs_error("create s1 from c1 failed. ret=%d", ret); - return ret; - } - srs_verbose("create s1 from c1 success."); - - c2s2 s2; - if ((ret = s2.s2_create(&c1)) != ERROR_SUCCESS) { - srs_error("create s2 from c1 failed. ret=%d", ret); - return ret; - } - srs_verbose("create s2 from c1 success."); - - // sendout s0s1s2 - char* s0s1s2 = new char[3073]; - SrsAutoFree(char, s0s1s2, true); - // plain text required. - s0s1s2[0] = 0x03; - s1.dump(s0s1s2 + 1); - s2.dump(s0s1s2 + 1537); - if ((ret = skt.write(s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) { - srs_warn("complex handshake send s0s1s2 failed. ret=%d", ret); - return ret; - } - srs_verbose("complex handshake send s0s1s2 success."); - - // recv c2 - char* c2 = new char[1536]; - SrsAutoFree(char, c2, true); - if ((ret = skt.read_fully(c2, 1536, &nsize)) != ERROR_SUCCESS) { - srs_warn("complex handshake read c2 failed. ret=%d", ret); - return ret; - } - srs_verbose("complex handshake read c2 success."); - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include + +#include +#include +#include +#include + +void srs_random_generate(char* bytes, int size) +{ + static char cdata[] = { + 0x73, 0x69, 0x6d, 0x70, 0x6c, 0x65, 0x2d, 0x72, 0x74, 0x6d, 0x70, 0x2d, 0x73, 0x65, + 0x72, 0x76, 0x65, 0x72, 0x2d, 0x77, 0x69, 0x6e, 0x6c, 0x69, 0x6e, 0x2d, 0x77, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x40, 0x31, 0x32, 0x36, + 0x2e, 0x63, 0x6f, 0x6d + }; + for (int i = 0; i < size; i++) { + bytes[i] = cdata[rand() % (sizeof(cdata) - 1)]; + } +} + +// 68bytes FMS key which is used to sign the sever packet. +u_int8_t SrsGenuineFMSKey[] = { + 0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20, + 0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c, + 0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69, + 0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72, + 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001 + 0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8, + 0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57, + 0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab, + 0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae +}; // 68 + +// 62bytes FP key which is used to sign the client packet. +u_int8_t SrsGenuineFPKey[] = { + 0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20, + 0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C, + 0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79, + 0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001 + 0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8, + 0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57, + 0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB, + 0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE +}; // 62 + +#include +#include +int openssl_HMACsha256(const void* data, int data_size, const void* key, int key_size, void* digest) { + HMAC_CTX ctx; + + HMAC_CTX_init(&ctx); + HMAC_Init_ex(&ctx, (unsigned char*) key, key_size, EVP_sha256(), NULL); + HMAC_Update(&ctx, (unsigned char *) data, data_size); + + unsigned int digest_size; + HMAC_Final(&ctx, (unsigned char *) digest, &digest_size); + + HMAC_CTX_cleanup(&ctx); + + if (digest_size != 32) { + return ERROR_OpenSslSha256DigestSize; + } + + return ERROR_SUCCESS; +} + +#include +#define RFC2409_PRIME_1024 \ + "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \ + "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \ + "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \ + "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \ + "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \ + "FFFFFFFFFFFFFFFF" +int __openssl_generate_key( + u_int8_t*& _private_key, u_int8_t*& _public_key, int32_t& size, + DH*& pdh, int32_t& bits_count, u_int8_t*& shared_key, int32_t& shared_key_length, BIGNUM*& peer_public_key +){ + int ret = ERROR_SUCCESS; + + //1. Create the DH + if ((pdh = DH_new()) == NULL) { + ret = ERROR_OpenSslCreateDH; + return ret; + } + + //2. Create his internal p and g + if ((pdh->p = BN_new()) == NULL) { + ret = ERROR_OpenSslCreateP; + return ret; + } + if ((pdh->g = BN_new()) == NULL) { + ret = ERROR_OpenSslCreateG; + return ret; + } + + //3. initialize p, g and key length + if (BN_hex2bn(&pdh->p, RFC2409_PRIME_1024) == 0) { + ret = ERROR_OpenSslParseP1024; + return ret; + } + if (BN_set_word(pdh->g, 2) != 1) { + ret = ERROR_OpenSslSetG; + return ret; + } + + //4. Set the key length + pdh->length = bits_count; + + //5. Generate private and public key + if (DH_generate_key(pdh) != 1) { + ret = ERROR_OpenSslGenerateDHKeys; + return ret; + } + + // CreateSharedKey + if (pdh == NULL) { + ret = ERROR_OpenSslGenerateDHKeys; + return ret; + } + + if (shared_key_length != 0 || shared_key != NULL) { + ret = ERROR_OpenSslShareKeyComputed; + return ret; + } + + shared_key_length = DH_size(pdh); + if (shared_key_length <= 0 || shared_key_length > 1024) { + ret = ERROR_OpenSslGetSharedKeySize; + return ret; + } + shared_key = new u_int8_t[shared_key_length]; + memset(shared_key, 0, shared_key_length); + + peer_public_key = BN_bin2bn(_private_key, size, 0); + if (peer_public_key == NULL) { + ret = ERROR_OpenSslGetPeerPublicKey; + return ret; + } + + if (DH_compute_key(shared_key, peer_public_key, pdh) == -1) { + ret = ERROR_OpenSslComputeSharedKey; + return ret; + } + + // CopyPublicKey + if (pdh == NULL) { + ret = ERROR_OpenSslComputeSharedKey; + return ret; + } + + int32_t keySize = BN_num_bytes(pdh->pub_key); + if ((keySize <= 0) || (size <= 0) || (keySize > size)) { + //("CopyPublicKey failed due to either invalid DH state or invalid call"); return ret; + ret = ERROR_OpenSslInvalidDHState; + return ret; + } + + if (BN_bn2bin(pdh->pub_key, _public_key) != keySize) { + //("Unable to copy key"); return ret; + ret = ERROR_OpenSslCopyKey; + return ret; + } + + return ret; +} +int openssl_generate_key(char* _private_key, char* _public_key, int32_t size) +{ + int ret = ERROR_SUCCESS; + + // Initialize + DH* pdh = NULL; + int32_t bits_count = 1024; + u_int8_t* shared_key = NULL; + int32_t shared_key_length = 0; + BIGNUM* peer_public_key = NULL; + + ret = __openssl_generate_key( + (u_int8_t*&)_private_key, (u_int8_t*&)_public_key, size, + pdh, bits_count, shared_key, shared_key_length, peer_public_key + ); + + if (pdh != NULL) { + if (pdh->p != NULL) { + BN_free(pdh->p); + pdh->p = NULL; + } + if (pdh->g != NULL) { + BN_free(pdh->g); + pdh->g = NULL; + } + DH_free(pdh); + pdh = NULL; + } + + if (shared_key != NULL) { + delete[] shared_key; + shared_key = NULL; + } + + if (peer_public_key != NULL) { + BN_free(peer_public_key); + peer_public_key = NULL; + } + + return ret; +} + +// the digest key generate size. +#define OpensslHashSize 512 + +/** +* 764bytes key结构 +* random-data: (offset)bytes +* key-data: 128bytes +* random-data: (764-offset-128-4)bytes +* offset: 4bytes +*/ +struct key_block +{ + // (offset)bytes + char* random0; + int random0_size; + + // 128bytes + char key[128]; + + // (764-offset-128-4)bytes + char* random1; + int random1_size; + + // 4bytes + int32_t offset; +}; +// calc the offset of key, +// the key->offset cannot be used as the offset of key. +int srs_key_block_get_offset(key_block* key) +{ + int max_offset_size = 764 - 128 - 4; + + int offset = 0; + u_int8_t* pp = (u_int8_t*)&key->offset; + offset += *pp++; + offset += *pp++; + offset += *pp++; + offset += *pp++; + + return offset % max_offset_size; +} +// create new key block data. +// if created, user must free it by srs_key_block_free +void srs_key_block_init(key_block* key) +{ + key->offset = (int32_t)rand(); + key->random0 = NULL; + key->random1 = NULL; + + int offset = srs_key_block_get_offset(key); + srs_assert(offset >= 0); + + key->random0_size = offset; + if (key->random0_size > 0) { + key->random0 = new char[key->random0_size]; + srs_random_generate(key->random0, key->random0_size); + } + + srs_random_generate(key->key, sizeof(key->key)); + + key->random1_size = 764 - offset - 128 - 4; + if (key->random1_size > 0) { + key->random1 = new char[key->random1_size]; + srs_random_generate(key->random1, key->random1_size); + } +} +// parse key block from c1s1. +// if created, user must free it by srs_key_block_free +// @c1s1_key_bytes the key start bytes, maybe c1s1 or c1s1+764 +int srs_key_block_parse(key_block* key, char* c1s1_key_bytes) +{ + int ret = ERROR_SUCCESS; + + char* pp = c1s1_key_bytes + 764; + + pp -= sizeof(int32_t); + key->offset = *(int32_t*)pp; + + key->random0 = NULL; + key->random1 = NULL; + + int offset = srs_key_block_get_offset(key); + srs_assert(offset >= 0); + + pp = c1s1_key_bytes; + key->random0_size = offset; + if (key->random0_size > 0) { + key->random0 = new char[key->random0_size]; + memcpy(key->random0, pp, key->random0_size); + } + pp += key->random0_size; + + memcpy(key->key, pp, sizeof(key->key)); + pp += sizeof(key->key); + + key->random1_size = 764 - offset - 128 - 4; + if (key->random1_size > 0) { + key->random1 = new char[key->random1_size]; + memcpy(key->random1, pp, key->random1_size); + } + + return ret; +} +// free the block data create by +// srs_key_block_init or srs_key_block_parse +void srs_key_block_free(key_block* key) +{ + if (key->random0) { + srs_freepa(key->random0); + } + if (key->random1) { + srs_freepa(key->random1); + } +} + +/** +* 764bytes digest结构 +* offset: 4bytes +* random-data: (offset)bytes +* digest-data: 32bytes +* random-data: (764-4-offset-32)bytes +*/ +struct digest_block +{ + // 4bytes + int32_t offset; + + // (offset)bytes + char* random0; + int random0_size; + + // 32bytes + char digest[32]; + + // (764-4-offset-32)bytes + char* random1; + int random1_size; +}; +// calc the offset of digest, +// the key->offset cannot be used as the offset of digest. +int srs_digest_block_get_offset(digest_block* digest) +{ + int max_offset_size = 764 - 32 - 4; + + int offset = 0; + u_int8_t* pp = (u_int8_t*)&digest->offset; + offset += *pp++; + offset += *pp++; + offset += *pp++; + offset += *pp++; + + return offset % max_offset_size; +} +// create new digest block data. +// if created, user must free it by srs_digest_block_free +void srs_digest_block_init(digest_block* digest) +{ + digest->offset = (int32_t)rand(); + digest->random0 = NULL; + digest->random1 = NULL; + + int offset = srs_digest_block_get_offset(digest); + srs_assert(offset >= 0); + + digest->random0_size = offset; + if (digest->random0_size > 0) { + digest->random0 = new char[digest->random0_size]; + srs_random_generate(digest->random0, digest->random0_size); + } + + srs_random_generate(digest->digest, sizeof(digest->digest)); + + digest->random1_size = 764 - 4 - offset - 32; + if (digest->random1_size > 0) { + digest->random1 = new char[digest->random1_size]; + srs_random_generate(digest->random1, digest->random1_size); + } +} +// parse digest block from c1s1. +// if created, user must free it by srs_digest_block_free +// @c1s1_digest_bytes the digest start bytes, maybe c1s1 or c1s1+764 +int srs_digest_block_parse(digest_block* digest, char* c1s1_digest_bytes) +{ + int ret = ERROR_SUCCESS; + + char* pp = c1s1_digest_bytes; + + digest->offset = *(int32_t*)pp; + pp += sizeof(int32_t); + + digest->random0 = NULL; + digest->random1 = NULL; + + int offset = srs_digest_block_get_offset(digest); + srs_assert(offset >= 0); + + digest->random0_size = offset; + if (digest->random0_size > 0) { + digest->random0 = new char[digest->random0_size]; + memcpy(digest->random0, pp, digest->random0_size); + } + pp += digest->random0_size; + + memcpy(digest->digest, pp, sizeof(digest->digest)); + pp += sizeof(digest->digest); + + digest->random1_size = 764 - 4 - offset - 32; + if (digest->random1_size > 0) { + digest->random1 = new char[digest->random1_size]; + memcpy(digest->random1, pp, digest->random1_size); + } + + return ret; +} +// free the block data create by +// srs_digest_block_init or srs_digest_block_parse +void srs_digest_block_free(digest_block* digest) +{ + if (digest->random0) { + srs_freepa(digest->random0); + } + if (digest->random1) { + srs_freepa(digest->random1); + } +} + +/** +* the schema type. +*/ +enum srs_schema_type { + srs_schema0 = 0, // key-digest sequence + srs_schema1 = 1, // digest-key sequence + srs_schema_invalid = 2, +}; + +void __srs_time_copy_to(char*& pp, int32_t time) +{ + // 4bytes time + *(int32_t*)pp = time; + pp += 4; +} +void __srs_version_copy_to(char*& pp, int32_t version) +{ + // 4bytes version + *(int32_t*)pp = version; + pp += 4; +} +void __srs_key_copy_to(char*& pp, key_block* key) +{ + // 764bytes key block + if (key->random0_size > 0) { + memcpy(pp, key->random0, key->random0_size); + } + pp += key->random0_size; + + memcpy(pp, key->key, sizeof(key->key)); + pp += sizeof(key->key); + + if (key->random1_size > 0) { + memcpy(pp, key->random1, key->random1_size); + } + pp += key->random1_size; + + *(int32_t*)pp = key->offset; + pp += 4; +} +void __srs_digest_copy_to(char*& pp, digest_block* digest, bool with_digest) +{ + // 732bytes digest block without the 32bytes digest-data + // nbytes digest block part1 + *(int32_t*)pp = digest->offset; + pp += 4; + + if (digest->random0_size > 0) { + memcpy(pp, digest->random0, digest->random0_size); + } + pp += digest->random0_size; + + // digest + if (with_digest) { + memcpy(pp, digest->digest, 32); + pp += 32; + } + + // nbytes digest block part2 + if (digest->random1_size > 0) { + memcpy(pp, digest->random1, digest->random1_size); + } + pp += digest->random1_size; +} + +/** +* copy whole c1s1 to bytes. +*/ +void srs_schema0_copy_to(char* bytes, bool with_digest, + int32_t time, int32_t version, key_block* key, digest_block* digest) +{ + char* pp = bytes; + + __srs_time_copy_to(pp, time); + __srs_version_copy_to(pp, version); + __srs_key_copy_to(pp, key); + __srs_digest_copy_to(pp, digest, with_digest); + + if (with_digest) { + srs_assert(pp - bytes == 1536); + } else { + srs_assert(pp - bytes == 1536 - 32); + } +} +void srs_schema1_copy_to(char* bytes, bool with_digest, + int32_t time, int32_t version, digest_block* digest, key_block* key) +{ + char* pp = bytes; + + __srs_time_copy_to(pp, time); + __srs_version_copy_to(pp, version); + __srs_digest_copy_to(pp, digest, with_digest); + __srs_key_copy_to(pp, key); + + if (with_digest) { + srs_assert(pp - bytes == 1536); + } else { + srs_assert(pp - bytes == 1536 - 32); + } +} +/** +* c1s1 is splited by digest: +* c1s1-part1: n bytes (time, version, key and digest-part1). +* digest-data: 32bytes +* c1s1-part2: (1536-n-32)bytes (digest-part2) +*/ +char* srs_bytes_join_schema0(int32_t time, int32_t version, key_block* key, digest_block* digest) +{ + char* bytes = new char[1536 -32]; + + srs_schema0_copy_to(bytes, false, time, version, key, digest); + + return bytes; +} +/** +* c1s1 is splited by digest: +* c1s1-part1: n bytes (time, version and digest-part1). +* digest-data: 32bytes +* c1s1-part2: (1536-n-32)bytes (digest-part2 and key) +*/ +char* srs_bytes_join_schema1(int32_t time, int32_t version, digest_block* digest, key_block* key) +{ + char* bytes = new char[1536 -32]; + + srs_schema1_copy_to(bytes, false, time, version, digest, key); + + return bytes; +} + +/** +* compare the memory in bytes. +*/ +bool srs_bytes_equals(void* pa, void* pb, int size){ + u_int8_t* a = (u_int8_t*)pa; + u_int8_t* b = (u_int8_t*)pb; + + for(int i = 0; i < size; i++){ + if(a[i] != b[i]){ + return false; + } + } + + return true; +} + +/** +* c1s1 schema0 +* time: 4bytes +* version: 4bytes +* key: 764bytes +* digest: 764bytes +* c1s1 schema1 +* time: 4bytes +* version: 4bytes +* digest: 764bytes +* key: 764bytes +*/ +struct c1s1 +{ + union block { + key_block key; + digest_block digest; + }; + + // 4bytes + int32_t time; + // 4bytes + int32_t version; + // 764bytes + // if schema0, use key + // if schema1, use digest + block block0; + // 764bytes + // if schema0, use digest + // if schema1, use key + block block1; + + // the logic schema + srs_schema_type schema; + + c1s1(); + virtual ~c1s1(); + /** + * get the digest key. + */ + virtual char* get_digest(); + /** + * copy to bytes. + */ + virtual void dump(char* _c1s1); + + /** + * client: create and sign c1 by schema. + * sign the c1, generate the digest. + * calc_c1_digest(c1, schema) { + * get c1s1-joined from c1 by specified schema + * digest-data = HMACsha256(c1s1-joined, FPKey, 30) + * return digest-data; + * } + * random fill 1536bytes c1 // also fill the c1-128bytes-key + * time = time() // c1[0-3] + * version = [0x80, 0x00, 0x07, 0x02] // c1[4-7] + * schema = choose schema0 or schema1 + * digest-data = calc_c1_digest(c1, schema) + * copy digest-data to c1 + */ + virtual int c1_create(srs_schema_type _schema); + /** + * server: parse the c1s1, discovery the key and digest by schema. + * use the c1_validate_digest() to valid the digest of c1. + */ + virtual int c1_parse(char* _c1s1, srs_schema_type _schema); + /** + * server: validate the parsed schema and c1s1 + */ + virtual int c1_validate_digest(bool& is_valid); + /** + * server: create and sign the s1 from c1. + */ + virtual int s1_create(c1s1* c1); +private: + virtual int calc_s1_digest(char*& digest); + virtual int calc_c1_digest(char*& digest); + virtual void destroy_blocks(); +}; + +/** +* the c2s2 complex handshake structure. +* random-data: 1504bytes +* digest-data: 32bytes +*/ +struct c2s2 +{ + char random[1504]; + char digest[32]; + + c2s2(); + virtual ~c2s2(); + + /** + * copy to bytes. + */ + virtual void dump(char* _c2s2); + + /** + * create c2. + * random fill c2s2 1536 bytes + * + * // client generate C2, or server valid C2 + * temp-key = HMACsha256(s1-digest, FPKey, 62) + * c2-digest-data = HMACsha256(c2-random-data, temp-key, 32) + */ + virtual int c2_create(c1s1* s1); + + /** + * create s2. + * random fill c2s2 1536 bytes + * + * // server generate S2, or client valid S2 + * temp-key = HMACsha256(c1-digest, FMSKey, 68) + * s2-digest-data = HMACsha256(s2-random-data, temp-key, 32) + */ + virtual int s2_create(c1s1* c1); +}; + +c2s2::c2s2() +{ + srs_random_generate(random, 1504); + srs_random_generate(digest, 32); +} + +c2s2::~c2s2() +{ +} + +void c2s2::dump(char* _c2s2) +{ + memcpy(_c2s2, random, 1504); + memcpy(_c2s2 + 1504, digest, 32); +} + +int c2s2::c2_create(c1s1* s1) +{ + int ret = ERROR_SUCCESS; + + char temp_key[OpensslHashSize]; + if ((ret = openssl_HMACsha256(s1->get_digest(), 32, SrsGenuineFPKey, 62, temp_key)) != ERROR_SUCCESS) { + srs_error("create c2 temp key failed. ret=%d", ret); + return ret; + } + srs_verbose("generate c2 temp key success."); + + char _digest[OpensslHashSize]; + if ((ret = openssl_HMACsha256(random, 1504, temp_key, 32, _digest)) != ERROR_SUCCESS) { + srs_error("create c2 digest failed. ret=%d", ret); + return ret; + } + srs_verbose("generate c2 digest success."); + + memcpy(digest, _digest, 32); + + return ret; +} + +int c2s2::s2_create(c1s1* c1) +{ + int ret = ERROR_SUCCESS; + + char temp_key[OpensslHashSize]; + if ((ret = openssl_HMACsha256(c1->get_digest(), 32, SrsGenuineFMSKey, 68, temp_key)) != ERROR_SUCCESS) { + srs_error("create s2 temp key failed. ret=%d", ret); + return ret; + } + srs_verbose("generate s2 temp key success."); + + char _digest[OpensslHashSize]; + if ((ret = openssl_HMACsha256(random, 1504, temp_key, 32, _digest)) != ERROR_SUCCESS) { + srs_error("create s2 digest failed. ret=%d", ret); + return ret; + } + srs_verbose("generate s2 digest success."); + + memcpy(digest, _digest, 32); + + return ret; +} + +c1s1::c1s1() +{ + schema = srs_schema_invalid; +} +c1s1::~c1s1() +{ + destroy_blocks(); +} + +char* c1s1::get_digest() +{ + srs_assert(schema != srs_schema_invalid); + + if (schema == srs_schema0) { + return block1.digest.digest; + } else { + return block0.digest.digest; + } +} + +void c1s1::dump(char* _c1s1) +{ + srs_assert(schema != srs_schema_invalid); + + if (schema == srs_schema0) { + srs_schema0_copy_to(_c1s1, true, time, version, &block0.key, &block1.digest); + } else { + srs_schema1_copy_to(_c1s1, true, time, version, &block0.digest, &block1.key); + } +} + +int c1s1::c1_create(srs_schema_type _schema) +{ + int ret = ERROR_SUCCESS; + + if (_schema == srs_schema_invalid) { + ret = ERROR_RTMP_CH_SCHEMA; + srs_error("create c1 failed. invalid schema=%d, ret=%d", _schema, ret); + return ret; + } + + destroy_blocks(); + + time = ::time(NULL); + version = 0x02070080; // client c1 version + + if (_schema == srs_schema0) { + srs_key_block_init(&block0.key); + srs_digest_block_init(&block1.digest); + } else { + srs_digest_block_init(&block0.digest); + srs_key_block_init(&block1.key); + } + + schema = _schema; + + char* digest = NULL; + + if ((ret = calc_c1_digest(digest)) != ERROR_SUCCESS) { + srs_error("sign c1 error, failed to calc digest. ret=%d", ret); + return ret; + } + + srs_assert(digest != NULL); + SrsAutoFree(char, digest, true); + + if (schema == srs_schema0) { + memcpy(block1.digest.digest, digest, 32); + } else { + memcpy(block0.digest.digest, digest, 32); + } + + return ret; +} + +int c1s1::c1_parse(char* _c1s1, srs_schema_type _schema) +{ + int ret = ERROR_SUCCESS; + + if (_schema == srs_schema_invalid) { + ret = ERROR_RTMP_CH_SCHEMA; + srs_error("parse c1 failed. invalid schema=%d, ret=%d", _schema, ret); + return ret; + } + + destroy_blocks(); + + time = *(int32_t*)_c1s1; + version = *(int32_t*)(_c1s1 + 4); // client c1 version + + if (_schema == srs_schema0) { + if ((ret = srs_key_block_parse(&block0.key, _c1s1 + 8)) != ERROR_SUCCESS) { + srs_error("parse the c1 key failed. ret=%d", ret); + return ret; + } + if ((ret = srs_digest_block_parse(&block1.digest, _c1s1 + 8 + 764)) != ERROR_SUCCESS) { + srs_error("parse the c1 digest failed. ret=%d", ret); + return ret; + } + srs_verbose("parse c1 key-digest success"); + } else if (_schema == srs_schema1) { + if ((ret = srs_digest_block_parse(&block0.digest, _c1s1 + 8)) != ERROR_SUCCESS) { + srs_error("parse the c1 key failed. ret=%d", ret); + return ret; + } + if ((ret = srs_key_block_parse(&block1.key, _c1s1 + 8 + 764)) != ERROR_SUCCESS) { + srs_error("parse the c1 digest failed. ret=%d", ret); + return ret; + } + srs_verbose("parse c1 digest-key success"); + } else { + ret = ERROR_RTMP_CH_SCHEMA; + srs_error("parse c1 failed. invalid schema=%d, ret=%d", _schema, ret); + return ret; + } + + schema = _schema; + + return ret; +} + +int c1s1::c1_validate_digest(bool& is_valid) +{ + int ret = ERROR_SUCCESS; + + char* c1_digest = NULL; + + if ((ret = calc_c1_digest(c1_digest)) != ERROR_SUCCESS) { + srs_error("validate c1 error, failed to calc digest. ret=%d", ret); + return ret; + } + + srs_assert(c1_digest != NULL); + SrsAutoFree(char, c1_digest, true); + + if (schema == srs_schema0) { + is_valid = srs_bytes_equals(block1.digest.digest, c1_digest, 32); + } else { + is_valid = srs_bytes_equals(block0.digest.digest, c1_digest, 32); + } + + return ret; +} + +int c1s1::s1_create(c1s1* c1) +{ + int ret = ERROR_SUCCESS; + + if (c1->schema == srs_schema_invalid) { + ret = ERROR_RTMP_CH_SCHEMA; + srs_error("create s1 failed. invalid schema=%d, ret=%d", c1->schema, ret); + return ret; + } + + destroy_blocks(); + schema = c1->schema; + + time = ::time(NULL); + version = 0x01000504; // server s1 version + + if (schema == srs_schema0) { + srs_key_block_init(&block0.key); + srs_digest_block_init(&block1.digest); + } else { + srs_digest_block_init(&block0.digest); + srs_key_block_init(&block1.key); + } + + if (schema == srs_schema0) { + if ((ret = openssl_generate_key(c1->block0.key.key, block0.key.key, 128)) != ERROR_SUCCESS) { + srs_error("calc s1 key failed. ret=%d", ret); + return ret; + } + } else { + if ((ret = openssl_generate_key(c1->block1.key.key, block1.key.key, 128)) != ERROR_SUCCESS) { + srs_error("calc s1 key failed. ret=%d", ret); + return ret; + } + } + srs_verbose("calc s1 key success."); + + char* s1_digest = NULL; + if ((ret = calc_s1_digest(s1_digest)) != ERROR_SUCCESS) { + srs_error("calc s1 digest failed. ret=%d", ret); + return ret; + } + srs_verbose("calc s1 digest success."); + + srs_assert(s1_digest != NULL); + SrsAutoFree(char, s1_digest, true); + + if (schema == srs_schema0) { + memcpy(block1.digest.digest, s1_digest, 32); + } else { + memcpy(block0.digest.digest, s1_digest, 32); + } + srs_verbose("copy s1 key success."); + + return ret; +} + +int c1s1::calc_s1_digest(char*& digest) +{ + int ret = ERROR_SUCCESS; + + srs_assert(schema == srs_schema0 || schema == srs_schema1); + + char* c1s1_joined_bytes = NULL; + + if (schema == srs_schema0) { + c1s1_joined_bytes = srs_bytes_join_schema0(time, version, &block0.key, &block1.digest); + } else { + c1s1_joined_bytes = srs_bytes_join_schema1(time, version, &block0.digest, &block1.key); + } + + srs_assert(c1s1_joined_bytes != NULL); + SrsAutoFree(char, c1s1_joined_bytes, true); + + digest = new char[OpensslHashSize]; + if ((ret = openssl_HMACsha256(c1s1_joined_bytes, 1536 - 32, SrsGenuineFMSKey, 36, digest)) != ERROR_SUCCESS) { + srs_error("calc digest for s1 failed. ret=%d", ret); + return ret; + } + srs_verbose("digest calculated for s1"); + + return ret; +} + +int c1s1::calc_c1_digest(char*& digest) +{ + int ret = ERROR_SUCCESS; + + srs_assert(schema == srs_schema0 || schema == srs_schema1); + + char* c1s1_joined_bytes = NULL; + + if (schema == srs_schema0) { + c1s1_joined_bytes = srs_bytes_join_schema0(time, version, &block0.key, &block1.digest); + } else { + c1s1_joined_bytes = srs_bytes_join_schema1(time, version, &block0.digest, &block1.key); + } + + srs_assert(c1s1_joined_bytes != NULL); + SrsAutoFree(char, c1s1_joined_bytes, true); + + digest = new char[OpensslHashSize]; + if ((ret = openssl_HMACsha256(c1s1_joined_bytes, 1536 - 32, SrsGenuineFPKey, 30, digest)) != ERROR_SUCCESS) { + srs_error("calc digest for c1 failed. ret=%d", ret); + return ret; + } + srs_verbose("digest calculated for c1"); + + return ret; +} + +void c1s1::destroy_blocks() +{ + if (schema == srs_schema_invalid) { + return; + } + + if (schema == srs_schema0) { + srs_key_block_free(&block0.key); + srs_digest_block_free(&block1.digest); + } else { + srs_digest_block_free(&block0.digest); + srs_key_block_free(&block1.key); + } +} + +SrsSimpleHandshake::SrsSimpleHandshake() +{ +} + +SrsSimpleHandshake::~SrsSimpleHandshake() +{ +} + +int SrsSimpleHandshake::handshake(SrsSocket& skt, SrsComplexHandshake& complex_hs) +{ + int ret = ERROR_SUCCESS; + + ssize_t nsize; + + char* c0c1 = new char[1537]; + SrsAutoFree(char, c0c1, true); + if ((ret = skt.read_fully(c0c1, 1537, &nsize)) != ERROR_SUCCESS) { + srs_warn("read c0c1 failed. ret=%d", ret); + return ret; + } + srs_verbose("read c0c1 success."); + + // plain text required. + if (c0c1[0] != 0x03) { + ret = ERROR_RTMP_PLAIN_REQUIRED; + srs_warn("only support rtmp plain text. ret=%d", ret); + return ret; + } + srs_verbose("check c0 success, required plain text."); + + // try complex handshake + ret = complex_hs.handshake(skt, c0c1 + 1); + if (ret == ERROR_SUCCESS) { + srs_trace("complex handshake success."); + return ret; + } + if (ret != ERROR_RTMP_TRY_SIMPLE_HS) { + srs_error("complex handshake failed. ret=%d", ret); + return ret; + } + srs_info("rollback complex to simple handshake. ret=%d", ret); + + char* s0s1s2 = new char[3073]; + srs_random_generate(s0s1s2, 3073); + SrsAutoFree(char, s0s1s2, true); + // plain text required. + s0s1s2[0] = 0x03; + if ((ret = skt.write(s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) { + srs_warn("simple handshake send s0s1s2 failed. ret=%d", ret); + return ret; + } + srs_verbose("simple handshake send s0s1s2 success."); + + char* c2 = new char[1536]; + SrsAutoFree(char, c2, true); + if ((ret = skt.read_fully(c2, 1536, &nsize)) != ERROR_SUCCESS) { + srs_warn("simple handshake read c2 failed. ret=%d", ret); + return ret; + } + srs_verbose("simple handshake read c2 success."); + + srs_trace("simple handshake success."); + + return ret; +} + +SrsComplexHandshake::SrsComplexHandshake() +{ +} + +SrsComplexHandshake::~SrsComplexHandshake() +{ +} + +int SrsComplexHandshake::handshake(SrsSocket& skt, char* _c1) +{ + int ret = ERROR_SUCCESS; + + ssize_t nsize; + + static bool _random_initialized = false; + if (!_random_initialized) { + srand(0); + _random_initialized = true; + srs_trace("srand initialized the random."); + } + + // decode c1 + c1s1 c1; + // try schema0. + if ((ret = c1.c1_parse(_c1, srs_schema0)) != ERROR_SUCCESS) { + srs_error("parse c1 schema%d error. ret=%d", srs_schema0, ret); + return ret; + } + // try schema1 + bool is_valid = false; + if ((ret = c1.c1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) { + if ((ret = c1.c1_parse(_c1, srs_schema1)) != ERROR_SUCCESS) { + srs_error("parse c1 schema%d error. ret=%d", srs_schema1, ret); + return ret; + } + + if ((ret = c1.c1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) { + ret = ERROR_RTMP_TRY_SIMPLE_HS; + srs_info("all schema valid failed, try simple handshake. ret=%d", ret); + return ret; + } + } + srs_verbose("decode c1 success."); + + // encode s1 + c1s1 s1; + if ((ret = s1.s1_create(&c1)) != ERROR_SUCCESS) { + srs_error("create s1 from c1 failed. ret=%d", ret); + return ret; + } + srs_verbose("create s1 from c1 success."); + + c2s2 s2; + if ((ret = s2.s2_create(&c1)) != ERROR_SUCCESS) { + srs_error("create s2 from c1 failed. ret=%d", ret); + return ret; + } + srs_verbose("create s2 from c1 success."); + + // sendout s0s1s2 + char* s0s1s2 = new char[3073]; + SrsAutoFree(char, s0s1s2, true); + // plain text required. + s0s1s2[0] = 0x03; + s1.dump(s0s1s2 + 1); + s2.dump(s0s1s2 + 1537); + if ((ret = skt.write(s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) { + srs_warn("complex handshake send s0s1s2 failed. ret=%d", ret); + return ret; + } + srs_verbose("complex handshake send s0s1s2 success."); + + // recv c2 + char* c2 = new char[1536]; + SrsAutoFree(char, c2, true); + if ((ret = skt.read_fully(c2, 1536, &nsize)) != ERROR_SUCCESS) { + srs_warn("complex handshake read c2 failed. ret=%d", ret); + return ret; + } + srs_verbose("complex handshake read c2 success."); + + return ret; +} + diff --git a/trunk/src/core/srs_core_handshake.hpp b/trunk/src/core/srs_core_handshake.hpp index c276bed03..96da2d7f0 100755 --- a/trunk/src/core/srs_core_handshake.hpp +++ b/trunk/src/core/srs_core_handshake.hpp @@ -1,76 +1,76 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_HANDSHKAE_HPP -#define SRS_CORE_HANDSHKAE_HPP - -/* -#include -*/ - -#include - -class SrsSocket; -class SrsComplexHandshake; - -/** -* try complex handshake, if failed, fallback to simple handshake. -*/ -class SrsSimpleHandshake -{ -public: - SrsSimpleHandshake(); - virtual ~SrsSimpleHandshake(); -public: - /** - * simple handshake. - * @param complex_hs, try complex handshake first, - * if failed, rollback to simple handshake. - */ - virtual int handshake(SrsSocket& skt, SrsComplexHandshake& complex_hs); -}; - -/** -* rtmp complex handshake, -* @see also crtmp(crtmpserver) or librtmp, -* @see also: http://blog.csdn.net/win_lin/article/details/13006803 -*/ -class SrsComplexHandshake -{ -public: - SrsComplexHandshake(); - virtual ~SrsComplexHandshake(); -public: - /** - * complex hanshake. - * @_c1, size of c1 must be 1536. - * @remark, user must free the c1. - * @return user must: - * continue connect app if success, - * try simple handshake if error is ERROR_RTMP_TRY_SIMPLE_HS, - * otherwise, disconnect - */ - virtual int handshake(SrsSocket& skt, char* _c1); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_HANDSHKAE_HPP +#define SRS_CORE_HANDSHKAE_HPP + +/* +#include +*/ + +#include + +class SrsSocket; +class SrsComplexHandshake; + +/** +* try complex handshake, if failed, fallback to simple handshake. +*/ +class SrsSimpleHandshake +{ +public: + SrsSimpleHandshake(); + virtual ~SrsSimpleHandshake(); +public: + /** + * simple handshake. + * @param complex_hs, try complex handshake first, + * if failed, rollback to simple handshake. + */ + virtual int handshake(SrsSocket& skt, SrsComplexHandshake& complex_hs); +}; + +/** +* rtmp complex handshake, +* @see also crtmp(crtmpserver) or librtmp, +* @see also: http://blog.csdn.net/win_lin/article/details/13006803 +*/ +class SrsComplexHandshake +{ +public: + SrsComplexHandshake(); + virtual ~SrsComplexHandshake(); +public: + /** + * complex hanshake. + * @_c1, size of c1 must be 1536. + * @remark, user must free the c1. + * @return user must: + * continue connect app if success, + * try simple handshake if error is ERROR_RTMP_TRY_SIMPLE_HS, + * otherwise, disconnect + */ + virtual int handshake(SrsSocket& skt, char* _c1); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_log.cpp b/trunk/src/core/srs_core_log.cpp index 2719b9b7f..3b74f3006 100755 --- a/trunk/src/core/srs_core_log.cpp +++ b/trunk/src/core/srs_core_log.cpp @@ -1,126 +1,126 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include - -#include -#include - -#include - -ILogContext::ILogContext() -{ -} - -ILogContext::~ILogContext() -{ -} - -class LogContext : public ILogContext -{ -private: - class DateTime - { - private: - // %d-%02d-%02d %02d:%02d:%02d.%03d - #define DATE_LEN 24 - char time_data[DATE_LEN]; - public: - DateTime(); - virtual ~DateTime(); - public: - virtual const char* format_time(); - }; -private: - DateTime time; - std::map cache; -public: - LogContext(); - virtual ~LogContext(); -public: - virtual void generate_id(); - virtual int get_id(); -public: - virtual const char* format_time(); -}; - -ILogContext* log_context = new LogContext(); - -LogContext::DateTime::DateTime() -{ - memset(time_data, 0, DATE_LEN); -} - -LogContext::DateTime::~DateTime() -{ -} - -const char* LogContext::DateTime::format_time() -{ - // clock time - timeval tv; - if (gettimeofday(&tv, NULL) == -1) { - return ""; - } - // to calendar time - struct tm* tm; - if ((tm = localtime(&tv.tv_sec)) == NULL) { - return ""; - } - - // log header, the time/pid/level of log - // reserved 1bytes for the new line. - snprintf(time_data, DATE_LEN, "%d-%02d-%02d %02d:%02d:%02d.%03d", - 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, - (int)(tv.tv_usec / 1000)); - - return time_data; -} - -LogContext::LogContext() -{ -} - -LogContext::~LogContext() -{ -} - -void LogContext::generate_id() -{ - static int id = 1; - cache[st_thread_self()] = id++; -} - -int LogContext::get_id() -{ - return cache[st_thread_self()]; -} - -const char* LogContext::format_time() -{ - return time.format_time(); -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include + +#include +#include + +#include + +ILogContext::ILogContext() +{ +} + +ILogContext::~ILogContext() +{ +} + +class LogContext : public ILogContext +{ +private: + class DateTime + { + private: + // %d-%02d-%02d %02d:%02d:%02d.%03d + #define DATE_LEN 24 + char time_data[DATE_LEN]; + public: + DateTime(); + virtual ~DateTime(); + public: + virtual const char* format_time(); + }; +private: + DateTime time; + std::map cache; +public: + LogContext(); + virtual ~LogContext(); +public: + virtual void generate_id(); + virtual int get_id(); +public: + virtual const char* format_time(); +}; + +ILogContext* log_context = new LogContext(); + +LogContext::DateTime::DateTime() +{ + memset(time_data, 0, DATE_LEN); +} + +LogContext::DateTime::~DateTime() +{ +} + +const char* LogContext::DateTime::format_time() +{ + // clock time + timeval tv; + if (gettimeofday(&tv, NULL) == -1) { + return ""; + } + // to calendar time + struct tm* tm; + if ((tm = localtime(&tv.tv_sec)) == NULL) { + return ""; + } + + // log header, the time/pid/level of log + // reserved 1bytes for the new line. + snprintf(time_data, DATE_LEN, "%d-%02d-%02d %02d:%02d:%02d.%03d", + 1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec, + (int)(tv.tv_usec / 1000)); + + return time_data; +} + +LogContext::LogContext() +{ +} + +LogContext::~LogContext() +{ +} + +void LogContext::generate_id() +{ + static int id = 1; + cache[st_thread_self()] = id++; +} + +int LogContext::get_id() +{ + return cache[st_thread_self()]; +} + +const char* LogContext::format_time() +{ + return time.format_time(); +} + diff --git a/trunk/src/core/srs_core_log.hpp b/trunk/src/core/srs_core_log.hpp index 4ded57019..e97c51bdc 100755 --- a/trunk/src/core/srs_core_log.hpp +++ b/trunk/src/core/srs_core_log.hpp @@ -1,90 +1,90 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_LOG_HPP -#define SRS_CORE_LOG_HPP - -/* -#include -*/ - -#include - -#include - -#include -#include - -// the context for multiple clients. -class ILogContext -{ -public: - ILogContext(); - virtual ~ILogContext(); -public: - virtual void generate_id() = 0; - virtual int get_id() = 0; -public: - virtual const char* format_time() = 0; -}; - -// user must implements the LogContext and define a global instance. -extern ILogContext* log_context; - -// donot print method -#if 0 - #define srs_verbose(msg, ...) printf("[%s][%d][verbs] ", log_context->format_time(), log_context->get_id());printf(msg, ##__VA_ARGS__);printf("\n") - #define srs_info(msg, ...) printf("[%s][%d][infos] ", log_context->format_time(), log_context->get_id());printf(msg, ##__VA_ARGS__);printf("\n") - #define srs_trace(msg, ...) printf("[%s][%d][trace] ", log_context->format_time(), log_context->get_id());printf(msg, ##__VA_ARGS__);printf("\n") - #define srs_warn(msg, ...) printf("[%s][%d][warns] ", log_context->format_time(), log_context->get_id());printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") - #define srs_error(msg, ...) printf("[%s][%d][error] ", log_context->format_time(), log_context->get_id());printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") -// use __FUNCTION__ to print c method -#elif 1 - #define srs_verbose(msg, ...) printf("[%s][%d][verbs][%s] ", log_context->format_time(), log_context->get_id(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") - #define srs_info(msg, ...) printf("[%s][%d][infos][%s] ", log_context->format_time(), log_context->get_id(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") - #define srs_trace(msg, ...) printf("[%s][%d][trace][%s] ", log_context->format_time(), log_context->get_id(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") - #define srs_warn(msg, ...) printf("[%s][%d][warns][%s] ", log_context->format_time(), log_context->get_id(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") - #define srs_error(msg, ...) printf("[%s][%d][error][%s] ", log_context->format_time(), log_context->get_id(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") -// use __PRETTY_FUNCTION__ to print c++ class:method -#else - #define srs_verbose(msg, ...) printf("[%s][%d][verbs][%s] ", log_context->format_time(), log_context->get_id(), __PRETTY_FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") - #define srs_info(msg, ...) printf("[%s][%d][infos][%s] ", log_context->format_time(), log_context->get_id(), __PRETTY_FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") - #define srs_trace(msg, ...) printf("[%s][%d][trace][%s] ", log_context->format_time(), log_context->get_id(), __PRETTY_FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") - #define srs_warn(msg, ...) printf("[%s][%d][warns][%s] ", log_context->format_time(), log_context->get_id(), __PRETTY_FUNCTION__);printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") - #define srs_error(msg, ...) printf("[%s][%d][error][%s] ", log_context->format_time(), log_context->get_id(), __PRETTY_FUNCTION__);printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") -#endif - -#if 1 - #undef srs_verbose - #define srs_verbose(msg, ...) (void)0 -#endif -#if 1 - #undef srs_info - #define srs_info(msg, ...) (void)0 -#endif -#if 0 - #undef srs_trace - #define srs_trace(msg, ...) (void)0 -#endif - -#endif +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_LOG_HPP +#define SRS_CORE_LOG_HPP + +/* +#include +*/ + +#include + +#include + +#include +#include + +// the context for multiple clients. +class ILogContext +{ +public: + ILogContext(); + virtual ~ILogContext(); +public: + virtual void generate_id() = 0; + virtual int get_id() = 0; +public: + virtual const char* format_time() = 0; +}; + +// user must implements the LogContext and define a global instance. +extern ILogContext* log_context; + +// donot print method +#if 0 + #define srs_verbose(msg, ...) printf("[%s][%d][verbs] ", log_context->format_time(), log_context->get_id());printf(msg, ##__VA_ARGS__);printf("\n") + #define srs_info(msg, ...) printf("[%s][%d][infos] ", log_context->format_time(), log_context->get_id());printf(msg, ##__VA_ARGS__);printf("\n") + #define srs_trace(msg, ...) printf("[%s][%d][trace] ", log_context->format_time(), log_context->get_id());printf(msg, ##__VA_ARGS__);printf("\n") + #define srs_warn(msg, ...) printf("[%s][%d][warns] ", log_context->format_time(), log_context->get_id());printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") + #define srs_error(msg, ...) printf("[%s][%d][error] ", log_context->format_time(), log_context->get_id());printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") +// use __FUNCTION__ to print c method +#elif 1 + #define srs_verbose(msg, ...) printf("[%s][%d][verbs][%s] ", log_context->format_time(), log_context->get_id(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") + #define srs_info(msg, ...) printf("[%s][%d][infos][%s] ", log_context->format_time(), log_context->get_id(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") + #define srs_trace(msg, ...) printf("[%s][%d][trace][%s] ", log_context->format_time(), log_context->get_id(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") + #define srs_warn(msg, ...) printf("[%s][%d][warns][%s] ", log_context->format_time(), log_context->get_id(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") + #define srs_error(msg, ...) printf("[%s][%d][error][%s] ", log_context->format_time(), log_context->get_id(), __FUNCTION__);printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") +// use __PRETTY_FUNCTION__ to print c++ class:method +#else + #define srs_verbose(msg, ...) printf("[%s][%d][verbs][%s] ", log_context->format_time(), log_context->get_id(), __PRETTY_FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") + #define srs_info(msg, ...) printf("[%s][%d][infos][%s] ", log_context->format_time(), log_context->get_id(), __PRETTY_FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") + #define srs_trace(msg, ...) printf("[%s][%d][trace][%s] ", log_context->format_time(), log_context->get_id(), __PRETTY_FUNCTION__);printf(msg, ##__VA_ARGS__);printf("\n") + #define srs_warn(msg, ...) printf("[%s][%d][warns][%s] ", log_context->format_time(), log_context->get_id(), __PRETTY_FUNCTION__);printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") + #define srs_error(msg, ...) printf("[%s][%d][error][%s] ", log_context->format_time(), log_context->get_id(), __PRETTY_FUNCTION__);printf(msg, ##__VA_ARGS__);printf(" errno=%d(%s)", errno, strerror(errno));printf("\n") +#endif + +#if 1 + #undef srs_verbose + #define srs_verbose(msg, ...) (void)0 +#endif +#if 1 + #undef srs_info + #define srs_info(msg, ...) (void)0 +#endif +#if 0 + #undef srs_trace + #define srs_trace(msg, ...) (void)0 +#endif + +#endif diff --git a/trunk/src/core/srs_core_pithy_print.cpp b/trunk/src/core/srs_core_pithy_print.cpp index 96954c41f..fc875d399 100755 --- a/trunk/src/core/srs_core_pithy_print.cpp +++ b/trunk/src/core/srs_core_pithy_print.cpp @@ -1,164 +1,164 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include - -#include -#include -#include -#include - -#define SRS_STAGE_DEFAULT_INTERVAL_MS 1200 -#define SRS_STAGE_PLAY_USER_INTERVAL_MS 1300 -#define SRS_STAGE_PUBLISH_USER_INTERVAL_MS 1100 - -struct SrsStageInfo : public SrsReloadHandler -{ - int stage_id; - int pithy_print_time_ms; - int nb_clients; - - SrsStageInfo(int _stage_id) - { - stage_id = _stage_id; - nb_clients = 0; - - update_print_time(); - - config->subscribe(this); - } - virtual ~SrsStageInfo() - { - config->unsubscribe(this); - } - void update_print_time() - { - switch (stage_id) { - case SRS_STAGE_PLAY_USER: { - pithy_print_time_ms = SRS_STAGE_PLAY_USER_INTERVAL_MS; - SrsConfDirective* conf = config->get_pithy_print_play(); - if (conf && !conf->arg0().empty()) { - pithy_print_time_ms = ::atoi(conf->arg0().c_str()); - } - break; - } - case SRS_STAGE_PUBLISH_USER: { - pithy_print_time_ms = SRS_STAGE_PUBLISH_USER_INTERVAL_MS; - SrsConfDirective* conf = config->get_pithy_print_publish(); - if (conf && !conf->arg0().empty()) { - pithy_print_time_ms = ::atoi(conf->arg0().c_str()); - } - break; - } - default: { - pithy_print_time_ms = SRS_STAGE_DEFAULT_INTERVAL_MS; - break; - } - } - } -public: - virtual int on_reload_pithy_print() - { - update_print_time(); - return ERROR_SUCCESS; - } -}; -static std::map _srs_stages; - -SrsPithyPrint::SrsPithyPrint(int _stage_id) -{ - stage_id = _stage_id; - client_id = enter_stage(); - printed_age = age = 0; -} - -SrsPithyPrint::~SrsPithyPrint() -{ - leave_stage(); -} - -int SrsPithyPrint::enter_stage() -{ - SrsStageInfo* stage = NULL; - - std::map::iterator it = _srs_stages.find(stage_id); - if (it == _srs_stages.end()) { - stage = _srs_stages[stage_id] = new SrsStageInfo(stage_id); - } else { - stage = it->second; - } - - srs_assert(stage != NULL); - client_id = stage->nb_clients++; - - srs_verbose("enter stage, stage_id=%d, client_id=%d, nb_clients=%d, time_ms=%d", - stage->stage_id, client_id, stage->nb_clients, stage->pithy_print_time_ms); - - return client_id; -} - -void SrsPithyPrint::leave_stage() -{ - SrsStageInfo* stage = _srs_stages[stage_id]; - srs_assert(stage != NULL); - - stage->nb_clients--; - - srs_verbose("leave stage, stage_id=%d, client_id=%d, nb_clients=%d, time_ms=%d", - stage->stage_id, client_id, stage->nb_clients, stage->pithy_print_time_ms); -} - -void SrsPithyPrint::elapse(int64_t time_ms) -{ - age += time_ms; -} - -bool SrsPithyPrint::can_print() -{ - SrsStageInfo* stage = _srs_stages[stage_id]; - srs_assert(stage != NULL); - - int64_t alive_age = age - printed_age; - int64_t can_print_age = stage->nb_clients * stage->pithy_print_time_ms; - - bool can_print = alive_age >= can_print_age; - if (can_print) { - printed_age = age; - } - - return can_print; -} - -int64_t SrsPithyPrint::get_age() -{ - return age; -} - -void SrsPithyPrint::set_age(int64_t _age) -{ - age = _age; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include + +#include +#include +#include +#include + +#define SRS_STAGE_DEFAULT_INTERVAL_MS 1200 +#define SRS_STAGE_PLAY_USER_INTERVAL_MS 1300 +#define SRS_STAGE_PUBLISH_USER_INTERVAL_MS 1100 + +struct SrsStageInfo : public SrsReloadHandler +{ + int stage_id; + int pithy_print_time_ms; + int nb_clients; + + SrsStageInfo(int _stage_id) + { + stage_id = _stage_id; + nb_clients = 0; + + update_print_time(); + + config->subscribe(this); + } + virtual ~SrsStageInfo() + { + config->unsubscribe(this); + } + void update_print_time() + { + switch (stage_id) { + case SRS_STAGE_PLAY_USER: { + pithy_print_time_ms = SRS_STAGE_PLAY_USER_INTERVAL_MS; + SrsConfDirective* conf = config->get_pithy_print_play(); + if (conf && !conf->arg0().empty()) { + pithy_print_time_ms = ::atoi(conf->arg0().c_str()); + } + break; + } + case SRS_STAGE_PUBLISH_USER: { + pithy_print_time_ms = SRS_STAGE_PUBLISH_USER_INTERVAL_MS; + SrsConfDirective* conf = config->get_pithy_print_publish(); + if (conf && !conf->arg0().empty()) { + pithy_print_time_ms = ::atoi(conf->arg0().c_str()); + } + break; + } + default: { + pithy_print_time_ms = SRS_STAGE_DEFAULT_INTERVAL_MS; + break; + } + } + } +public: + virtual int on_reload_pithy_print() + { + update_print_time(); + return ERROR_SUCCESS; + } +}; +static std::map _srs_stages; + +SrsPithyPrint::SrsPithyPrint(int _stage_id) +{ + stage_id = _stage_id; + client_id = enter_stage(); + printed_age = age = 0; +} + +SrsPithyPrint::~SrsPithyPrint() +{ + leave_stage(); +} + +int SrsPithyPrint::enter_stage() +{ + SrsStageInfo* stage = NULL; + + std::map::iterator it = _srs_stages.find(stage_id); + if (it == _srs_stages.end()) { + stage = _srs_stages[stage_id] = new SrsStageInfo(stage_id); + } else { + stage = it->second; + } + + srs_assert(stage != NULL); + client_id = stage->nb_clients++; + + srs_verbose("enter stage, stage_id=%d, client_id=%d, nb_clients=%d, time_ms=%d", + stage->stage_id, client_id, stage->nb_clients, stage->pithy_print_time_ms); + + return client_id; +} + +void SrsPithyPrint::leave_stage() +{ + SrsStageInfo* stage = _srs_stages[stage_id]; + srs_assert(stage != NULL); + + stage->nb_clients--; + + srs_verbose("leave stage, stage_id=%d, client_id=%d, nb_clients=%d, time_ms=%d", + stage->stage_id, client_id, stage->nb_clients, stage->pithy_print_time_ms); +} + +void SrsPithyPrint::elapse(int64_t time_ms) +{ + age += time_ms; +} + +bool SrsPithyPrint::can_print() +{ + SrsStageInfo* stage = _srs_stages[stage_id]; + srs_assert(stage != NULL); + + int64_t alive_age = age - printed_age; + int64_t can_print_age = stage->nb_clients * stage->pithy_print_time_ms; + + bool can_print = alive_age >= can_print_age; + if (can_print) { + printed_age = age; + } + + return can_print; +} + +int64_t SrsPithyPrint::get_age() +{ + return age; +} + +void SrsPithyPrint::set_age(int64_t _age) +{ + age = _age; +} + diff --git a/trunk/src/core/srs_core_pithy_print.hpp b/trunk/src/core/srs_core_pithy_print.hpp index 18ed8131d..a0e60a484 100755 --- a/trunk/src/core/srs_core_pithy_print.hpp +++ b/trunk/src/core/srs_core_pithy_print.hpp @@ -1,82 +1,82 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_PITHY_PRINT_HPP -#define SRS_CORE_PITHY_PRINT_HPP - -/* -#include -*/ - -#include - -// the pithy stage for all play clients. -#define SRS_STAGE_PLAY_USER 1 -// the pithy stage for all publish clients. -#define SRS_STAGE_PUBLISH_USER 2 - -/** -* the stage is used for a collection of object to do print, -* the print time in a stage is constant and not changed. -* for example, stage #1 for all play clients, print time is 3s, -* if there is 10clients, then all clients should print in 10*3s. -*/ -class SrsPithyPrint -{ -private: - int client_id; - int stage_id; - int64_t age; - int64_t printed_age; -public: - /** - * @param _stage_id defined in SRS_STAGE_xxx, eg. SRS_STAGE_PLAY_USER. - */ - SrsPithyPrint(int _stage_id); - virtual ~SrsPithyPrint(); -private: - /** - * enter the specified stage, return the client id. - */ - virtual int enter_stage(); - /** - * leave the specified stage, release the client id. - */ - virtual void leave_stage(); -public: - /** - * specified client elapse some time. - */ - virtual void elapse(int64_t time_ms); - /** - * whether current client can print. - */ - virtual bool can_print(); - /** - * get the elapsed time in ms. - */ - virtual int64_t get_age(); - virtual void set_age(int64_t _age); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_PITHY_PRINT_HPP +#define SRS_CORE_PITHY_PRINT_HPP + +/* +#include +*/ + +#include + +// the pithy stage for all play clients. +#define SRS_STAGE_PLAY_USER 1 +// the pithy stage for all publish clients. +#define SRS_STAGE_PUBLISH_USER 2 + +/** +* the stage is used for a collection of object to do print, +* the print time in a stage is constant and not changed. +* for example, stage #1 for all play clients, print time is 3s, +* if there is 10clients, then all clients should print in 10*3s. +*/ +class SrsPithyPrint +{ +private: + int client_id; + int stage_id; + int64_t age; + int64_t printed_age; +public: + /** + * @param _stage_id defined in SRS_STAGE_xxx, eg. SRS_STAGE_PLAY_USER. + */ + SrsPithyPrint(int _stage_id); + virtual ~SrsPithyPrint(); +private: + /** + * enter the specified stage, return the client id. + */ + virtual int enter_stage(); + /** + * leave the specified stage, release the client id. + */ + virtual void leave_stage(); +public: + /** + * specified client elapse some time. + */ + virtual void elapse(int64_t time_ms); + /** + * whether current client can print. + */ + virtual bool can_print(); + /** + * get the elapsed time in ms. + */ + virtual int64_t get_age(); + virtual void set_age(int64_t _age); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_protocol.cpp b/trunk/src/core/srs_core_protocol.cpp index b66f283da..63cc01617 100755 --- a/trunk/src/core/srs_core_protocol.cpp +++ b/trunk/src/core/srs_core_protocol.cpp @@ -1,2694 +1,2694 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include - -/**************************************************************************** -***************************************************************************** -****************************************************************************/ -/** -5. Protocol Control Messages -RTMP reserves message type IDs 1-7 for protocol control messages. -These messages contain information needed by the RTM Chunk Stream -protocol or RTMP itself. Protocol messages with IDs 1 & 2 are -reserved for usage with RTM Chunk Stream protocol. Protocol messages -with IDs 3-6 are reserved for usage of RTMP. Protocol message with ID -7 is used between edge server and origin server. -*/ -#define RTMP_MSG_SetChunkSize 0x01 -#define RTMP_MSG_AbortMessage 0x02 -#define RTMP_MSG_Acknowledgement 0x03 -#define RTMP_MSG_UserControlMessage 0x04 -#define RTMP_MSG_WindowAcknowledgementSize 0x05 -#define RTMP_MSG_SetPeerBandwidth 0x06 -#define RTMP_MSG_EdgeAndOriginServerCommand 0x07 -/** -3. Types of messages -The server and the client send messages over the network to -communicate with each other. The messages can be of any type which -includes audio messages, video messages, command messages, shared -object messages, data messages, and user control messages. -3.1. Command message -Command messages carry the AMF-encoded commands between the client -and the server. These messages have been assigned message type value -of 20 for AMF0 encoding and message type value of 17 for AMF3 -encoding. These messages are sent to perform some operations like -connect, createStream, publish, play, pause on the peer. Command -messages like onstatus, result etc. are used to inform the sender -about the status of the requested commands. A command message -consists of command name, transaction ID, and command object that -contains related parameters. A client or a server can request Remote -Procedure Calls (RPC) over streams that are communicated using the -command messages to the peer. -*/ -#define RTMP_MSG_AMF3CommandMessage 17 // 0x11 -#define RTMP_MSG_AMF0CommandMessage 20 // 0x14 -/** -3.2. Data message -The client or the server sends this message to send Metadata or any -user data to the peer. Metadata includes details about the -data(audio, video etc.) like creation time, duration, theme and so -on. These messages have been assigned message type value of 18 for -AMF0 and message type value of 15 for AMF3. -*/ -#define RTMP_MSG_AMF0DataMessage 18 // 0x12 -#define RTMP_MSG_AMF3DataMessage 15 // 0x0F -/** -3.3. Shared object message -A shared object is a Flash object (a collection of name value pairs) -that are in synchronization across multiple clients, instances, and -so on. The message types kMsgContainer=19 for AMF0 and -kMsgContainerEx=16 for AMF3 are reserved for shared object events. -Each message can contain multiple events. -*/ -#define RTMP_MSG_AMF3SharedObject 16 // 0x10 -#define RTMP_MSG_AMF0SharedObject 19 // 0x13 -/** -3.4. Audio message -The client or the server sends this message to send audio data to the -peer. The message type value of 8 is reserved for audio messages. -*/ -#define RTMP_MSG_AudioMessage 8 // 0x08 -/* * -3.5. Video message -The client or the server sends this message to send video data to the -peer. The message type value of 9 is reserved for video messages. -These messages are large and can delay the sending of other type of -messages. To avoid such a situation, the video message is assigned -the lowest priority. -*/ -#define RTMP_MSG_VideoMessage 9 // 0x09 -/** -3.6. Aggregate message -An aggregate message is a single message that contains a list of submessages. -The message type value of 22 is reserved for aggregate -messages. -*/ -#define RTMP_MSG_AggregateMessage 22 // 0x16 - -/**************************************************************************** -***************************************************************************** -****************************************************************************/ -/** -* 6.1.2. Chunk Message Header -* There are four different formats for the chunk message header, -* selected by the "fmt" field in the chunk basic header. -*/ -// 6.1.2.1. Type 0 -// Chunks of Type 0 are 11 bytes long. This type MUST be used at the -// start of a chunk stream, and whenever the stream timestamp goes -// backward (e.g., because of a backward seek). -#define RTMP_FMT_TYPE0 0 -// 6.1.2.2. Type 1 -// Chunks of Type 1 are 7 bytes long. The message stream ID is not -// included; this chunk takes the same stream ID as the preceding chunk. -// Streams with variable-sized messages (for example, many video -// formats) SHOULD use this format for the first chunk of each new -// message after the first. -#define RTMP_FMT_TYPE1 1 -// 6.1.2.3. Type 2 -// Chunks of Type 2 are 3 bytes long. Neither the stream ID nor the -// message length is included; this chunk has the same stream ID and -// message length as the preceding chunk. Streams with constant-sized -// messages (for example, some audio and data formats) SHOULD use this -// format for the first chunk of each message after the first. -#define RTMP_FMT_TYPE2 2 -// 6.1.2.4. Type 3 -// Chunks of Type 3 have no header. Stream ID, message length and -// timestamp delta are not present; chunks of this type take values from -// the preceding chunk. When a single message is split into chunks, all -// chunks of a message except the first one, SHOULD use this type. Refer -// to example 2 in section 6.2.2. Stream consisting of messages of -// exactly the same size, stream ID and spacing in time SHOULD use this -// type for all chunks after chunk of Type 2. Refer to example 1 in -// section 6.2.1. If the delta between the first message and the second -// message is same as the time stamp of first message, then chunk of -// type 3 would immediately follow the chunk of type 0 as there is no -// need for a chunk of type 2 to register the delta. If Type 3 chunk -// follows a Type 0 chunk, then timestamp delta for this Type 3 chunk is -// the same as the timestamp of Type 0 chunk. -#define RTMP_FMT_TYPE3 3 - -/**************************************************************************** -***************************************************************************** -****************************************************************************/ -/** -* 6. Chunking -* The chunk size is configurable. It can be set using a control -* message(Set Chunk Size) as described in section 7.1. The maximum -* chunk size can be 65536 bytes and minimum 128 bytes. Larger values -* reduce CPU usage, but also commit to larger writes that can delay -* other content on lower bandwidth connections. Smaller chunks are not -* good for high-bit rate streaming. Chunk size is maintained -* independently for each direction. -*/ -#define RTMP_DEFAULT_CHUNK_SIZE 128 -#define RTMP_MIN_CHUNK_SIZE 2 - -/** -* 6.1. Chunk Format -* Extended timestamp: 0 or 4 bytes -* This field MUST be sent when the normal timsestamp is set to -* 0xffffff, it MUST NOT be sent if the normal timestamp is set to -* anything else. So for values less than 0xffffff the normal -* timestamp field SHOULD be used in which case the extended timestamp -* MUST NOT be present. For values greater than or equal to 0xffffff -* the normal timestamp field MUST NOT be used and MUST be set to -* 0xffffff and the extended timestamp MUST be sent. -*/ -#define RTMP_EXTENDED_TIMESTAMP 0xFFFFFF - -/**************************************************************************** -***************************************************************************** -****************************************************************************/ -/** -* amf0 command message, command name macros -*/ -#define RTMP_AMF0_COMMAND_CONNECT "connect" -#define RTMP_AMF0_COMMAND_CREATE_STREAM "createStream" -#define RTMP_AMF0_COMMAND_PLAY "play" -#define RTMP_AMF0_COMMAND_PAUSE "pause" -#define RTMP_AMF0_COMMAND_ON_BW_DONE "onBWDone" -#define RTMP_AMF0_COMMAND_ON_STATUS "onStatus" -#define RTMP_AMF0_COMMAND_RESULT "_result" -#define RTMP_AMF0_COMMAND_RELEASE_STREAM "releaseStream" -#define RTMP_AMF0_COMMAND_FC_PUBLISH "FCPublish" -#define RTMP_AMF0_COMMAND_UNPUBLISH "FCUnpublish" -#define RTMP_AMF0_COMMAND_PUBLISH "publish" -#define RTMP_AMF0_DATA_SAMPLE_ACCESS "|RtmpSampleAccess" -#define RTMP_AMF0_DATA_SET_DATAFRAME "@setDataFrame" -#define RTMP_AMF0_DATA_ON_METADATA "onMetaData" - -/**************************************************************************** -***************************************************************************** -****************************************************************************/ -/** -* the chunk stream id used for some under-layer message, -* for example, the PC(protocol control) message. -*/ -#define RTMP_CID_ProtocolControl 0x02 -/** -* the AMF0/AMF3 command message, invoke method and return the result, over NetConnection. -* generally use 0x03. -*/ -#define RTMP_CID_OverConnection 0x03 -/** -* the AMF0/AMF3 command message, invoke method and return the result, over NetConnection, -* the midst state(we guess). -* rarely used, e.g. onStatus(NetStream.Play.Reset). -*/ -#define RTMP_CID_OverConnection2 0x04 -/** -* the stream message(amf0/amf3), over NetStream. -* generally use 0x05. -*/ -#define RTMP_CID_OverStream 0x05 -/** -* the stream message(amf0/amf3), over NetStream, the midst state(we guess). -* rarely used, e.g. play("mp4:mystram.f4v") -*/ -#define RTMP_CID_OverStream2 0x08 -/** -* the stream message(video), over NetStream -* generally use 0x06. -*/ -#define RTMP_CID_Video 0x06 -/** -* the stream message(audio), over NetStream. -* generally use 0x07. -*/ -#define RTMP_CID_Audio 0x07 - -/**************************************************************************** -***************************************************************************** -****************************************************************************/ -// when got a messae header, increase recv timeout to got an entire message. -#define SRS_MIN_RECV_TIMEOUT_US 3000 - -SrsProtocol::AckWindowSize::AckWindowSize() -{ - ack_window_size = acked_size = 0; -} - -SrsProtocol::SrsProtocol(st_netfd_t client_stfd) -{ - stfd = client_stfd; - buffer = new SrsBuffer(); - skt = new SrsSocket(stfd); - - in_chunk_size = out_chunk_size = RTMP_DEFAULT_CHUNK_SIZE; -} - -SrsProtocol::~SrsProtocol() -{ - std::map::iterator it; - - for (it = chunk_streams.begin(); it != chunk_streams.end(); ++it) { - SrsChunkStream* stream = it->second; - srs_freep(stream); - } - - chunk_streams.clear(); - - srs_freep(buffer); - srs_freep(skt); -} - -void SrsProtocol::set_recv_timeout(int64_t timeout_us) -{ - return skt->set_recv_timeout(timeout_us); -} - -int64_t SrsProtocol::get_recv_timeout() -{ - return skt->get_recv_timeout(); -} - -void SrsProtocol::set_send_timeout(int64_t timeout_us) -{ - return skt->set_send_timeout(timeout_us); -} - -int64_t SrsProtocol::get_recv_bytes() -{ - return skt->get_recv_bytes(); -} - -int64_t SrsProtocol::get_send_bytes() -{ - return skt->get_send_bytes(); -} - -int SrsProtocol::get_recv_kbps() -{ - return skt->get_recv_kbps(); -} - -int SrsProtocol::get_send_kbps() -{ - return skt->get_send_kbps(); -} - -int SrsProtocol::recv_message(SrsCommonMessage** pmsg) -{ - *pmsg = NULL; - - int ret = ERROR_SUCCESS; - - while (true) { - SrsCommonMessage* msg = NULL; - - if ((ret = recv_interlaced_message(&msg)) != ERROR_SUCCESS) { - if (ret != ERROR_SOCKET_TIMEOUT) { - srs_error("recv interlaced message failed. ret=%d", ret); - } - return ret; - } - srs_verbose("entire msg received"); - - if (!msg) { - continue; - } - - if (msg->size <= 0 || msg->header.payload_length <= 0) { - srs_trace("ignore empty message(type=%d, size=%d, time=%d, sid=%d).", - msg->header.message_type, msg->header.payload_length, - msg->header.timestamp, msg->header.stream_id); - srs_freep(msg); - continue; - } - - if ((ret = on_recv_message(msg)) != ERROR_SUCCESS) { - srs_error("hook the received msg failed. ret=%d", ret); - srs_freep(msg); - return ret; - } - - srs_verbose("get a msg with raw/undecoded payload"); - *pmsg = msg; - break; - } - - return ret; -} - -int SrsProtocol::send_message(ISrsMessage* msg) -{ - int ret = ERROR_SUCCESS; - - // free msg whatever return value. - SrsAutoFree(ISrsMessage, msg, false); - - if ((ret = msg->encode_packet()) != ERROR_SUCCESS) { - srs_error("encode packet to message payload failed. ret=%d", ret); - return ret; - } - srs_info("encode packet to message payload success"); - - // p set to current write position, - // it's ok when payload is NULL and size is 0. - char* p = (char*)msg->payload; - - // always write the header event payload is empty. - do { - // generate the header. - char* pheader = NULL; - int header_size = 0; - - if (p == (char*)msg->payload) { - // write new chunk stream header, fmt is 0 - pheader = out_header_fmt0; - *pheader++ = 0x00 | (msg->get_perfer_cid() & 0x3F); - - // chunk message header, 11 bytes - // timestamp, 3bytes, big-endian - if (msg->header.timestamp >= RTMP_EXTENDED_TIMESTAMP) { - *pheader++ = 0xFF; - *pheader++ = 0xFF; - *pheader++ = 0xFF; - } else { - pp = (char*)&msg->header.timestamp; - *pheader++ = pp[2]; - *pheader++ = pp[1]; - *pheader++ = pp[0]; - } - - // message_length, 3bytes, big-endian - pp = (char*)&msg->header.payload_length; - *pheader++ = pp[2]; - *pheader++ = pp[1]; - *pheader++ = pp[0]; - - // message_type, 1bytes - *pheader++ = msg->header.message_type; - - // message_length, 3bytes, little-endian - pp = (char*)&msg->header.stream_id; - *pheader++ = pp[0]; - *pheader++ = pp[1]; - *pheader++ = pp[2]; - *pheader++ = pp[3]; - - // chunk extended timestamp header, 0 or 4 bytes, big-endian - if(msg->header.timestamp >= RTMP_EXTENDED_TIMESTAMP){ - pp = (char*)&msg->header.timestamp; - *pheader++ = pp[3]; - *pheader++ = pp[2]; - *pheader++ = pp[1]; - *pheader++ = pp[0]; - } - - header_size = pheader - out_header_fmt0; - pheader = out_header_fmt0; - } else { - // write no message header chunk stream, fmt is 3 - pheader = out_header_fmt3; - *pheader++ = 0xC0 | (msg->get_perfer_cid() & 0x3F); - - // chunk extended timestamp header, 0 or 4 bytes, big-endian - // 6.1.3. Extended Timestamp - // This field is transmitted only when the normal time stamp in the - // chunk message header is set to 0x00ffffff. If normal time stamp is - // set to any value less than 0x00ffffff, this field MUST NOT be - // present. This field MUST NOT be present if the timestamp field is not - // present. Type 3 chunks MUST NOT have this field. - // adobe changed for Type3 chunk: - // FMLE always sendout the extended-timestamp, - // must send the extended-timestamp to FMS, - // must send the extended-timestamp to flash-player. - // @see: ngx_rtmp_prepare_message - // @see: http://blog.csdn.net/win_lin/article/details/13363699 - if(msg->header.timestamp >= RTMP_EXTENDED_TIMESTAMP){ - pp = (char*)&msg->header.timestamp; - *pheader++ = pp[3]; - *pheader++ = pp[2]; - *pheader++ = pp[1]; - *pheader++ = pp[0]; - } - - header_size = pheader - out_header_fmt3; - pheader = out_header_fmt3; - } - - // sendout header and payload by writev. - // decrease the sys invoke count to get higher performance. - int payload_size = msg->size - (p - (char*)msg->payload); - payload_size = srs_min(payload_size, out_chunk_size); - - // send by writev - iovec iov[2]; - iov[0].iov_base = pheader; - iov[0].iov_len = header_size; - iov[1].iov_base = p; - iov[1].iov_len = payload_size; - - ssize_t nwrite; - if ((ret = skt->writev(iov, 2, &nwrite)) != ERROR_SUCCESS) { - srs_error("send with writev failed. ret=%d", ret); - return ret; - } - - // consume sendout bytes when not empty packet. - if (msg->payload && msg->size > 0) { - p += payload_size; - } - } while (p < (char*)msg->payload + msg->size); - - if ((ret = on_send_message(msg)) != ERROR_SUCCESS) { - srs_error("hook the send message failed. ret=%d", ret); - return ret; - } - - return ret; -} - -int SrsProtocol::response_acknowledgement_message() -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsAcknowledgementPacket* pkt = new SrsAcknowledgementPacket(); - - in_ack_size.acked_size = pkt->sequence_number = skt->get_recv_bytes(); - msg->set_packet(pkt, 0); - - if ((ret = send_message(msg)) != ERROR_SUCCESS) { - srs_error("send acknowledgement failed. ret=%d", ret); - return ret; - } - srs_verbose("send acknowledgement success."); - - return ret; -} - -int SrsProtocol::response_ping_message(int32_t timestamp) -{ - int ret = ERROR_SUCCESS; - - srs_trace("get a ping request, response it. timestamp=%d", timestamp); - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCPingResponse; - pkt->event_data = timestamp; - msg->set_packet(pkt, 0); - - if ((ret = send_message(msg)) != ERROR_SUCCESS) { - srs_error("send ping response failed. ret=%d", ret); - return ret; - } - srs_verbose("send ping response success."); - - return ret; -} - -int SrsProtocol::on_recv_message(SrsCommonMessage* msg) -{ - int ret = ERROR_SUCCESS; - - srs_assert(msg != NULL); - - // acknowledgement - if (in_ack_size.ack_window_size > 0 && skt->get_recv_bytes() - in_ack_size.acked_size > in_ack_size.ack_window_size) { - if ((ret = response_acknowledgement_message()) != ERROR_SUCCESS) { - return ret; - } - } - - switch (msg->header.message_type) { - case RTMP_MSG_SetChunkSize: - case RTMP_MSG_UserControlMessage: - case RTMP_MSG_WindowAcknowledgementSize: - if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { - srs_error("decode packet from message payload failed. ret=%d", ret); - return ret; - } - srs_verbose("decode packet from message payload success."); - break; - } - - switch (msg->header.message_type) { - case RTMP_MSG_WindowAcknowledgementSize: { - SrsSetWindowAckSizePacket* pkt = dynamic_cast(msg->get_packet()); - srs_assert(pkt != NULL); - - if (pkt->ackowledgement_window_size > 0) { - in_ack_size.ack_window_size = pkt->ackowledgement_window_size; - srs_trace("set ack window size to %d", pkt->ackowledgement_window_size); - } else { - srs_warn("ignored. set ack window size is %d", pkt->ackowledgement_window_size); - } - break; - } - case RTMP_MSG_SetChunkSize: { - SrsSetChunkSizePacket* pkt = dynamic_cast(msg->get_packet()); - srs_assert(pkt != NULL); - - in_chunk_size = pkt->chunk_size; - - srs_trace("set input chunk size to %d", pkt->chunk_size); - break; - } - case RTMP_MSG_UserControlMessage: { - SrsUserControlPacket* pkt = dynamic_cast(msg->get_packet()); - srs_assert(pkt != NULL); - - if (pkt->event_type == SrcPCUCSetBufferLength) { - srs_trace("ignored. set buffer length to %d", pkt->extra_data); - } - if (pkt->event_type == SrcPCUCPingRequest) { - if ((ret = response_ping_message(pkt->event_data)) != ERROR_SUCCESS) { - return ret; - } - } - break; - } - } - - return ret; -} - -int SrsProtocol::on_send_message(ISrsMessage* msg) -{ - int ret = ERROR_SUCCESS; - - if (!msg->can_decode()) { - srs_verbose("ignore the un-decodable message."); - return ret; - } - - SrsCommonMessage* common_msg = dynamic_cast(msg); - if (!msg) { - srs_verbose("ignore the shared ptr message."); - return ret; - } - - srs_assert(common_msg != NULL); - - switch (common_msg->header.message_type) { - case RTMP_MSG_SetChunkSize: { - SrsSetChunkSizePacket* pkt = dynamic_cast(common_msg->get_packet()); - srs_assert(pkt != NULL); - - out_chunk_size = pkt->chunk_size; - - srs_trace("set output chunk size to %d", pkt->chunk_size); - break; - } - } - - return ret; -} - -int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg) -{ - int ret = ERROR_SUCCESS; - - // chunk stream basic header. - char fmt = 0; - int cid = 0; - int bh_size = 0; - if ((ret = read_basic_header(fmt, cid, bh_size)) != ERROR_SUCCESS) { - if (ret != ERROR_SOCKET_TIMEOUT) { - srs_error("read basic header failed. ret=%d", ret); - } - return ret; - } - srs_verbose("read basic header success. fmt=%d, cid=%d, bh_size=%d", fmt, cid, bh_size); - - // once we got the chunk message header, - // that is there is a real message in cache, - // increase the timeout to got it. - // For example, in the play loop, we set timeout to 100ms, - // when we got a chunk header, we should increase the timeout, - // or we maybe timeout and disconnect the client. - int64_t timeout_us = skt->get_recv_timeout(); - if (timeout_us != (int64_t)ST_UTIME_NO_TIMEOUT) { - int64_t pkt_timeout_us = srs_max(timeout_us, SRS_MIN_RECV_TIMEOUT_US); - skt->set_recv_timeout(pkt_timeout_us); - srs_verbose("change recv timeout_us " - "from %"PRId64" to %"PRId64"", timeout_us, pkt_timeout_us); - } - - // get the cached chunk stream. - SrsChunkStream* chunk = NULL; - - if (chunk_streams.find(cid) == chunk_streams.end()) { - chunk = chunk_streams[cid] = new SrsChunkStream(cid); - srs_verbose("cache new chunk stream: fmt=%d, cid=%d", fmt, cid); - } else { - chunk = chunk_streams[cid]; - srs_verbose("cached chunk stream: fmt=%d, cid=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)", - chunk->fmt, chunk->cid, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, chunk->header.payload_length, - chunk->header.timestamp, chunk->header.stream_id); - } - - // chunk stream message header - int mh_size = 0; - if ((ret = read_message_header(chunk, fmt, bh_size, mh_size)) != ERROR_SUCCESS) { - if (ret != ERROR_SOCKET_TIMEOUT) { - srs_error("read message header failed. ret=%d", ret); - } - return ret; - } - srs_verbose("read message header success. " - "fmt=%d, mh_size=%d, ext_time=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)", - fmt, mh_size, chunk->extended_timestamp, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, - chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id); - - // read msg payload from chunk stream. - SrsCommonMessage* msg = NULL; - int payload_size = 0; - if ((ret = read_message_payload(chunk, bh_size, mh_size, payload_size, &msg)) != ERROR_SUCCESS) { - if (ret != ERROR_SOCKET_TIMEOUT) { - srs_error("read message payload failed. ret=%d", ret); - } - return ret; - } - - // reset the recv timeout - if (timeout_us != (int64_t)ST_UTIME_NO_TIMEOUT) { - skt->set_recv_timeout(timeout_us); - srs_verbose("reset recv timeout_us to %"PRId64"", timeout_us); - } - - // not got an entire RTMP message, try next chunk. - if (!msg) { - srs_verbose("get partial message success. chunk_payload_size=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)", - payload_size, (msg? msg->size : (chunk->msg? chunk->msg->size : 0)), chunk->header.message_type, chunk->header.payload_length, - chunk->header.timestamp, chunk->header.stream_id); - return ret; - } - - *pmsg = msg; - srs_info("get entire message success. chunk_payload_size=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)", - payload_size, (msg? msg->size : (chunk->msg? chunk->msg->size : 0)), chunk->header.message_type, chunk->header.payload_length, - chunk->header.timestamp, chunk->header.stream_id); - - return ret; -} - -int SrsProtocol::read_basic_header(char& fmt, int& cid, int& bh_size) -{ - int ret = ERROR_SUCCESS; - - int required_size = 1; - if ((ret = buffer->ensure_buffer_bytes(skt, required_size)) != ERROR_SUCCESS) { - if (ret != ERROR_SOCKET_TIMEOUT) { - srs_error("read 1bytes basic header failed. required_size=%d, ret=%d", required_size, ret); - } - return ret; - } - - char* p = buffer->bytes(); - - fmt = (*p >> 6) & 0x03; - cid = *p & 0x3f; - bh_size = 1; - - if (cid > 1) { - srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", bh_size, fmt, cid); - return ret; - } - - if (cid == 0) { - required_size = 2; - if ((ret = buffer->ensure_buffer_bytes(skt, required_size)) != ERROR_SUCCESS) { - if (ret != ERROR_SOCKET_TIMEOUT) { - srs_error("read 2bytes basic header failed. required_size=%d, ret=%d", required_size, ret); - } - return ret; - } - - cid = 64; - cid += *(++p); - bh_size = 2; - srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", bh_size, fmt, cid); - } else if (cid == 1) { - required_size = 3; - if ((ret = buffer->ensure_buffer_bytes(skt, 3)) != ERROR_SUCCESS) { - if (ret != ERROR_SOCKET_TIMEOUT) { - srs_error("read 3bytes basic header failed. required_size=%d, ret=%d", required_size, ret); - } - return ret; - } - - cid = 64; - cid += *(++p); - cid += *(++p) * 256; - bh_size = 3; - srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", bh_size, fmt, cid); - } else { - srs_error("invalid path, impossible basic header."); - srs_assert(false); - } - - return ret; -} - -int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size) -{ - int ret = ERROR_SUCCESS; - - /** - * we should not assert anything about fmt, for the first packet. - * (when first packet, the chunk->msg is NULL). - * the fmt maybe 0/1/2/3, the FMLE will send a 0xC4 for some audio packet. - * the previous packet is: - * 04 // fmt=0, cid=4 - * 00 00 1a // timestamp=26 - * 00 00 9d // payload_length=157 - * 08 // message_type=8(audio) - * 01 00 00 00 // stream_id=1 - * the current packet maybe: - * c4 // fmt=3, cid=4 - * it's ok, for the packet is audio, and timestamp delta is 26. - * the current packet must be parsed as: - * fmt=0, cid=4 - * timestamp=26+26=52 - * payload_length=157 - * message_type=8(audio) - * stream_id=1 - * so we must update the timestamp even fmt=3 for first packet. - */ - // fresh packet used to update the timestamp even fmt=3 for first packet. - bool is_fresh_packet = !chunk->msg; - - // but, we can ensure that when a chunk stream is fresh, - // the fmt must be 0, a new stream. - if (chunk->msg_count == 0 && fmt != RTMP_FMT_TYPE0) { - ret = ERROR_RTMP_CHUNK_START; - srs_error("chunk stream is fresh, " - "fmt must be %d, actual is %d. ret=%d", RTMP_FMT_TYPE0, fmt, ret); - return ret; - } - - // when exists cache msg, means got an partial message, - // the fmt must not be type0 which means new message. - if (chunk->msg && fmt == RTMP_FMT_TYPE0) { - ret = ERROR_RTMP_CHUNK_START; - srs_error("chunk stream exists, " - "fmt must not be %d, actual is %d. ret=%d", RTMP_FMT_TYPE0, fmt, ret); - return ret; - } - - // create msg when new chunk stream start - if (!chunk->msg) { - chunk->msg = new SrsCommonMessage(); - srs_verbose("create message for new chunk, fmt=%d, cid=%d", fmt, chunk->cid); - } - - // read message header from socket to buffer. - static char mh_sizes[] = {11, 7, 3, 0}; - mh_size = mh_sizes[(int)fmt]; - srs_verbose("calc chunk message header size. fmt=%d, mh_size=%d", fmt, mh_size); - - int required_size = bh_size + mh_size; - if ((ret = buffer->ensure_buffer_bytes(skt, required_size)) != ERROR_SUCCESS) { - if (ret != ERROR_SOCKET_TIMEOUT) { - srs_error("read %dbytes message header failed. required_size=%d, ret=%d", mh_size, required_size, ret); - } - return ret; - } - char* p = buffer->bytes() + bh_size; - - // parse the message header. - // see also: ngx_rtmp_recv - if (fmt <= RTMP_FMT_TYPE2) { - char* pp = (char*)&chunk->header.timestamp_delta; - pp[2] = *p++; - pp[1] = *p++; - pp[0] = *p++; - pp[3] = 0; - - // fmt: 0 - // timestamp: 3 bytes - // If the timestamp is greater than or equal to 16777215 - // (hexadecimal 0x00ffffff), this value MUST be 16777215, and the - // ‘extended timestamp header’ MUST be present. Otherwise, this value - // SHOULD be the entire timestamp. - // - // fmt: 1 or 2 - // timestamp delta: 3 bytes - // If the delta is greater than or equal to 16777215 (hexadecimal - // 0x00ffffff), this value MUST be 16777215, and the ‘extended - // timestamp header’ MUST be present. Otherwise, this value SHOULD be - // the entire delta. - chunk->extended_timestamp = (chunk->header.timestamp_delta >= RTMP_EXTENDED_TIMESTAMP); - if (chunk->extended_timestamp) { - // Extended timestamp: 0 or 4 bytes - // This field MUST be sent when the normal timsestamp is set to - // 0xffffff, it MUST NOT be sent if the normal timestamp is set to - // anything else. So for values less than 0xffffff the normal - // timestamp field SHOULD be used in which case the extended timestamp - // MUST NOT be present. For values greater than or equal to 0xffffff - // the normal timestamp field MUST NOT be used and MUST be set to - // 0xffffff and the extended timestamp MUST be sent. - // - // if extended timestamp, the timestamp must >= RTMP_EXTENDED_TIMESTAMP - // we set the timestamp to RTMP_EXTENDED_TIMESTAMP to identify we - // got an extended timestamp. - chunk->header.timestamp = RTMP_EXTENDED_TIMESTAMP; - } else { - if (fmt == RTMP_FMT_TYPE0) { - // 6.1.2.1. Type 0 - // For a type-0 chunk, the absolute timestamp of the message is sent - // here. - chunk->header.timestamp = chunk->header.timestamp_delta; - } else { - // 6.1.2.2. Type 1 - // 6.1.2.3. Type 2 - // For a type-1 or type-2 chunk, the difference between the previous - // chunk's timestamp and the current chunk's timestamp is sent here. - chunk->header.timestamp += chunk->header.timestamp_delta; - } - } - - if (fmt <= RTMP_FMT_TYPE1) { - pp = (char*)&chunk->header.payload_length; - pp[2] = *p++; - pp[1] = *p++; - pp[0] = *p++; - pp[3] = 0; - - // if msg exists in cache, the size must not changed. - if (chunk->msg->size > 0 && chunk->msg->size != chunk->header.payload_length) { - ret = ERROR_RTMP_PACKET_SIZE; - srs_error("msg exists in chunk cache, " - "size=%d cannot change to %d, ret=%d", - chunk->msg->size, chunk->header.payload_length, ret); - return ret; - } - - chunk->header.message_type = *p++; - - if (fmt == 0) { - pp = (char*)&chunk->header.stream_id; - pp[0] = *p++; - pp[1] = *p++; - pp[2] = *p++; - pp[3] = *p++; - srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d, payload=%d, type=%d, sid=%d", - fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp, chunk->header.payload_length, - chunk->header.message_type, chunk->header.stream_id); - } else { - srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d, payload=%d, type=%d", - fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp, chunk->header.payload_length, - chunk->header.message_type); - } - } else { - srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d", - fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp); - } - } else { - // update the timestamp even fmt=3 for first stream - if (is_fresh_packet && !chunk->extended_timestamp) { - chunk->header.timestamp += chunk->header.timestamp_delta; - } - srs_verbose("header read completed. fmt=%d, size=%d, ext_time=%d", - fmt, mh_size, chunk->extended_timestamp); - } - - if (chunk->extended_timestamp) { - mh_size += 4; - required_size = bh_size + mh_size; - srs_verbose("read header ext time. fmt=%d, ext_time=%d, mh_size=%d", fmt, chunk->extended_timestamp, mh_size); - if ((ret = buffer->ensure_buffer_bytes(skt, required_size)) != ERROR_SUCCESS) { - if (ret != ERROR_SOCKET_TIMEOUT) { - srs_error("read %dbytes message header failed. required_size=%d, ret=%d", mh_size, required_size, ret); - } - return ret; - } - - // ffmpeg/librtmp may donot send this filed, need to detect the value. - // @see also: http://blog.csdn.net/win_lin/article/details/13363699 - int32_t timestamp = 0x00; - char* pp = (char*)×tamp; - pp[3] = *p++; - pp[2] = *p++; - pp[1] = *p++; - pp[0] = *p++; - - // compare to the chunk timestamp, which is set by chunk message header - // type 0,1 or 2. - int32_t chunk_timestamp = chunk->header.timestamp; - if (chunk_timestamp > RTMP_EXTENDED_TIMESTAMP && chunk_timestamp != timestamp) { - mh_size -= 4; - srs_verbose("ignore the 4bytes extended timestamp. mh_size=%d", mh_size); - } else { - chunk->header.timestamp = timestamp; - } - srs_verbose("header read ext_time completed. time=%d", chunk->header.timestamp); - } - - // valid message - if (chunk->header.payload_length < 0) { - ret = ERROR_RTMP_MSG_INVLIAD_SIZE; - srs_error("RTMP message size must not be negative. size=%d, ret=%d", - chunk->header.payload_length, ret); - return ret; - } - - // copy header to msg - chunk->msg->header = chunk->header; - - // increase the msg count, the chunk stream can accept fmt=1/2/3 message now. - chunk->msg_count++; - - return ret; -} - -int SrsProtocol::read_message_payload(SrsChunkStream* chunk, int bh_size, int mh_size, int& payload_size, SrsCommonMessage** pmsg) -{ - int ret = ERROR_SUCCESS; - - // empty message - if (chunk->header.payload_length <= 0) { - // need erase the header in buffer. - buffer->erase(bh_size + mh_size); - - srs_trace("get an empty RTMP " - "message(type=%d, size=%d, time=%d, sid=%d)", chunk->header.message_type, - chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id); - - *pmsg = chunk->msg; - chunk->msg = NULL; - - return ret; - } - srs_assert(chunk->header.payload_length > 0); - - // the chunk payload size. - payload_size = chunk->header.payload_length - chunk->msg->size; - payload_size = srs_min(payload_size, in_chunk_size); - srs_verbose("chunk payload size is %d, message_size=%d, received_size=%d, in_chunk_size=%d", - payload_size, chunk->header.payload_length, chunk->msg->size, in_chunk_size); - - // create msg payload if not initialized - if (!chunk->msg->payload) { - chunk->msg->payload = new int8_t[chunk->header.payload_length]; - memset(chunk->msg->payload, 0, chunk->header.payload_length); - srs_verbose("create empty payload for RTMP message. size=%d", chunk->header.payload_length); - } - - // read payload to buffer - int required_size = bh_size + mh_size + payload_size; - if ((ret = buffer->ensure_buffer_bytes(skt, required_size)) != ERROR_SUCCESS) { - if (ret != ERROR_SOCKET_TIMEOUT) { - srs_error("read payload failed. required_size=%d, ret=%d", required_size, ret); - } - return ret; - } - memcpy(chunk->msg->payload + chunk->msg->size, buffer->bytes() + bh_size + mh_size, payload_size); - buffer->erase(bh_size + mh_size + payload_size); - chunk->msg->size += payload_size; - - srs_verbose("chunk payload read completed. bh_size=%d, mh_size=%d, payload_size=%d", bh_size, mh_size, payload_size); - - // got entire RTMP message? - if (chunk->header.payload_length == chunk->msg->size) { - *pmsg = chunk->msg; - chunk->msg = NULL; - srs_verbose("get entire RTMP message(type=%d, size=%d, time=%d, sid=%d)", - chunk->header.message_type, chunk->header.payload_length, - chunk->header.timestamp, chunk->header.stream_id); - return ret; - } - - srs_verbose("get partial RTMP message(type=%d, size=%d, time=%d, sid=%d), partial size=%d", - chunk->header.message_type, chunk->header.payload_length, - chunk->header.timestamp, chunk->header.stream_id, - chunk->msg->size); - - return ret; -} - -SrsMessageHeader::SrsMessageHeader() -{ - message_type = 0; - payload_length = 0; - timestamp_delta = 0; - stream_id = 0; - - timestamp = 0; -} - -SrsMessageHeader::~SrsMessageHeader() -{ -} - -bool SrsMessageHeader::is_audio() -{ - return message_type == RTMP_MSG_AudioMessage; -} - -bool SrsMessageHeader::is_video() -{ - return message_type == RTMP_MSG_VideoMessage; -} - -bool SrsMessageHeader::is_amf0_command() -{ - return message_type == RTMP_MSG_AMF0CommandMessage; -} - -bool SrsMessageHeader::is_amf0_data() -{ - return message_type == RTMP_MSG_AMF0DataMessage; -} - -bool SrsMessageHeader::is_amf3_command() -{ - return message_type == RTMP_MSG_AMF3CommandMessage; -} - -bool SrsMessageHeader::is_amf3_data() -{ - return message_type == RTMP_MSG_AMF3DataMessage; -} - -bool SrsMessageHeader::is_window_ackledgement_size() -{ - return message_type == RTMP_MSG_WindowAcknowledgementSize; -} - -bool SrsMessageHeader::is_set_chunk_size() -{ - return message_type == RTMP_MSG_SetChunkSize; -} - -bool SrsMessageHeader::is_user_control_message() -{ - return message_type == RTMP_MSG_UserControlMessage; -} - -SrsChunkStream::SrsChunkStream(int _cid) -{ - fmt = 0; - cid = _cid; - extended_timestamp = false; - msg = NULL; - msg_count = 0; -} - -SrsChunkStream::~SrsChunkStream() -{ - srs_freep(msg); -} - -ISrsMessage::ISrsMessage() -{ - payload = NULL; - size = 0; -} - -ISrsMessage::~ISrsMessage() -{ -} - -SrsCommonMessage::SrsCommonMessage() -{ - stream = NULL; - packet = NULL; -} - -SrsCommonMessage::~SrsCommonMessage() -{ - // we must directly free the ptrs, - // nevery use the virtual functions to delete, - // for in the destructor, the virtual functions is disabled. - - srs_freepa(payload); - srs_freep(packet); - srs_freep(stream); -} - -bool SrsCommonMessage::can_decode() -{ - return true; -} - -int SrsCommonMessage::decode_packet() -{ - int ret = ERROR_SUCCESS; - - srs_assert(payload != NULL); - srs_assert(size > 0); - - if (packet) { - srs_verbose("msg already decoded"); - return ret; - } - - if (!stream) { - srs_verbose("create decode stream for message."); - stream = new SrsStream(); - } - - // initialize the decode stream for all message, - // it's ok for the initialize if fast and without memory copy. - if ((ret = stream->initialize((char*)payload, size)) != ERROR_SUCCESS) { - srs_error("initialize stream failed. ret=%d", ret); - return ret; - } - srs_verbose("decode stream initialized success"); - - // decode specified packet type - if (header.is_amf0_command() || header.is_amf3_command() || header.is_amf0_data() || header.is_amf3_data()) { - srs_verbose("start to decode AMF0/AMF3 command message."); - - // skip 1bytes to decode the amf3 command. - if (header.is_amf3_command() && stream->require(1)) { - srs_verbose("skip 1bytes to decode AMF3 command"); - stream->skip(1); - } - - // amf0 command message. - // need to read the command name. - std::string command; - if ((ret = srs_amf0_read_string(stream, command)) != ERROR_SUCCESS) { - srs_error("decode AMF0/AMF3 command name failed. ret=%d", ret); - return ret; - } - srs_verbose("AMF0/AMF3 command message, command_name=%s", command.c_str()); - - // reset to zero(amf3 to 1) to restart decode. - stream->reset(); - if (header.is_amf3_command()) { - stream->skip(1); - } - - // decode command object. - if (command == RTMP_AMF0_COMMAND_CONNECT) { - srs_info("decode the AMF0/AMF3 command(connect vhost/app message)."); - packet = new SrsConnectAppPacket(); - return packet->decode(stream); - } else if(command == RTMP_AMF0_COMMAND_CREATE_STREAM) { - srs_info("decode the AMF0/AMF3 command(createStream message)."); - packet = new SrsCreateStreamPacket(); - return packet->decode(stream); - } else if(command == RTMP_AMF0_COMMAND_PLAY) { - srs_info("decode the AMF0/AMF3 command(paly message)."); - packet = new SrsPlayPacket(); - return packet->decode(stream); - } else if(command == RTMP_AMF0_COMMAND_PAUSE) { - srs_info("decode the AMF0/AMF3 command(pause message)."); - packet = new SrsPausePacket(); - return packet->decode(stream); - } else if(command == RTMP_AMF0_COMMAND_RELEASE_STREAM) { - srs_info("decode the AMF0/AMF3 command(FMLE releaseStream message)."); - packet = new SrsFMLEStartPacket(); - return packet->decode(stream); - } else if(command == RTMP_AMF0_COMMAND_FC_PUBLISH) { - srs_info("decode the AMF0/AMF3 command(FMLE FCPublish message)."); - packet = new SrsFMLEStartPacket(); - return packet->decode(stream); - } else if(command == RTMP_AMF0_COMMAND_PUBLISH) { - srs_info("decode the AMF0/AMF3 command(publish message)."); - packet = new SrsPublishPacket(); - return packet->decode(stream); - } else if(command == RTMP_AMF0_COMMAND_UNPUBLISH) { - srs_info("decode the AMF0/AMF3 command(unpublish message)."); - packet = new SrsFMLEStartPacket(); - return packet->decode(stream); - } else if(command == RTMP_AMF0_DATA_SET_DATAFRAME || command == RTMP_AMF0_DATA_ON_METADATA) { - srs_info("decode the AMF0/AMF3 data(onMetaData message)."); - packet = new SrsOnMetaDataPacket(); - return packet->decode(stream); - } - - // default packet to drop message. - srs_trace("drop the AMF0/AMF3 command message, command_name=%s", command.c_str()); - packet = new SrsPacket(); - return ret; - } else if(header.is_user_control_message()) { - srs_verbose("start to decode user control message."); - packet = new SrsUserControlPacket(); - return packet->decode(stream); - } else if(header.is_window_ackledgement_size()) { - srs_verbose("start to decode set ack window size message."); - packet = new SrsSetWindowAckSizePacket(); - return packet->decode(stream); - } else if(header.is_set_chunk_size()) { - srs_verbose("start to decode set chunk size message."); - packet = new SrsSetChunkSizePacket(); - return packet->decode(stream); - } else { - // default packet to drop message. - srs_trace("drop the unknown message, type=%d", header.message_type); - packet = new SrsPacket(); - } - - return ret; -} - -SrsPacket* SrsCommonMessage::get_packet() -{ - if (!packet) { - srs_error("the payload is raw/undecoded, invoke decode_packet to decode it."); - } - srs_assert(packet != NULL); - - return packet; -} - -int SrsCommonMessage::get_perfer_cid() -{ - if (!packet) { - return RTMP_CID_ProtocolControl; - } - - // we donot use the complex basic header, - // ensure the basic header is 1bytes. - if (packet->get_perfer_cid() < 2) { - return packet->get_perfer_cid(); - } - - return packet->get_perfer_cid(); -} - -void SrsCommonMessage::set_packet(SrsPacket* pkt, int stream_id) -{ - srs_freep(packet); - - packet = pkt; - - header.message_type = packet->get_message_type(); - header.payload_length = packet->get_payload_length(); - header.stream_id = stream_id; -} - -int SrsCommonMessage::encode_packet() -{ - int ret = ERROR_SUCCESS; - - if (packet == NULL) { - srs_warn("packet is empty, send out empty message."); - return ret; - } - // realloc the payload. - size = 0; - srs_freepa(payload); - - return packet->encode(size, (char*&)payload); -} - -SrsSharedPtrMessage::SrsSharedPtr::SrsSharedPtr() -{ - payload = NULL; - size = 0; - perfer_cid = 0; - shared_count = 0; -} - -SrsSharedPtrMessage::SrsSharedPtr::~SrsSharedPtr() -{ - srs_freepa(payload); -} - -SrsSharedPtrMessage::SrsSharedPtrMessage() -{ - ptr = NULL; -} - -SrsSharedPtrMessage::~SrsSharedPtrMessage() -{ - if (ptr) { - if (ptr->shared_count == 0) { - srs_freep(ptr); - } else { - ptr->shared_count--; - } - } -} - -bool SrsSharedPtrMessage::can_decode() -{ - return false; -} - -int SrsSharedPtrMessage::initialize(ISrsMessage* msg, char* payload, int size) -{ - int ret = ERROR_SUCCESS; - - srs_assert(msg != NULL); - if (ptr) { - ret = ERROR_SYSTEM_ASSERT_FAILED; - srs_error("should not set the payload twice. ret=%d", ret); - srs_assert(false); - - return ret; - } - - header = msg->header; - header.payload_length = size; - - ptr = new SrsSharedPtr(); - ptr->payload = payload; - ptr->size = size; - - if (msg->header.is_video()) { - ptr->perfer_cid = RTMP_CID_Video; - } else if (msg->header.is_audio()) { - ptr->perfer_cid = RTMP_CID_Audio; - } else { - ptr->perfer_cid = RTMP_CID_OverConnection2; - } - - super::payload = (int8_t*)ptr->payload; - super::size = ptr->size; - - return ret; -} - -SrsSharedPtrMessage* SrsSharedPtrMessage::copy() -{ - if (!ptr) { - srs_error("invoke initialize to initialize the ptr."); - srs_assert(false); - return NULL; - } - - SrsSharedPtrMessage* copy = new SrsSharedPtrMessage(); - - copy->header = header; - - copy->ptr = ptr; - ptr->shared_count++; - - copy->payload = (int8_t*)ptr->payload; - copy->size = ptr->size; - - return copy; -} - -int SrsSharedPtrMessage::get_perfer_cid() -{ - if (!ptr) { - return 0; - } - - return ptr->perfer_cid; -} - -int SrsSharedPtrMessage::encode_packet() -{ - srs_verbose("shared message ignore the encode method."); - return ERROR_SUCCESS; -} - -SrsPacket::SrsPacket() -{ -} - -SrsPacket::~SrsPacket() -{ -} - -int SrsPacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - srs_assert(stream != NULL); - - ret = ERROR_SYSTEM_PACKET_INVALID; - srs_error("current packet is not support to decode. " - "paket=%s, ret=%d", get_class_name(), ret); - - return ret; -} - -int SrsPacket::get_perfer_cid() -{ - return 0; -} - -int SrsPacket::get_message_type() -{ - return 0; -} - -int SrsPacket::get_payload_length() -{ - return get_size(); -} - -int SrsPacket::encode(int& psize, char*& ppayload) -{ - int ret = ERROR_SUCCESS; - - int size = get_size(); - char* payload = NULL; - - SrsStream stream; - - if (size > 0) { - payload = new char[size]; - - if ((ret = stream.initialize(payload, size)) != ERROR_SUCCESS) { - srs_error("initialize the stream failed. ret=%d", ret); - srs_freepa(payload); - return ret; - } - } - - if ((ret = encode_packet(&stream)) != ERROR_SUCCESS) { - srs_error("encode the packet failed. ret=%d", ret); - srs_freepa(payload); - return ret; - } - - psize = size; - ppayload = payload; - srs_verbose("encode the packet success. size=%d", size); - - return ret; -} - -int SrsPacket::get_size() -{ - return 0; -} - -int SrsPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - srs_assert(stream != NULL); - - ret = ERROR_SYSTEM_PACKET_INVALID; - srs_error("current packet is not support to encode. " - "paket=%s, ret=%d", get_class_name(), ret); - - return ret; -} - -SrsConnectAppPacket::SrsConnectAppPacket() -{ - command_name = RTMP_AMF0_COMMAND_CONNECT; - transaction_id = 1; - command_object = NULL; -} - -SrsConnectAppPacket::~SrsConnectAppPacket() -{ - srs_freep(command_object); -} - -int SrsConnectAppPacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode connect command_name failed. ret=%d", ret); - return ret; - } - if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_CONNECT) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 decode connect command_name failed. " - "command_name=%s, ret=%d", command_name.c_str(), ret); - return ret; - } - - if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode connect transaction_id failed. ret=%d", ret); - return ret; - } - if (transaction_id != 1.0) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 decode connect transaction_id failed. " - "required=%.1f, actual=%.1f, ret=%d", 1.0, transaction_id, ret); - return ret; - } - - if ((ret = srs_amf0_read_object(stream, command_object)) != ERROR_SUCCESS) { - srs_error("amf0 decode connect command_object failed. ret=%d", ret); - return ret; - } - if (command_object == NULL) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 decode connect command_object failed. ret=%d", ret); - return ret; - } - - srs_info("amf0 decode connect packet success"); - - return ret; -} - -SrsConnectAppResPacket::SrsConnectAppResPacket() -{ - command_name = RTMP_AMF0_COMMAND_RESULT; - transaction_id = 1; - props = new SrsAmf0Object(); - info = new SrsAmf0Object(); -} - -SrsConnectAppResPacket::~SrsConnectAppResPacket() -{ - srs_freep(props); - srs_freep(info); -} - -int SrsConnectAppResPacket::get_perfer_cid() -{ - return RTMP_CID_OverConnection; -} - -int SrsConnectAppResPacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsConnectAppResPacket::get_size() -{ - return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() - + srs_amf0_get_object_size(props)+ srs_amf0_get_object_size(info); -} - -int SrsConnectAppResPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("encode command_name failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_name success."); - - if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("encode transaction_id failed. ret=%d", ret); - return ret; - } - srs_verbose("encode transaction_id success."); - - if ((ret = srs_amf0_write_object(stream, props)) != ERROR_SUCCESS) { - srs_error("encode props failed. ret=%d", ret); - return ret; - } - srs_verbose("encode props success."); - - if ((ret = srs_amf0_write_object(stream, info)) != ERROR_SUCCESS) { - srs_error("encode info failed. ret=%d", ret); - return ret; - } - srs_verbose("encode info success."); - - srs_info("encode connect app response packet success."); - - return ret; -} - -SrsCreateStreamPacket::SrsCreateStreamPacket() -{ - command_name = RTMP_AMF0_COMMAND_CREATE_STREAM; - transaction_id = 2; - command_object = new SrsAmf0Null(); -} - -SrsCreateStreamPacket::~SrsCreateStreamPacket() -{ - srs_freep(command_object); -} - -int SrsCreateStreamPacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode createStream command_name failed. ret=%d", ret); - return ret; - } - if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_CREATE_STREAM) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 decode createStream command_name failed. " - "command_name=%s, ret=%d", command_name.c_str(), ret); - return ret; - } - - if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode createStream transaction_id failed. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode createStream command_object failed. ret=%d", ret); - return ret; - } - - srs_info("amf0 decode createStream packet success"); - - return ret; -} - -SrsCreateStreamResPacket::SrsCreateStreamResPacket(double _transaction_id, double _stream_id) -{ - command_name = RTMP_AMF0_COMMAND_RESULT; - transaction_id = _transaction_id; - command_object = new SrsAmf0Null(); - stream_id = _stream_id; -} - -SrsCreateStreamResPacket::~SrsCreateStreamResPacket() -{ - srs_freep(command_object); -} - -int SrsCreateStreamResPacket::get_perfer_cid() -{ - return RTMP_CID_OverConnection; -} - -int SrsCreateStreamResPacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsCreateStreamResPacket::get_size() -{ - return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() - + srs_amf0_get_null_size() + srs_amf0_get_number_size(); -} - -int SrsCreateStreamResPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("encode command_name failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_name success."); - - if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("encode transaction_id failed. ret=%d", ret); - return ret; - } - srs_verbose("encode transaction_id success."); - - if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { - srs_error("encode command_object failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_object success."); - - if ((ret = srs_amf0_write_number(stream, stream_id)) != ERROR_SUCCESS) { - srs_error("encode stream_id failed. ret=%d", ret); - return ret; - } - srs_verbose("encode stream_id success."); - - - srs_info("encode createStream response packet success."); - - return ret; -} - -SrsFMLEStartPacket::SrsFMLEStartPacket() -{ - command_name = RTMP_AMF0_COMMAND_CREATE_STREAM; - transaction_id = 0; - command_object = new SrsAmf0Null(); -} - -SrsFMLEStartPacket::~SrsFMLEStartPacket() -{ - srs_freep(command_object); -} - -int SrsFMLEStartPacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode FMLE start command_name failed. ret=%d", ret); - return ret; - } - if (command_name.empty() - || (command_name != RTMP_AMF0_COMMAND_RELEASE_STREAM - && command_name != RTMP_AMF0_COMMAND_FC_PUBLISH - && command_name != RTMP_AMF0_COMMAND_UNPUBLISH) - ) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 decode FMLE start command_name failed. " - "command_name=%s, ret=%d", command_name.c_str(), ret); - return ret; - } - - if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode FMLE start transaction_id failed. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode FMLE start command_object failed. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode FMLE start stream_name failed. ret=%d", ret); - return ret; - } - - srs_info("amf0 decode FMLE start packet success"); - - return ret; -} - -SrsFMLEStartResPacket::SrsFMLEStartResPacket(double _transaction_id) -{ - command_name = RTMP_AMF0_COMMAND_RESULT; - transaction_id = _transaction_id; - command_object = new SrsAmf0Null(); - args = new SrsAmf0Undefined(); -} - -SrsFMLEStartResPacket::~SrsFMLEStartResPacket() -{ - srs_freep(command_object); - srs_freep(args); -} - -int SrsFMLEStartResPacket::get_perfer_cid() -{ - return RTMP_CID_OverConnection; -} - -int SrsFMLEStartResPacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsFMLEStartResPacket::get_size() -{ - return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() - + srs_amf0_get_null_size() + srs_amf0_get_undefined_size(); -} - -int SrsFMLEStartResPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("encode command_name failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_name success."); - - if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("encode transaction_id failed. ret=%d", ret); - return ret; - } - srs_verbose("encode transaction_id success."); - - if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { - srs_error("encode command_object failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_object success."); - - if ((ret = srs_amf0_write_undefined(stream)) != ERROR_SUCCESS) { - srs_error("encode args failed. ret=%d", ret); - return ret; - } - srs_verbose("encode args success."); - - - srs_info("encode FMLE start response packet success."); - - return ret; -} - -SrsPublishPacket::SrsPublishPacket() -{ - command_name = RTMP_AMF0_COMMAND_PUBLISH; - transaction_id = 0; - command_object = new SrsAmf0Null(); - type = "live"; -} - -SrsPublishPacket::~SrsPublishPacket() -{ - srs_freep(command_object); -} - -int SrsPublishPacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode publish command_name failed. ret=%d", ret); - return ret; - } - if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_PUBLISH) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 decode publish command_name failed. " - "command_name=%s, ret=%d", command_name.c_str(), ret); - return ret; - } - - if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode publish transaction_id failed. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode publish command_object failed. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode publish stream_name failed. ret=%d", ret); - return ret; - } - - if (!stream->empty() && (ret = srs_amf0_read_string(stream, type)) != ERROR_SUCCESS) { - srs_error("amf0 decode publish type failed. ret=%d", ret); - return ret; - } - - srs_info("amf0 decode publish packet success"); - - return ret; -} - -SrsPausePacket::SrsPausePacket() -{ - command_name = RTMP_AMF0_COMMAND_PAUSE; - transaction_id = 0; - command_object = new SrsAmf0Null(); - - time_ms = 0; - is_pause = true; -} - -SrsPausePacket::~SrsPausePacket() -{ - srs_freep(command_object); -} - -int SrsPausePacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode pause command_name failed. ret=%d", ret); - return ret; - } - if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_PAUSE) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 decode pause command_name failed. " - "command_name=%s, ret=%d", command_name.c_str(), ret); - return ret; - } - - if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode pause transaction_id failed. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode pause command_object failed. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_read_boolean(stream, is_pause)) != ERROR_SUCCESS) { - srs_error("amf0 decode pause is_pause failed. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_read_number(stream, time_ms)) != ERROR_SUCCESS) { - srs_error("amf0 decode pause time_ms failed. ret=%d", ret); - return ret; - } - - srs_info("amf0 decode pause packet success"); - - return ret; -} - -SrsPlayPacket::SrsPlayPacket() -{ - command_name = RTMP_AMF0_COMMAND_PLAY; - transaction_id = 0; - command_object = new SrsAmf0Null(); - - start = -2; - duration = -1; - reset = true; -} - -SrsPlayPacket::~SrsPlayPacket() -{ - srs_freep(command_object); -} - -int SrsPlayPacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode play command_name failed. ret=%d", ret); - return ret; - } - if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_PLAY) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 decode play command_name failed. " - "command_name=%s, ret=%d", command_name.c_str(), ret); - return ret; - } - - if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode play transaction_id failed. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode play command_object failed. ret=%d", ret); - return ret; - } - - if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode play stream_name failed. ret=%d", ret); - return ret; - } - - if (!stream->empty() && (ret = srs_amf0_read_number(stream, start)) != ERROR_SUCCESS) { - srs_error("amf0 decode play start failed. ret=%d", ret); - return ret; - } - if (!stream->empty() && (ret = srs_amf0_read_number(stream, duration)) != ERROR_SUCCESS) { - srs_error("amf0 decode play duration failed. ret=%d", ret); - return ret; - } - if (!stream->empty() && (ret = srs_amf0_read_boolean(stream, reset)) != ERROR_SUCCESS) { - srs_error("amf0 decode play reset failed. ret=%d", ret); - return ret; - } - - srs_info("amf0 decode play packet success"); - - return ret; -} - -SrsPlayResPacket::SrsPlayResPacket() -{ - command_name = RTMP_AMF0_COMMAND_RESULT; - transaction_id = 0; - command_object = new SrsAmf0Null(); - desc = new SrsAmf0Object(); -} - -SrsPlayResPacket::~SrsPlayResPacket() -{ - srs_freep(command_object); - srs_freep(desc); -} - -int SrsPlayResPacket::get_perfer_cid() -{ - return RTMP_CID_OverStream; -} - -int SrsPlayResPacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsPlayResPacket::get_size() -{ - return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() - + srs_amf0_get_null_size() + srs_amf0_get_object_size(desc); -} - -int SrsPlayResPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("encode command_name failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_name success."); - - if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("encode transaction_id failed. ret=%d", ret); - return ret; - } - srs_verbose("encode transaction_id success."); - - if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { - srs_error("encode command_object failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_object success."); - - if ((ret = srs_amf0_write_object(stream, desc)) != ERROR_SUCCESS) { - srs_error("encode desc failed. ret=%d", ret); - return ret; - } - srs_verbose("encode desc success."); - - - srs_info("encode play response packet success."); - - return ret; -} - -SrsOnBWDonePacket::SrsOnBWDonePacket() -{ - command_name = RTMP_AMF0_COMMAND_ON_BW_DONE; - transaction_id = 0; - args = new SrsAmf0Null(); -} - -SrsOnBWDonePacket::~SrsOnBWDonePacket() -{ - srs_freep(args); -} - -int SrsOnBWDonePacket::get_perfer_cid() -{ - return RTMP_CID_OverConnection; -} - -int SrsOnBWDonePacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsOnBWDonePacket::get_size() -{ - return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() - + srs_amf0_get_null_size(); -} - -int SrsOnBWDonePacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("encode command_name failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_name success."); - - if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("encode transaction_id failed. ret=%d", ret); - return ret; - } - srs_verbose("encode transaction_id success."); - - if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { - srs_error("encode args failed. ret=%d", ret); - return ret; - } - srs_verbose("encode args success."); - - srs_info("encode onBWDone packet success."); - - return ret; -} - -SrsOnStatusCallPacket::SrsOnStatusCallPacket() -{ - command_name = RTMP_AMF0_COMMAND_ON_STATUS; - transaction_id = 0; - args = new SrsAmf0Null(); - data = new SrsAmf0Object(); -} - -SrsOnStatusCallPacket::~SrsOnStatusCallPacket() -{ - srs_freep(args); - srs_freep(data); -} - -int SrsOnStatusCallPacket::get_perfer_cid() -{ - return RTMP_CID_OverStream; -} - -int SrsOnStatusCallPacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsOnStatusCallPacket::get_size() -{ - return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() - + srs_amf0_get_null_size() + srs_amf0_get_object_size(data); -} - -int SrsOnStatusCallPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("encode command_name failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_name success."); - - if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("encode transaction_id failed. ret=%d", ret); - return ret; - } - srs_verbose("encode transaction_id success."); - - if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { - srs_error("encode args failed. ret=%d", ret); - return ret; - } - srs_verbose("encode args success.");; - - if ((ret = srs_amf0_write_object(stream, data)) != ERROR_SUCCESS) { - srs_error("encode data failed. ret=%d", ret); - return ret; - } - srs_verbose("encode data success."); - - srs_info("encode onStatus(Call) packet success."); - - return ret; -} - -SrsOnStatusDataPacket::SrsOnStatusDataPacket() -{ - command_name = RTMP_AMF0_COMMAND_ON_STATUS; - data = new SrsAmf0Object(); -} - -SrsOnStatusDataPacket::~SrsOnStatusDataPacket() -{ - srs_freep(data); -} - -int SrsOnStatusDataPacket::get_perfer_cid() -{ - return RTMP_CID_OverStream; -} - -int SrsOnStatusDataPacket::get_message_type() -{ - return RTMP_MSG_AMF0DataMessage; -} - -int SrsOnStatusDataPacket::get_size() -{ - return srs_amf0_get_string_size(command_name) + srs_amf0_get_object_size(data); -} - -int SrsOnStatusDataPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("encode command_name failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_name success."); - - if ((ret = srs_amf0_write_object(stream, data)) != ERROR_SUCCESS) { - srs_error("encode data failed. ret=%d", ret); - return ret; - } - srs_verbose("encode data success."); - - srs_info("encode onStatus(Data) packet success."); - - return ret; -} - -SrsSampleAccessPacket::SrsSampleAccessPacket() -{ - command_name = RTMP_AMF0_DATA_SAMPLE_ACCESS; - video_sample_access = false; - audio_sample_access = false; -} - -SrsSampleAccessPacket::~SrsSampleAccessPacket() -{ -} - -int SrsSampleAccessPacket::get_perfer_cid() -{ - return RTMP_CID_OverStream; -} - -int SrsSampleAccessPacket::get_message_type() -{ - return RTMP_MSG_AMF0DataMessage; -} - -int SrsSampleAccessPacket::get_size() -{ - return srs_amf0_get_string_size(command_name) - + srs_amf0_get_boolean_size() + srs_amf0_get_boolean_size(); -} - -int SrsSampleAccessPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("encode command_name failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_name success."); - - if ((ret = srs_amf0_write_boolean(stream, video_sample_access)) != ERROR_SUCCESS) { - srs_error("encode video_sample_access failed. ret=%d", ret); - return ret; - } - srs_verbose("encode video_sample_access success."); - - if ((ret = srs_amf0_write_boolean(stream, audio_sample_access)) != ERROR_SUCCESS) { - srs_error("encode audio_sample_access failed. ret=%d", ret); - return ret; - } - srs_verbose("encode audio_sample_access success.");; - - srs_info("encode |RtmpSampleAccess packet success."); - - return ret; -} - -SrsOnMetaDataPacket::SrsOnMetaDataPacket() -{ - name = RTMP_AMF0_DATA_ON_METADATA; - metadata = new SrsAmf0Object(); -} - -SrsOnMetaDataPacket::~SrsOnMetaDataPacket() -{ - srs_freep(metadata); -} - -int SrsOnMetaDataPacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_read_string(stream, name)) != ERROR_SUCCESS) { - srs_error("decode metadata name failed. ret=%d", ret); - return ret; - } - - // ignore the @setDataFrame - if (name == RTMP_AMF0_DATA_SET_DATAFRAME) { - if ((ret = srs_amf0_read_string(stream, name)) != ERROR_SUCCESS) { - srs_error("decode metadata name failed. ret=%d", ret); - return ret; - } - } - - srs_verbose("decode metadata name success. name=%s", name.c_str()); - - // the metadata maybe object or ecma array - SrsAmf0Any* any = NULL; - if ((ret = srs_amf0_read_any(stream, any)) != ERROR_SUCCESS) { - srs_error("decode metadata metadata failed. ret=%d", ret); - return ret; - } - - if (any->is_object()) { - srs_freep(metadata); - metadata = srs_amf0_convert(any); - srs_info("decode metadata object success"); - return ret; - } - - SrsASrsAmf0EcmaArray* arr = dynamic_cast(any); - if (!arr) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("decode metadata array failed. ret=%d", ret); - srs_freep(any); - return ret; - } - - for (int i = 0; i < arr->size(); i++) { - metadata->set(arr->key_at(i), arr->value_at(i)); - } - arr->clear(); - srs_info("decode metadata array success"); - - return ret; -} - -int SrsOnMetaDataPacket::get_perfer_cid() -{ - return RTMP_CID_OverConnection2; -} - -int SrsOnMetaDataPacket::get_message_type() -{ - return RTMP_MSG_AMF0DataMessage; -} - -int SrsOnMetaDataPacket::get_size() -{ - return srs_amf0_get_string_size(name) + srs_amf0_get_object_size(metadata); -} - -int SrsOnMetaDataPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_write_string(stream, name)) != ERROR_SUCCESS) { - srs_error("encode name failed. ret=%d", ret); - return ret; - } - srs_verbose("encode name success."); - - if ((ret = srs_amf0_write_object(stream, metadata)) != ERROR_SUCCESS) { - srs_error("encode metadata failed. ret=%d", ret); - return ret; - } - srs_verbose("encode metadata success."); - - srs_info("encode onMetaData packet success."); - return ret; -} - -SrsSetWindowAckSizePacket::SrsSetWindowAckSizePacket() -{ - ackowledgement_window_size = 0; -} - -SrsSetWindowAckSizePacket::~SrsSetWindowAckSizePacket() -{ -} - -int SrsSetWindowAckSizePacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if (!stream->require(4)) { - ret = ERROR_RTMP_MESSAGE_DECODE; - srs_error("decode ack window size failed. ret=%d", ret); - return ret; - } - - ackowledgement_window_size = stream->read_4bytes(); - srs_info("decode ack window size success"); - - return ret; -} - -int SrsSetWindowAckSizePacket::get_perfer_cid() -{ - return RTMP_CID_ProtocolControl; -} - -int SrsSetWindowAckSizePacket::get_message_type() -{ - return RTMP_MSG_WindowAcknowledgementSize; -} - -int SrsSetWindowAckSizePacket::get_size() -{ - return 4; -} - -int SrsSetWindowAckSizePacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if (!stream->require(4)) { - ret = ERROR_RTMP_MESSAGE_ENCODE; - srs_error("encode ack size packet failed. ret=%d", ret); - return ret; - } - - stream->write_4bytes(ackowledgement_window_size); - - srs_verbose("encode ack size packet " - "success. ack_size=%d", ackowledgement_window_size); - - return ret; -} - -SrsAcknowledgementPacket::SrsAcknowledgementPacket() -{ - sequence_number = 0; -} - -SrsAcknowledgementPacket::~SrsAcknowledgementPacket() -{ -} - -int SrsAcknowledgementPacket::get_perfer_cid() -{ - return RTMP_CID_ProtocolControl; -} - -int SrsAcknowledgementPacket::get_message_type() -{ - return RTMP_MSG_Acknowledgement; -} - -int SrsAcknowledgementPacket::get_size() -{ - return 4; -} - -int SrsAcknowledgementPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if (!stream->require(4)) { - ret = ERROR_RTMP_MESSAGE_ENCODE; - srs_error("encode acknowledgement packet failed. ret=%d", ret); - return ret; - } - - stream->write_4bytes(sequence_number); - - srs_verbose("encode acknowledgement packet " - "success. sequence_number=%d", sequence_number); - - return ret; -} - -SrsSetChunkSizePacket::SrsSetChunkSizePacket() -{ - chunk_size = RTMP_DEFAULT_CHUNK_SIZE; -} - -SrsSetChunkSizePacket::~SrsSetChunkSizePacket() -{ -} - -int SrsSetChunkSizePacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if (!stream->require(4)) { - ret = ERROR_RTMP_MESSAGE_DECODE; - srs_error("decode chunk size failed. ret=%d", ret); - return ret; - } - - chunk_size = stream->read_4bytes(); - srs_info("decode chunk size success. chunk_size=%d", chunk_size); - - if (chunk_size < RTMP_MIN_CHUNK_SIZE) { - ret = ERROR_RTMP_CHUNK_SIZE; - srs_error("invalid chunk size. min=%d, actual=%d, ret=%d", - ERROR_RTMP_CHUNK_SIZE, chunk_size, ret); - return ret; - } - - return ret; -} - -int SrsSetChunkSizePacket::get_perfer_cid() -{ - return RTMP_CID_ProtocolControl; -} - -int SrsSetChunkSizePacket::get_message_type() -{ - return RTMP_MSG_SetChunkSize; -} - -int SrsSetChunkSizePacket::get_size() -{ - return 4; -} - -int SrsSetChunkSizePacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if (!stream->require(4)) { - ret = ERROR_RTMP_MESSAGE_ENCODE; - srs_error("encode chunk packet failed. ret=%d", ret); - return ret; - } - - stream->write_4bytes(chunk_size); - - srs_verbose("encode chunk packet success. ack_size=%d", chunk_size); - - return ret; -} - -SrsSetPeerBandwidthPacket::SrsSetPeerBandwidthPacket() -{ - bandwidth = 0; - type = 2; -} - -SrsSetPeerBandwidthPacket::~SrsSetPeerBandwidthPacket() -{ -} - -int SrsSetPeerBandwidthPacket::get_perfer_cid() -{ - return RTMP_CID_ProtocolControl; -} - -int SrsSetPeerBandwidthPacket::get_message_type() -{ - return RTMP_MSG_SetPeerBandwidth; -} - -int SrsSetPeerBandwidthPacket::get_size() -{ - return 5; -} - -int SrsSetPeerBandwidthPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if (!stream->require(5)) { - ret = ERROR_RTMP_MESSAGE_ENCODE; - srs_error("encode set bandwidth packet failed. ret=%d", ret); - return ret; - } - - stream->write_4bytes(bandwidth); - stream->write_1bytes(type); - - srs_verbose("encode set bandwidth packet " - "success. bandwidth=%d, type=%d", bandwidth, type); - - return ret; -} - -SrsUserControlPacket::SrsUserControlPacket() -{ - event_type = 0; - event_data = 0; - extra_data = 0; -} - -SrsUserControlPacket::~SrsUserControlPacket() -{ -} - -int SrsUserControlPacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if (!stream->require(6)) { - ret = ERROR_RTMP_MESSAGE_DECODE; - srs_error("decode user control failed. ret=%d", ret); - return ret; - } - - event_type = stream->read_2bytes(); - event_data = stream->read_4bytes(); - - if (event_type == SrcPCUCSetBufferLength) { - if (!stream->require(4)) { - ret = ERROR_RTMP_MESSAGE_ENCODE; - srs_error("decode user control packet failed. ret=%d", ret); - return ret; - } - extra_data = stream->read_4bytes(); - } - - srs_info("decode user control success. " - "event_type=%d, event_data=%d, extra_data=%d", - event_type, event_data, extra_data); - - return ret; -} - -int SrsUserControlPacket::get_perfer_cid() -{ - return RTMP_CID_ProtocolControl; -} - -int SrsUserControlPacket::get_message_type() -{ - return RTMP_MSG_UserControlMessage; -} - -int SrsUserControlPacket::get_size() -{ - if (event_type == SrcPCUCSetBufferLength) { - return 2 + 4 + 4; - } else { - return 2 + 4; - } -} - -int SrsUserControlPacket::encode_packet(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if (!stream->require(get_size())) { - ret = ERROR_RTMP_MESSAGE_ENCODE; - srs_error("encode user control packet failed. ret=%d", ret); - return ret; - } - - stream->write_2bytes(event_type); - stream->write_4bytes(event_data); - - // when event type is set buffer length, - // read the extra buffer length. - if (event_type == SrcPCUCSetBufferLength) { - stream->write_2bytes(extra_data); - srs_verbose("user control message, buffer_length=%d", extra_data); - } - - srs_verbose("encode user control packet success. " - "event_type=%d, event_data=%d", event_type, event_data); - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +/**************************************************************************** +***************************************************************************** +****************************************************************************/ +/** +5. Protocol Control Messages +RTMP reserves message type IDs 1-7 for protocol control messages. +These messages contain information needed by the RTM Chunk Stream +protocol or RTMP itself. Protocol messages with IDs 1 & 2 are +reserved for usage with RTM Chunk Stream protocol. Protocol messages +with IDs 3-6 are reserved for usage of RTMP. Protocol message with ID +7 is used between edge server and origin server. +*/ +#define RTMP_MSG_SetChunkSize 0x01 +#define RTMP_MSG_AbortMessage 0x02 +#define RTMP_MSG_Acknowledgement 0x03 +#define RTMP_MSG_UserControlMessage 0x04 +#define RTMP_MSG_WindowAcknowledgementSize 0x05 +#define RTMP_MSG_SetPeerBandwidth 0x06 +#define RTMP_MSG_EdgeAndOriginServerCommand 0x07 +/** +3. Types of messages +The server and the client send messages over the network to +communicate with each other. The messages can be of any type which +includes audio messages, video messages, command messages, shared +object messages, data messages, and user control messages. +3.1. Command message +Command messages carry the AMF-encoded commands between the client +and the server. These messages have been assigned message type value +of 20 for AMF0 encoding and message type value of 17 for AMF3 +encoding. These messages are sent to perform some operations like +connect, createStream, publish, play, pause on the peer. Command +messages like onstatus, result etc. are used to inform the sender +about the status of the requested commands. A command message +consists of command name, transaction ID, and command object that +contains related parameters. A client or a server can request Remote +Procedure Calls (RPC) over streams that are communicated using the +command messages to the peer. +*/ +#define RTMP_MSG_AMF3CommandMessage 17 // 0x11 +#define RTMP_MSG_AMF0CommandMessage 20 // 0x14 +/** +3.2. Data message +The client or the server sends this message to send Metadata or any +user data to the peer. Metadata includes details about the +data(audio, video etc.) like creation time, duration, theme and so +on. These messages have been assigned message type value of 18 for +AMF0 and message type value of 15 for AMF3. +*/ +#define RTMP_MSG_AMF0DataMessage 18 // 0x12 +#define RTMP_MSG_AMF3DataMessage 15 // 0x0F +/** +3.3. Shared object message +A shared object is a Flash object (a collection of name value pairs) +that are in synchronization across multiple clients, instances, and +so on. The message types kMsgContainer=19 for AMF0 and +kMsgContainerEx=16 for AMF3 are reserved for shared object events. +Each message can contain multiple events. +*/ +#define RTMP_MSG_AMF3SharedObject 16 // 0x10 +#define RTMP_MSG_AMF0SharedObject 19 // 0x13 +/** +3.4. Audio message +The client or the server sends this message to send audio data to the +peer. The message type value of 8 is reserved for audio messages. +*/ +#define RTMP_MSG_AudioMessage 8 // 0x08 +/* * +3.5. Video message +The client or the server sends this message to send video data to the +peer. The message type value of 9 is reserved for video messages. +These messages are large and can delay the sending of other type of +messages. To avoid such a situation, the video message is assigned +the lowest priority. +*/ +#define RTMP_MSG_VideoMessage 9 // 0x09 +/** +3.6. Aggregate message +An aggregate message is a single message that contains a list of submessages. +The message type value of 22 is reserved for aggregate +messages. +*/ +#define RTMP_MSG_AggregateMessage 22 // 0x16 + +/**************************************************************************** +***************************************************************************** +****************************************************************************/ +/** +* 6.1.2. Chunk Message Header +* There are four different formats for the chunk message header, +* selected by the "fmt" field in the chunk basic header. +*/ +// 6.1.2.1. Type 0 +// Chunks of Type 0 are 11 bytes long. This type MUST be used at the +// start of a chunk stream, and whenever the stream timestamp goes +// backward (e.g., because of a backward seek). +#define RTMP_FMT_TYPE0 0 +// 6.1.2.2. Type 1 +// Chunks of Type 1 are 7 bytes long. The message stream ID is not +// included; this chunk takes the same stream ID as the preceding chunk. +// Streams with variable-sized messages (for example, many video +// formats) SHOULD use this format for the first chunk of each new +// message after the first. +#define RTMP_FMT_TYPE1 1 +// 6.1.2.3. Type 2 +// Chunks of Type 2 are 3 bytes long. Neither the stream ID nor the +// message length is included; this chunk has the same stream ID and +// message length as the preceding chunk. Streams with constant-sized +// messages (for example, some audio and data formats) SHOULD use this +// format for the first chunk of each message after the first. +#define RTMP_FMT_TYPE2 2 +// 6.1.2.4. Type 3 +// Chunks of Type 3 have no header. Stream ID, message length and +// timestamp delta are not present; chunks of this type take values from +// the preceding chunk. When a single message is split into chunks, all +// chunks of a message except the first one, SHOULD use this type. Refer +// to example 2 in section 6.2.2. Stream consisting of messages of +// exactly the same size, stream ID and spacing in time SHOULD use this +// type for all chunks after chunk of Type 2. Refer to example 1 in +// section 6.2.1. If the delta between the first message and the second +// message is same as the time stamp of first message, then chunk of +// type 3 would immediately follow the chunk of type 0 as there is no +// need for a chunk of type 2 to register the delta. If Type 3 chunk +// follows a Type 0 chunk, then timestamp delta for this Type 3 chunk is +// the same as the timestamp of Type 0 chunk. +#define RTMP_FMT_TYPE3 3 + +/**************************************************************************** +***************************************************************************** +****************************************************************************/ +/** +* 6. Chunking +* The chunk size is configurable. It can be set using a control +* message(Set Chunk Size) as described in section 7.1. The maximum +* chunk size can be 65536 bytes and minimum 128 bytes. Larger values +* reduce CPU usage, but also commit to larger writes that can delay +* other content on lower bandwidth connections. Smaller chunks are not +* good for high-bit rate streaming. Chunk size is maintained +* independently for each direction. +*/ +#define RTMP_DEFAULT_CHUNK_SIZE 128 +#define RTMP_MIN_CHUNK_SIZE 2 + +/** +* 6.1. Chunk Format +* Extended timestamp: 0 or 4 bytes +* This field MUST be sent when the normal timsestamp is set to +* 0xffffff, it MUST NOT be sent if the normal timestamp is set to +* anything else. So for values less than 0xffffff the normal +* timestamp field SHOULD be used in which case the extended timestamp +* MUST NOT be present. For values greater than or equal to 0xffffff +* the normal timestamp field MUST NOT be used and MUST be set to +* 0xffffff and the extended timestamp MUST be sent. +*/ +#define RTMP_EXTENDED_TIMESTAMP 0xFFFFFF + +/**************************************************************************** +***************************************************************************** +****************************************************************************/ +/** +* amf0 command message, command name macros +*/ +#define RTMP_AMF0_COMMAND_CONNECT "connect" +#define RTMP_AMF0_COMMAND_CREATE_STREAM "createStream" +#define RTMP_AMF0_COMMAND_PLAY "play" +#define RTMP_AMF0_COMMAND_PAUSE "pause" +#define RTMP_AMF0_COMMAND_ON_BW_DONE "onBWDone" +#define RTMP_AMF0_COMMAND_ON_STATUS "onStatus" +#define RTMP_AMF0_COMMAND_RESULT "_result" +#define RTMP_AMF0_COMMAND_RELEASE_STREAM "releaseStream" +#define RTMP_AMF0_COMMAND_FC_PUBLISH "FCPublish" +#define RTMP_AMF0_COMMAND_UNPUBLISH "FCUnpublish" +#define RTMP_AMF0_COMMAND_PUBLISH "publish" +#define RTMP_AMF0_DATA_SAMPLE_ACCESS "|RtmpSampleAccess" +#define RTMP_AMF0_DATA_SET_DATAFRAME "@setDataFrame" +#define RTMP_AMF0_DATA_ON_METADATA "onMetaData" + +/**************************************************************************** +***************************************************************************** +****************************************************************************/ +/** +* the chunk stream id used for some under-layer message, +* for example, the PC(protocol control) message. +*/ +#define RTMP_CID_ProtocolControl 0x02 +/** +* the AMF0/AMF3 command message, invoke method and return the result, over NetConnection. +* generally use 0x03. +*/ +#define RTMP_CID_OverConnection 0x03 +/** +* the AMF0/AMF3 command message, invoke method and return the result, over NetConnection, +* the midst state(we guess). +* rarely used, e.g. onStatus(NetStream.Play.Reset). +*/ +#define RTMP_CID_OverConnection2 0x04 +/** +* the stream message(amf0/amf3), over NetStream. +* generally use 0x05. +*/ +#define RTMP_CID_OverStream 0x05 +/** +* the stream message(amf0/amf3), over NetStream, the midst state(we guess). +* rarely used, e.g. play("mp4:mystram.f4v") +*/ +#define RTMP_CID_OverStream2 0x08 +/** +* the stream message(video), over NetStream +* generally use 0x06. +*/ +#define RTMP_CID_Video 0x06 +/** +* the stream message(audio), over NetStream. +* generally use 0x07. +*/ +#define RTMP_CID_Audio 0x07 + +/**************************************************************************** +***************************************************************************** +****************************************************************************/ +// when got a messae header, increase recv timeout to got an entire message. +#define SRS_MIN_RECV_TIMEOUT_US 3000 + +SrsProtocol::AckWindowSize::AckWindowSize() +{ + ack_window_size = acked_size = 0; +} + +SrsProtocol::SrsProtocol(st_netfd_t client_stfd) +{ + stfd = client_stfd; + buffer = new SrsBuffer(); + skt = new SrsSocket(stfd); + + in_chunk_size = out_chunk_size = RTMP_DEFAULT_CHUNK_SIZE; +} + +SrsProtocol::~SrsProtocol() +{ + std::map::iterator it; + + for (it = chunk_streams.begin(); it != chunk_streams.end(); ++it) { + SrsChunkStream* stream = it->second; + srs_freep(stream); + } + + chunk_streams.clear(); + + srs_freep(buffer); + srs_freep(skt); +} + +void SrsProtocol::set_recv_timeout(int64_t timeout_us) +{ + return skt->set_recv_timeout(timeout_us); +} + +int64_t SrsProtocol::get_recv_timeout() +{ + return skt->get_recv_timeout(); +} + +void SrsProtocol::set_send_timeout(int64_t timeout_us) +{ + return skt->set_send_timeout(timeout_us); +} + +int64_t SrsProtocol::get_recv_bytes() +{ + return skt->get_recv_bytes(); +} + +int64_t SrsProtocol::get_send_bytes() +{ + return skt->get_send_bytes(); +} + +int SrsProtocol::get_recv_kbps() +{ + return skt->get_recv_kbps(); +} + +int SrsProtocol::get_send_kbps() +{ + return skt->get_send_kbps(); +} + +int SrsProtocol::recv_message(SrsCommonMessage** pmsg) +{ + *pmsg = NULL; + + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + + if ((ret = recv_interlaced_message(&msg)) != ERROR_SUCCESS) { + if (ret != ERROR_SOCKET_TIMEOUT) { + srs_error("recv interlaced message failed. ret=%d", ret); + } + return ret; + } + srs_verbose("entire msg received"); + + if (!msg) { + continue; + } + + if (msg->size <= 0 || msg->header.payload_length <= 0) { + srs_trace("ignore empty message(type=%d, size=%d, time=%d, sid=%d).", + msg->header.message_type, msg->header.payload_length, + msg->header.timestamp, msg->header.stream_id); + srs_freep(msg); + continue; + } + + if ((ret = on_recv_message(msg)) != ERROR_SUCCESS) { + srs_error("hook the received msg failed. ret=%d", ret); + srs_freep(msg); + return ret; + } + + srs_verbose("get a msg with raw/undecoded payload"); + *pmsg = msg; + break; + } + + return ret; +} + +int SrsProtocol::send_message(ISrsMessage* msg) +{ + int ret = ERROR_SUCCESS; + + // free msg whatever return value. + SrsAutoFree(ISrsMessage, msg, false); + + if ((ret = msg->encode_packet()) != ERROR_SUCCESS) { + srs_error("encode packet to message payload failed. ret=%d", ret); + return ret; + } + srs_info("encode packet to message payload success"); + + // p set to current write position, + // it's ok when payload is NULL and size is 0. + char* p = (char*)msg->payload; + + // always write the header event payload is empty. + do { + // generate the header. + char* pheader = NULL; + int header_size = 0; + + if (p == (char*)msg->payload) { + // write new chunk stream header, fmt is 0 + pheader = out_header_fmt0; + *pheader++ = 0x00 | (msg->get_perfer_cid() & 0x3F); + + // chunk message header, 11 bytes + // timestamp, 3bytes, big-endian + if (msg->header.timestamp >= RTMP_EXTENDED_TIMESTAMP) { + *pheader++ = 0xFF; + *pheader++ = 0xFF; + *pheader++ = 0xFF; + } else { + pp = (char*)&msg->header.timestamp; + *pheader++ = pp[2]; + *pheader++ = pp[1]; + *pheader++ = pp[0]; + } + + // message_length, 3bytes, big-endian + pp = (char*)&msg->header.payload_length; + *pheader++ = pp[2]; + *pheader++ = pp[1]; + *pheader++ = pp[0]; + + // message_type, 1bytes + *pheader++ = msg->header.message_type; + + // message_length, 3bytes, little-endian + pp = (char*)&msg->header.stream_id; + *pheader++ = pp[0]; + *pheader++ = pp[1]; + *pheader++ = pp[2]; + *pheader++ = pp[3]; + + // chunk extended timestamp header, 0 or 4 bytes, big-endian + if(msg->header.timestamp >= RTMP_EXTENDED_TIMESTAMP){ + pp = (char*)&msg->header.timestamp; + *pheader++ = pp[3]; + *pheader++ = pp[2]; + *pheader++ = pp[1]; + *pheader++ = pp[0]; + } + + header_size = pheader - out_header_fmt0; + pheader = out_header_fmt0; + } else { + // write no message header chunk stream, fmt is 3 + pheader = out_header_fmt3; + *pheader++ = 0xC0 | (msg->get_perfer_cid() & 0x3F); + + // chunk extended timestamp header, 0 or 4 bytes, big-endian + // 6.1.3. Extended Timestamp + // This field is transmitted only when the normal time stamp in the + // chunk message header is set to 0x00ffffff. If normal time stamp is + // set to any value less than 0x00ffffff, this field MUST NOT be + // present. This field MUST NOT be present if the timestamp field is not + // present. Type 3 chunks MUST NOT have this field. + // adobe changed for Type3 chunk: + // FMLE always sendout the extended-timestamp, + // must send the extended-timestamp to FMS, + // must send the extended-timestamp to flash-player. + // @see: ngx_rtmp_prepare_message + // @see: http://blog.csdn.net/win_lin/article/details/13363699 + if(msg->header.timestamp >= RTMP_EXTENDED_TIMESTAMP){ + pp = (char*)&msg->header.timestamp; + *pheader++ = pp[3]; + *pheader++ = pp[2]; + *pheader++ = pp[1]; + *pheader++ = pp[0]; + } + + header_size = pheader - out_header_fmt3; + pheader = out_header_fmt3; + } + + // sendout header and payload by writev. + // decrease the sys invoke count to get higher performance. + int payload_size = msg->size - (p - (char*)msg->payload); + payload_size = srs_min(payload_size, out_chunk_size); + + // send by writev + iovec iov[2]; + iov[0].iov_base = pheader; + iov[0].iov_len = header_size; + iov[1].iov_base = p; + iov[1].iov_len = payload_size; + + ssize_t nwrite; + if ((ret = skt->writev(iov, 2, &nwrite)) != ERROR_SUCCESS) { + srs_error("send with writev failed. ret=%d", ret); + return ret; + } + + // consume sendout bytes when not empty packet. + if (msg->payload && msg->size > 0) { + p += payload_size; + } + } while (p < (char*)msg->payload + msg->size); + + if ((ret = on_send_message(msg)) != ERROR_SUCCESS) { + srs_error("hook the send message failed. ret=%d", ret); + return ret; + } + + return ret; +} + +int SrsProtocol::response_acknowledgement_message() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsAcknowledgementPacket* pkt = new SrsAcknowledgementPacket(); + + in_ack_size.acked_size = pkt->sequence_number = skt->get_recv_bytes(); + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send acknowledgement failed. ret=%d", ret); + return ret; + } + srs_verbose("send acknowledgement success."); + + return ret; +} + +int SrsProtocol::response_ping_message(int32_t timestamp) +{ + int ret = ERROR_SUCCESS; + + srs_trace("get a ping request, response it. timestamp=%d", timestamp); + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCPingResponse; + pkt->event_data = timestamp; + msg->set_packet(pkt, 0); + + if ((ret = send_message(msg)) != ERROR_SUCCESS) { + srs_error("send ping response failed. ret=%d", ret); + return ret; + } + srs_verbose("send ping response success."); + + return ret; +} + +int SrsProtocol::on_recv_message(SrsCommonMessage* msg) +{ + int ret = ERROR_SUCCESS; + + srs_assert(msg != NULL); + + // acknowledgement + if (in_ack_size.ack_window_size > 0 && skt->get_recv_bytes() - in_ack_size.acked_size > in_ack_size.ack_window_size) { + if ((ret = response_acknowledgement_message()) != ERROR_SUCCESS) { + return ret; + } + } + + switch (msg->header.message_type) { + case RTMP_MSG_SetChunkSize: + case RTMP_MSG_UserControlMessage: + case RTMP_MSG_WindowAcknowledgementSize: + if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { + srs_error("decode packet from message payload failed. ret=%d", ret); + return ret; + } + srs_verbose("decode packet from message payload success."); + break; + } + + switch (msg->header.message_type) { + case RTMP_MSG_WindowAcknowledgementSize: { + SrsSetWindowAckSizePacket* pkt = dynamic_cast(msg->get_packet()); + srs_assert(pkt != NULL); + + if (pkt->ackowledgement_window_size > 0) { + in_ack_size.ack_window_size = pkt->ackowledgement_window_size; + srs_trace("set ack window size to %d", pkt->ackowledgement_window_size); + } else { + srs_warn("ignored. set ack window size is %d", pkt->ackowledgement_window_size); + } + break; + } + case RTMP_MSG_SetChunkSize: { + SrsSetChunkSizePacket* pkt = dynamic_cast(msg->get_packet()); + srs_assert(pkt != NULL); + + in_chunk_size = pkt->chunk_size; + + srs_trace("set input chunk size to %d", pkt->chunk_size); + break; + } + case RTMP_MSG_UserControlMessage: { + SrsUserControlPacket* pkt = dynamic_cast(msg->get_packet()); + srs_assert(pkt != NULL); + + if (pkt->event_type == SrcPCUCSetBufferLength) { + srs_trace("ignored. set buffer length to %d", pkt->extra_data); + } + if (pkt->event_type == SrcPCUCPingRequest) { + if ((ret = response_ping_message(pkt->event_data)) != ERROR_SUCCESS) { + return ret; + } + } + break; + } + } + + return ret; +} + +int SrsProtocol::on_send_message(ISrsMessage* msg) +{ + int ret = ERROR_SUCCESS; + + if (!msg->can_decode()) { + srs_verbose("ignore the un-decodable message."); + return ret; + } + + SrsCommonMessage* common_msg = dynamic_cast(msg); + if (!msg) { + srs_verbose("ignore the shared ptr message."); + return ret; + } + + srs_assert(common_msg != NULL); + + switch (common_msg->header.message_type) { + case RTMP_MSG_SetChunkSize: { + SrsSetChunkSizePacket* pkt = dynamic_cast(common_msg->get_packet()); + srs_assert(pkt != NULL); + + out_chunk_size = pkt->chunk_size; + + srs_trace("set output chunk size to %d", pkt->chunk_size); + break; + } + } + + return ret; +} + +int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg) +{ + int ret = ERROR_SUCCESS; + + // chunk stream basic header. + char fmt = 0; + int cid = 0; + int bh_size = 0; + if ((ret = read_basic_header(fmt, cid, bh_size)) != ERROR_SUCCESS) { + if (ret != ERROR_SOCKET_TIMEOUT) { + srs_error("read basic header failed. ret=%d", ret); + } + return ret; + } + srs_verbose("read basic header success. fmt=%d, cid=%d, bh_size=%d", fmt, cid, bh_size); + + // once we got the chunk message header, + // that is there is a real message in cache, + // increase the timeout to got it. + // For example, in the play loop, we set timeout to 100ms, + // when we got a chunk header, we should increase the timeout, + // or we maybe timeout and disconnect the client. + int64_t timeout_us = skt->get_recv_timeout(); + if (timeout_us != (int64_t)ST_UTIME_NO_TIMEOUT) { + int64_t pkt_timeout_us = srs_max(timeout_us, SRS_MIN_RECV_TIMEOUT_US); + skt->set_recv_timeout(pkt_timeout_us); + srs_verbose("change recv timeout_us " + "from %"PRId64" to %"PRId64"", timeout_us, pkt_timeout_us); + } + + // get the cached chunk stream. + SrsChunkStream* chunk = NULL; + + if (chunk_streams.find(cid) == chunk_streams.end()) { + chunk = chunk_streams[cid] = new SrsChunkStream(cid); + srs_verbose("cache new chunk stream: fmt=%d, cid=%d", fmt, cid); + } else { + chunk = chunk_streams[cid]; + srs_verbose("cached chunk stream: fmt=%d, cid=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)", + chunk->fmt, chunk->cid, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, chunk->header.payload_length, + chunk->header.timestamp, chunk->header.stream_id); + } + + // chunk stream message header + int mh_size = 0; + if ((ret = read_message_header(chunk, fmt, bh_size, mh_size)) != ERROR_SUCCESS) { + if (ret != ERROR_SOCKET_TIMEOUT) { + srs_error("read message header failed. ret=%d", ret); + } + return ret; + } + srs_verbose("read message header success. " + "fmt=%d, mh_size=%d, ext_time=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)", + fmt, mh_size, chunk->extended_timestamp, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, + chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id); + + // read msg payload from chunk stream. + SrsCommonMessage* msg = NULL; + int payload_size = 0; + if ((ret = read_message_payload(chunk, bh_size, mh_size, payload_size, &msg)) != ERROR_SUCCESS) { + if (ret != ERROR_SOCKET_TIMEOUT) { + srs_error("read message payload failed. ret=%d", ret); + } + return ret; + } + + // reset the recv timeout + if (timeout_us != (int64_t)ST_UTIME_NO_TIMEOUT) { + skt->set_recv_timeout(timeout_us); + srs_verbose("reset recv timeout_us to %"PRId64"", timeout_us); + } + + // not got an entire RTMP message, try next chunk. + if (!msg) { + srs_verbose("get partial message success. chunk_payload_size=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)", + payload_size, (msg? msg->size : (chunk->msg? chunk->msg->size : 0)), chunk->header.message_type, chunk->header.payload_length, + chunk->header.timestamp, chunk->header.stream_id); + return ret; + } + + *pmsg = msg; + srs_info("get entire message success. chunk_payload_size=%d, size=%d, message(type=%d, size=%d, time=%d, sid=%d)", + payload_size, (msg? msg->size : (chunk->msg? chunk->msg->size : 0)), chunk->header.message_type, chunk->header.payload_length, + chunk->header.timestamp, chunk->header.stream_id); + + return ret; +} + +int SrsProtocol::read_basic_header(char& fmt, int& cid, int& bh_size) +{ + int ret = ERROR_SUCCESS; + + int required_size = 1; + if ((ret = buffer->ensure_buffer_bytes(skt, required_size)) != ERROR_SUCCESS) { + if (ret != ERROR_SOCKET_TIMEOUT) { + srs_error("read 1bytes basic header failed. required_size=%d, ret=%d", required_size, ret); + } + return ret; + } + + char* p = buffer->bytes(); + + fmt = (*p >> 6) & 0x03; + cid = *p & 0x3f; + bh_size = 1; + + if (cid > 1) { + srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", bh_size, fmt, cid); + return ret; + } + + if (cid == 0) { + required_size = 2; + if ((ret = buffer->ensure_buffer_bytes(skt, required_size)) != ERROR_SUCCESS) { + if (ret != ERROR_SOCKET_TIMEOUT) { + srs_error("read 2bytes basic header failed. required_size=%d, ret=%d", required_size, ret); + } + return ret; + } + + cid = 64; + cid += *(++p); + bh_size = 2; + srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", bh_size, fmt, cid); + } else if (cid == 1) { + required_size = 3; + if ((ret = buffer->ensure_buffer_bytes(skt, 3)) != ERROR_SUCCESS) { + if (ret != ERROR_SOCKET_TIMEOUT) { + srs_error("read 3bytes basic header failed. required_size=%d, ret=%d", required_size, ret); + } + return ret; + } + + cid = 64; + cid += *(++p); + cid += *(++p) * 256; + bh_size = 3; + srs_verbose("%dbytes basic header parsed. fmt=%d, cid=%d", bh_size, fmt, cid); + } else { + srs_error("invalid path, impossible basic header."); + srs_assert(false); + } + + return ret; +} + +int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size) +{ + int ret = ERROR_SUCCESS; + + /** + * we should not assert anything about fmt, for the first packet. + * (when first packet, the chunk->msg is NULL). + * the fmt maybe 0/1/2/3, the FMLE will send a 0xC4 for some audio packet. + * the previous packet is: + * 04 // fmt=0, cid=4 + * 00 00 1a // timestamp=26 + * 00 00 9d // payload_length=157 + * 08 // message_type=8(audio) + * 01 00 00 00 // stream_id=1 + * the current packet maybe: + * c4 // fmt=3, cid=4 + * it's ok, for the packet is audio, and timestamp delta is 26. + * the current packet must be parsed as: + * fmt=0, cid=4 + * timestamp=26+26=52 + * payload_length=157 + * message_type=8(audio) + * stream_id=1 + * so we must update the timestamp even fmt=3 for first packet. + */ + // fresh packet used to update the timestamp even fmt=3 for first packet. + bool is_fresh_packet = !chunk->msg; + + // but, we can ensure that when a chunk stream is fresh, + // the fmt must be 0, a new stream. + if (chunk->msg_count == 0 && fmt != RTMP_FMT_TYPE0) { + ret = ERROR_RTMP_CHUNK_START; + srs_error("chunk stream is fresh, " + "fmt must be %d, actual is %d. ret=%d", RTMP_FMT_TYPE0, fmt, ret); + return ret; + } + + // when exists cache msg, means got an partial message, + // the fmt must not be type0 which means new message. + if (chunk->msg && fmt == RTMP_FMT_TYPE0) { + ret = ERROR_RTMP_CHUNK_START; + srs_error("chunk stream exists, " + "fmt must not be %d, actual is %d. ret=%d", RTMP_FMT_TYPE0, fmt, ret); + return ret; + } + + // create msg when new chunk stream start + if (!chunk->msg) { + chunk->msg = new SrsCommonMessage(); + srs_verbose("create message for new chunk, fmt=%d, cid=%d", fmt, chunk->cid); + } + + // read message header from socket to buffer. + static char mh_sizes[] = {11, 7, 3, 0}; + mh_size = mh_sizes[(int)fmt]; + srs_verbose("calc chunk message header size. fmt=%d, mh_size=%d", fmt, mh_size); + + int required_size = bh_size + mh_size; + if ((ret = buffer->ensure_buffer_bytes(skt, required_size)) != ERROR_SUCCESS) { + if (ret != ERROR_SOCKET_TIMEOUT) { + srs_error("read %dbytes message header failed. required_size=%d, ret=%d", mh_size, required_size, ret); + } + return ret; + } + char* p = buffer->bytes() + bh_size; + + // parse the message header. + // see also: ngx_rtmp_recv + if (fmt <= RTMP_FMT_TYPE2) { + char* pp = (char*)&chunk->header.timestamp_delta; + pp[2] = *p++; + pp[1] = *p++; + pp[0] = *p++; + pp[3] = 0; + + // fmt: 0 + // timestamp: 3 bytes + // If the timestamp is greater than or equal to 16777215 + // (hexadecimal 0x00ffffff), this value MUST be 16777215, and the + // ‘extended timestamp header’ MUST be present. Otherwise, this value + // SHOULD be the entire timestamp. + // + // fmt: 1 or 2 + // timestamp delta: 3 bytes + // If the delta is greater than or equal to 16777215 (hexadecimal + // 0x00ffffff), this value MUST be 16777215, and the ‘extended + // timestamp header’ MUST be present. Otherwise, this value SHOULD be + // the entire delta. + chunk->extended_timestamp = (chunk->header.timestamp_delta >= RTMP_EXTENDED_TIMESTAMP); + if (chunk->extended_timestamp) { + // Extended timestamp: 0 or 4 bytes + // This field MUST be sent when the normal timsestamp is set to + // 0xffffff, it MUST NOT be sent if the normal timestamp is set to + // anything else. So for values less than 0xffffff the normal + // timestamp field SHOULD be used in which case the extended timestamp + // MUST NOT be present. For values greater than or equal to 0xffffff + // the normal timestamp field MUST NOT be used and MUST be set to + // 0xffffff and the extended timestamp MUST be sent. + // + // if extended timestamp, the timestamp must >= RTMP_EXTENDED_TIMESTAMP + // we set the timestamp to RTMP_EXTENDED_TIMESTAMP to identify we + // got an extended timestamp. + chunk->header.timestamp = RTMP_EXTENDED_TIMESTAMP; + } else { + if (fmt == RTMP_FMT_TYPE0) { + // 6.1.2.1. Type 0 + // For a type-0 chunk, the absolute timestamp of the message is sent + // here. + chunk->header.timestamp = chunk->header.timestamp_delta; + } else { + // 6.1.2.2. Type 1 + // 6.1.2.3. Type 2 + // For a type-1 or type-2 chunk, the difference between the previous + // chunk's timestamp and the current chunk's timestamp is sent here. + chunk->header.timestamp += chunk->header.timestamp_delta; + } + } + + if (fmt <= RTMP_FMT_TYPE1) { + pp = (char*)&chunk->header.payload_length; + pp[2] = *p++; + pp[1] = *p++; + pp[0] = *p++; + pp[3] = 0; + + // if msg exists in cache, the size must not changed. + if (chunk->msg->size > 0 && chunk->msg->size != chunk->header.payload_length) { + ret = ERROR_RTMP_PACKET_SIZE; + srs_error("msg exists in chunk cache, " + "size=%d cannot change to %d, ret=%d", + chunk->msg->size, chunk->header.payload_length, ret); + return ret; + } + + chunk->header.message_type = *p++; + + if (fmt == 0) { + pp = (char*)&chunk->header.stream_id; + pp[0] = *p++; + pp[1] = *p++; + pp[2] = *p++; + pp[3] = *p++; + srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d, payload=%d, type=%d, sid=%d", + fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp, chunk->header.payload_length, + chunk->header.message_type, chunk->header.stream_id); + } else { + srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d, payload=%d, type=%d", + fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp, chunk->header.payload_length, + chunk->header.message_type); + } + } else { + srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%d", + fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp); + } + } else { + // update the timestamp even fmt=3 for first stream + if (is_fresh_packet && !chunk->extended_timestamp) { + chunk->header.timestamp += chunk->header.timestamp_delta; + } + srs_verbose("header read completed. fmt=%d, size=%d, ext_time=%d", + fmt, mh_size, chunk->extended_timestamp); + } + + if (chunk->extended_timestamp) { + mh_size += 4; + required_size = bh_size + mh_size; + srs_verbose("read header ext time. fmt=%d, ext_time=%d, mh_size=%d", fmt, chunk->extended_timestamp, mh_size); + if ((ret = buffer->ensure_buffer_bytes(skt, required_size)) != ERROR_SUCCESS) { + if (ret != ERROR_SOCKET_TIMEOUT) { + srs_error("read %dbytes message header failed. required_size=%d, ret=%d", mh_size, required_size, ret); + } + return ret; + } + + // ffmpeg/librtmp may donot send this filed, need to detect the value. + // @see also: http://blog.csdn.net/win_lin/article/details/13363699 + int32_t timestamp = 0x00; + char* pp = (char*)×tamp; + pp[3] = *p++; + pp[2] = *p++; + pp[1] = *p++; + pp[0] = *p++; + + // compare to the chunk timestamp, which is set by chunk message header + // type 0,1 or 2. + int32_t chunk_timestamp = chunk->header.timestamp; + if (chunk_timestamp > RTMP_EXTENDED_TIMESTAMP && chunk_timestamp != timestamp) { + mh_size -= 4; + srs_verbose("ignore the 4bytes extended timestamp. mh_size=%d", mh_size); + } else { + chunk->header.timestamp = timestamp; + } + srs_verbose("header read ext_time completed. time=%d", chunk->header.timestamp); + } + + // valid message + if (chunk->header.payload_length < 0) { + ret = ERROR_RTMP_MSG_INVLIAD_SIZE; + srs_error("RTMP message size must not be negative. size=%d, ret=%d", + chunk->header.payload_length, ret); + return ret; + } + + // copy header to msg + chunk->msg->header = chunk->header; + + // increase the msg count, the chunk stream can accept fmt=1/2/3 message now. + chunk->msg_count++; + + return ret; +} + +int SrsProtocol::read_message_payload(SrsChunkStream* chunk, int bh_size, int mh_size, int& payload_size, SrsCommonMessage** pmsg) +{ + int ret = ERROR_SUCCESS; + + // empty message + if (chunk->header.payload_length <= 0) { + // need erase the header in buffer. + buffer->erase(bh_size + mh_size); + + srs_trace("get an empty RTMP " + "message(type=%d, size=%d, time=%d, sid=%d)", chunk->header.message_type, + chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id); + + *pmsg = chunk->msg; + chunk->msg = NULL; + + return ret; + } + srs_assert(chunk->header.payload_length > 0); + + // the chunk payload size. + payload_size = chunk->header.payload_length - chunk->msg->size; + payload_size = srs_min(payload_size, in_chunk_size); + srs_verbose("chunk payload size is %d, message_size=%d, received_size=%d, in_chunk_size=%d", + payload_size, chunk->header.payload_length, chunk->msg->size, in_chunk_size); + + // create msg payload if not initialized + if (!chunk->msg->payload) { + chunk->msg->payload = new int8_t[chunk->header.payload_length]; + memset(chunk->msg->payload, 0, chunk->header.payload_length); + srs_verbose("create empty payload for RTMP message. size=%d", chunk->header.payload_length); + } + + // read payload to buffer + int required_size = bh_size + mh_size + payload_size; + if ((ret = buffer->ensure_buffer_bytes(skt, required_size)) != ERROR_SUCCESS) { + if (ret != ERROR_SOCKET_TIMEOUT) { + srs_error("read payload failed. required_size=%d, ret=%d", required_size, ret); + } + return ret; + } + memcpy(chunk->msg->payload + chunk->msg->size, buffer->bytes() + bh_size + mh_size, payload_size); + buffer->erase(bh_size + mh_size + payload_size); + chunk->msg->size += payload_size; + + srs_verbose("chunk payload read completed. bh_size=%d, mh_size=%d, payload_size=%d", bh_size, mh_size, payload_size); + + // got entire RTMP message? + if (chunk->header.payload_length == chunk->msg->size) { + *pmsg = chunk->msg; + chunk->msg = NULL; + srs_verbose("get entire RTMP message(type=%d, size=%d, time=%d, sid=%d)", + chunk->header.message_type, chunk->header.payload_length, + chunk->header.timestamp, chunk->header.stream_id); + return ret; + } + + srs_verbose("get partial RTMP message(type=%d, size=%d, time=%d, sid=%d), partial size=%d", + chunk->header.message_type, chunk->header.payload_length, + chunk->header.timestamp, chunk->header.stream_id, + chunk->msg->size); + + return ret; +} + +SrsMessageHeader::SrsMessageHeader() +{ + message_type = 0; + payload_length = 0; + timestamp_delta = 0; + stream_id = 0; + + timestamp = 0; +} + +SrsMessageHeader::~SrsMessageHeader() +{ +} + +bool SrsMessageHeader::is_audio() +{ + return message_type == RTMP_MSG_AudioMessage; +} + +bool SrsMessageHeader::is_video() +{ + return message_type == RTMP_MSG_VideoMessage; +} + +bool SrsMessageHeader::is_amf0_command() +{ + return message_type == RTMP_MSG_AMF0CommandMessage; +} + +bool SrsMessageHeader::is_amf0_data() +{ + return message_type == RTMP_MSG_AMF0DataMessage; +} + +bool SrsMessageHeader::is_amf3_command() +{ + return message_type == RTMP_MSG_AMF3CommandMessage; +} + +bool SrsMessageHeader::is_amf3_data() +{ + return message_type == RTMP_MSG_AMF3DataMessage; +} + +bool SrsMessageHeader::is_window_ackledgement_size() +{ + return message_type == RTMP_MSG_WindowAcknowledgementSize; +} + +bool SrsMessageHeader::is_set_chunk_size() +{ + return message_type == RTMP_MSG_SetChunkSize; +} + +bool SrsMessageHeader::is_user_control_message() +{ + return message_type == RTMP_MSG_UserControlMessage; +} + +SrsChunkStream::SrsChunkStream(int _cid) +{ + fmt = 0; + cid = _cid; + extended_timestamp = false; + msg = NULL; + msg_count = 0; +} + +SrsChunkStream::~SrsChunkStream() +{ + srs_freep(msg); +} + +ISrsMessage::ISrsMessage() +{ + payload = NULL; + size = 0; +} + +ISrsMessage::~ISrsMessage() +{ +} + +SrsCommonMessage::SrsCommonMessage() +{ + stream = NULL; + packet = NULL; +} + +SrsCommonMessage::~SrsCommonMessage() +{ + // we must directly free the ptrs, + // nevery use the virtual functions to delete, + // for in the destructor, the virtual functions is disabled. + + srs_freepa(payload); + srs_freep(packet); + srs_freep(stream); +} + +bool SrsCommonMessage::can_decode() +{ + return true; +} + +int SrsCommonMessage::decode_packet() +{ + int ret = ERROR_SUCCESS; + + srs_assert(payload != NULL); + srs_assert(size > 0); + + if (packet) { + srs_verbose("msg already decoded"); + return ret; + } + + if (!stream) { + srs_verbose("create decode stream for message."); + stream = new SrsStream(); + } + + // initialize the decode stream for all message, + // it's ok for the initialize if fast and without memory copy. + if ((ret = stream->initialize((char*)payload, size)) != ERROR_SUCCESS) { + srs_error("initialize stream failed. ret=%d", ret); + return ret; + } + srs_verbose("decode stream initialized success"); + + // decode specified packet type + if (header.is_amf0_command() || header.is_amf3_command() || header.is_amf0_data() || header.is_amf3_data()) { + srs_verbose("start to decode AMF0/AMF3 command message."); + + // skip 1bytes to decode the amf3 command. + if (header.is_amf3_command() && stream->require(1)) { + srs_verbose("skip 1bytes to decode AMF3 command"); + stream->skip(1); + } + + // amf0 command message. + // need to read the command name. + std::string command; + if ((ret = srs_amf0_read_string(stream, command)) != ERROR_SUCCESS) { + srs_error("decode AMF0/AMF3 command name failed. ret=%d", ret); + return ret; + } + srs_verbose("AMF0/AMF3 command message, command_name=%s", command.c_str()); + + // reset to zero(amf3 to 1) to restart decode. + stream->reset(); + if (header.is_amf3_command()) { + stream->skip(1); + } + + // decode command object. + if (command == RTMP_AMF0_COMMAND_CONNECT) { + srs_info("decode the AMF0/AMF3 command(connect vhost/app message)."); + packet = new SrsConnectAppPacket(); + return packet->decode(stream); + } else if(command == RTMP_AMF0_COMMAND_CREATE_STREAM) { + srs_info("decode the AMF0/AMF3 command(createStream message)."); + packet = new SrsCreateStreamPacket(); + return packet->decode(stream); + } else if(command == RTMP_AMF0_COMMAND_PLAY) { + srs_info("decode the AMF0/AMF3 command(paly message)."); + packet = new SrsPlayPacket(); + return packet->decode(stream); + } else if(command == RTMP_AMF0_COMMAND_PAUSE) { + srs_info("decode the AMF0/AMF3 command(pause message)."); + packet = new SrsPausePacket(); + return packet->decode(stream); + } else if(command == RTMP_AMF0_COMMAND_RELEASE_STREAM) { + srs_info("decode the AMF0/AMF3 command(FMLE releaseStream message)."); + packet = new SrsFMLEStartPacket(); + return packet->decode(stream); + } else if(command == RTMP_AMF0_COMMAND_FC_PUBLISH) { + srs_info("decode the AMF0/AMF3 command(FMLE FCPublish message)."); + packet = new SrsFMLEStartPacket(); + return packet->decode(stream); + } else if(command == RTMP_AMF0_COMMAND_PUBLISH) { + srs_info("decode the AMF0/AMF3 command(publish message)."); + packet = new SrsPublishPacket(); + return packet->decode(stream); + } else if(command == RTMP_AMF0_COMMAND_UNPUBLISH) { + srs_info("decode the AMF0/AMF3 command(unpublish message)."); + packet = new SrsFMLEStartPacket(); + return packet->decode(stream); + } else if(command == RTMP_AMF0_DATA_SET_DATAFRAME || command == RTMP_AMF0_DATA_ON_METADATA) { + srs_info("decode the AMF0/AMF3 data(onMetaData message)."); + packet = new SrsOnMetaDataPacket(); + return packet->decode(stream); + } + + // default packet to drop message. + srs_trace("drop the AMF0/AMF3 command message, command_name=%s", command.c_str()); + packet = new SrsPacket(); + return ret; + } else if(header.is_user_control_message()) { + srs_verbose("start to decode user control message."); + packet = new SrsUserControlPacket(); + return packet->decode(stream); + } else if(header.is_window_ackledgement_size()) { + srs_verbose("start to decode set ack window size message."); + packet = new SrsSetWindowAckSizePacket(); + return packet->decode(stream); + } else if(header.is_set_chunk_size()) { + srs_verbose("start to decode set chunk size message."); + packet = new SrsSetChunkSizePacket(); + return packet->decode(stream); + } else { + // default packet to drop message. + srs_trace("drop the unknown message, type=%d", header.message_type); + packet = new SrsPacket(); + } + + return ret; +} + +SrsPacket* SrsCommonMessage::get_packet() +{ + if (!packet) { + srs_error("the payload is raw/undecoded, invoke decode_packet to decode it."); + } + srs_assert(packet != NULL); + + return packet; +} + +int SrsCommonMessage::get_perfer_cid() +{ + if (!packet) { + return RTMP_CID_ProtocolControl; + } + + // we donot use the complex basic header, + // ensure the basic header is 1bytes. + if (packet->get_perfer_cid() < 2) { + return packet->get_perfer_cid(); + } + + return packet->get_perfer_cid(); +} + +void SrsCommonMessage::set_packet(SrsPacket* pkt, int stream_id) +{ + srs_freep(packet); + + packet = pkt; + + header.message_type = packet->get_message_type(); + header.payload_length = packet->get_payload_length(); + header.stream_id = stream_id; +} + +int SrsCommonMessage::encode_packet() +{ + int ret = ERROR_SUCCESS; + + if (packet == NULL) { + srs_warn("packet is empty, send out empty message."); + return ret; + } + // realloc the payload. + size = 0; + srs_freepa(payload); + + return packet->encode(size, (char*&)payload); +} + +SrsSharedPtrMessage::SrsSharedPtr::SrsSharedPtr() +{ + payload = NULL; + size = 0; + perfer_cid = 0; + shared_count = 0; +} + +SrsSharedPtrMessage::SrsSharedPtr::~SrsSharedPtr() +{ + srs_freepa(payload); +} + +SrsSharedPtrMessage::SrsSharedPtrMessage() +{ + ptr = NULL; +} + +SrsSharedPtrMessage::~SrsSharedPtrMessage() +{ + if (ptr) { + if (ptr->shared_count == 0) { + srs_freep(ptr); + } else { + ptr->shared_count--; + } + } +} + +bool SrsSharedPtrMessage::can_decode() +{ + return false; +} + +int SrsSharedPtrMessage::initialize(ISrsMessage* msg, char* payload, int size) +{ + int ret = ERROR_SUCCESS; + + srs_assert(msg != NULL); + if (ptr) { + ret = ERROR_SYSTEM_ASSERT_FAILED; + srs_error("should not set the payload twice. ret=%d", ret); + srs_assert(false); + + return ret; + } + + header = msg->header; + header.payload_length = size; + + ptr = new SrsSharedPtr(); + ptr->payload = payload; + ptr->size = size; + + if (msg->header.is_video()) { + ptr->perfer_cid = RTMP_CID_Video; + } else if (msg->header.is_audio()) { + ptr->perfer_cid = RTMP_CID_Audio; + } else { + ptr->perfer_cid = RTMP_CID_OverConnection2; + } + + super::payload = (int8_t*)ptr->payload; + super::size = ptr->size; + + return ret; +} + +SrsSharedPtrMessage* SrsSharedPtrMessage::copy() +{ + if (!ptr) { + srs_error("invoke initialize to initialize the ptr."); + srs_assert(false); + return NULL; + } + + SrsSharedPtrMessage* copy = new SrsSharedPtrMessage(); + + copy->header = header; + + copy->ptr = ptr; + ptr->shared_count++; + + copy->payload = (int8_t*)ptr->payload; + copy->size = ptr->size; + + return copy; +} + +int SrsSharedPtrMessage::get_perfer_cid() +{ + if (!ptr) { + return 0; + } + + return ptr->perfer_cid; +} + +int SrsSharedPtrMessage::encode_packet() +{ + srs_verbose("shared message ignore the encode method."); + return ERROR_SUCCESS; +} + +SrsPacket::SrsPacket() +{ +} + +SrsPacket::~SrsPacket() +{ +} + +int SrsPacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + srs_assert(stream != NULL); + + ret = ERROR_SYSTEM_PACKET_INVALID; + srs_error("current packet is not support to decode. " + "paket=%s, ret=%d", get_class_name(), ret); + + return ret; +} + +int SrsPacket::get_perfer_cid() +{ + return 0; +} + +int SrsPacket::get_message_type() +{ + return 0; +} + +int SrsPacket::get_payload_length() +{ + return get_size(); +} + +int SrsPacket::encode(int& psize, char*& ppayload) +{ + int ret = ERROR_SUCCESS; + + int size = get_size(); + char* payload = NULL; + + SrsStream stream; + + if (size > 0) { + payload = new char[size]; + + if ((ret = stream.initialize(payload, size)) != ERROR_SUCCESS) { + srs_error("initialize the stream failed. ret=%d", ret); + srs_freepa(payload); + return ret; + } + } + + if ((ret = encode_packet(&stream)) != ERROR_SUCCESS) { + srs_error("encode the packet failed. ret=%d", ret); + srs_freepa(payload); + return ret; + } + + psize = size; + ppayload = payload; + srs_verbose("encode the packet success. size=%d", size); + + return ret; +} + +int SrsPacket::get_size() +{ + return 0; +} + +int SrsPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + srs_assert(stream != NULL); + + ret = ERROR_SYSTEM_PACKET_INVALID; + srs_error("current packet is not support to encode. " + "paket=%s, ret=%d", get_class_name(), ret); + + return ret; +} + +SrsConnectAppPacket::SrsConnectAppPacket() +{ + command_name = RTMP_AMF0_COMMAND_CONNECT; + transaction_id = 1; + command_object = NULL; +} + +SrsConnectAppPacket::~SrsConnectAppPacket() +{ + srs_freep(command_object); +} + +int SrsConnectAppPacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode connect command_name failed. ret=%d", ret); + return ret; + } + if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_CONNECT) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 decode connect command_name failed. " + "command_name=%s, ret=%d", command_name.c_str(), ret); + return ret; + } + + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode connect transaction_id failed. ret=%d", ret); + return ret; + } + if (transaction_id != 1.0) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 decode connect transaction_id failed. " + "required=%.1f, actual=%.1f, ret=%d", 1.0, transaction_id, ret); + return ret; + } + + if ((ret = srs_amf0_read_object(stream, command_object)) != ERROR_SUCCESS) { + srs_error("amf0 decode connect command_object failed. ret=%d", ret); + return ret; + } + if (command_object == NULL) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 decode connect command_object failed. ret=%d", ret); + return ret; + } + + srs_info("amf0 decode connect packet success"); + + return ret; +} + +SrsConnectAppResPacket::SrsConnectAppResPacket() +{ + command_name = RTMP_AMF0_COMMAND_RESULT; + transaction_id = 1; + props = new SrsAmf0Object(); + info = new SrsAmf0Object(); +} + +SrsConnectAppResPacket::~SrsConnectAppResPacket() +{ + srs_freep(props); + srs_freep(info); +} + +int SrsConnectAppResPacket::get_perfer_cid() +{ + return RTMP_CID_OverConnection; +} + +int SrsConnectAppResPacket::get_message_type() +{ + return RTMP_MSG_AMF0CommandMessage; +} + +int SrsConnectAppResPacket::get_size() +{ + return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() + + srs_amf0_get_object_size(props)+ srs_amf0_get_object_size(info); +} + +int SrsConnectAppResPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("encode command_name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_name success."); + + if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("encode transaction_id failed. ret=%d", ret); + return ret; + } + srs_verbose("encode transaction_id success."); + + if ((ret = srs_amf0_write_object(stream, props)) != ERROR_SUCCESS) { + srs_error("encode props failed. ret=%d", ret); + return ret; + } + srs_verbose("encode props success."); + + if ((ret = srs_amf0_write_object(stream, info)) != ERROR_SUCCESS) { + srs_error("encode info failed. ret=%d", ret); + return ret; + } + srs_verbose("encode info success."); + + srs_info("encode connect app response packet success."); + + return ret; +} + +SrsCreateStreamPacket::SrsCreateStreamPacket() +{ + command_name = RTMP_AMF0_COMMAND_CREATE_STREAM; + transaction_id = 2; + command_object = new SrsAmf0Null(); +} + +SrsCreateStreamPacket::~SrsCreateStreamPacket() +{ + srs_freep(command_object); +} + +int SrsCreateStreamPacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode createStream command_name failed. ret=%d", ret); + return ret; + } + if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_CREATE_STREAM) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 decode createStream command_name failed. " + "command_name=%s, ret=%d", command_name.c_str(), ret); + return ret; + } + + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode createStream transaction_id failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode createStream command_object failed. ret=%d", ret); + return ret; + } + + srs_info("amf0 decode createStream packet success"); + + return ret; +} + +SrsCreateStreamResPacket::SrsCreateStreamResPacket(double _transaction_id, double _stream_id) +{ + command_name = RTMP_AMF0_COMMAND_RESULT; + transaction_id = _transaction_id; + command_object = new SrsAmf0Null(); + stream_id = _stream_id; +} + +SrsCreateStreamResPacket::~SrsCreateStreamResPacket() +{ + srs_freep(command_object); +} + +int SrsCreateStreamResPacket::get_perfer_cid() +{ + return RTMP_CID_OverConnection; +} + +int SrsCreateStreamResPacket::get_message_type() +{ + return RTMP_MSG_AMF0CommandMessage; +} + +int SrsCreateStreamResPacket::get_size() +{ + return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() + + srs_amf0_get_null_size() + srs_amf0_get_number_size(); +} + +int SrsCreateStreamResPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("encode command_name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_name success."); + + if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("encode transaction_id failed. ret=%d", ret); + return ret; + } + srs_verbose("encode transaction_id success."); + + if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { + srs_error("encode command_object failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_object success."); + + if ((ret = srs_amf0_write_number(stream, stream_id)) != ERROR_SUCCESS) { + srs_error("encode stream_id failed. ret=%d", ret); + return ret; + } + srs_verbose("encode stream_id success."); + + + srs_info("encode createStream response packet success."); + + return ret; +} + +SrsFMLEStartPacket::SrsFMLEStartPacket() +{ + command_name = RTMP_AMF0_COMMAND_CREATE_STREAM; + transaction_id = 0; + command_object = new SrsAmf0Null(); +} + +SrsFMLEStartPacket::~SrsFMLEStartPacket() +{ + srs_freep(command_object); +} + +int SrsFMLEStartPacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode FMLE start command_name failed. ret=%d", ret); + return ret; + } + if (command_name.empty() + || (command_name != RTMP_AMF0_COMMAND_RELEASE_STREAM + && command_name != RTMP_AMF0_COMMAND_FC_PUBLISH + && command_name != RTMP_AMF0_COMMAND_UNPUBLISH) + ) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 decode FMLE start command_name failed. " + "command_name=%s, ret=%d", command_name.c_str(), ret); + return ret; + } + + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode FMLE start transaction_id failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode FMLE start command_object failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode FMLE start stream_name failed. ret=%d", ret); + return ret; + } + + srs_info("amf0 decode FMLE start packet success"); + + return ret; +} + +SrsFMLEStartResPacket::SrsFMLEStartResPacket(double _transaction_id) +{ + command_name = RTMP_AMF0_COMMAND_RESULT; + transaction_id = _transaction_id; + command_object = new SrsAmf0Null(); + args = new SrsAmf0Undefined(); +} + +SrsFMLEStartResPacket::~SrsFMLEStartResPacket() +{ + srs_freep(command_object); + srs_freep(args); +} + +int SrsFMLEStartResPacket::get_perfer_cid() +{ + return RTMP_CID_OverConnection; +} + +int SrsFMLEStartResPacket::get_message_type() +{ + return RTMP_MSG_AMF0CommandMessage; +} + +int SrsFMLEStartResPacket::get_size() +{ + return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() + + srs_amf0_get_null_size() + srs_amf0_get_undefined_size(); +} + +int SrsFMLEStartResPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("encode command_name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_name success."); + + if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("encode transaction_id failed. ret=%d", ret); + return ret; + } + srs_verbose("encode transaction_id success."); + + if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { + srs_error("encode command_object failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_object success."); + + if ((ret = srs_amf0_write_undefined(stream)) != ERROR_SUCCESS) { + srs_error("encode args failed. ret=%d", ret); + return ret; + } + srs_verbose("encode args success."); + + + srs_info("encode FMLE start response packet success."); + + return ret; +} + +SrsPublishPacket::SrsPublishPacket() +{ + command_name = RTMP_AMF0_COMMAND_PUBLISH; + transaction_id = 0; + command_object = new SrsAmf0Null(); + type = "live"; +} + +SrsPublishPacket::~SrsPublishPacket() +{ + srs_freep(command_object); +} + +int SrsPublishPacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode publish command_name failed. ret=%d", ret); + return ret; + } + if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_PUBLISH) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 decode publish command_name failed. " + "command_name=%s, ret=%d", command_name.c_str(), ret); + return ret; + } + + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode publish transaction_id failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode publish command_object failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode publish stream_name failed. ret=%d", ret); + return ret; + } + + if (!stream->empty() && (ret = srs_amf0_read_string(stream, type)) != ERROR_SUCCESS) { + srs_error("amf0 decode publish type failed. ret=%d", ret); + return ret; + } + + srs_info("amf0 decode publish packet success"); + + return ret; +} + +SrsPausePacket::SrsPausePacket() +{ + command_name = RTMP_AMF0_COMMAND_PAUSE; + transaction_id = 0; + command_object = new SrsAmf0Null(); + + time_ms = 0; + is_pause = true; +} + +SrsPausePacket::~SrsPausePacket() +{ + srs_freep(command_object); +} + +int SrsPausePacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode pause command_name failed. ret=%d", ret); + return ret; + } + if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_PAUSE) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 decode pause command_name failed. " + "command_name=%s, ret=%d", command_name.c_str(), ret); + return ret; + } + + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode pause transaction_id failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode pause command_object failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_boolean(stream, is_pause)) != ERROR_SUCCESS) { + srs_error("amf0 decode pause is_pause failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_number(stream, time_ms)) != ERROR_SUCCESS) { + srs_error("amf0 decode pause time_ms failed. ret=%d", ret); + return ret; + } + + srs_info("amf0 decode pause packet success"); + + return ret; +} + +SrsPlayPacket::SrsPlayPacket() +{ + command_name = RTMP_AMF0_COMMAND_PLAY; + transaction_id = 0; + command_object = new SrsAmf0Null(); + + start = -2; + duration = -1; + reset = true; +} + +SrsPlayPacket::~SrsPlayPacket() +{ + srs_freep(command_object); +} + +int SrsPlayPacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode play command_name failed. ret=%d", ret); + return ret; + } + if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_PLAY) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 decode play command_name failed. " + "command_name=%s, ret=%d", command_name.c_str(), ret); + return ret; + } + + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode play transaction_id failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode play command_object failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode play stream_name failed. ret=%d", ret); + return ret; + } + + if (!stream->empty() && (ret = srs_amf0_read_number(stream, start)) != ERROR_SUCCESS) { + srs_error("amf0 decode play start failed. ret=%d", ret); + return ret; + } + if (!stream->empty() && (ret = srs_amf0_read_number(stream, duration)) != ERROR_SUCCESS) { + srs_error("amf0 decode play duration failed. ret=%d", ret); + return ret; + } + if (!stream->empty() && (ret = srs_amf0_read_boolean(stream, reset)) != ERROR_SUCCESS) { + srs_error("amf0 decode play reset failed. ret=%d", ret); + return ret; + } + + srs_info("amf0 decode play packet success"); + + return ret; +} + +SrsPlayResPacket::SrsPlayResPacket() +{ + command_name = RTMP_AMF0_COMMAND_RESULT; + transaction_id = 0; + command_object = new SrsAmf0Null(); + desc = new SrsAmf0Object(); +} + +SrsPlayResPacket::~SrsPlayResPacket() +{ + srs_freep(command_object); + srs_freep(desc); +} + +int SrsPlayResPacket::get_perfer_cid() +{ + return RTMP_CID_OverStream; +} + +int SrsPlayResPacket::get_message_type() +{ + return RTMP_MSG_AMF0CommandMessage; +} + +int SrsPlayResPacket::get_size() +{ + return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() + + srs_amf0_get_null_size() + srs_amf0_get_object_size(desc); +} + +int SrsPlayResPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("encode command_name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_name success."); + + if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("encode transaction_id failed. ret=%d", ret); + return ret; + } + srs_verbose("encode transaction_id success."); + + if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { + srs_error("encode command_object failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_object success."); + + if ((ret = srs_amf0_write_object(stream, desc)) != ERROR_SUCCESS) { + srs_error("encode desc failed. ret=%d", ret); + return ret; + } + srs_verbose("encode desc success."); + + + srs_info("encode play response packet success."); + + return ret; +} + +SrsOnBWDonePacket::SrsOnBWDonePacket() +{ + command_name = RTMP_AMF0_COMMAND_ON_BW_DONE; + transaction_id = 0; + args = new SrsAmf0Null(); +} + +SrsOnBWDonePacket::~SrsOnBWDonePacket() +{ + srs_freep(args); +} + +int SrsOnBWDonePacket::get_perfer_cid() +{ + return RTMP_CID_OverConnection; +} + +int SrsOnBWDonePacket::get_message_type() +{ + return RTMP_MSG_AMF0CommandMessage; +} + +int SrsOnBWDonePacket::get_size() +{ + return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() + + srs_amf0_get_null_size(); +} + +int SrsOnBWDonePacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("encode command_name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_name success."); + + if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("encode transaction_id failed. ret=%d", ret); + return ret; + } + srs_verbose("encode transaction_id success."); + + if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { + srs_error("encode args failed. ret=%d", ret); + return ret; + } + srs_verbose("encode args success."); + + srs_info("encode onBWDone packet success."); + + return ret; +} + +SrsOnStatusCallPacket::SrsOnStatusCallPacket() +{ + command_name = RTMP_AMF0_COMMAND_ON_STATUS; + transaction_id = 0; + args = new SrsAmf0Null(); + data = new SrsAmf0Object(); +} + +SrsOnStatusCallPacket::~SrsOnStatusCallPacket() +{ + srs_freep(args); + srs_freep(data); +} + +int SrsOnStatusCallPacket::get_perfer_cid() +{ + return RTMP_CID_OverStream; +} + +int SrsOnStatusCallPacket::get_message_type() +{ + return RTMP_MSG_AMF0CommandMessage; +} + +int SrsOnStatusCallPacket::get_size() +{ + return srs_amf0_get_string_size(command_name) + srs_amf0_get_number_size() + + srs_amf0_get_null_size() + srs_amf0_get_object_size(data); +} + +int SrsOnStatusCallPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("encode command_name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_name success."); + + if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("encode transaction_id failed. ret=%d", ret); + return ret; + } + srs_verbose("encode transaction_id success."); + + if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { + srs_error("encode args failed. ret=%d", ret); + return ret; + } + srs_verbose("encode args success.");; + + if ((ret = srs_amf0_write_object(stream, data)) != ERROR_SUCCESS) { + srs_error("encode data failed. ret=%d", ret); + return ret; + } + srs_verbose("encode data success."); + + srs_info("encode onStatus(Call) packet success."); + + return ret; +} + +SrsOnStatusDataPacket::SrsOnStatusDataPacket() +{ + command_name = RTMP_AMF0_COMMAND_ON_STATUS; + data = new SrsAmf0Object(); +} + +SrsOnStatusDataPacket::~SrsOnStatusDataPacket() +{ + srs_freep(data); +} + +int SrsOnStatusDataPacket::get_perfer_cid() +{ + return RTMP_CID_OverStream; +} + +int SrsOnStatusDataPacket::get_message_type() +{ + return RTMP_MSG_AMF0DataMessage; +} + +int SrsOnStatusDataPacket::get_size() +{ + return srs_amf0_get_string_size(command_name) + srs_amf0_get_object_size(data); +} + +int SrsOnStatusDataPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("encode command_name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_name success."); + + if ((ret = srs_amf0_write_object(stream, data)) != ERROR_SUCCESS) { + srs_error("encode data failed. ret=%d", ret); + return ret; + } + srs_verbose("encode data success."); + + srs_info("encode onStatus(Data) packet success."); + + return ret; +} + +SrsSampleAccessPacket::SrsSampleAccessPacket() +{ + command_name = RTMP_AMF0_DATA_SAMPLE_ACCESS; + video_sample_access = false; + audio_sample_access = false; +} + +SrsSampleAccessPacket::~SrsSampleAccessPacket() +{ +} + +int SrsSampleAccessPacket::get_perfer_cid() +{ + return RTMP_CID_OverStream; +} + +int SrsSampleAccessPacket::get_message_type() +{ + return RTMP_MSG_AMF0DataMessage; +} + +int SrsSampleAccessPacket::get_size() +{ + return srs_amf0_get_string_size(command_name) + + srs_amf0_get_boolean_size() + srs_amf0_get_boolean_size(); +} + +int SrsSampleAccessPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("encode command_name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_name success."); + + if ((ret = srs_amf0_write_boolean(stream, video_sample_access)) != ERROR_SUCCESS) { + srs_error("encode video_sample_access failed. ret=%d", ret); + return ret; + } + srs_verbose("encode video_sample_access success."); + + if ((ret = srs_amf0_write_boolean(stream, audio_sample_access)) != ERROR_SUCCESS) { + srs_error("encode audio_sample_access failed. ret=%d", ret); + return ret; + } + srs_verbose("encode audio_sample_access success.");; + + srs_info("encode |RtmpSampleAccess packet success."); + + return ret; +} + +SrsOnMetaDataPacket::SrsOnMetaDataPacket() +{ + name = RTMP_AMF0_DATA_ON_METADATA; + metadata = new SrsAmf0Object(); +} + +SrsOnMetaDataPacket::~SrsOnMetaDataPacket() +{ + srs_freep(metadata); +} + +int SrsOnMetaDataPacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_read_string(stream, name)) != ERROR_SUCCESS) { + srs_error("decode metadata name failed. ret=%d", ret); + return ret; + } + + // ignore the @setDataFrame + if (name == RTMP_AMF0_DATA_SET_DATAFRAME) { + if ((ret = srs_amf0_read_string(stream, name)) != ERROR_SUCCESS) { + srs_error("decode metadata name failed. ret=%d", ret); + return ret; + } + } + + srs_verbose("decode metadata name success. name=%s", name.c_str()); + + // the metadata maybe object or ecma array + SrsAmf0Any* any = NULL; + if ((ret = srs_amf0_read_any(stream, any)) != ERROR_SUCCESS) { + srs_error("decode metadata metadata failed. ret=%d", ret); + return ret; + } + + if (any->is_object()) { + srs_freep(metadata); + metadata = srs_amf0_convert(any); + srs_info("decode metadata object success"); + return ret; + } + + SrsASrsAmf0EcmaArray* arr = dynamic_cast(any); + if (!arr) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("decode metadata array failed. ret=%d", ret); + srs_freep(any); + return ret; + } + + for (int i = 0; i < arr->size(); i++) { + metadata->set(arr->key_at(i), arr->value_at(i)); + } + arr->clear(); + srs_info("decode metadata array success"); + + return ret; +} + +int SrsOnMetaDataPacket::get_perfer_cid() +{ + return RTMP_CID_OverConnection2; +} + +int SrsOnMetaDataPacket::get_message_type() +{ + return RTMP_MSG_AMF0DataMessage; +} + +int SrsOnMetaDataPacket::get_size() +{ + return srs_amf0_get_string_size(name) + srs_amf0_get_object_size(metadata); +} + +int SrsOnMetaDataPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_write_string(stream, name)) != ERROR_SUCCESS) { + srs_error("encode name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode name success."); + + if ((ret = srs_amf0_write_object(stream, metadata)) != ERROR_SUCCESS) { + srs_error("encode metadata failed. ret=%d", ret); + return ret; + } + srs_verbose("encode metadata success."); + + srs_info("encode onMetaData packet success."); + return ret; +} + +SrsSetWindowAckSizePacket::SrsSetWindowAckSizePacket() +{ + ackowledgement_window_size = 0; +} + +SrsSetWindowAckSizePacket::~SrsSetWindowAckSizePacket() +{ +} + +int SrsSetWindowAckSizePacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if (!stream->require(4)) { + ret = ERROR_RTMP_MESSAGE_DECODE; + srs_error("decode ack window size failed. ret=%d", ret); + return ret; + } + + ackowledgement_window_size = stream->read_4bytes(); + srs_info("decode ack window size success"); + + return ret; +} + +int SrsSetWindowAckSizePacket::get_perfer_cid() +{ + return RTMP_CID_ProtocolControl; +} + +int SrsSetWindowAckSizePacket::get_message_type() +{ + return RTMP_MSG_WindowAcknowledgementSize; +} + +int SrsSetWindowAckSizePacket::get_size() +{ + return 4; +} + +int SrsSetWindowAckSizePacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if (!stream->require(4)) { + ret = ERROR_RTMP_MESSAGE_ENCODE; + srs_error("encode ack size packet failed. ret=%d", ret); + return ret; + } + + stream->write_4bytes(ackowledgement_window_size); + + srs_verbose("encode ack size packet " + "success. ack_size=%d", ackowledgement_window_size); + + return ret; +} + +SrsAcknowledgementPacket::SrsAcknowledgementPacket() +{ + sequence_number = 0; +} + +SrsAcknowledgementPacket::~SrsAcknowledgementPacket() +{ +} + +int SrsAcknowledgementPacket::get_perfer_cid() +{ + return RTMP_CID_ProtocolControl; +} + +int SrsAcknowledgementPacket::get_message_type() +{ + return RTMP_MSG_Acknowledgement; +} + +int SrsAcknowledgementPacket::get_size() +{ + return 4; +} + +int SrsAcknowledgementPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if (!stream->require(4)) { + ret = ERROR_RTMP_MESSAGE_ENCODE; + srs_error("encode acknowledgement packet failed. ret=%d", ret); + return ret; + } + + stream->write_4bytes(sequence_number); + + srs_verbose("encode acknowledgement packet " + "success. sequence_number=%d", sequence_number); + + return ret; +} + +SrsSetChunkSizePacket::SrsSetChunkSizePacket() +{ + chunk_size = RTMP_DEFAULT_CHUNK_SIZE; +} + +SrsSetChunkSizePacket::~SrsSetChunkSizePacket() +{ +} + +int SrsSetChunkSizePacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if (!stream->require(4)) { + ret = ERROR_RTMP_MESSAGE_DECODE; + srs_error("decode chunk size failed. ret=%d", ret); + return ret; + } + + chunk_size = stream->read_4bytes(); + srs_info("decode chunk size success. chunk_size=%d", chunk_size); + + if (chunk_size < RTMP_MIN_CHUNK_SIZE) { + ret = ERROR_RTMP_CHUNK_SIZE; + srs_error("invalid chunk size. min=%d, actual=%d, ret=%d", + ERROR_RTMP_CHUNK_SIZE, chunk_size, ret); + return ret; + } + + return ret; +} + +int SrsSetChunkSizePacket::get_perfer_cid() +{ + return RTMP_CID_ProtocolControl; +} + +int SrsSetChunkSizePacket::get_message_type() +{ + return RTMP_MSG_SetChunkSize; +} + +int SrsSetChunkSizePacket::get_size() +{ + return 4; +} + +int SrsSetChunkSizePacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if (!stream->require(4)) { + ret = ERROR_RTMP_MESSAGE_ENCODE; + srs_error("encode chunk packet failed. ret=%d", ret); + return ret; + } + + stream->write_4bytes(chunk_size); + + srs_verbose("encode chunk packet success. ack_size=%d", chunk_size); + + return ret; +} + +SrsSetPeerBandwidthPacket::SrsSetPeerBandwidthPacket() +{ + bandwidth = 0; + type = 2; +} + +SrsSetPeerBandwidthPacket::~SrsSetPeerBandwidthPacket() +{ +} + +int SrsSetPeerBandwidthPacket::get_perfer_cid() +{ + return RTMP_CID_ProtocolControl; +} + +int SrsSetPeerBandwidthPacket::get_message_type() +{ + return RTMP_MSG_SetPeerBandwidth; +} + +int SrsSetPeerBandwidthPacket::get_size() +{ + return 5; +} + +int SrsSetPeerBandwidthPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if (!stream->require(5)) { + ret = ERROR_RTMP_MESSAGE_ENCODE; + srs_error("encode set bandwidth packet failed. ret=%d", ret); + return ret; + } + + stream->write_4bytes(bandwidth); + stream->write_1bytes(type); + + srs_verbose("encode set bandwidth packet " + "success. bandwidth=%d, type=%d", bandwidth, type); + + return ret; +} + +SrsUserControlPacket::SrsUserControlPacket() +{ + event_type = 0; + event_data = 0; + extra_data = 0; +} + +SrsUserControlPacket::~SrsUserControlPacket() +{ +} + +int SrsUserControlPacket::decode(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if (!stream->require(6)) { + ret = ERROR_RTMP_MESSAGE_DECODE; + srs_error("decode user control failed. ret=%d", ret); + return ret; + } + + event_type = stream->read_2bytes(); + event_data = stream->read_4bytes(); + + if (event_type == SrcPCUCSetBufferLength) { + if (!stream->require(4)) { + ret = ERROR_RTMP_MESSAGE_ENCODE; + srs_error("decode user control packet failed. ret=%d", ret); + return ret; + } + extra_data = stream->read_4bytes(); + } + + srs_info("decode user control success. " + "event_type=%d, event_data=%d, extra_data=%d", + event_type, event_data, extra_data); + + return ret; +} + +int SrsUserControlPacket::get_perfer_cid() +{ + return RTMP_CID_ProtocolControl; +} + +int SrsUserControlPacket::get_message_type() +{ + return RTMP_MSG_UserControlMessage; +} + +int SrsUserControlPacket::get_size() +{ + if (event_type == SrcPCUCSetBufferLength) { + return 2 + 4 + 4; + } else { + return 2 + 4; + } +} + +int SrsUserControlPacket::encode_packet(SrsStream* stream) +{ + int ret = ERROR_SUCCESS; + + if (!stream->require(get_size())) { + ret = ERROR_RTMP_MESSAGE_ENCODE; + srs_error("encode user control packet failed. ret=%d", ret); + return ret; + } + + stream->write_2bytes(event_type); + stream->write_4bytes(event_data); + + // when event type is set buffer length, + // read the extra buffer length. + if (event_type == SrcPCUCSetBufferLength) { + stream->write_2bytes(extra_data); + srs_verbose("user control message, buffer_length=%d", extra_data); + } + + srs_verbose("encode user control packet success. " + "event_type=%d, event_data=%d", event_type, event_data); + + return ret; +} + diff --git a/trunk/src/core/srs_core_protocol.hpp b/trunk/src/core/srs_core_protocol.hpp index ca45441f7..adc26d289 100755 --- a/trunk/src/core/srs_core_protocol.hpp +++ b/trunk/src/core/srs_core_protocol.hpp @@ -1,1095 +1,1095 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_PROTOCOL_HPP -#define SRS_CORE_PROTOCOL_HPP - -/* -#include -*/ - -#include - -#include -#include - -#include - -#include -#include - -class SrsSocket; -class SrsBuffer; -class SrsPacket; -class SrsStream; -class SrsCommonMessage; -class SrsChunkStream; -class SrsAmf0Object; -class SrsAmf0Null; -class SrsAmf0Undefined; -class ISrsMessage; - -// convert class name to string. -#define CLASS_NAME_STRING(className) #className - -/** -* max rtmp header size: -* 1bytes basic header, -* 11bytes message header, -* 4bytes timestamp header, -* that is, 1+11+4=16bytes. -*/ -#define RTMP_MAX_FMT0_HEADER_SIZE 16 -/** -* max rtmp header size: -* 1bytes basic header, -* 4bytes timestamp header, -* that is, 1+4=5bytes. -*/ -#define RTMP_MAX_FMT3_HEADER_SIZE 5 - -/** -* the protocol provides the rtmp-message-protocol services, -* to recv RTMP message from RTMP chunk stream, -* and to send out RTMP message over RTMP chunk stream. -*/ -class SrsProtocol -{ -private: - struct AckWindowSize - { - int ack_window_size; - int64_t acked_size; - - AckWindowSize(); - }; -// peer in/out -private: - st_netfd_t stfd; - SrsSocket* skt; - char* pp; -// peer in -private: - std::map chunk_streams; - SrsBuffer* buffer; - int32_t in_chunk_size; - AckWindowSize in_ack_size; -// peer out -private: - char out_header_fmt0[RTMP_MAX_FMT0_HEADER_SIZE]; - char out_header_fmt3[RTMP_MAX_FMT3_HEADER_SIZE]; - int32_t out_chunk_size; -public: - SrsProtocol(st_netfd_t client_stfd); - virtual ~SrsProtocol(); -public: - /** - * set the timeout in us. - * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. - */ - virtual void set_recv_timeout(int64_t timeout_us); - virtual int64_t get_recv_timeout(); - virtual void set_send_timeout(int64_t timeout_us); - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - virtual int get_recv_kbps(); - virtual int get_send_kbps(); - /** - * recv a message with raw/undecoded payload from peer. - * the payload is not decoded, use srs_rtmp_expect_message if requires - * specifies message. - * @pmsg, user must free it. NULL if not success. - * @remark, only when success, user can use and must free the pmsg. - */ - virtual int recv_message(SrsCommonMessage** pmsg); - /** - * send out message with encoded payload to peer. - * use the message encode method to encode to payload, - * then sendout over socket. - * @msg this method will free it whatever return value. - */ - virtual int send_message(ISrsMessage* msg); -private: - /** - * when recv message, update the context. - */ - virtual int on_recv_message(SrsCommonMessage* msg); - virtual int response_acknowledgement_message(); - virtual int response_ping_message(int32_t timestamp); - /** - * when message sentout, update the context. - */ - virtual int on_send_message(ISrsMessage* msg); - /** - * try to recv interlaced message from peer, - * return error if error occur and nerver set the pmsg, - * return success and pmsg set to NULL if no entire message got, - * return success and pmsg set to entire message if got one. - */ - virtual int recv_interlaced_message(SrsCommonMessage** pmsg); - /** - * read the chunk basic header(fmt, cid) from chunk stream. - * user can discovery a SrsChunkStream by cid. - * @bh_size return the chunk basic header size, to remove the used bytes when finished. - */ - virtual int read_basic_header(char& fmt, int& cid, int& bh_size); - /** - * read the chunk message header(timestamp, payload_length, message_type, stream_id) - * from chunk stream and save to SrsChunkStream. - * @mh_size return the chunk message header size, to remove the used bytes when finished. - */ - virtual int read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size); - /** - * read the chunk payload, remove the used bytes in buffer, - * if got entire message, set the pmsg. - * @payload_size read size in this roundtrip, generally a chunk size or left message size. - */ - virtual int read_message_payload(SrsChunkStream* chunk, int bh_size, int mh_size, int& payload_size, SrsCommonMessage** pmsg); -}; - -/** -* 4.1. Message Header -*/ -struct SrsMessageHeader -{ - /** - * One byte field to represent the message type. A range of type IDs - * (1-7) are reserved for protocol control messages. - */ - int8_t message_type; - /** - * Three-byte field that represents the size of the payload in bytes. - * It is set in big-endian format. - */ - int32_t payload_length; - /** - * Three-byte field that contains a timestamp delta of the message. - * The 4 bytes are packed in the big-endian order. - * @remark, only used for decoding message from chunk stream. - */ - int32_t timestamp_delta; - /** - * Three-byte field that identifies the stream of the message. These - * bytes are set in big-endian format. - */ - int32_t stream_id; - - /** - * Four-byte field that contains a timestamp of the message. - * The 4 bytes are packed in the big-endian order. - * @remark, used as calc timestamp when decode and encode time. - */ - int32_t timestamp; - - SrsMessageHeader(); - virtual ~SrsMessageHeader(); - - bool is_audio(); - bool is_video(); - bool is_amf0_command(); - bool is_amf0_data(); - bool is_amf3_command(); - bool is_amf3_data(); - bool is_window_ackledgement_size(); - bool is_set_chunk_size(); - bool is_user_control_message(); -}; - -/** -* incoming chunk stream maybe interlaced, -* use the chunk stream to cache the input RTMP chunk streams. -*/ -class SrsChunkStream -{ -public: - /** - * represents the basic header fmt, - * which used to identify the variant message header type. - */ - char fmt; - /** - * represents the basic header cid, - * which is the chunk stream id. - */ - int cid; - /** - * cached message header - */ - SrsMessageHeader header; - /** - * whether the chunk message header has extended timestamp. - */ - bool extended_timestamp; - /** - * partially read message. - */ - SrsCommonMessage* msg; - /** - * decoded msg count, to identify whether the chunk stream is fresh. - */ - int64_t msg_count; -public: - SrsChunkStream(int _cid); - virtual ~SrsChunkStream(); -}; - -/** -* message to output. -*/ -class ISrsMessage -{ -// 4.1. Message Header -public: - SrsMessageHeader header; -// 4.2. Message Payload -public: - /** - * The other part which is the payload is the actual data that is - * contained in the message. For example, it could be some audio samples - * or compressed video data. The payload format and interpretation are - * beyond the scope of this document. - */ - int32_t size; - int8_t* payload; -public: - ISrsMessage(); - virtual ~ISrsMessage(); -public: - /** - * whether message canbe decoded. - * only update the context when message canbe decoded. - */ - virtual bool can_decode() = 0; -/** -* encode functions. -*/ -public: - /** - * get the perfered cid(chunk stream id) which sendout over. - */ - virtual int get_perfer_cid() = 0; - /** - * encode the packet to message payload bytes. - * @remark there exists empty packet, so maybe the payload is NULL. - */ - virtual int encode_packet() = 0; -}; - -/** -* common RTMP message defines in rtmp.part2.Message-Formats.pdf. -* cannbe parse and decode. -*/ -class SrsCommonMessage : public ISrsMessage -{ -private: - typedef ISrsMessage super; -// decoded message payload. -private: - SrsStream* stream; - SrsPacket* packet; -public: - SrsCommonMessage(); - virtual ~SrsCommonMessage(); -public: - virtual bool can_decode(); -/** -* decode functions. -*/ -public: - /** - * decode packet from message payload. - */ - virtual int decode_packet(); - /** - * get the decoded packet which decoded by decode_packet(). - * @remark, user never free the pkt, the message will auto free it. - */ - virtual SrsPacket* get_packet(); -/** -* encode functions. -*/ -public: - /** - * get the perfered cid(chunk stream id) which sendout over. - */ - virtual int get_perfer_cid(); - /** - * set the encoded packet to encode_packet() to payload. - * @stream_id, the id of stream which is created by createStream. - * @remark, user never free the pkt, the message will auto free it. - */ - virtual void set_packet(SrsPacket* pkt, int stream_id); - /** - * encode the packet to message payload bytes. - * @remark there exists empty packet, so maybe the payload is NULL. - */ - virtual int encode_packet(); -}; - -/** -* shared ptr message. -* for audio/video/data message that need less memory copy. -* and only for output. -*/ -class SrsSharedPtrMessage : public ISrsMessage -{ -private: - typedef ISrsMessage super; -private: - struct SrsSharedPtr - { - char* payload; - int size; - int perfer_cid; - int shared_count; - - SrsSharedPtr(); - virtual ~SrsSharedPtr(); - }; - SrsSharedPtr* ptr; -public: - SrsSharedPtrMessage(); - virtual ~SrsSharedPtrMessage(); -public: - virtual bool can_decode(); -public: - /** - * set the shared payload. - */ - virtual int initialize(ISrsMessage* msg, char* payload, int size); - virtual SrsSharedPtrMessage* copy(); -public: - /** - * get the perfered cid(chunk stream id) which sendout over. - */ - virtual int get_perfer_cid(); - /** - * ignored. - * for shared message, nothing should be done. - * use initialize() to set the data. - */ - virtual int encode_packet(); -}; - -/** -* the decoded message payload. -* @remark we seperate the packet from message, -* for the packet focus on logic and domain data, -* the message bind to the protocol and focus on protocol, such as header. -* we can merge the message and packet, using OOAD hierachy, packet extends from message, -* it's better for me to use components -- the message use the packet as payload. -*/ -class SrsPacket -{ -protected: - /** - * subpacket must override to provide the right class name. - */ - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsPacket); - } -public: - SrsPacket(); - virtual ~SrsPacket(); -/** -* decode functions. -*/ -public: - /** - * subpacket must override to decode packet from stream. - * @remark never invoke the super.decode, it always failed. - */ - virtual int decode(SrsStream* stream); -/** -* encode functions. -*/ -public: - virtual int get_perfer_cid(); - virtual int get_payload_length(); -public: - /** - * subpacket must override to provide the right message type. - */ - virtual int get_message_type(); - /** - * the subpacket can override this encode, - * for example, video and audio will directly set the payload withou memory copy, - * other packet which need to serialize/encode to bytes by override the - * get_size and encode_packet. - */ - virtual int encode(int& size, char*& payload); -protected: - /** - * subpacket can override to calc the packet size. - */ - virtual int get_size(); - /** - * subpacket can override to encode the payload to stream. - * @remark never invoke the super.encode_packet, it always failed. - */ - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 4.1.1. connect -* The client sends the connect command to the server to request -* connection to a server application instance. -*/ -class SrsConnectAppPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsConnectAppPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Object* command_object; -public: - SrsConnectAppPacket(); - virtual ~SrsConnectAppPacket(); -public: - virtual int decode(SrsStream* stream); -}; -/** -* response for SrsConnectAppPacket. -*/ -class SrsConnectAppResPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsConnectAppResPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Object* props; - SrsAmf0Object* info; -public: - SrsConnectAppResPacket(); - virtual ~SrsConnectAppResPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 4.1.3. createStream -* The client sends this command to the server to create a logical -* channel for message communication The publishing of audio, video, and -* metadata is carried out over stream channel created using the -* createStream command. -*/ -class SrsCreateStreamPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsCreateStreamPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; -public: - SrsCreateStreamPacket(); - virtual ~SrsCreateStreamPacket(); -public: - virtual int decode(SrsStream* stream); -}; -/** -* response for SrsCreateStreamPacket. -*/ -class SrsCreateStreamResPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsCreateStreamResPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - double stream_id; -public: - SrsCreateStreamResPacket(double _transaction_id, double _stream_id); - virtual ~SrsCreateStreamResPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* FMLE start publish: ReleaseStream/PublishStream -*/ -class SrsFMLEStartPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsFMLEStartPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - std::string stream_name; -public: - SrsFMLEStartPacket(); - virtual ~SrsFMLEStartPacket(); -public: - virtual int decode(SrsStream* stream); -}; -/** -* response for SrsFMLEStartPacket. -*/ -class SrsFMLEStartResPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsFMLEStartResPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - SrsAmf0Undefined* args; -public: - SrsFMLEStartResPacket(double _transaction_id); - virtual ~SrsFMLEStartResPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* FMLE/flash publish -* 4.2.6. Publish -* The client sends the publish command to publish a named stream to the -* server. Using this name, any client can play this stream and receive -* the published audio, video, and data messages. -*/ -class SrsPublishPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsPublishPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - std::string stream_name; - // optional, default to live. - std::string type; -public: - SrsPublishPacket(); - virtual ~SrsPublishPacket(); -public: - virtual int decode(SrsStream* stream); -}; - -/** -* 4.2.8. pause -* The client sends the pause command to tell the server to pause or -* start playing. -*/ -class SrsPausePacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsPausePacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - bool is_pause; - double time_ms; -public: - SrsPausePacket(); - virtual ~SrsPausePacket(); -public: - virtual int decode(SrsStream* stream); -}; - -/** -* 4.2.1. play -* The client sends this command to the server to play a stream. -*/ -class SrsPlayPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsPlayPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - std::string stream_name; - double start; - double duration; - bool reset; -public: - SrsPlayPacket(); - virtual ~SrsPlayPacket(); -public: - virtual int decode(SrsStream* stream); -}; -/** -* response for SrsPlayPacket. -* @remark, user must set the stream_id in header. -*/ -class SrsPlayResPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsPlayResPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* command_object; - SrsAmf0Object* desc; -public: - SrsPlayResPacket(); - virtual ~SrsPlayResPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* when bandwidth test done, notice client. -*/ -class SrsOnBWDonePacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsOnBWDonePacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* args; -public: - SrsOnBWDonePacket(); - virtual ~SrsOnBWDonePacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* onStatus command, AMF0 Call -* @remark, user must set the stream_id by SrsMessage.set_packet(). -*/ -class SrsOnStatusCallPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsOnStatusCallPacket); - } -public: - std::string command_name; - double transaction_id; - SrsAmf0Null* args; - SrsAmf0Object* data; -public: - SrsOnStatusCallPacket(); - virtual ~SrsOnStatusCallPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* onStatus data, AMF0 Data -* @remark, user must set the stream_id by SrsMessage.set_packet(). -*/ -class SrsOnStatusDataPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsOnStatusDataPacket); - } -public: - std::string command_name; - SrsAmf0Object* data; -public: - SrsOnStatusDataPacket(); - virtual ~SrsOnStatusDataPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* AMF0Data RtmpSampleAccess -* @remark, user must set the stream_id by SrsMessage.set_packet(). -*/ -class SrsSampleAccessPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsSampleAccessPacket); - } -public: - std::string command_name; - bool video_sample_access; - bool audio_sample_access; -public: - SrsSampleAccessPacket(); - virtual ~SrsSampleAccessPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* the stream metadata. -* FMLE: @setDataFrame -* others: onMetaData -*/ -class SrsOnMetaDataPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsOnMetaDataPacket); - } -public: - std::string name; - SrsAmf0Object* metadata; -public: - SrsOnMetaDataPacket(); - virtual ~SrsOnMetaDataPacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 5.5. Window Acknowledgement Size (5) -* The client or the server sends this message to inform the peer which -* window size to use when sending acknowledgment. -*/ -class SrsSetWindowAckSizePacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsSetWindowAckSizePacket); - } -public: - int32_t ackowledgement_window_size; -public: - SrsSetWindowAckSizePacket(); - virtual ~SrsSetWindowAckSizePacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 5.3. Acknowledgement (3) -* The client or the server sends the acknowledgment to the peer after -* receiving bytes equal to the window size. -*/ -class SrsAcknowledgementPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsAcknowledgementPacket); - } -public: - int32_t sequence_number; -public: - SrsAcknowledgementPacket(); - virtual ~SrsAcknowledgementPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 7.1. Set Chunk Size -* Protocol control message 1, Set Chunk Size, is used to notify the -* peer about the new maximum chunk size. -*/ -class SrsSetChunkSizePacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsSetChunkSizePacket); - } -public: - int32_t chunk_size; -public: - SrsSetChunkSizePacket(); - virtual ~SrsSetChunkSizePacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* 5.6. Set Peer Bandwidth (6) -* The client or the server sends this message to update the output -* bandwidth of the peer. -*/ -class SrsSetPeerBandwidthPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsSetPeerBandwidthPacket); - } -public: - int32_t bandwidth; - int8_t type; -public: - SrsSetPeerBandwidthPacket(); - virtual ~SrsSetPeerBandwidthPacket(); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -// 3.7. User Control message -enum SrcPCUCEventType -{ - // generally, 4bytes event-data - SrcPCUCStreamBegin = 0x00, - SrcPCUCStreamEOF = 0x01, - SrcPCUCStreamDry = 0x02, - SrcPCUCSetBufferLength = 0x03, // 8bytes event-data - SrcPCUCStreamIsRecorded = 0x04, - SrcPCUCPingRequest = 0x06, - SrcPCUCPingResponse = 0x07, -}; - -/** -* for the EventData is 4bytes. -* Stream Begin(=0) 4-bytes stream ID -* Stream EOF(=1) 4-bytes stream ID -* StreamDry(=2) 4-bytes stream ID -* SetBufferLength(=3) 8-bytes 4bytes stream ID, 4bytes buffer length. -* StreamIsRecorded(=4) 4-bytes stream ID -* PingRequest(=6) 4-bytes timestamp local server time -* PingResponse(=7) 4-bytes timestamp received ping request. -* -* 3.7. User Control message -* +------------------------------+------------------------- -* | Event Type ( 2- bytes ) | Event Data -* +------------------------------+------------------------- -* Figure 5 Pay load for the ‘User Control Message’. -*/ -class SrsUserControlPacket : public SrsPacket -{ -private: - typedef SrsPacket super; -protected: - virtual const char* get_class_name() - { - return CLASS_NAME_STRING(SrsUserControlPacket); - } -public: - int16_t event_type; - int32_t event_data; - /** - * 4bytes if event_type is SetBufferLength; otherwise 0. - */ - int32_t extra_data; -public: - SrsUserControlPacket(); - virtual ~SrsUserControlPacket(); -public: - virtual int decode(SrsStream* stream); -public: - virtual int get_perfer_cid(); -public: - virtual int get_message_type(); -protected: - virtual int get_size(); - virtual int encode_packet(SrsStream* stream); -}; - -/** -* expect a specified message, drop others util got specified one. -* @pmsg, user must free it. NULL if not success. -* @ppacket, store in the pmsg, user must never free it. NULL if not success. -* @remark, only when success, user can use and must free the pmsg/ppacket. -*/ -template -int srs_rtmp_expect_message(SrsProtocol* protocol, SrsCommonMessage** pmsg, T** ppacket) -{ - *pmsg = NULL; - *ppacket = NULL; - - int ret = ERROR_SUCCESS; - - while (true) { - SrsCommonMessage* msg = NULL; - if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { - srs_error("recv message failed. ret=%d", ret); - return ret; - } - srs_verbose("recv message success."); - - if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { - delete msg; - srs_error("decode message failed. ret=%d", ret); - return ret; - } - - T* pkt = dynamic_cast(msg->get_packet()); - if (!pkt) { - delete msg; - srs_trace("drop message(type=%d, size=%d, time=%d, sid=%d).", - msg->header.message_type, msg->header.payload_length, - msg->header.timestamp, msg->header.stream_id); - continue; - } - - *pmsg = msg; - *ppacket = pkt; - break; - } - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_PROTOCOL_HPP +#define SRS_CORE_PROTOCOL_HPP + +/* +#include +*/ + +#include + +#include +#include + +#include + +#include +#include + +class SrsSocket; +class SrsBuffer; +class SrsPacket; +class SrsStream; +class SrsCommonMessage; +class SrsChunkStream; +class SrsAmf0Object; +class SrsAmf0Null; +class SrsAmf0Undefined; +class ISrsMessage; + +// convert class name to string. +#define CLASS_NAME_STRING(className) #className + +/** +* max rtmp header size: +* 1bytes basic header, +* 11bytes message header, +* 4bytes timestamp header, +* that is, 1+11+4=16bytes. +*/ +#define RTMP_MAX_FMT0_HEADER_SIZE 16 +/** +* max rtmp header size: +* 1bytes basic header, +* 4bytes timestamp header, +* that is, 1+4=5bytes. +*/ +#define RTMP_MAX_FMT3_HEADER_SIZE 5 + +/** +* the protocol provides the rtmp-message-protocol services, +* to recv RTMP message from RTMP chunk stream, +* and to send out RTMP message over RTMP chunk stream. +*/ +class SrsProtocol +{ +private: + struct AckWindowSize + { + int ack_window_size; + int64_t acked_size; + + AckWindowSize(); + }; +// peer in/out +private: + st_netfd_t stfd; + SrsSocket* skt; + char* pp; +// peer in +private: + std::map chunk_streams; + SrsBuffer* buffer; + int32_t in_chunk_size; + AckWindowSize in_ack_size; +// peer out +private: + char out_header_fmt0[RTMP_MAX_FMT0_HEADER_SIZE]; + char out_header_fmt3[RTMP_MAX_FMT3_HEADER_SIZE]; + int32_t out_chunk_size; +public: + SrsProtocol(st_netfd_t client_stfd); + virtual ~SrsProtocol(); +public: + /** + * set the timeout in us. + * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. + */ + virtual void set_recv_timeout(int64_t timeout_us); + virtual int64_t get_recv_timeout(); + virtual void set_send_timeout(int64_t timeout_us); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual int get_recv_kbps(); + virtual int get_send_kbps(); + /** + * recv a message with raw/undecoded payload from peer. + * the payload is not decoded, use srs_rtmp_expect_message if requires + * specifies message. + * @pmsg, user must free it. NULL if not success. + * @remark, only when success, user can use and must free the pmsg. + */ + virtual int recv_message(SrsCommonMessage** pmsg); + /** + * send out message with encoded payload to peer. + * use the message encode method to encode to payload, + * then sendout over socket. + * @msg this method will free it whatever return value. + */ + virtual int send_message(ISrsMessage* msg); +private: + /** + * when recv message, update the context. + */ + virtual int on_recv_message(SrsCommonMessage* msg); + virtual int response_acknowledgement_message(); + virtual int response_ping_message(int32_t timestamp); + /** + * when message sentout, update the context. + */ + virtual int on_send_message(ISrsMessage* msg); + /** + * try to recv interlaced message from peer, + * return error if error occur and nerver set the pmsg, + * return success and pmsg set to NULL if no entire message got, + * return success and pmsg set to entire message if got one. + */ + virtual int recv_interlaced_message(SrsCommonMessage** pmsg); + /** + * read the chunk basic header(fmt, cid) from chunk stream. + * user can discovery a SrsChunkStream by cid. + * @bh_size return the chunk basic header size, to remove the used bytes when finished. + */ + virtual int read_basic_header(char& fmt, int& cid, int& bh_size); + /** + * read the chunk message header(timestamp, payload_length, message_type, stream_id) + * from chunk stream and save to SrsChunkStream. + * @mh_size return the chunk message header size, to remove the used bytes when finished. + */ + virtual int read_message_header(SrsChunkStream* chunk, char fmt, int bh_size, int& mh_size); + /** + * read the chunk payload, remove the used bytes in buffer, + * if got entire message, set the pmsg. + * @payload_size read size in this roundtrip, generally a chunk size or left message size. + */ + virtual int read_message_payload(SrsChunkStream* chunk, int bh_size, int mh_size, int& payload_size, SrsCommonMessage** pmsg); +}; + +/** +* 4.1. Message Header +*/ +struct SrsMessageHeader +{ + /** + * One byte field to represent the message type. A range of type IDs + * (1-7) are reserved for protocol control messages. + */ + int8_t message_type; + /** + * Three-byte field that represents the size of the payload in bytes. + * It is set in big-endian format. + */ + int32_t payload_length; + /** + * Three-byte field that contains a timestamp delta of the message. + * The 4 bytes are packed in the big-endian order. + * @remark, only used for decoding message from chunk stream. + */ + int32_t timestamp_delta; + /** + * Three-byte field that identifies the stream of the message. These + * bytes are set in big-endian format. + */ + int32_t stream_id; + + /** + * Four-byte field that contains a timestamp of the message. + * The 4 bytes are packed in the big-endian order. + * @remark, used as calc timestamp when decode and encode time. + */ + int32_t timestamp; + + SrsMessageHeader(); + virtual ~SrsMessageHeader(); + + bool is_audio(); + bool is_video(); + bool is_amf0_command(); + bool is_amf0_data(); + bool is_amf3_command(); + bool is_amf3_data(); + bool is_window_ackledgement_size(); + bool is_set_chunk_size(); + bool is_user_control_message(); +}; + +/** +* incoming chunk stream maybe interlaced, +* use the chunk stream to cache the input RTMP chunk streams. +*/ +class SrsChunkStream +{ +public: + /** + * represents the basic header fmt, + * which used to identify the variant message header type. + */ + char fmt; + /** + * represents the basic header cid, + * which is the chunk stream id. + */ + int cid; + /** + * cached message header + */ + SrsMessageHeader header; + /** + * whether the chunk message header has extended timestamp. + */ + bool extended_timestamp; + /** + * partially read message. + */ + SrsCommonMessage* msg; + /** + * decoded msg count, to identify whether the chunk stream is fresh. + */ + int64_t msg_count; +public: + SrsChunkStream(int _cid); + virtual ~SrsChunkStream(); +}; + +/** +* message to output. +*/ +class ISrsMessage +{ +// 4.1. Message Header +public: + SrsMessageHeader header; +// 4.2. Message Payload +public: + /** + * The other part which is the payload is the actual data that is + * contained in the message. For example, it could be some audio samples + * or compressed video data. The payload format and interpretation are + * beyond the scope of this document. + */ + int32_t size; + int8_t* payload; +public: + ISrsMessage(); + virtual ~ISrsMessage(); +public: + /** + * whether message canbe decoded. + * only update the context when message canbe decoded. + */ + virtual bool can_decode() = 0; +/** +* encode functions. +*/ +public: + /** + * get the perfered cid(chunk stream id) which sendout over. + */ + virtual int get_perfer_cid() = 0; + /** + * encode the packet to message payload bytes. + * @remark there exists empty packet, so maybe the payload is NULL. + */ + virtual int encode_packet() = 0; +}; + +/** +* common RTMP message defines in rtmp.part2.Message-Formats.pdf. +* cannbe parse and decode. +*/ +class SrsCommonMessage : public ISrsMessage +{ +private: + typedef ISrsMessage super; +// decoded message payload. +private: + SrsStream* stream; + SrsPacket* packet; +public: + SrsCommonMessage(); + virtual ~SrsCommonMessage(); +public: + virtual bool can_decode(); +/** +* decode functions. +*/ +public: + /** + * decode packet from message payload. + */ + virtual int decode_packet(); + /** + * get the decoded packet which decoded by decode_packet(). + * @remark, user never free the pkt, the message will auto free it. + */ + virtual SrsPacket* get_packet(); +/** +* encode functions. +*/ +public: + /** + * get the perfered cid(chunk stream id) which sendout over. + */ + virtual int get_perfer_cid(); + /** + * set the encoded packet to encode_packet() to payload. + * @stream_id, the id of stream which is created by createStream. + * @remark, user never free the pkt, the message will auto free it. + */ + virtual void set_packet(SrsPacket* pkt, int stream_id); + /** + * encode the packet to message payload bytes. + * @remark there exists empty packet, so maybe the payload is NULL. + */ + virtual int encode_packet(); +}; + +/** +* shared ptr message. +* for audio/video/data message that need less memory copy. +* and only for output. +*/ +class SrsSharedPtrMessage : public ISrsMessage +{ +private: + typedef ISrsMessage super; +private: + struct SrsSharedPtr + { + char* payload; + int size; + int perfer_cid; + int shared_count; + + SrsSharedPtr(); + virtual ~SrsSharedPtr(); + }; + SrsSharedPtr* ptr; +public: + SrsSharedPtrMessage(); + virtual ~SrsSharedPtrMessage(); +public: + virtual bool can_decode(); +public: + /** + * set the shared payload. + */ + virtual int initialize(ISrsMessage* msg, char* payload, int size); + virtual SrsSharedPtrMessage* copy(); +public: + /** + * get the perfered cid(chunk stream id) which sendout over. + */ + virtual int get_perfer_cid(); + /** + * ignored. + * for shared message, nothing should be done. + * use initialize() to set the data. + */ + virtual int encode_packet(); +}; + +/** +* the decoded message payload. +* @remark we seperate the packet from message, +* for the packet focus on logic and domain data, +* the message bind to the protocol and focus on protocol, such as header. +* we can merge the message and packet, using OOAD hierachy, packet extends from message, +* it's better for me to use components -- the message use the packet as payload. +*/ +class SrsPacket +{ +protected: + /** + * subpacket must override to provide the right class name. + */ + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsPacket); + } +public: + SrsPacket(); + virtual ~SrsPacket(); +/** +* decode functions. +*/ +public: + /** + * subpacket must override to decode packet from stream. + * @remark never invoke the super.decode, it always failed. + */ + virtual int decode(SrsStream* stream); +/** +* encode functions. +*/ +public: + virtual int get_perfer_cid(); + virtual int get_payload_length(); +public: + /** + * subpacket must override to provide the right message type. + */ + virtual int get_message_type(); + /** + * the subpacket can override this encode, + * for example, video and audio will directly set the payload withou memory copy, + * other packet which need to serialize/encode to bytes by override the + * get_size and encode_packet. + */ + virtual int encode(int& size, char*& payload); +protected: + /** + * subpacket can override to calc the packet size. + */ + virtual int get_size(); + /** + * subpacket can override to encode the payload to stream. + * @remark never invoke the super.encode_packet, it always failed. + */ + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 4.1.1. connect +* The client sends the connect command to the server to request +* connection to a server application instance. +*/ +class SrsConnectAppPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsConnectAppPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Object* command_object; +public: + SrsConnectAppPacket(); + virtual ~SrsConnectAppPacket(); +public: + virtual int decode(SrsStream* stream); +}; +/** +* response for SrsConnectAppPacket. +*/ +class SrsConnectAppResPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsConnectAppResPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Object* props; + SrsAmf0Object* info; +public: + SrsConnectAppResPacket(); + virtual ~SrsConnectAppResPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 4.1.3. createStream +* The client sends this command to the server to create a logical +* channel for message communication The publishing of audio, video, and +* metadata is carried out over stream channel created using the +* createStream command. +*/ +class SrsCreateStreamPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsCreateStreamPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; +public: + SrsCreateStreamPacket(); + virtual ~SrsCreateStreamPacket(); +public: + virtual int decode(SrsStream* stream); +}; +/** +* response for SrsCreateStreamPacket. +*/ +class SrsCreateStreamResPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsCreateStreamResPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + double stream_id; +public: + SrsCreateStreamResPacket(double _transaction_id, double _stream_id); + virtual ~SrsCreateStreamResPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* FMLE start publish: ReleaseStream/PublishStream +*/ +class SrsFMLEStartPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsFMLEStartPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + std::string stream_name; +public: + SrsFMLEStartPacket(); + virtual ~SrsFMLEStartPacket(); +public: + virtual int decode(SrsStream* stream); +}; +/** +* response for SrsFMLEStartPacket. +*/ +class SrsFMLEStartResPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsFMLEStartResPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + SrsAmf0Undefined* args; +public: + SrsFMLEStartResPacket(double _transaction_id); + virtual ~SrsFMLEStartResPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* FMLE/flash publish +* 4.2.6. Publish +* The client sends the publish command to publish a named stream to the +* server. Using this name, any client can play this stream and receive +* the published audio, video, and data messages. +*/ +class SrsPublishPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsPublishPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + std::string stream_name; + // optional, default to live. + std::string type; +public: + SrsPublishPacket(); + virtual ~SrsPublishPacket(); +public: + virtual int decode(SrsStream* stream); +}; + +/** +* 4.2.8. pause +* The client sends the pause command to tell the server to pause or +* start playing. +*/ +class SrsPausePacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsPausePacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + bool is_pause; + double time_ms; +public: + SrsPausePacket(); + virtual ~SrsPausePacket(); +public: + virtual int decode(SrsStream* stream); +}; + +/** +* 4.2.1. play +* The client sends this command to the server to play a stream. +*/ +class SrsPlayPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsPlayPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + std::string stream_name; + double start; + double duration; + bool reset; +public: + SrsPlayPacket(); + virtual ~SrsPlayPacket(); +public: + virtual int decode(SrsStream* stream); +}; +/** +* response for SrsPlayPacket. +* @remark, user must set the stream_id in header. +*/ +class SrsPlayResPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsPlayResPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* command_object; + SrsAmf0Object* desc; +public: + SrsPlayResPacket(); + virtual ~SrsPlayResPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* when bandwidth test done, notice client. +*/ +class SrsOnBWDonePacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsOnBWDonePacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* args; +public: + SrsOnBWDonePacket(); + virtual ~SrsOnBWDonePacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* onStatus command, AMF0 Call +* @remark, user must set the stream_id by SrsMessage.set_packet(). +*/ +class SrsOnStatusCallPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsOnStatusCallPacket); + } +public: + std::string command_name; + double transaction_id; + SrsAmf0Null* args; + SrsAmf0Object* data; +public: + SrsOnStatusCallPacket(); + virtual ~SrsOnStatusCallPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* onStatus data, AMF0 Data +* @remark, user must set the stream_id by SrsMessage.set_packet(). +*/ +class SrsOnStatusDataPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsOnStatusDataPacket); + } +public: + std::string command_name; + SrsAmf0Object* data; +public: + SrsOnStatusDataPacket(); + virtual ~SrsOnStatusDataPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* AMF0Data RtmpSampleAccess +* @remark, user must set the stream_id by SrsMessage.set_packet(). +*/ +class SrsSampleAccessPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsSampleAccessPacket); + } +public: + std::string command_name; + bool video_sample_access; + bool audio_sample_access; +public: + SrsSampleAccessPacket(); + virtual ~SrsSampleAccessPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* the stream metadata. +* FMLE: @setDataFrame +* others: onMetaData +*/ +class SrsOnMetaDataPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsOnMetaDataPacket); + } +public: + std::string name; + SrsAmf0Object* metadata; +public: + SrsOnMetaDataPacket(); + virtual ~SrsOnMetaDataPacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 5.5. Window Acknowledgement Size (5) +* The client or the server sends this message to inform the peer which +* window size to use when sending acknowledgment. +*/ +class SrsSetWindowAckSizePacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsSetWindowAckSizePacket); + } +public: + int32_t ackowledgement_window_size; +public: + SrsSetWindowAckSizePacket(); + virtual ~SrsSetWindowAckSizePacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 5.3. Acknowledgement (3) +* The client or the server sends the acknowledgment to the peer after +* receiving bytes equal to the window size. +*/ +class SrsAcknowledgementPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsAcknowledgementPacket); + } +public: + int32_t sequence_number; +public: + SrsAcknowledgementPacket(); + virtual ~SrsAcknowledgementPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 7.1. Set Chunk Size +* Protocol control message 1, Set Chunk Size, is used to notify the +* peer about the new maximum chunk size. +*/ +class SrsSetChunkSizePacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsSetChunkSizePacket); + } +public: + int32_t chunk_size; +public: + SrsSetChunkSizePacket(); + virtual ~SrsSetChunkSizePacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* 5.6. Set Peer Bandwidth (6) +* The client or the server sends this message to update the output +* bandwidth of the peer. +*/ +class SrsSetPeerBandwidthPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsSetPeerBandwidthPacket); + } +public: + int32_t bandwidth; + int8_t type; +public: + SrsSetPeerBandwidthPacket(); + virtual ~SrsSetPeerBandwidthPacket(); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +// 3.7. User Control message +enum SrcPCUCEventType +{ + // generally, 4bytes event-data + SrcPCUCStreamBegin = 0x00, + SrcPCUCStreamEOF = 0x01, + SrcPCUCStreamDry = 0x02, + SrcPCUCSetBufferLength = 0x03, // 8bytes event-data + SrcPCUCStreamIsRecorded = 0x04, + SrcPCUCPingRequest = 0x06, + SrcPCUCPingResponse = 0x07, +}; + +/** +* for the EventData is 4bytes. +* Stream Begin(=0) 4-bytes stream ID +* Stream EOF(=1) 4-bytes stream ID +* StreamDry(=2) 4-bytes stream ID +* SetBufferLength(=3) 8-bytes 4bytes stream ID, 4bytes buffer length. +* StreamIsRecorded(=4) 4-bytes stream ID +* PingRequest(=6) 4-bytes timestamp local server time +* PingResponse(=7) 4-bytes timestamp received ping request. +* +* 3.7. User Control message +* +------------------------------+------------------------- +* | Event Type ( 2- bytes ) | Event Data +* +------------------------------+------------------------- +* Figure 5 Pay load for the ‘User Control Message’. +*/ +class SrsUserControlPacket : public SrsPacket +{ +private: + typedef SrsPacket super; +protected: + virtual const char* get_class_name() + { + return CLASS_NAME_STRING(SrsUserControlPacket); + } +public: + int16_t event_type; + int32_t event_data; + /** + * 4bytes if event_type is SetBufferLength; otherwise 0. + */ + int32_t extra_data; +public: + SrsUserControlPacket(); + virtual ~SrsUserControlPacket(); +public: + virtual int decode(SrsStream* stream); +public: + virtual int get_perfer_cid(); +public: + virtual int get_message_type(); +protected: + virtual int get_size(); + virtual int encode_packet(SrsStream* stream); +}; + +/** +* expect a specified message, drop others util got specified one. +* @pmsg, user must free it. NULL if not success. +* @ppacket, store in the pmsg, user must never free it. NULL if not success. +* @remark, only when success, user can use and must free the pmsg/ppacket. +*/ +template +int srs_rtmp_expect_message(SrsProtocol* protocol, SrsCommonMessage** pmsg, T** ppacket) +{ + *pmsg = NULL; + *ppacket = NULL; + + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { + srs_error("recv message failed. ret=%d", ret); + return ret; + } + srs_verbose("recv message success."); + + if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { + delete msg; + srs_error("decode message failed. ret=%d", ret); + return ret; + } + + T* pkt = dynamic_cast(msg->get_packet()); + if (!pkt) { + delete msg; + srs_trace("drop message(type=%d, size=%d, time=%d, sid=%d).", + msg->header.message_type, msg->header.payload_length, + msg->header.timestamp, msg->header.stream_id); + continue; + } + + *pmsg = msg; + *ppacket = pkt; + break; + } + + return ret; +} + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_refer.cpp b/trunk/src/core/srs_core_refer.cpp index 7fd1aa860..fbe70b559 100755 --- a/trunk/src/core/srs_core_refer.cpp +++ b/trunk/src/core/srs_core_refer.cpp @@ -1,88 +1,88 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include - -int SrsRefer::check(std::string page_url, SrsConfDirective* refer) -{ - int ret = ERROR_SUCCESS; - - if (!refer) { - srs_verbose("ignore refer check for page_url=%s", page_url.c_str()); - return ret; - } - - for (int i = 0; i < (int)refer->args.size(); i++) { - if ((ret = check_single_refer(page_url, refer->args.at(i))) == ERROR_SUCCESS) { - srs_verbose("check refer success. page_url=%s, refer=%s", - page_url.c_str(), refer->args.at(i).c_str()); - return ret; - } - } - - ret = ERROR_RTMP_ACCESS_DENIED; - srs_error("check refer failed. ret=%d", ret); - - return ret; -} - -int SrsRefer::check_single_refer(std::string page_url, std::string refer) -{ - int ret = ERROR_SUCCESS; - - size_t pos = std::string::npos; - - std::string domain_name = page_url; - if ((pos = domain_name.find("://")) != std::string::npos) { - domain_name = domain_name.substr(pos + 3); - } - - if ((pos = domain_name.find("/")) != std::string::npos) { - domain_name = domain_name.substr(0, pos); - } - - if ((pos = domain_name.find(":")) != std::string::npos) { - domain_name = domain_name.substr(0, pos); - } - - pos = domain_name.find(refer); - if (pos == std::string::npos) { - ret = ERROR_RTMP_ACCESS_DENIED; - } - // match primary domain. - if (pos != domain_name.length() - refer.length()) { - ret = ERROR_RTMP_ACCESS_DENIED; - } - - if (ret != ERROR_SUCCESS) { - srs_verbose("access denied, page_url=%s, domain_name=%s, refer=%s, ret=%d", - page_url.c_str(), domain_name.c_str(), refer.c_str(), ret); - } - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include + +int SrsRefer::check(std::string page_url, SrsConfDirective* refer) +{ + int ret = ERROR_SUCCESS; + + if (!refer) { + srs_verbose("ignore refer check for page_url=%s", page_url.c_str()); + return ret; + } + + for (int i = 0; i < (int)refer->args.size(); i++) { + if ((ret = check_single_refer(page_url, refer->args.at(i))) == ERROR_SUCCESS) { + srs_verbose("check refer success. page_url=%s, refer=%s", + page_url.c_str(), refer->args.at(i).c_str()); + return ret; + } + } + + ret = ERROR_RTMP_ACCESS_DENIED; + srs_error("check refer failed. ret=%d", ret); + + return ret; +} + +int SrsRefer::check_single_refer(std::string page_url, std::string refer) +{ + int ret = ERROR_SUCCESS; + + size_t pos = std::string::npos; + + std::string domain_name = page_url; + if ((pos = domain_name.find("://")) != std::string::npos) { + domain_name = domain_name.substr(pos + 3); + } + + if ((pos = domain_name.find("/")) != std::string::npos) { + domain_name = domain_name.substr(0, pos); + } + + if ((pos = domain_name.find(":")) != std::string::npos) { + domain_name = domain_name.substr(0, pos); + } + + pos = domain_name.find(refer); + if (pos == std::string::npos) { + ret = ERROR_RTMP_ACCESS_DENIED; + } + // match primary domain. + if (pos != domain_name.length() - refer.length()) { + ret = ERROR_RTMP_ACCESS_DENIED; + } + + if (ret != ERROR_SUCCESS) { + srs_verbose("access denied, page_url=%s, domain_name=%s, refer=%s, ret=%d", + page_url.c_str(), domain_name.c_str(), refer.c_str(), ret); + } + + return ret; +} + diff --git a/trunk/src/core/srs_core_refer.hpp b/trunk/src/core/srs_core_refer.hpp index aeab41733..17067e297 100755 --- a/trunk/src/core/srs_core_refer.hpp +++ b/trunk/src/core/srs_core_refer.hpp @@ -1,49 +1,49 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_REFER_HPP -#define SRS_CORE_REFER_HPP - -/* -#include -*/ -#include - -#include - -class SrsConfDirective; - -class SrsRefer -{ -public: - /** - * to check the refer. - * @param page_url the client page url. - * @param refer the refer in config. - */ - virtual int check(std::string page_url, SrsConfDirective* refer); -private: - virtual int check_single_refer(std::string page_url, std::string refer); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_REFER_HPP +#define SRS_CORE_REFER_HPP + +/* +#include +*/ +#include + +#include + +class SrsConfDirective; + +class SrsRefer +{ +public: + /** + * to check the refer. + * @param page_url the client page url. + * @param refer the refer in config. + */ + virtual int check(std::string page_url, SrsConfDirective* refer); +private: + virtual int check_single_refer(std::string page_url, std::string refer); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_reload.cpp b/trunk/src/core/srs_core_reload.cpp index 56b115652..684f3c2df 100755 --- a/trunk/src/core/srs_core_reload.cpp +++ b/trunk/src/core/srs_core_reload.cpp @@ -1,45 +1,45 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include - -SrsReloadHandler::SrsReloadHandler() -{ -} - -SrsReloadHandler::~SrsReloadHandler() -{ -} - -int SrsReloadHandler::on_reload_listen() -{ - return ERROR_SUCCESS; -} - -int SrsReloadHandler::on_reload_pithy_print() -{ - return ERROR_SUCCESS; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include + +SrsReloadHandler::SrsReloadHandler() +{ +} + +SrsReloadHandler::~SrsReloadHandler() +{ +} + +int SrsReloadHandler::on_reload_listen() +{ + return ERROR_SUCCESS; +} + +int SrsReloadHandler::on_reload_pithy_print() +{ + return ERROR_SUCCESS; +} + diff --git a/trunk/src/core/srs_core_reload.hpp b/trunk/src/core/srs_core_reload.hpp index 8748ea7c3..68813a3fa 100755 --- a/trunk/src/core/srs_core_reload.hpp +++ b/trunk/src/core/srs_core_reload.hpp @@ -1,45 +1,45 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_RELOAD_HPP -#define SRS_CORE_RELOAD_HPP - -/* -#include -*/ -#include - -/** -* the handler for config reload. -*/ -class SrsReloadHandler -{ -public: - SrsReloadHandler(); - virtual ~SrsReloadHandler(); -public: - virtual int on_reload_listen(); - virtual int on_reload_pithy_print(); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_RELOAD_HPP +#define SRS_CORE_RELOAD_HPP + +/* +#include +*/ +#include + +/** +* the handler for config reload. +*/ +class SrsReloadHandler +{ +public: + SrsReloadHandler(); + virtual ~SrsReloadHandler(); +public: + virtual int on_reload_listen(); + virtual int on_reload_pithy_print(); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_rtmp.cpp b/trunk/src/core/srs_core_rtmp.cpp index 80849448b..d1d4bf992 100755 --- a/trunk/src/core/srs_core_rtmp.cpp +++ b/trunk/src/core/srs_core_rtmp.cpp @@ -1,894 +1,894 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include -#include -#include -#include -#include - -/** -* the signature for packets to client. -*/ -#define RTMP_SIG_FMS_VER "3,5,3,888" -#define RTMP_SIG_AMF0_VER 0 -#define RTMP_SIG_CLIENT_ID "ASAICiss" - -/** -* onStatus consts. -*/ -#define StatusLevel "level" -#define StatusCode "code" -#define StatusDescription "description" -#define StatusDetails "details" -#define StatusClientId "clientid" -// status value -#define StatusLevelStatus "status" -// code value -#define StatusCodeConnectSuccess "NetConnection.Connect.Success" -#define StatusCodeStreamReset "NetStream.Play.Reset" -#define StatusCodeStreamStart "NetStream.Play.Start" -#define StatusCodeStreamPause "NetStream.Pause.Notify" -#define StatusCodeStreamUnpause "NetStream.Unpause.Notify" -#define StatusCodePublishStart "NetStream.Publish.Start" -#define StatusCodeDataStart "NetStream.Data.Start" -#define StatusCodeUnpublishSuccess "NetStream.Unpublish.Success" - -// FMLE -#define RTMP_AMF0_COMMAND_ON_FC_PUBLISH "onFCPublish" -#define RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH "onFCUnpublish" - -// default stream id for response the createStream request. -#define SRS_DEFAULT_SID 1 - -SrsRequest::SrsRequest() -{ - objectEncoding = RTMP_SIG_AMF0_VER; -} - -SrsRequest::~SrsRequest() -{ -} - -int SrsRequest::discovery_app() -{ - int ret = ERROR_SUCCESS; - - size_t pos = std::string::npos; - std::string url = tcUrl; - - if ((pos = url.find("://")) != std::string::npos) { - schema = url.substr(0, pos); - url = url.substr(schema.length() + 3); - srs_verbose("discovery schema=%s", schema.c_str()); - } - - if ((pos = url.find("/")) != std::string::npos) { - vhost = url.substr(0, pos); - url = url.substr(vhost.length() + 1); - srs_verbose("discovery vhost=%s", vhost.c_str()); - } - - port = "1935"; - if ((pos = vhost.find(":")) != std::string::npos) { - port = vhost.substr(pos + 1); - vhost = vhost.substr(0, pos); - srs_verbose("discovery vhost=%s, port=%s", vhost.c_str(), port.c_str()); - } - - app = url; - srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s", - schema.c_str(), vhost.c_str(), port.c_str(), app.c_str()); - - if (schema.empty() || vhost.empty() || port.empty() || app.empty()) { - ret = ERROR_RTMP_REQ_TCURL; - srs_error("discovery tcUrl failed. " - "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d", - tcUrl.c_str(), schema.c_str(), vhost.c_str(), port.c_str(), app.c_str(), ret); - return ret; - } - - strip(); - - return ret; -} - -std::string SrsRequest::get_stream_url() -{ - std::string url = ""; - - url += vhost; - url += "/"; - url += app; - url += "/"; - url += stream; - - return url; -} - -void SrsRequest::strip() -{ - trim(vhost, "/ \n\r\t"); - trim(app, "/ \n\r\t"); - trim(stream, "/ \n\r\t"); -} - -std::string& SrsRequest::trim(std::string& str, std::string chs) -{ - for (int i = 0; i < (int)chs.length(); i++) { - char ch = chs.at(i); - - for (std::string::iterator it = str.begin(); it != str.end();) { - if (ch == *it) { - it = str.erase(it); - } else { - ++it; - } - } - } - - return str; -} - -SrsResponse::SrsResponse() -{ - stream_id = SRS_DEFAULT_SID; -} - -SrsResponse::~SrsResponse() -{ -} - -SrsRtmp::SrsRtmp(st_netfd_t client_stfd) -{ - protocol = new SrsProtocol(client_stfd); - stfd = client_stfd; -} - -SrsRtmp::~SrsRtmp() -{ - srs_freep(protocol); -} - -void SrsRtmp::set_recv_timeout(int64_t timeout_us) -{ - return protocol->set_recv_timeout(timeout_us); -} - -int64_t SrsRtmp::get_recv_timeout() -{ - return protocol->get_recv_timeout(); -} - -void SrsRtmp::set_send_timeout(int64_t timeout_us) -{ - return protocol->set_send_timeout(timeout_us); -} - -int64_t SrsRtmp::get_recv_bytes() -{ - return protocol->get_recv_bytes(); -} - -int64_t SrsRtmp::get_send_bytes() -{ - return protocol->get_send_bytes(); -} - -int SrsRtmp::get_recv_kbps() -{ - return protocol->get_recv_kbps(); -} - -int SrsRtmp::get_send_kbps() -{ - return protocol->get_send_kbps(); -} - -int SrsRtmp::recv_message(SrsCommonMessage** pmsg) -{ - return protocol->recv_message(pmsg); -} - -int SrsRtmp::send_message(ISrsMessage* msg) -{ - return protocol->send_message(msg); -} - -int SrsRtmp::handshake() -{ - int ret = ERROR_SUCCESS; - - SrsSocket skt(stfd); - - SrsComplexHandshake complex_hs; - SrsSimpleHandshake simple_hs; - if ((ret = simple_hs.handshake(skt, complex_hs)) != ERROR_SUCCESS) { - return ret; - } - - return ret; -} - -int SrsRtmp::connect_app(SrsRequest* req) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = NULL; - SrsConnectAppPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect connect app message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg, false); - srs_info("get connect app message"); - - SrsAmf0Any* prop = NULL; - - if ((prop = pkt->command_object->ensure_property_string("tcUrl")) == NULL) { - ret = ERROR_RTMP_REQ_CONNECT; - srs_error("invalid request, must specifies the tcUrl. ret=%d", ret); - return ret; - } - req->tcUrl = srs_amf0_convert(prop)->value; - - if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) { - req->pageUrl = srs_amf0_convert(prop)->value; - } - - if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) { - req->swfUrl = srs_amf0_convert(prop)->value; - } - - if ((prop = pkt->command_object->ensure_property_number("objectEncoding")) != NULL) { - req->objectEncoding = srs_amf0_convert(prop)->value; - } - - srs_info("get connect app message params success."); - - return req->discovery_app(); -} - -int SrsRtmp::set_window_ack_size(int ack_size) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); - - pkt->ackowledgement_window_size = ack_size; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send ack size message failed. ret=%d", ret); - return ret; - } - srs_info("send ack size message success. ack_size=%d", ack_size); - - return ret; -} - -int SrsRtmp::set_peer_bandwidth(int bandwidth, int type) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket(); - - pkt->bandwidth = bandwidth; - pkt->type = type; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send set bandwidth message failed. ret=%d", ret); - return ret; - } - srs_info("send set bandwidth message " - "success. bandwidth=%d, type=%d", bandwidth, type); - - return ret; -} - -int SrsRtmp::response_connect_app(SrsRequest* req) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); - - pkt->props->set("fmsVer", new SrsAmf0String("FMS/"RTMP_SIG_FMS_VER)); - pkt->props->set("capabilities", new SrsAmf0Number(127)); - pkt->props->set("mode", new SrsAmf0Number(1)); - - pkt->info->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->info->set(StatusCode, new SrsAmf0String(StatusCodeConnectSuccess)); - pkt->info->set(StatusDescription, new SrsAmf0String("Connection succeeded")); - pkt->info->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); - SrsASrsAmf0EcmaArray* data = new SrsASrsAmf0EcmaArray(); - pkt->info->set("data", data); - - data->set("srs_version", new SrsAmf0String(RTMP_SIG_FMS_VER)); - data->set("srs_server", new SrsAmf0String(RTMP_SIG_SRS_NAME)); - data->set("srs_license", new SrsAmf0String(RTMP_SIG_SRS_LICENSE)); - data->set("srs_role", new SrsAmf0String(RTMP_SIG_SRS_ROLE)); - data->set("srs_url", new SrsAmf0String(RTMP_SIG_SRS_URL)); - data->set("srs_version", new SrsAmf0String(RTMP_SIG_SRS_VERSION)); - data->set("srs_site", new SrsAmf0String(RTMP_SIG_SRS_WEB)); - data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL)); - data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT)); - - msg->set_packet(pkt, 0); - - 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; -} - -int SrsRtmp::on_bw_done() -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket(); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onBWDone message failed. ret=%d", ret); - return ret; - } - srs_info("send onBWDone message success."); - - return ret; -} - -int SrsRtmp::identify_client(int stream_id, SrsClientType& type, std::string& stream_name) -{ - type = SrsClientUnknown; - int ret = ERROR_SUCCESS; - - while (true) { - SrsCommonMessage* msg = NULL; - if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { - srs_error("recv identify client message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsCommonMessage, msg, false); - - if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { - srs_trace("identify ignore messages except " - "AMF0/AMF3 command message. type=%#x", msg->header.message_type); - continue; - } - - if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { - srs_error("identify decode message failed. ret=%d", ret); - return ret; - } - - SrsPacket* pkt = msg->get_packet(); - if (dynamic_cast(pkt)) { - srs_info("identify client by create stream, play or flash publish."); - return identify_create_stream_client( - dynamic_cast(pkt), stream_id, type, stream_name); - } - if (dynamic_cast(pkt)) { - srs_info("identify client by releaseStream, fmle publish."); - return identify_fmle_publish_client( - dynamic_cast(pkt), type, stream_name); - } - - srs_trace("ignore AMF0/AMF3 command message."); - } - - return ret; -} - -int SrsRtmp::set_chunk_size(int chunk_size) -{ - int ret = ERROR_SUCCESS; - - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); - - pkt->chunk_size = chunk_size; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send set chunk size message failed. ret=%d", ret); - return ret; - } - srs_info("send set chunk size message success. chunk_size=%d", chunk_size); - - return ret; -} - -int SrsRtmp::start_play(int stream_id) -{ - int ret = ERROR_SUCCESS; - - // StreamBegin - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCStreamBegin; - pkt->event_data = stream_id; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send PCUC(StreamBegin) message failed. ret=%d", ret); - return ret; - } - srs_info("send PCUC(StreamBegin) message success."); - } - - // onStatus(NetStream.Play.Reset) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamReset)); - pkt->data->set(StatusDescription, new SrsAmf0String("Playing and resetting stream.")); - pkt->data->set(StatusDetails, new SrsAmf0String("stream")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Play.Reset) message success."); - } - - // onStatus(NetStream.Play.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started playing stream.")); - pkt->data->set(StatusDetails, new SrsAmf0String("stream")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Play.Reset) message success."); - } - - // |RtmpSampleAccess(false, false) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket(); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send |RtmpSampleAccess(false, false) message failed. ret=%d", ret); - return ret; - } - srs_info("send |RtmpSampleAccess(false, false) message success."); - } - - // onStatus(NetStream.Data.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket(); - - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeDataStart)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Data.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Data.Start) message success."); - } - - srs_info("start play success."); - - return ret; -} - -int SrsRtmp::on_play_client_pause(int stream_id, bool is_pause) -{ - int ret = ERROR_SUCCESS; - - if (is_pause) { - // onStatus(NetStream.Pause.Notify) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamPause)); - pkt->data->set(StatusDescription, new SrsAmf0String("Paused stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Pause.Notify) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Pause.Notify) message success."); - } - // StreamEOF - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCStreamEOF; - pkt->event_data = stream_id; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send PCUC(StreamEOF) message failed. ret=%d", ret); - return ret; - } - srs_info("send PCUC(StreamEOF) message success."); - } - } else { - // onStatus(NetStream.Unpause.Notify) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamUnpause)); - pkt->data->set(StatusDescription, new SrsAmf0String("Unpaused stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Unpause.Notify) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Unpause.Notify) message success."); - } - // StreanBegin - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCStreamBegin; - pkt->event_data = stream_id; - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send PCUC(StreanBegin) message failed. ret=%d", ret); - return ret; - } - srs_info("send PCUC(StreanBegin) message success."); - } - } - - return ret; -} - -int SrsRtmp::start_fmle_publish(int stream_id) -{ - int ret = ERROR_SUCCESS; - - // FCPublish - double fc_publish_tid = 0; - if (true) { - SrsCommonMessage* msg = NULL; - SrsFMLEStartPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("recv FCPublish message failed. ret=%d", ret); - return ret; - } - srs_info("recv FCPublish request message success."); - - SrsAutoFree(SrsCommonMessage, msg, false); - fc_publish_tid = pkt->transaction_id; - } - // FCPublish response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send FCPublish response message failed. ret=%d", ret); - return ret; - } - srs_info("send FCPublish response message success."); - } - - // createStream - double create_stream_tid = 0; - if (true) { - SrsCommonMessage* msg = NULL; - SrsCreateStreamPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("recv createStream message failed. ret=%d", ret); - return ret; - } - srs_info("recv createStream request message success."); - - SrsAutoFree(SrsCommonMessage, msg, false); - create_stream_tid = pkt->transaction_id; - } - // createStream response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send createStream response message failed. ret=%d", ret); - return ret; - } - srs_info("send createStream response message success."); - } - - // publish - if (true) { - SrsCommonMessage* msg = NULL; - SrsPublishPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - srs_error("recv publish message failed. ret=%d", ret); - return ret; - } - srs_info("recv publish request message success."); - - SrsAutoFree(SrsCommonMessage, msg, false); - } - // publish response onFCPublish(NetStream.Publish.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH; - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onFCPublish(NetStream.Publish.Start) message success."); - } - // publish response onStatus(NetStream.Publish.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Publish.Start) message success."); - } - - srs_info("FMLE publish success."); - - return ret; -} - -int SrsRtmp::fmle_unpublish(int stream_id, double unpublish_tid) -{ - int ret = ERROR_SUCCESS; - - // publish response onFCUnpublish(NetStream.unpublish.Success) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH; - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); - pkt->data->set(StatusDescription, new SrsAmf0String("Stop publishing stream.")); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onFCUnpublish(NetStream.unpublish.Success) message failed. ret=%d", ret); - return ret; - } - srs_info("send onFCUnpublish(NetStream.unpublish.Success) message success."); - } - // FCUnpublish response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(unpublish_tid); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send FCUnpublish response message failed. ret=%d", ret); - return ret; - } - srs_info("send FCUnpublish response message success."); - } - // publish response onStatus(NetStream.Unpublish.Success) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); - pkt->data->set(StatusDescription, new SrsAmf0String("Stream is now unpublished")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Unpublish.Success) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Unpublish.Success) message success."); - } - - srs_info("FMLE unpublish success."); - - return ret; -} - -int SrsRtmp::start_flash_publish(int stream_id) -{ - int ret = ERROR_SUCCESS; - - // publish response onStatus(NetStream.Publish.Start) - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); - pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); - pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); - pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); - - msg->set_packet(pkt, stream_id); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Publish.Start) message success."); - } - - srs_info("flash publish success."); - - return ret; -} - -int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name) -{ - int ret = ERROR_SUCCESS; - - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send createStream response message failed. ret=%d", ret); - return ret; - } - srs_info("send createStream response message success."); - } - - while (true) { - SrsCommonMessage* msg = NULL; - if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { - srs_error("recv identify client message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsCommonMessage, msg, false); - - if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { - srs_trace("identify ignore messages except " - "AMF0/AMF3 command message. type=%#x", msg->header.message_type); - continue; - } - - if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { - srs_error("identify decode message failed. ret=%d", ret); - return ret; - } - - SrsPacket* pkt = msg->get_packet(); - if (dynamic_cast(pkt)) { - SrsPlayPacket* play = dynamic_cast(pkt); - type = SrsClientPlay; - stream_name = play->stream_name; - srs_trace("identity client type=play, stream_name=%s", stream_name.c_str()); - return ret; - } - if (dynamic_cast(pkt)) { - srs_info("identify client by publish, falsh publish."); - return identify_flash_publish_client( - dynamic_cast(pkt), type, stream_name); - } - - srs_trace("ignore AMF0/AMF3 command message."); - } - - return ret; -} - -int SrsRtmp::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name) -{ - int ret = ERROR_SUCCESS; - - type = SrsClientFMLEPublish; - stream_name = req->stream_name; - - // releaseStream response - if (true) { - SrsCommonMessage* msg = new SrsCommonMessage(); - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id); - - msg->set_packet(pkt, 0); - - if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { - srs_error("send releaseStream response message failed. ret=%d", ret); - return ret; - } - srs_info("send releaseStream response message success."); - } - - return ret; -} - -int SrsRtmp::identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name) -{ - int ret = ERROR_SUCCESS; - - type = SrsClientFlashPublish; - stream_name = req->stream_name; - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include +#include +#include +#include +#include + +/** +* the signature for packets to client. +*/ +#define RTMP_SIG_FMS_VER "3,5,3,888" +#define RTMP_SIG_AMF0_VER 0 +#define RTMP_SIG_CLIENT_ID "ASAICiss" + +/** +* onStatus consts. +*/ +#define StatusLevel "level" +#define StatusCode "code" +#define StatusDescription "description" +#define StatusDetails "details" +#define StatusClientId "clientid" +// status value +#define StatusLevelStatus "status" +// code value +#define StatusCodeConnectSuccess "NetConnection.Connect.Success" +#define StatusCodeStreamReset "NetStream.Play.Reset" +#define StatusCodeStreamStart "NetStream.Play.Start" +#define StatusCodeStreamPause "NetStream.Pause.Notify" +#define StatusCodeStreamUnpause "NetStream.Unpause.Notify" +#define StatusCodePublishStart "NetStream.Publish.Start" +#define StatusCodeDataStart "NetStream.Data.Start" +#define StatusCodeUnpublishSuccess "NetStream.Unpublish.Success" + +// FMLE +#define RTMP_AMF0_COMMAND_ON_FC_PUBLISH "onFCPublish" +#define RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH "onFCUnpublish" + +// default stream id for response the createStream request. +#define SRS_DEFAULT_SID 1 + +SrsRequest::SrsRequest() +{ + objectEncoding = RTMP_SIG_AMF0_VER; +} + +SrsRequest::~SrsRequest() +{ +} + +int SrsRequest::discovery_app() +{ + int ret = ERROR_SUCCESS; + + size_t pos = std::string::npos; + std::string url = tcUrl; + + if ((pos = url.find("://")) != std::string::npos) { + schema = url.substr(0, pos); + url = url.substr(schema.length() + 3); + srs_verbose("discovery schema=%s", schema.c_str()); + } + + if ((pos = url.find("/")) != std::string::npos) { + vhost = url.substr(0, pos); + url = url.substr(vhost.length() + 1); + srs_verbose("discovery vhost=%s", vhost.c_str()); + } + + port = "1935"; + if ((pos = vhost.find(":")) != std::string::npos) { + port = vhost.substr(pos + 1); + vhost = vhost.substr(0, pos); + srs_verbose("discovery vhost=%s, port=%s", vhost.c_str(), port.c_str()); + } + + app = url; + srs_info("discovery app success. schema=%s, vhost=%s, port=%s, app=%s", + schema.c_str(), vhost.c_str(), port.c_str(), app.c_str()); + + if (schema.empty() || vhost.empty() || port.empty() || app.empty()) { + ret = ERROR_RTMP_REQ_TCURL; + srs_error("discovery tcUrl failed. " + "tcUrl=%s, schema=%s, vhost=%s, port=%s, app=%s, ret=%d", + tcUrl.c_str(), schema.c_str(), vhost.c_str(), port.c_str(), app.c_str(), ret); + return ret; + } + + strip(); + + return ret; +} + +std::string SrsRequest::get_stream_url() +{ + std::string url = ""; + + url += vhost; + url += "/"; + url += app; + url += "/"; + url += stream; + + return url; +} + +void SrsRequest::strip() +{ + trim(vhost, "/ \n\r\t"); + trim(app, "/ \n\r\t"); + trim(stream, "/ \n\r\t"); +} + +std::string& SrsRequest::trim(std::string& str, std::string chs) +{ + for (int i = 0; i < (int)chs.length(); i++) { + char ch = chs.at(i); + + for (std::string::iterator it = str.begin(); it != str.end();) { + if (ch == *it) { + it = str.erase(it); + } else { + ++it; + } + } + } + + return str; +} + +SrsResponse::SrsResponse() +{ + stream_id = SRS_DEFAULT_SID; +} + +SrsResponse::~SrsResponse() +{ +} + +SrsRtmp::SrsRtmp(st_netfd_t client_stfd) +{ + protocol = new SrsProtocol(client_stfd); + stfd = client_stfd; +} + +SrsRtmp::~SrsRtmp() +{ + srs_freep(protocol); +} + +void SrsRtmp::set_recv_timeout(int64_t timeout_us) +{ + return protocol->set_recv_timeout(timeout_us); +} + +int64_t SrsRtmp::get_recv_timeout() +{ + return protocol->get_recv_timeout(); +} + +void SrsRtmp::set_send_timeout(int64_t timeout_us) +{ + return protocol->set_send_timeout(timeout_us); +} + +int64_t SrsRtmp::get_recv_bytes() +{ + return protocol->get_recv_bytes(); +} + +int64_t SrsRtmp::get_send_bytes() +{ + return protocol->get_send_bytes(); +} + +int SrsRtmp::get_recv_kbps() +{ + return protocol->get_recv_kbps(); +} + +int SrsRtmp::get_send_kbps() +{ + return protocol->get_send_kbps(); +} + +int SrsRtmp::recv_message(SrsCommonMessage** pmsg) +{ + return protocol->recv_message(pmsg); +} + +int SrsRtmp::send_message(ISrsMessage* msg) +{ + return protocol->send_message(msg); +} + +int SrsRtmp::handshake() +{ + int ret = ERROR_SUCCESS; + + SrsSocket skt(stfd); + + SrsComplexHandshake complex_hs; + SrsSimpleHandshake simple_hs; + if ((ret = simple_hs.handshake(skt, complex_hs)) != ERROR_SUCCESS) { + return ret; + } + + return ret; +} + +int SrsRtmp::connect_app(SrsRequest* req) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = NULL; + SrsConnectAppPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect connect app message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg, false); + srs_info("get connect app message"); + + SrsAmf0Any* prop = NULL; + + if ((prop = pkt->command_object->ensure_property_string("tcUrl")) == NULL) { + ret = ERROR_RTMP_REQ_CONNECT; + srs_error("invalid request, must specifies the tcUrl. ret=%d", ret); + return ret; + } + req->tcUrl = srs_amf0_convert(prop)->value; + + if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) { + req->pageUrl = srs_amf0_convert(prop)->value; + } + + if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) { + req->swfUrl = srs_amf0_convert(prop)->value; + } + + if ((prop = pkt->command_object->ensure_property_number("objectEncoding")) != NULL) { + req->objectEncoding = srs_amf0_convert(prop)->value; + } + + srs_info("get connect app message params success."); + + return req->discovery_app(); +} + +int SrsRtmp::set_window_ack_size(int ack_size) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); + + pkt->ackowledgement_window_size = ack_size; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send ack size message failed. ret=%d", ret); + return ret; + } + srs_info("send ack size message success. ack_size=%d", ack_size); + + return ret; +} + +int SrsRtmp::set_peer_bandwidth(int bandwidth, int type) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket(); + + pkt->bandwidth = bandwidth; + pkt->type = type; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set bandwidth message failed. ret=%d", ret); + return ret; + } + srs_info("send set bandwidth message " + "success. bandwidth=%d, type=%d", bandwidth, type); + + return ret; +} + +int SrsRtmp::response_connect_app(SrsRequest* req) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); + + pkt->props->set("fmsVer", new SrsAmf0String("FMS/"RTMP_SIG_FMS_VER)); + pkt->props->set("capabilities", new SrsAmf0Number(127)); + pkt->props->set("mode", new SrsAmf0Number(1)); + + pkt->info->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->info->set(StatusCode, new SrsAmf0String(StatusCodeConnectSuccess)); + pkt->info->set(StatusDescription, new SrsAmf0String("Connection succeeded")); + pkt->info->set("objectEncoding", new SrsAmf0Number(req->objectEncoding)); + SrsASrsAmf0EcmaArray* data = new SrsASrsAmf0EcmaArray(); + pkt->info->set("data", data); + + data->set("srs_version", new SrsAmf0String(RTMP_SIG_FMS_VER)); + data->set("srs_server", new SrsAmf0String(RTMP_SIG_SRS_NAME)); + data->set("srs_license", new SrsAmf0String(RTMP_SIG_SRS_LICENSE)); + data->set("srs_role", new SrsAmf0String(RTMP_SIG_SRS_ROLE)); + data->set("srs_url", new SrsAmf0String(RTMP_SIG_SRS_URL)); + data->set("srs_version", new SrsAmf0String(RTMP_SIG_SRS_VERSION)); + data->set("srs_site", new SrsAmf0String(RTMP_SIG_SRS_WEB)); + data->set("srs_email", new SrsAmf0String(RTMP_SIG_SRS_EMAIL)); + data->set("srs_copyright", new SrsAmf0String(RTMP_SIG_SRS_COPYRIGHT)); + + msg->set_packet(pkt, 0); + + 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; +} + +int SrsRtmp::on_bw_done() +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket(); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onBWDone message failed. ret=%d", ret); + return ret; + } + srs_info("send onBWDone message success."); + + return ret; +} + +int SrsRtmp::identify_client(int stream_id, SrsClientType& type, std::string& stream_name) +{ + type = SrsClientUnknown; + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { + srs_error("recv identify client message failed. ret=%d", ret); + return ret; + } + + SrsAutoFree(SrsCommonMessage, msg, false); + + if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { + srs_trace("identify ignore messages except " + "AMF0/AMF3 command message. type=%#x", msg->header.message_type); + continue; + } + + if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { + srs_error("identify decode message failed. ret=%d", ret); + return ret; + } + + SrsPacket* pkt = msg->get_packet(); + if (dynamic_cast(pkt)) { + srs_info("identify client by create stream, play or flash publish."); + return identify_create_stream_client( + dynamic_cast(pkt), stream_id, type, stream_name); + } + if (dynamic_cast(pkt)) { + srs_info("identify client by releaseStream, fmle publish."); + return identify_fmle_publish_client( + dynamic_cast(pkt), type, stream_name); + } + + srs_trace("ignore AMF0/AMF3 command message."); + } + + return ret; +} + +int SrsRtmp::set_chunk_size(int chunk_size) +{ + int ret = ERROR_SUCCESS; + + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); + + pkt->chunk_size = chunk_size; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send set chunk size message failed. ret=%d", ret); + return ret; + } + srs_info("send set chunk size message success. chunk_size=%d", chunk_size); + + return ret; +} + +int SrsRtmp::start_play(int stream_id) +{ + int ret = ERROR_SUCCESS; + + // StreamBegin + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCStreamBegin; + pkt->event_data = stream_id; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send PCUC(StreamBegin) message failed. ret=%d", ret); + return ret; + } + srs_info("send PCUC(StreamBegin) message success."); + } + + // onStatus(NetStream.Play.Reset) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamReset)); + pkt->data->set(StatusDescription, new SrsAmf0String("Playing and resetting stream.")); + pkt->data->set(StatusDetails, new SrsAmf0String("stream")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Play.Reset) message success."); + } + + // onStatus(NetStream.Play.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started playing stream.")); + pkt->data->set(StatusDetails, new SrsAmf0String("stream")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Play.Reset) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Play.Reset) message success."); + } + + // |RtmpSampleAccess(false, false) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket(); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send |RtmpSampleAccess(false, false) message failed. ret=%d", ret); + return ret; + } + srs_info("send |RtmpSampleAccess(false, false) message success."); + } + + // onStatus(NetStream.Data.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket(); + + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeDataStart)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Data.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Data.Start) message success."); + } + + srs_info("start play success."); + + return ret; +} + +int SrsRtmp::on_play_client_pause(int stream_id, bool is_pause) +{ + int ret = ERROR_SUCCESS; + + if (is_pause) { + // onStatus(NetStream.Pause.Notify) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamPause)); + pkt->data->set(StatusDescription, new SrsAmf0String("Paused stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Pause.Notify) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Pause.Notify) message success."); + } + // StreamEOF + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCStreamEOF; + pkt->event_data = stream_id; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send PCUC(StreamEOF) message failed. ret=%d", ret); + return ret; + } + srs_info("send PCUC(StreamEOF) message success."); + } + } else { + // onStatus(NetStream.Unpause.Notify) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeStreamUnpause)); + pkt->data->set(StatusDescription, new SrsAmf0String("Unpaused stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Unpause.Notify) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Unpause.Notify) message success."); + } + // StreanBegin + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCStreamBegin; + pkt->event_data = stream_id; + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send PCUC(StreanBegin) message failed. ret=%d", ret); + return ret; + } + srs_info("send PCUC(StreanBegin) message success."); + } + } + + return ret; +} + +int SrsRtmp::start_fmle_publish(int stream_id) +{ + int ret = ERROR_SUCCESS; + + // FCPublish + double fc_publish_tid = 0; + if (true) { + SrsCommonMessage* msg = NULL; + SrsFMLEStartPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("recv FCPublish message failed. ret=%d", ret); + return ret; + } + srs_info("recv FCPublish request message success."); + + SrsAutoFree(SrsCommonMessage, msg, false); + fc_publish_tid = pkt->transaction_id; + } + // FCPublish response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send FCPublish response message failed. ret=%d", ret); + return ret; + } + srs_info("send FCPublish response message success."); + } + + // createStream + double create_stream_tid = 0; + if (true) { + SrsCommonMessage* msg = NULL; + SrsCreateStreamPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("recv createStream message failed. ret=%d", ret); + return ret; + } + srs_info("recv createStream request message success."); + + SrsAutoFree(SrsCommonMessage, msg, false); + create_stream_tid = pkt->transaction_id; + } + // createStream response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send createStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send createStream response message success."); + } + + // publish + if (true) { + SrsCommonMessage* msg = NULL; + SrsPublishPacket* pkt = NULL; + if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + srs_error("recv publish message failed. ret=%d", ret); + return ret; + } + srs_info("recv publish request message success."); + + SrsAutoFree(SrsCommonMessage, msg, false); + } + // publish response onFCPublish(NetStream.Publish.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH; + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onFCPublish(NetStream.Publish.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onFCPublish(NetStream.Publish.Start) message success."); + } + // publish response onStatus(NetStream.Publish.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Publish.Start) message success."); + } + + srs_info("FMLE publish success."); + + return ret; +} + +int SrsRtmp::fmle_unpublish(int stream_id, double unpublish_tid) +{ + int ret = ERROR_SUCCESS; + + // publish response onFCUnpublish(NetStream.unpublish.Success) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH; + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); + pkt->data->set(StatusDescription, new SrsAmf0String("Stop publishing stream.")); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onFCUnpublish(NetStream.unpublish.Success) message failed. ret=%d", ret); + return ret; + } + srs_info("send onFCUnpublish(NetStream.unpublish.Success) message success."); + } + // FCUnpublish response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(unpublish_tid); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send FCUnpublish response message failed. ret=%d", ret); + return ret; + } + srs_info("send FCUnpublish response message success."); + } + // publish response onStatus(NetStream.Unpublish.Success) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodeUnpublishSuccess)); + pkt->data->set(StatusDescription, new SrsAmf0String("Stream is now unpublished")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Unpublish.Success) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Unpublish.Success) message success."); + } + + srs_info("FMLE unpublish success."); + + return ret; +} + +int SrsRtmp::start_flash_publish(int stream_id) +{ + int ret = ERROR_SUCCESS; + + // publish response onStatus(NetStream.Publish.Start) + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, new SrsAmf0String(StatusLevelStatus)); + pkt->data->set(StatusCode, new SrsAmf0String(StatusCodePublishStart)); + pkt->data->set(StatusDescription, new SrsAmf0String("Started publishing stream.")); + pkt->data->set(StatusClientId, new SrsAmf0String(RTMP_SIG_CLIENT_ID)); + + msg->set_packet(pkt, stream_id); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Publish.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Publish.Start) message success."); + } + + srs_info("flash publish success."); + + return ret; +} + +int SrsRtmp::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name) +{ + int ret = ERROR_SUCCESS; + + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send createStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send createStream response message success."); + } + + while (true) { + SrsCommonMessage* msg = NULL; + if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { + srs_error("recv identify client message failed. ret=%d", ret); + return ret; + } + + SrsAutoFree(SrsCommonMessage, msg, false); + + if (!msg->header.is_amf0_command() && !msg->header.is_amf3_command()) { + srs_trace("identify ignore messages except " + "AMF0/AMF3 command message. type=%#x", msg->header.message_type); + continue; + } + + if ((ret = msg->decode_packet()) != ERROR_SUCCESS) { + srs_error("identify decode message failed. ret=%d", ret); + return ret; + } + + SrsPacket* pkt = msg->get_packet(); + if (dynamic_cast(pkt)) { + SrsPlayPacket* play = dynamic_cast(pkt); + type = SrsClientPlay; + stream_name = play->stream_name; + srs_trace("identity client type=play, stream_name=%s", stream_name.c_str()); + return ret; + } + if (dynamic_cast(pkt)) { + srs_info("identify client by publish, falsh publish."); + return identify_flash_publish_client( + dynamic_cast(pkt), type, stream_name); + } + + srs_trace("ignore AMF0/AMF3 command message."); + } + + return ret; +} + +int SrsRtmp::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name) +{ + int ret = ERROR_SUCCESS; + + type = SrsClientFMLEPublish; + stream_name = req->stream_name; + + // releaseStream response + if (true) { + SrsCommonMessage* msg = new SrsCommonMessage(); + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id); + + msg->set_packet(pkt, 0); + + if ((ret = protocol->send_message(msg)) != ERROR_SUCCESS) { + srs_error("send releaseStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send releaseStream response message success."); + } + + return ret; +} + +int SrsRtmp::identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name) +{ + int ret = ERROR_SUCCESS; + + type = SrsClientFlashPublish; + stream_name = req->stream_name; + + return ret; +} + diff --git a/trunk/src/core/srs_core_rtmp.hpp b/trunk/src/core/srs_core_rtmp.hpp index 7590de411..2f40c2846 100755 --- a/trunk/src/core/srs_core_rtmp.hpp +++ b/trunk/src/core/srs_core_rtmp.hpp @@ -1,184 +1,184 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_RTMP_HPP -#define SRS_CORE_RTMP_HPP - -/* -#include -*/ - -#include - -#include - -#include - -class SrsProtocol; -class ISrsMessage; -class SrsCommonMessage; -class SrsCreateStreamPacket; -class SrsFMLEStartPacket; -class SrsPublishPacket; - -/** -* the original request from client. -*/ -struct SrsRequest -{ - std::string tcUrl; - std::string pageUrl; - std::string swfUrl; - double objectEncoding; - - std::string schema; - std::string vhost; - std::string port; - std::string app; - std::string stream; - - SrsRequest(); - virtual ~SrsRequest(); - - /** - * disconvery vhost/app from tcUrl. - */ - virtual int discovery_app(); - virtual std::string get_stream_url(); - virtual void strip(); -private: - std::string& trim(std::string& str, std::string chs); -}; - -/** -* the response to client. -*/ -struct SrsResponse -{ - int stream_id; - - SrsResponse(); - virtual ~SrsResponse(); -}; - -/** -* the rtmp client type. -*/ -enum SrsClientType -{ - SrsClientUnknown, - SrsClientPlay, - SrsClientFMLEPublish, - SrsClientFlashPublish, -}; - -/** -* the rtmp provices rtmp-command-protocol services, -* a high level protocol, media stream oriented services, -* such as connect to vhost/app, play stream, get audio/video data. -*/ -class SrsRtmp -{ -private: - SrsProtocol* protocol; - st_netfd_t stfd; -public: - SrsRtmp(st_netfd_t client_stfd); - virtual ~SrsRtmp(); -public: - virtual void set_recv_timeout(int64_t timeout_us); - virtual int64_t get_recv_timeout(); - virtual void set_send_timeout(int64_t timeout_us); - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - virtual int get_recv_kbps(); - virtual int get_send_kbps(); - virtual int recv_message(SrsCommonMessage** pmsg); - virtual int send_message(ISrsMessage* msg); -public: - virtual int handshake(); - virtual int connect_app(SrsRequest* req); - virtual int set_window_ack_size(int ack_size); - /** - * @type: The sender can mark this message hard (0), soft (1), or dynamic (2) - * using the Limit type field. - */ - virtual int set_peer_bandwidth(int bandwidth, int type); - virtual int response_connect_app(SrsRequest* req); - virtual int on_bw_done(); - /** - * recv some message to identify the client. - * @stream_id, client will createStream to play or publish by flash, - * the stream_id used to response the createStream request. - * @type, output the client type. - */ - virtual int identify_client(int stream_id, SrsClientType& type, std::string& stream_name); - /** - * set the chunk size when client type identified. - */ - virtual int set_chunk_size(int chunk_size); - /** - * when client type is play, response with packets: - * StreamBegin, - * onStatus(NetStream.Play.Reset), onStatus(NetStream.Play.Start)., - * |RtmpSampleAccess(false, false), - * onStatus(NetStream.Data.Start). - */ - virtual int start_play(int stream_id); - /** - * when client(type is play) send pause message, - * if is_pause, response the following packets: - * onStatus(NetStream.Pause.Notify) - * StreamEOF - * if not is_pause, response the following packets: - * onStatus(NetStream.Unpause.Notify) - * StreamBegin - */ - virtual int on_play_client_pause(int stream_id, bool is_pause); - /** - * when client type is publish, response with packets: - * releaseStream response - * FCPublish - * FCPublish response - * createStream response - * onFCPublish(NetStream.Publish.Start) - * onStatus(NetStream.Publish.Start) - */ - virtual int start_fmle_publish(int stream_id); - /** - * process the FMLE unpublish event. - * @unpublish_tid the unpublish request transaction id. - */ - virtual int fmle_unpublish(int stream_id, double unpublish_tid); - /** - * when client type is publish, response with packets: - * onStatus(NetStream.Publish.Start) - */ - virtual int start_flash_publish(int stream_id); -private: - virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name); - virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name); - virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_RTMP_HPP +#define SRS_CORE_RTMP_HPP + +/* +#include +*/ + +#include + +#include + +#include + +class SrsProtocol; +class ISrsMessage; +class SrsCommonMessage; +class SrsCreateStreamPacket; +class SrsFMLEStartPacket; +class SrsPublishPacket; + +/** +* the original request from client. +*/ +struct SrsRequest +{ + std::string tcUrl; + std::string pageUrl; + std::string swfUrl; + double objectEncoding; + + std::string schema; + std::string vhost; + std::string port; + std::string app; + std::string stream; + + SrsRequest(); + virtual ~SrsRequest(); + + /** + * disconvery vhost/app from tcUrl. + */ + virtual int discovery_app(); + virtual std::string get_stream_url(); + virtual void strip(); +private: + std::string& trim(std::string& str, std::string chs); +}; + +/** +* the response to client. +*/ +struct SrsResponse +{ + int stream_id; + + SrsResponse(); + virtual ~SrsResponse(); +}; + +/** +* the rtmp client type. +*/ +enum SrsClientType +{ + SrsClientUnknown, + SrsClientPlay, + SrsClientFMLEPublish, + SrsClientFlashPublish, +}; + +/** +* the rtmp provices rtmp-command-protocol services, +* a high level protocol, media stream oriented services, +* such as connect to vhost/app, play stream, get audio/video data. +*/ +class SrsRtmp +{ +private: + SrsProtocol* protocol; + st_netfd_t stfd; +public: + SrsRtmp(st_netfd_t client_stfd); + virtual ~SrsRtmp(); +public: + virtual void set_recv_timeout(int64_t timeout_us); + virtual int64_t get_recv_timeout(); + virtual void set_send_timeout(int64_t timeout_us); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual int get_recv_kbps(); + virtual int get_send_kbps(); + virtual int recv_message(SrsCommonMessage** pmsg); + virtual int send_message(ISrsMessage* msg); +public: + virtual int handshake(); + virtual int connect_app(SrsRequest* req); + virtual int set_window_ack_size(int ack_size); + /** + * @type: The sender can mark this message hard (0), soft (1), or dynamic (2) + * using the Limit type field. + */ + virtual int set_peer_bandwidth(int bandwidth, int type); + virtual int response_connect_app(SrsRequest* req); + virtual int on_bw_done(); + /** + * recv some message to identify the client. + * @stream_id, client will createStream to play or publish by flash, + * the stream_id used to response the createStream request. + * @type, output the client type. + */ + virtual int identify_client(int stream_id, SrsClientType& type, std::string& stream_name); + /** + * set the chunk size when client type identified. + */ + virtual int set_chunk_size(int chunk_size); + /** + * when client type is play, response with packets: + * StreamBegin, + * onStatus(NetStream.Play.Reset), onStatus(NetStream.Play.Start)., + * |RtmpSampleAccess(false, false), + * onStatus(NetStream.Data.Start). + */ + virtual int start_play(int stream_id); + /** + * when client(type is play) send pause message, + * if is_pause, response the following packets: + * onStatus(NetStream.Pause.Notify) + * StreamEOF + * if not is_pause, response the following packets: + * onStatus(NetStream.Unpause.Notify) + * StreamBegin + */ + virtual int on_play_client_pause(int stream_id, bool is_pause); + /** + * when client type is publish, response with packets: + * releaseStream response + * FCPublish + * FCPublish response + * createStream response + * onFCPublish(NetStream.Publish.Start) + * onStatus(NetStream.Publish.Start) + */ + virtual int start_fmle_publish(int stream_id); + /** + * process the FMLE unpublish event. + * @unpublish_tid the unpublish request transaction id. + */ + virtual int fmle_unpublish(int stream_id, double unpublish_tid); + /** + * when client type is publish, response with packets: + * onStatus(NetStream.Publish.Start) + */ + virtual int start_flash_publish(int stream_id); +private: + virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsClientType& type, std::string& stream_name); + virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsClientType& type, std::string& stream_name); + virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsClientType& type, std::string& stream_name); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_server.cpp b/trunk/src/core/srs_core_server.cpp index f6d0672bf..0c6da8e34 100755 --- a/trunk/src/core/srs_core_server.cpp +++ b/trunk/src/core/srs_core_server.cpp @@ -1,336 +1,336 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include -#include -#include - -#include - -#include - -#include -#include -#include -#include - -#define SERVER_LISTEN_BACKLOG 10 -#define SRS_TIME_RESOLUTION_MS 500 - -SrsListener::SrsListener(SrsServer* _server, SrsListenerType _type) -{ - fd = -1; - stfd = NULL; - - port = 0; - server = _server; - type = _type; - - tid = NULL; - loop = false; -} - -SrsListener::~SrsListener() -{ - if (stfd) { - st_netfd_close(stfd); - stfd = NULL; - } - - if (tid) { - loop = false; - st_thread_interrupt(tid); - st_thread_join(tid, NULL); - tid = NULL; - } - - // st does not close it sometimes, - // close it manually. - close(fd); -} - -int SrsListener::listen(int _port) -{ - int ret = ERROR_SUCCESS; - - port = _port; - - if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { - ret = ERROR_SOCKET_CREATE; - srs_error("create linux socket error. ret=%d", ret); - return ret; - } - srs_verbose("create linux socket success. fd=%d", fd); - - int reuse_socket = 1; - if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) { - ret = ERROR_SOCKET_SETREUSE; - srs_error("setsockopt reuse-addr error. ret=%d", ret); - return ret; - } - srs_verbose("setsockopt reuse-addr success. fd=%d", fd); - - sockaddr_in addr; - addr.sin_family = AF_INET; - addr.sin_port = htons(port); - addr.sin_addr.s_addr = INADDR_ANY; - if (bind(fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { - ret = ERROR_SOCKET_BIND; - srs_error("bind socket error. ret=%d", ret); - return ret; - } - srs_verbose("bind socket success. fd=%d", fd); - - if (::listen(fd, SERVER_LISTEN_BACKLOG) == -1) { - ret = ERROR_SOCKET_LISTEN; - srs_error("listen socket error. ret=%d", ret); - return ret; - } - srs_verbose("listen socket success. fd=%d", fd); - - if ((stfd = st_netfd_open_socket(fd)) == NULL){ - ret = ERROR_ST_OPEN_SOCKET; - srs_error("st_netfd_open_socket open socket failed. ret=%d", ret); - return ret; - } - srs_verbose("st open socket success. fd=%d", fd); - - if ((tid = st_thread_create(listen_thread, this, 1, 0)) == NULL) { - ret = ERROR_ST_CREATE_LISTEN_THREAD; - srs_error("st_thread_create listen thread error. ret=%d", ret); - return ret; - } - srs_verbose("create st listen thread success."); - - srs_trace("server started, listen at port=%d, fd=%d", port, fd); - - return ret; -} - -void SrsListener::listen_cycle() -{ - int ret = ERROR_SUCCESS; - - log_context->generate_id(); - srs_trace("listen cycle start, port=%d, type=%d, fd=%d", port, type, fd); - - while (loop) { - st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT); - - if(client_stfd == NULL){ - // ignore error. - srs_warn("ignore accept thread stoppped for accept client error"); - continue; - } - srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd)); - - if ((ret = server->accept_client(type, client_stfd)) != ERROR_SUCCESS) { - srs_warn("accept client error. ret=%d", ret); - continue; - } - - srs_verbose("accept client finished. conns=%d, ret=%d", (int)conns.size(), ret); - } -} - -void* SrsListener::listen_thread(void* arg) -{ - SrsListener* obj = (SrsListener*)arg; - srs_assert(obj != NULL); - - obj->loop = true; - obj->listen_cycle(); - - return NULL; -} - -SrsServer::SrsServer() -{ - signal_reload = false; - - srs_assert(config); - config->subscribe(this); -} - -SrsServer::~SrsServer() -{ - config->unsubscribe(this); - - if (true) { - std::vector::iterator it; - for (it = conns.begin(); it != conns.end(); ++it) { - SrsConnection* conn = *it; - srs_freep(conn); - } - conns.clear(); - } - - close_listeners(); -} - -int SrsServer::initialize() -{ - int ret = ERROR_SUCCESS; - - // use linux epoll. - if (st_set_eventsys(ST_EVENTSYS_ALT) == -1) { - ret = ERROR_ST_SET_EPOLL; - srs_error("st_set_eventsys use linux epoll failed. ret=%d", ret); - return ret; - } - srs_verbose("st_set_eventsys use linux epoll success"); - - if(st_init() != 0){ - ret = ERROR_ST_INITIALIZE; - srs_error("st_init failed. ret=%d", ret); - return ret; - } - srs_verbose("st_init success"); - - // set current log id. - log_context->generate_id(); - srs_info("log set id success"); - - return ret; -} - -int SrsServer::listen() -{ - int ret = ERROR_SUCCESS; - - SrsConfDirective* conf = NULL; - - // stream service port. - conf = config->get_listen(); - srs_assert(conf); - - close_listeners(); - - for (int i = 0; i < (int)conf->args.size(); i++) { - SrsListener* listener = new SrsListener(this, SrsListenerStream); - listeners.push_back(listener); - - int port = ::atoi(conf->args.at(i).c_str()); - if ((ret = listener->listen(port)) != ERROR_SUCCESS) { - srs_error("listen at port %d failed. ret=%d", port, ret); - return ret; - } - } - - return ret; -} - -int SrsServer::cycle() -{ - int ret = ERROR_SUCCESS; - - // the deamon thread, update the time cache - while (true) { - st_usleep(SRS_TIME_RESOLUTION_MS * 1000); - srs_update_system_time_ms(); - - if (signal_reload) { - signal_reload = false; - srs_info("get signal reload, to reload the config."); - - if ((ret = config->reload()) != ERROR_SUCCESS) { - srs_error("reload config failed. ret=%d", ret); - return ret; - } - srs_trace("reload config success."); - } - } - - return ret; -} - -void SrsServer::remove(SrsConnection* conn) -{ - std::vector::iterator it = std::find(conns.begin(), conns.end(), conn); - - if (it != conns.end()) { - conns.erase(it); - } - - srs_info("conn removed. conns=%d", (int)conns.size()); - - // all connections are created by server, - // so we free it here. - srs_freep(conn); -} - -void SrsServer::on_signal(int signo) -{ - if (signo == SIGNAL_RELOAD) { - signal_reload = true; - } -} - -void SrsServer::close_listeners() -{ - std::vector::iterator it; - for (it = listeners.begin(); it != listeners.end(); ++it) { - SrsListener* listener = *it; - srs_freep(listener); - } - listeners.clear(); -} - -int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd) -{ - int ret = ERROR_SUCCESS; - - SrsConnection* conn = NULL; - if (type == SrsListenerStream) { - conn = new SrsClient(this, client_stfd); - } else { - // handler others - } - srs_assert(conn); - - // directly enqueue, the cycle thread will remove the client. - conns.push_back(conn); - srs_verbose("add conn from port %d to vector. conns=%d", port, (int)conns.size()); - - // cycle will start process thread and when finished remove the client. - if ((ret = conn->start()) != ERROR_SUCCESS) { - return ret; - } - srs_verbose("conn start finished. ret=%d", ret); - - return ret; -} - -int SrsServer::on_reload_listen() -{ - return listen(); -} - -SrsServer* _server() -{ - static SrsServer server; - return &server; -} +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include +#include +#include + +#include + +#include + +#include +#include +#include +#include + +#define SERVER_LISTEN_BACKLOG 10 +#define SRS_TIME_RESOLUTION_MS 500 + +SrsListener::SrsListener(SrsServer* _server, SrsListenerType _type) +{ + fd = -1; + stfd = NULL; + + port = 0; + server = _server; + type = _type; + + tid = NULL; + loop = false; +} + +SrsListener::~SrsListener() +{ + if (stfd) { + st_netfd_close(stfd); + stfd = NULL; + } + + if (tid) { + loop = false; + st_thread_interrupt(tid); + st_thread_join(tid, NULL); + tid = NULL; + } + + // st does not close it sometimes, + // close it manually. + close(fd); +} + +int SrsListener::listen(int _port) +{ + int ret = ERROR_SUCCESS; + + port = _port; + + if ((fd = socket(AF_INET, SOCK_STREAM, 0)) == -1) { + ret = ERROR_SOCKET_CREATE; + srs_error("create linux socket error. ret=%d", ret); + return ret; + } + srs_verbose("create linux socket success. fd=%d", fd); + + int reuse_socket = 1; + if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &reuse_socket, sizeof(int)) == -1) { + ret = ERROR_SOCKET_SETREUSE; + srs_error("setsockopt reuse-addr error. ret=%d", ret); + return ret; + } + srs_verbose("setsockopt reuse-addr success. fd=%d", fd); + + sockaddr_in addr; + addr.sin_family = AF_INET; + addr.sin_port = htons(port); + addr.sin_addr.s_addr = INADDR_ANY; + if (bind(fd, (const sockaddr*)&addr, sizeof(sockaddr_in)) == -1) { + ret = ERROR_SOCKET_BIND; + srs_error("bind socket error. ret=%d", ret); + return ret; + } + srs_verbose("bind socket success. fd=%d", fd); + + if (::listen(fd, SERVER_LISTEN_BACKLOG) == -1) { + ret = ERROR_SOCKET_LISTEN; + srs_error("listen socket error. ret=%d", ret); + return ret; + } + srs_verbose("listen socket success. fd=%d", fd); + + if ((stfd = st_netfd_open_socket(fd)) == NULL){ + ret = ERROR_ST_OPEN_SOCKET; + srs_error("st_netfd_open_socket open socket failed. ret=%d", ret); + return ret; + } + srs_verbose("st open socket success. fd=%d", fd); + + if ((tid = st_thread_create(listen_thread, this, 1, 0)) == NULL) { + ret = ERROR_ST_CREATE_LISTEN_THREAD; + srs_error("st_thread_create listen thread error. ret=%d", ret); + return ret; + } + srs_verbose("create st listen thread success."); + + srs_trace("server started, listen at port=%d, fd=%d", port, fd); + + return ret; +} + +void SrsListener::listen_cycle() +{ + int ret = ERROR_SUCCESS; + + log_context->generate_id(); + srs_trace("listen cycle start, port=%d, type=%d, fd=%d", port, type, fd); + + while (loop) { + st_netfd_t client_stfd = st_accept(stfd, NULL, NULL, ST_UTIME_NO_TIMEOUT); + + if(client_stfd == NULL){ + // ignore error. + srs_warn("ignore accept thread stoppped for accept client error"); + continue; + } + srs_verbose("get a client. fd=%d", st_netfd_fileno(client_stfd)); + + if ((ret = server->accept_client(type, client_stfd)) != ERROR_SUCCESS) { + srs_warn("accept client error. ret=%d", ret); + continue; + } + + srs_verbose("accept client finished. conns=%d, ret=%d", (int)conns.size(), ret); + } +} + +void* SrsListener::listen_thread(void* arg) +{ + SrsListener* obj = (SrsListener*)arg; + srs_assert(obj != NULL); + + obj->loop = true; + obj->listen_cycle(); + + return NULL; +} + +SrsServer::SrsServer() +{ + signal_reload = false; + + srs_assert(config); + config->subscribe(this); +} + +SrsServer::~SrsServer() +{ + config->unsubscribe(this); + + if (true) { + std::vector::iterator it; + for (it = conns.begin(); it != conns.end(); ++it) { + SrsConnection* conn = *it; + srs_freep(conn); + } + conns.clear(); + } + + close_listeners(); +} + +int SrsServer::initialize() +{ + int ret = ERROR_SUCCESS; + + // use linux epoll. + if (st_set_eventsys(ST_EVENTSYS_ALT) == -1) { + ret = ERROR_ST_SET_EPOLL; + srs_error("st_set_eventsys use linux epoll failed. ret=%d", ret); + return ret; + } + srs_verbose("st_set_eventsys use linux epoll success"); + + if(st_init() != 0){ + ret = ERROR_ST_INITIALIZE; + srs_error("st_init failed. ret=%d", ret); + return ret; + } + srs_verbose("st_init success"); + + // set current log id. + log_context->generate_id(); + srs_info("log set id success"); + + return ret; +} + +int SrsServer::listen() +{ + int ret = ERROR_SUCCESS; + + SrsConfDirective* conf = NULL; + + // stream service port. + conf = config->get_listen(); + srs_assert(conf); + + close_listeners(); + + for (int i = 0; i < (int)conf->args.size(); i++) { + SrsListener* listener = new SrsListener(this, SrsListenerStream); + listeners.push_back(listener); + + int port = ::atoi(conf->args.at(i).c_str()); + if ((ret = listener->listen(port)) != ERROR_SUCCESS) { + srs_error("listen at port %d failed. ret=%d", port, ret); + return ret; + } + } + + return ret; +} + +int SrsServer::cycle() +{ + int ret = ERROR_SUCCESS; + + // the deamon thread, update the time cache + while (true) { + st_usleep(SRS_TIME_RESOLUTION_MS * 1000); + srs_update_system_time_ms(); + + if (signal_reload) { + signal_reload = false; + srs_info("get signal reload, to reload the config."); + + if ((ret = config->reload()) != ERROR_SUCCESS) { + srs_error("reload config failed. ret=%d", ret); + return ret; + } + srs_trace("reload config success."); + } + } + + return ret; +} + +void SrsServer::remove(SrsConnection* conn) +{ + std::vector::iterator it = std::find(conns.begin(), conns.end(), conn); + + if (it != conns.end()) { + conns.erase(it); + } + + srs_info("conn removed. conns=%d", (int)conns.size()); + + // all connections are created by server, + // so we free it here. + srs_freep(conn); +} + +void SrsServer::on_signal(int signo) +{ + if (signo == SIGNAL_RELOAD) { + signal_reload = true; + } +} + +void SrsServer::close_listeners() +{ + std::vector::iterator it; + for (it = listeners.begin(); it != listeners.end(); ++it) { + SrsListener* listener = *it; + srs_freep(listener); + } + listeners.clear(); +} + +int SrsServer::accept_client(SrsListenerType type, st_netfd_t client_stfd) +{ + int ret = ERROR_SUCCESS; + + SrsConnection* conn = NULL; + if (type == SrsListenerStream) { + conn = new SrsClient(this, client_stfd); + } else { + // handler others + } + srs_assert(conn); + + // directly enqueue, the cycle thread will remove the client. + conns.push_back(conn); + srs_verbose("add conn from port %d to vector. conns=%d", port, (int)conns.size()); + + // cycle will start process thread and when finished remove the client. + if ((ret = conn->start()) != ERROR_SUCCESS) { + return ret; + } + srs_verbose("conn start finished. ret=%d", ret); + + return ret; +} + +int SrsServer::on_reload_listen() +{ + return listen(); +} + +SrsServer* _server() +{ + static SrsServer server; + return &server; +} diff --git a/trunk/src/core/srs_core_server.hpp b/trunk/src/core/srs_core_server.hpp index 7e99ee99f..1d4bf5c9f 100755 --- a/trunk/src/core/srs_core_server.hpp +++ b/trunk/src/core/srs_core_server.hpp @@ -1,94 +1,94 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_SERVER_HPP -#define SRS_CORE_SERVER_HPP - -/* -#include -*/ - -#include - -#include - -#include - -#include - -class SrsServer; -class SrsConnection; - -enum SrsListenerType -{ - SrsListenerStream = 0, - SrsListenerApi -}; - -class SrsListener -{ -public: - SrsListenerType type; -private: - int fd; - st_netfd_t stfd; - int port; - SrsServer* server; - st_thread_t tid; - bool loop; -public: - SrsListener(SrsServer* _server, SrsListenerType _type); - virtual ~SrsListener(); -public: - virtual int listen(int port); -private: - virtual void listen_cycle(); - static void* listen_thread(void* arg); -}; - -class SrsServer : public SrsReloadHandler -{ - friend class SrsListener; -private: - std::vector conns; - std::vector listeners; - bool signal_reload; -public: - SrsServer(); - virtual ~SrsServer(); -public: - virtual int initialize(); - virtual int listen(); - virtual int cycle(); - virtual void remove(SrsConnection* conn); - virtual void on_signal(int signo); -private: - virtual void close_listeners(); - virtual int accept_client(SrsListenerType type, st_netfd_t client_stfd); -public: - virtual int on_reload_listen(); -}; - -SrsServer* _server(); - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_SERVER_HPP +#define SRS_CORE_SERVER_HPP + +/* +#include +*/ + +#include + +#include + +#include + +#include + +class SrsServer; +class SrsConnection; + +enum SrsListenerType +{ + SrsListenerStream = 0, + SrsListenerApi +}; + +class SrsListener +{ +public: + SrsListenerType type; +private: + int fd; + st_netfd_t stfd; + int port; + SrsServer* server; + st_thread_t tid; + bool loop; +public: + SrsListener(SrsServer* _server, SrsListenerType _type); + virtual ~SrsListener(); +public: + virtual int listen(int port); +private: + virtual void listen_cycle(); + static void* listen_thread(void* arg); +}; + +class SrsServer : public SrsReloadHandler +{ + friend class SrsListener; +private: + std::vector conns; + std::vector listeners; + bool signal_reload; +public: + SrsServer(); + virtual ~SrsServer(); +public: + virtual int initialize(); + virtual int listen(); + virtual int cycle(); + virtual void remove(SrsConnection* conn); + virtual void on_signal(int signo); +private: + virtual void close_listeners(); + virtual int accept_client(SrsListenerType type, st_netfd_t client_stfd); +public: + virtual int on_reload_listen(); +}; + +SrsServer* _server(); + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_socket.cpp b/trunk/src/core/srs_core_socket.cpp index be4f92b26..e4c6a0c64 100755 --- a/trunk/src/core/srs_core_socket.cpp +++ b/trunk/src/core/srs_core_socket.cpp @@ -1,174 +1,174 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include - -SrsSocket::SrsSocket(st_netfd_t client_stfd) -{ - stfd = client_stfd; - send_timeout = recv_timeout = ST_UTIME_NO_TIMEOUT; - recv_bytes = send_bytes = 0; - start_time_ms = srs_get_system_time_ms(); -} - -SrsSocket::~SrsSocket() -{ -} - -void SrsSocket::set_recv_timeout(int64_t timeout_us) -{ - recv_timeout = timeout_us; -} - -int64_t SrsSocket::get_recv_timeout() -{ - return recv_timeout; -} - -void SrsSocket::set_send_timeout(int64_t timeout_us) -{ - send_timeout = timeout_us; -} - -int64_t SrsSocket::get_recv_bytes() -{ - return recv_bytes; -} - -int64_t SrsSocket::get_send_bytes() -{ - return send_bytes; -} - -int SrsSocket::get_recv_kbps() -{ - int64_t diff_ms = srs_get_system_time_ms() - start_time_ms; - - if (diff_ms <= 0) { - return 0; - } - - return recv_bytes * 8 / diff_ms; -} - -int SrsSocket::get_send_kbps() -{ - int64_t diff_ms = srs_get_system_time_ms() - start_time_ms; - - if (diff_ms <= 0) { - return 0; - } - - return send_bytes * 8 / diff_ms; -} - -int SrsSocket::read(const void* buf, size_t size, ssize_t* nread) -{ - int ret = ERROR_SUCCESS; - - *nread = st_read(stfd, (void*)buf, size, recv_timeout); - - // On success a non-negative integer indicating the number of bytes actually read is returned - // (a value of 0 means the network connection is closed or end of file is reached). - if (*nread <= 0) { - if (errno == ETIME) { - return ERROR_SOCKET_TIMEOUT; - } - - if (*nread == 0) { - errno = ECONNRESET; - } - - return ERROR_SOCKET_READ; - } - - recv_bytes += *nread; - - return ret; -} - -int SrsSocket::read_fully(const void* buf, size_t size, ssize_t* nread) -{ - int ret = ERROR_SUCCESS; - - *nread = st_read_fully(stfd, (void*)buf, size, recv_timeout); - - // On success a non-negative integer indicating the number of bytes actually read is returned - // (a value less than nbyte means the network connection is closed or end of file is reached) - if (*nread != (ssize_t)size) { - if (errno == ETIME) { - return ERROR_SOCKET_TIMEOUT; - } - - if (*nread >= 0) { - errno = ECONNRESET; - } - - return ERROR_SOCKET_READ_FULLY; - } - - recv_bytes += *nread; - - return ret; -} - -int SrsSocket::write(const void* buf, size_t size, ssize_t* nwrite) -{ - int ret = ERROR_SUCCESS; - - *nwrite = st_write(stfd, (void*)buf, size, send_timeout); - - if (*nwrite <= 0) { - if (errno == ETIME) { - return ERROR_SOCKET_TIMEOUT; - } - - return ERROR_SOCKET_WRITE; - } - - send_bytes += *nwrite; - - return ret; -} - -int SrsSocket::writev(const iovec *iov, int iov_size, ssize_t* nwrite) -{ - int ret = ERROR_SUCCESS; - - *nwrite = st_writev(stfd, iov, iov_size, send_timeout); - - if (*nwrite <= 0) { - if (errno == ETIME) { - return ERROR_SOCKET_TIMEOUT; - } - - return ERROR_SOCKET_WRITE; - } - - send_bytes += *nwrite; - - return ret; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include + +SrsSocket::SrsSocket(st_netfd_t client_stfd) +{ + stfd = client_stfd; + send_timeout = recv_timeout = ST_UTIME_NO_TIMEOUT; + recv_bytes = send_bytes = 0; + start_time_ms = srs_get_system_time_ms(); +} + +SrsSocket::~SrsSocket() +{ +} + +void SrsSocket::set_recv_timeout(int64_t timeout_us) +{ + recv_timeout = timeout_us; +} + +int64_t SrsSocket::get_recv_timeout() +{ + return recv_timeout; +} + +void SrsSocket::set_send_timeout(int64_t timeout_us) +{ + send_timeout = timeout_us; +} + +int64_t SrsSocket::get_recv_bytes() +{ + return recv_bytes; +} + +int64_t SrsSocket::get_send_bytes() +{ + return send_bytes; +} + +int SrsSocket::get_recv_kbps() +{ + int64_t diff_ms = srs_get_system_time_ms() - start_time_ms; + + if (diff_ms <= 0) { + return 0; + } + + return recv_bytes * 8 / diff_ms; +} + +int SrsSocket::get_send_kbps() +{ + int64_t diff_ms = srs_get_system_time_ms() - start_time_ms; + + if (diff_ms <= 0) { + return 0; + } + + return send_bytes * 8 / diff_ms; +} + +int SrsSocket::read(const void* buf, size_t size, ssize_t* nread) +{ + int ret = ERROR_SUCCESS; + + *nread = st_read(stfd, (void*)buf, size, recv_timeout); + + // On success a non-negative integer indicating the number of bytes actually read is returned + // (a value of 0 means the network connection is closed or end of file is reached). + if (*nread <= 0) { + if (errno == ETIME) { + return ERROR_SOCKET_TIMEOUT; + } + + if (*nread == 0) { + errno = ECONNRESET; + } + + return ERROR_SOCKET_READ; + } + + recv_bytes += *nread; + + return ret; +} + +int SrsSocket::read_fully(const void* buf, size_t size, ssize_t* nread) +{ + int ret = ERROR_SUCCESS; + + *nread = st_read_fully(stfd, (void*)buf, size, recv_timeout); + + // On success a non-negative integer indicating the number of bytes actually read is returned + // (a value less than nbyte means the network connection is closed or end of file is reached) + if (*nread != (ssize_t)size) { + if (errno == ETIME) { + return ERROR_SOCKET_TIMEOUT; + } + + if (*nread >= 0) { + errno = ECONNRESET; + } + + return ERROR_SOCKET_READ_FULLY; + } + + recv_bytes += *nread; + + return ret; +} + +int SrsSocket::write(const void* buf, size_t size, ssize_t* nwrite) +{ + int ret = ERROR_SUCCESS; + + *nwrite = st_write(stfd, (void*)buf, size, send_timeout); + + if (*nwrite <= 0) { + if (errno == ETIME) { + return ERROR_SOCKET_TIMEOUT; + } + + return ERROR_SOCKET_WRITE; + } + + send_bytes += *nwrite; + + return ret; +} + +int SrsSocket::writev(const iovec *iov, int iov_size, ssize_t* nwrite) +{ + int ret = ERROR_SUCCESS; + + *nwrite = st_writev(stfd, iov, iov_size, send_timeout); + + if (*nwrite <= 0) { + if (errno == ETIME) { + return ERROR_SOCKET_TIMEOUT; + } + + return ERROR_SOCKET_WRITE; + } + + send_bytes += *nwrite; + + return ret; +} + diff --git a/trunk/src/core/srs_core_socket.hpp b/trunk/src/core/srs_core_socket.hpp index 32048d384..74887fc12 100755 --- a/trunk/src/core/srs_core_socket.hpp +++ b/trunk/src/core/srs_core_socket.hpp @@ -1,66 +1,66 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_SOCKET_HPP -#define SRS_CORE_SOCKET_HPP - -/* -#include -*/ - -#include - -#include - -/** -* the socket provides TCP socket over st, -* that is, the sync socket mechanism. -*/ -class SrsSocket -{ -private: - int64_t recv_timeout; - int64_t send_timeout; - int64_t recv_bytes; - int64_t send_bytes; - int64_t start_time_ms; - st_netfd_t stfd; -public: - SrsSocket(st_netfd_t client_stfd); - virtual ~SrsSocket(); -public: - virtual void set_recv_timeout(int64_t timeout_us); - virtual int64_t get_recv_timeout(); - virtual void set_send_timeout(int64_t timeout_us); - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - virtual int get_recv_kbps(); - virtual int get_send_kbps(); -public: - virtual int read(const void* buf, size_t size, ssize_t* nread); - virtual int read_fully(const void* buf, size_t size, ssize_t* nread); - virtual int write(const void* buf, size_t size, ssize_t* nwrite); - virtual int writev(const iovec *iov, int iov_size, ssize_t* nwrite); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_SOCKET_HPP +#define SRS_CORE_SOCKET_HPP + +/* +#include +*/ + +#include + +#include + +/** +* the socket provides TCP socket over st, +* that is, the sync socket mechanism. +*/ +class SrsSocket +{ +private: + int64_t recv_timeout; + int64_t send_timeout; + int64_t recv_bytes; + int64_t send_bytes; + int64_t start_time_ms; + st_netfd_t stfd; +public: + SrsSocket(st_netfd_t client_stfd); + virtual ~SrsSocket(); +public: + virtual void set_recv_timeout(int64_t timeout_us); + virtual int64_t get_recv_timeout(); + virtual void set_send_timeout(int64_t timeout_us); + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + virtual int get_recv_kbps(); + virtual int get_send_kbps(); +public: + virtual int read(const void* buf, size_t size, ssize_t* nread); + virtual int read_fully(const void* buf, size_t size, ssize_t* nread); + virtual int write(const void* buf, size_t size, ssize_t* nwrite); + virtual int writev(const iovec *iov, int iov_size, ssize_t* nwrite); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_source.cpp b/trunk/src/core/srs_core_source.cpp index 04586af42..2a11a2b2f 100755 --- a/trunk/src/core/srs_core_source.cpp +++ b/trunk/src/core/srs_core_source.cpp @@ -1,543 +1,543 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include - -#include -#include -#include -#include -#include - -#define CONST_MAX_JITTER_MS 500 -#define DEFAULT_FRAME_TIME_MS 10 -#define PAUSED_SHRINK_SIZE 250 - -std::map SrsSource::pool; - -SrsSource* SrsSource::find(std::string stream_url) -{ - if (pool.find(stream_url) == pool.end()) { - pool[stream_url] = new SrsSource(stream_url); - srs_verbose("create new source for url=%s", stream_url.c_str()); - } - - return pool[stream_url]; -} - -SrsConsumer::SrsConsumer(SrsSource* _source) -{ - source = _source; - last_pkt_correct_time = last_pkt_time = 0; - paused = false; - codec = new SrsCodec(); -} - -SrsConsumer::~SrsConsumer() -{ - clear(); - srs_freep(codec); - - source->on_consumer_destroy(this); -} - -int SrsConsumer::get_time() -{ - return (int)last_pkt_correct_time; -} - -int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, int audio_sample_rate, int video_frame_rate) -{ - int ret = ERROR_SUCCESS; - - if ((ret = jitter_correct(msg, audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { - return ret; - } - - msgs.push_back(msg); - - return ret; -} - -int SrsConsumer::get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count) -{ - int ret = ERROR_SUCCESS; - - if (msgs.empty()) { - return ret; - } - - if (paused) { - if ((int)msgs.size() >= PAUSED_SHRINK_SIZE) { - shrink(); - } - return ret; - } - - if (max_count == 0) { - count = (int)msgs.size(); - } else { - count = srs_min(max_count, (int)msgs.size()); - } - - pmsgs = new SrsSharedPtrMessage*[count]; - - for (int i = 0; i < count; i++) { - pmsgs[i] = msgs[i]; - } - - if (count == (int)msgs.size()) { - msgs.clear(); - } else { - msgs.erase(msgs.begin(), msgs.begin() + count); - } - - return ret; -} - -int SrsConsumer::on_play_client_pause(bool is_pause) -{ - int ret = ERROR_SUCCESS; - - srs_trace("stream consumer change pause state %d=>%d", paused, is_pause); - paused = is_pause; - - return ret; -} - -void SrsConsumer::shrink() -{ - int i = 0; - std::vector::iterator it; - - // issue the last video iframe. - bool has_video = false; - int frame_to_remove = 0; - std::vector::iterator iframe = msgs.end(); - for (i = 0, it = msgs.begin(); it != msgs.end(); ++it, i++) { - SrsSharedPtrMessage* msg = *it; - if (msg->header.is_video()) { - has_video = true; - if (codec->video_is_keyframe(msg->payload, msg->size)) { - iframe = it; - frame_to_remove = i + 1; - } - } - } - - // last iframe is the first elem, ignore it. - if (iframe == msgs.begin()) { - return; - } - - // recalc the frame to remove - if (iframe == msgs.end()) { - frame_to_remove = 0; - } - if (!has_video) { - frame_to_remove = (int)msgs.size(); - } - - srs_trace("shrink the cache queue, has_video=%d, has_iframe=%d, size=%d, removed=%d", - has_video, iframe != msgs.end(), (int)msgs.size(), frame_to_remove); - - // if no video, remove all audio. - if (!has_video) { - clear(); - return; - } - - // if exists video Iframe, remove the frames before it. - if (iframe != msgs.end()) { - for (it = msgs.begin(); it != iframe; ++it) { - SrsSharedPtrMessage* msg = *it; - srs_freep(msg); - } - msgs.erase(msgs.begin(), iframe); - } -} - -int SrsConsumer::jitter_correct(SrsSharedPtrMessage* msg, int audio_sample_rate, int video_frame_rate) -{ - int ret = ERROR_SUCCESS; - - /** - * we use a very simple time jitter detect/correct algorithm: - * 1. delta: ensure the delta is positive and valid, - * we set the delta to DEFAULT_FRAME_TIME_MS, - * if the delta of time is nagative or greater than CONST_MAX_JITTER_MS. - * 2. last_pkt_time: specifies the original packet time, - * is used to detect next jitter. - * 3. last_pkt_correct_time: simply add the positive delta, - * and enforce the time monotonically. - */ - int32_t time = msg->header.timestamp; - int32_t delta = time - last_pkt_time; - - // if jitter detected, reset the delta. - if (delta < 0 || delta > CONST_MAX_JITTER_MS) { - // calc the right diff by audio sample rate - if (msg->header.is_audio() && audio_sample_rate > 0) { - delta = (int32_t)(delta * 1000.0 / audio_sample_rate); - } else if (msg->header.is_video() && video_frame_rate > 0) { - delta = (int32_t)(delta * 1.0 / video_frame_rate); - } else { - delta = DEFAULT_FRAME_TIME_MS; - } - - // sometimes, the time is absolute time, so correct it again. - if (delta > CONST_MAX_JITTER_MS) { - delta = DEFAULT_FRAME_TIME_MS; - } - - srs_info("jitter detected, last_pts=%d, pts=%d, diff=%d, last_time=%d, time=%d, diff=%d", - last_pkt_time, time, time - last_pkt_time, last_pkt_correct_time, last_pkt_correct_time + delta, delta); - } else { - srs_verbose("timestamp no jitter. time=%d, last_pkt=%d, correct_to=%d", - time, last_pkt_time, last_pkt_correct_time + delta); - } - - last_pkt_correct_time = srs_max(0, last_pkt_correct_time + delta); - msg->header.timestamp = last_pkt_correct_time; - last_pkt_time = time; - - return ret; -} - -void SrsConsumer::clear() -{ - std::vector::iterator it; - for (it = msgs.begin(); it != msgs.end(); ++it) { - SrsSharedPtrMessage* msg = *it; - srs_freep(msg); - } - msgs.clear(); -} - -SrsSource::SrsSource(std::string _stream_url) -{ - stream_url = _stream_url; - codec = new SrsCodec(); - - cache_metadata = cache_sh_video = cache_sh_audio = NULL; - - cached_video_count = 0; - enable_gop_cache = true; - - video_frame_rate = audio_sample_rate = 0; -} - -SrsSource::~SrsSource() -{ - std::vector::iterator it; - for (it = consumers.begin(); it != consumers.end(); ++it) { - SrsConsumer* consumer = *it; - srs_freep(consumer); - } - consumers.clear(); - - clear_gop_cache(); - - srs_freep(cache_metadata); - srs_freep(cache_sh_video); - srs_freep(cache_sh_audio); - - srs_freep(codec); -} - -int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata) -{ - int ret = ERROR_SUCCESS; - - metadata->metadata->set("server", new SrsAmf0String( - RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); - - SrsAmf0Any* prop = NULL; - if ((prop = metadata->metadata->get_property("audiosamplerate")) != NULL) { - if (prop->is_number()) { - audio_sample_rate = (int)(srs_amf0_convert(prop)->value); - } - } - if ((prop = metadata->metadata->get_property("framerate")) != NULL) { - if (prop->is_number()) { - video_frame_rate = (int)(srs_amf0_convert(prop)->value); - } - } - - // encode the metadata to payload - int size = metadata->get_payload_length(); - if (size <= 0) { - srs_warn("ignore the invalid metadata. size=%d", size); - return ret; - } - srs_verbose("get metadata size success."); - - char* payload = new char[size]; - memset(payload, 0, size); - if ((ret = metadata->encode(size, payload)) != ERROR_SUCCESS) { - srs_error("encode metadata error. ret=%d", ret); - srs_freepa(payload); - return ret; - } - srs_verbose("encode metadata success."); - - // create a shared ptr message. - srs_freep(cache_metadata); - cache_metadata = new SrsSharedPtrMessage(); - - // dump message to shared ptr message. - if ((ret = cache_metadata->initialize(msg, payload, size)) != ERROR_SUCCESS) { - srs_error("initialize the cache metadata failed. ret=%d", ret); - return ret; - } - srs_verbose("initialize shared ptr metadata success."); - - // copy to all consumer - std::vector::iterator it; - for (it = consumers.begin(); it != consumers.end(); ++it) { - SrsConsumer* consumer = *it; - if ((ret = consumer->enqueue(cache_metadata->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch the metadata failed. ret=%d", ret); - return ret; - } - } - srs_trace("dispatch metadata success."); - - return ret; -} - -int SrsSource::on_audio(SrsCommonMessage* audio) -{ - int ret = ERROR_SUCCESS; - - SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); - SrsAutoFree(SrsSharedPtrMessage, msg, false); - if ((ret = msg->initialize(audio, (char*)audio->payload, audio->size)) != ERROR_SUCCESS) { - srs_error("initialize the audio failed. ret=%d", ret); - return ret; - } - srs_verbose("initialize shared ptr audio success."); - - // detach the original audio - audio->payload = NULL; - audio->size = 0; - - // copy to all consumer - std::vector::iterator it; - for (it = consumers.begin(); it != consumers.end(); ++it) { - SrsConsumer* consumer = *it; - if ((ret = consumer->enqueue(msg->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch the audio failed. ret=%d", ret); - return ret; - } - } - srs_info("dispatch audio success."); - - // cache the sequence header if h264 - if (codec->audio_is_sequence_header(msg->payload, msg->size)) { - srs_freep(cache_sh_audio); - cache_sh_audio = msg->copy(); - srs_trace("update audio sequence header success. size=%d", msg->header.payload_length); - return ret; - } - - // cache the last gop packets - if ((ret = cache_last_gop(msg)) != ERROR_SUCCESS) { - srs_error("shrink gop cache failed. ret=%d", ret); - return ret; - } - srs_verbose("cache gop success."); - - return ret; -} - -int SrsSource::on_video(SrsCommonMessage* video) -{ - int ret = ERROR_SUCCESS; - - SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); - SrsAutoFree(SrsSharedPtrMessage, msg, false); - if ((ret = msg->initialize(video, (char*)video->payload, video->size)) != ERROR_SUCCESS) { - srs_error("initialize the video failed. ret=%d", ret); - return ret; - } - srs_verbose("initialize shared ptr video success."); - - // detach the original audio - video->payload = NULL; - video->size = 0; - - // copy to all consumer - std::vector::iterator it; - for (it = consumers.begin(); it != consumers.end(); ++it) { - SrsConsumer* consumer = *it; - if ((ret = consumer->enqueue(msg->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch the video failed. ret=%d", ret); - return ret; - } - } - srs_info("dispatch video success."); - - // cache the sequence header if h264 - if (codec->video_is_sequence_header(msg->payload, msg->size)) { - srs_freep(cache_sh_video); - cache_sh_video = msg->copy(); - srs_trace("update video sequence header success. size=%d", msg->header.payload_length); - return ret; - } - - // cache the last gop packets - if ((ret = cache_last_gop(msg)) != ERROR_SUCCESS) { - srs_error("shrink gop cache failed. ret=%d", ret); - return ret; - } - srs_verbose("cache gop success."); - - return ret; -} - - int SrsSource::create_consumer(SrsConsumer*& consumer) -{ - int ret = ERROR_SUCCESS; - - consumer = new SrsConsumer(this); - consumers.push_back(consumer); - - if (cache_metadata && (ret = consumer->enqueue(cache_metadata->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch metadata failed. ret=%d", ret); - return ret; - } - srs_info("dispatch metadata success"); - - if (cache_sh_video && (ret = consumer->enqueue(cache_sh_video->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch video sequence header failed. ret=%d", ret); - return ret; - } - srs_info("dispatch video sequence header success"); - - if (cache_sh_audio && (ret = consumer->enqueue(cache_sh_audio->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch audio sequence header failed. ret=%d", ret); - return ret; - } - srs_info("dispatch audio sequence header success"); - - std::vector::iterator it; - for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { - SrsSharedPtrMessage* msg = *it; - if ((ret = consumer->enqueue(msg->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { - srs_error("dispatch cached gop failed. ret=%d", ret); - return ret; - } - } - srs_trace("dispatch cached gop success. count=%d, duration=%d", (int)gop_cache.size(), consumer->get_time()); - - return ret; -} - -void SrsSource::on_consumer_destroy(SrsConsumer* consumer) -{ - std::vector::iterator it; - it = std::find(consumers.begin(), consumers.end(), consumer); - if (it != consumers.end()) { - consumers.erase(it); - } - srs_info("handle consumer destroy success."); -} - -void SrsSource::on_unpublish() -{ - clear_gop_cache(); - - srs_freep(cache_metadata); - video_frame_rate = audio_sample_rate = 0; - - srs_freep(cache_sh_video); - srs_freep(cache_sh_audio); - - srs_trace("clear cache/metadata/sequence-headers when unpublish."); -} - -void SrsSource::set_cache(bool enabled) -{ - enable_gop_cache = enabled; - - if (!enabled) { - srs_info("disable gop cache, clear %d packets.", (int)gop_cache.size()); - clear_gop_cache(); - return; - } - - srs_info("enable gop cache"); -} - -int SrsSource::cache_last_gop(SrsSharedPtrMessage* msg) -{ - int ret = ERROR_SUCCESS; - - if (!enable_gop_cache) { - srs_verbose("gop cache is disabled."); - return ret; - } - - // got video, update the video count if acceptable - if (msg->header.is_video()) { - cached_video_count++; - } - - // no acceptable video or pure audio, disable the cache. - if (cached_video_count == 0) { - srs_verbose("ignore any frame util got a h264 video frame."); - return ret; - } - - // clear gop cache when got key frame - if (msg->header.is_video() && codec->video_is_keyframe(msg->payload, msg->size)) { - srs_info("clear gop cache when got keyframe. vcount=%d, count=%d", - cached_video_count, (int)gop_cache.size()); - - clear_gop_cache(); - - // curent msg is video frame, so we set to 1. - cached_video_count = 1; - } - - // cache the frame. - gop_cache.push_back(msg->copy()); - - return ret; -} - -void SrsSource::clear_gop_cache() -{ - std::vector::iterator it; - for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { - SrsSharedPtrMessage* msg = *it; - srs_freep(msg); - } - gop_cache.clear(); - - cached_video_count = 0; -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include + +#include +#include +#include +#include +#include + +#define CONST_MAX_JITTER_MS 500 +#define DEFAULT_FRAME_TIME_MS 10 +#define PAUSED_SHRINK_SIZE 250 + +std::map SrsSource::pool; + +SrsSource* SrsSource::find(std::string stream_url) +{ + if (pool.find(stream_url) == pool.end()) { + pool[stream_url] = new SrsSource(stream_url); + srs_verbose("create new source for url=%s", stream_url.c_str()); + } + + return pool[stream_url]; +} + +SrsConsumer::SrsConsumer(SrsSource* _source) +{ + source = _source; + last_pkt_correct_time = last_pkt_time = 0; + paused = false; + codec = new SrsCodec(); +} + +SrsConsumer::~SrsConsumer() +{ + clear(); + srs_freep(codec); + + source->on_consumer_destroy(this); +} + +int SrsConsumer::get_time() +{ + return (int)last_pkt_correct_time; +} + +int SrsConsumer::enqueue(SrsSharedPtrMessage* msg, int audio_sample_rate, int video_frame_rate) +{ + int ret = ERROR_SUCCESS; + + if ((ret = jitter_correct(msg, audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { + return ret; + } + + msgs.push_back(msg); + + return ret; +} + +int SrsConsumer::get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count) +{ + int ret = ERROR_SUCCESS; + + if (msgs.empty()) { + return ret; + } + + if (paused) { + if ((int)msgs.size() >= PAUSED_SHRINK_SIZE) { + shrink(); + } + return ret; + } + + if (max_count == 0) { + count = (int)msgs.size(); + } else { + count = srs_min(max_count, (int)msgs.size()); + } + + pmsgs = new SrsSharedPtrMessage*[count]; + + for (int i = 0; i < count; i++) { + pmsgs[i] = msgs[i]; + } + + if (count == (int)msgs.size()) { + msgs.clear(); + } else { + msgs.erase(msgs.begin(), msgs.begin() + count); + } + + return ret; +} + +int SrsConsumer::on_play_client_pause(bool is_pause) +{ + int ret = ERROR_SUCCESS; + + srs_trace("stream consumer change pause state %d=>%d", paused, is_pause); + paused = is_pause; + + return ret; +} + +void SrsConsumer::shrink() +{ + int i = 0; + std::vector::iterator it; + + // issue the last video iframe. + bool has_video = false; + int frame_to_remove = 0; + std::vector::iterator iframe = msgs.end(); + for (i = 0, it = msgs.begin(); it != msgs.end(); ++it, i++) { + SrsSharedPtrMessage* msg = *it; + if (msg->header.is_video()) { + has_video = true; + if (codec->video_is_keyframe(msg->payload, msg->size)) { + iframe = it; + frame_to_remove = i + 1; + } + } + } + + // last iframe is the first elem, ignore it. + if (iframe == msgs.begin()) { + return; + } + + // recalc the frame to remove + if (iframe == msgs.end()) { + frame_to_remove = 0; + } + if (!has_video) { + frame_to_remove = (int)msgs.size(); + } + + srs_trace("shrink the cache queue, has_video=%d, has_iframe=%d, size=%d, removed=%d", + has_video, iframe != msgs.end(), (int)msgs.size(), frame_to_remove); + + // if no video, remove all audio. + if (!has_video) { + clear(); + return; + } + + // if exists video Iframe, remove the frames before it. + if (iframe != msgs.end()) { + for (it = msgs.begin(); it != iframe; ++it) { + SrsSharedPtrMessage* msg = *it; + srs_freep(msg); + } + msgs.erase(msgs.begin(), iframe); + } +} + +int SrsConsumer::jitter_correct(SrsSharedPtrMessage* msg, int audio_sample_rate, int video_frame_rate) +{ + int ret = ERROR_SUCCESS; + + /** + * we use a very simple time jitter detect/correct algorithm: + * 1. delta: ensure the delta is positive and valid, + * we set the delta to DEFAULT_FRAME_TIME_MS, + * if the delta of time is nagative or greater than CONST_MAX_JITTER_MS. + * 2. last_pkt_time: specifies the original packet time, + * is used to detect next jitter. + * 3. last_pkt_correct_time: simply add the positive delta, + * and enforce the time monotonically. + */ + int32_t time = msg->header.timestamp; + int32_t delta = time - last_pkt_time; + + // if jitter detected, reset the delta. + if (delta < 0 || delta > CONST_MAX_JITTER_MS) { + // calc the right diff by audio sample rate + if (msg->header.is_audio() && audio_sample_rate > 0) { + delta = (int32_t)(delta * 1000.0 / audio_sample_rate); + } else if (msg->header.is_video() && video_frame_rate > 0) { + delta = (int32_t)(delta * 1.0 / video_frame_rate); + } else { + delta = DEFAULT_FRAME_TIME_MS; + } + + // sometimes, the time is absolute time, so correct it again. + if (delta > CONST_MAX_JITTER_MS) { + delta = DEFAULT_FRAME_TIME_MS; + } + + srs_info("jitter detected, last_pts=%d, pts=%d, diff=%d, last_time=%d, time=%d, diff=%d", + last_pkt_time, time, time - last_pkt_time, last_pkt_correct_time, last_pkt_correct_time + delta, delta); + } else { + srs_verbose("timestamp no jitter. time=%d, last_pkt=%d, correct_to=%d", + time, last_pkt_time, last_pkt_correct_time + delta); + } + + last_pkt_correct_time = srs_max(0, last_pkt_correct_time + delta); + msg->header.timestamp = last_pkt_correct_time; + last_pkt_time = time; + + return ret; +} + +void SrsConsumer::clear() +{ + std::vector::iterator it; + for (it = msgs.begin(); it != msgs.end(); ++it) { + SrsSharedPtrMessage* msg = *it; + srs_freep(msg); + } + msgs.clear(); +} + +SrsSource::SrsSource(std::string _stream_url) +{ + stream_url = _stream_url; + codec = new SrsCodec(); + + cache_metadata = cache_sh_video = cache_sh_audio = NULL; + + cached_video_count = 0; + enable_gop_cache = true; + + video_frame_rate = audio_sample_rate = 0; +} + +SrsSource::~SrsSource() +{ + std::vector::iterator it; + for (it = consumers.begin(); it != consumers.end(); ++it) { + SrsConsumer* consumer = *it; + srs_freep(consumer); + } + consumers.clear(); + + clear_gop_cache(); + + srs_freep(cache_metadata); + srs_freep(cache_sh_video); + srs_freep(cache_sh_audio); + + srs_freep(codec); +} + +int SrsSource::on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata) +{ + int ret = ERROR_SUCCESS; + + metadata->metadata->set("server", new SrsAmf0String( + RTMP_SIG_SRS_KEY" "RTMP_SIG_SRS_VERSION" ("RTMP_SIG_SRS_URL_SHORT")")); + + SrsAmf0Any* prop = NULL; + if ((prop = metadata->metadata->get_property("audiosamplerate")) != NULL) { + if (prop->is_number()) { + audio_sample_rate = (int)(srs_amf0_convert(prop)->value); + } + } + if ((prop = metadata->metadata->get_property("framerate")) != NULL) { + if (prop->is_number()) { + video_frame_rate = (int)(srs_amf0_convert(prop)->value); + } + } + + // encode the metadata to payload + int size = metadata->get_payload_length(); + if (size <= 0) { + srs_warn("ignore the invalid metadata. size=%d", size); + return ret; + } + srs_verbose("get metadata size success."); + + char* payload = new char[size]; + memset(payload, 0, size); + if ((ret = metadata->encode(size, payload)) != ERROR_SUCCESS) { + srs_error("encode metadata error. ret=%d", ret); + srs_freepa(payload); + return ret; + } + srs_verbose("encode metadata success."); + + // create a shared ptr message. + srs_freep(cache_metadata); + cache_metadata = new SrsSharedPtrMessage(); + + // dump message to shared ptr message. + if ((ret = cache_metadata->initialize(msg, payload, size)) != ERROR_SUCCESS) { + srs_error("initialize the cache metadata failed. ret=%d", ret); + return ret; + } + srs_verbose("initialize shared ptr metadata success."); + + // copy to all consumer + std::vector::iterator it; + for (it = consumers.begin(); it != consumers.end(); ++it) { + SrsConsumer* consumer = *it; + if ((ret = consumer->enqueue(cache_metadata->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch the metadata failed. ret=%d", ret); + return ret; + } + } + srs_trace("dispatch metadata success."); + + return ret; +} + +int SrsSource::on_audio(SrsCommonMessage* audio) +{ + int ret = ERROR_SUCCESS; + + SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); + SrsAutoFree(SrsSharedPtrMessage, msg, false); + if ((ret = msg->initialize(audio, (char*)audio->payload, audio->size)) != ERROR_SUCCESS) { + srs_error("initialize the audio failed. ret=%d", ret); + return ret; + } + srs_verbose("initialize shared ptr audio success."); + + // detach the original audio + audio->payload = NULL; + audio->size = 0; + + // copy to all consumer + std::vector::iterator it; + for (it = consumers.begin(); it != consumers.end(); ++it) { + SrsConsumer* consumer = *it; + if ((ret = consumer->enqueue(msg->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch the audio failed. ret=%d", ret); + return ret; + } + } + srs_info("dispatch audio success."); + + // cache the sequence header if h264 + if (codec->audio_is_sequence_header(msg->payload, msg->size)) { + srs_freep(cache_sh_audio); + cache_sh_audio = msg->copy(); + srs_trace("update audio sequence header success. size=%d", msg->header.payload_length); + return ret; + } + + // cache the last gop packets + if ((ret = cache_last_gop(msg)) != ERROR_SUCCESS) { + srs_error("shrink gop cache failed. ret=%d", ret); + return ret; + } + srs_verbose("cache gop success."); + + return ret; +} + +int SrsSource::on_video(SrsCommonMessage* video) +{ + int ret = ERROR_SUCCESS; + + SrsSharedPtrMessage* msg = new SrsSharedPtrMessage(); + SrsAutoFree(SrsSharedPtrMessage, msg, false); + if ((ret = msg->initialize(video, (char*)video->payload, video->size)) != ERROR_SUCCESS) { + srs_error("initialize the video failed. ret=%d", ret); + return ret; + } + srs_verbose("initialize shared ptr video success."); + + // detach the original audio + video->payload = NULL; + video->size = 0; + + // copy to all consumer + std::vector::iterator it; + for (it = consumers.begin(); it != consumers.end(); ++it) { + SrsConsumer* consumer = *it; + if ((ret = consumer->enqueue(msg->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch the video failed. ret=%d", ret); + return ret; + } + } + srs_info("dispatch video success."); + + // cache the sequence header if h264 + if (codec->video_is_sequence_header(msg->payload, msg->size)) { + srs_freep(cache_sh_video); + cache_sh_video = msg->copy(); + srs_trace("update video sequence header success. size=%d", msg->header.payload_length); + return ret; + } + + // cache the last gop packets + if ((ret = cache_last_gop(msg)) != ERROR_SUCCESS) { + srs_error("shrink gop cache failed. ret=%d", ret); + return ret; + } + srs_verbose("cache gop success."); + + return ret; +} + + int SrsSource::create_consumer(SrsConsumer*& consumer) +{ + int ret = ERROR_SUCCESS; + + consumer = new SrsConsumer(this); + consumers.push_back(consumer); + + if (cache_metadata && (ret = consumer->enqueue(cache_metadata->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch metadata failed. ret=%d", ret); + return ret; + } + srs_info("dispatch metadata success"); + + if (cache_sh_video && (ret = consumer->enqueue(cache_sh_video->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch video sequence header failed. ret=%d", ret); + return ret; + } + srs_info("dispatch video sequence header success"); + + if (cache_sh_audio && (ret = consumer->enqueue(cache_sh_audio->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch audio sequence header failed. ret=%d", ret); + return ret; + } + srs_info("dispatch audio sequence header success"); + + std::vector::iterator it; + for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { + SrsSharedPtrMessage* msg = *it; + if ((ret = consumer->enqueue(msg->copy(), audio_sample_rate, video_frame_rate)) != ERROR_SUCCESS) { + srs_error("dispatch cached gop failed. ret=%d", ret); + return ret; + } + } + srs_trace("dispatch cached gop success. count=%d, duration=%d", (int)gop_cache.size(), consumer->get_time()); + + return ret; +} + +void SrsSource::on_consumer_destroy(SrsConsumer* consumer) +{ + std::vector::iterator it; + it = std::find(consumers.begin(), consumers.end(), consumer); + if (it != consumers.end()) { + consumers.erase(it); + } + srs_info("handle consumer destroy success."); +} + +void SrsSource::on_unpublish() +{ + clear_gop_cache(); + + srs_freep(cache_metadata); + video_frame_rate = audio_sample_rate = 0; + + srs_freep(cache_sh_video); + srs_freep(cache_sh_audio); + + srs_trace("clear cache/metadata/sequence-headers when unpublish."); +} + +void SrsSource::set_cache(bool enabled) +{ + enable_gop_cache = enabled; + + if (!enabled) { + srs_info("disable gop cache, clear %d packets.", (int)gop_cache.size()); + clear_gop_cache(); + return; + } + + srs_info("enable gop cache"); +} + +int SrsSource::cache_last_gop(SrsSharedPtrMessage* msg) +{ + int ret = ERROR_SUCCESS; + + if (!enable_gop_cache) { + srs_verbose("gop cache is disabled."); + return ret; + } + + // got video, update the video count if acceptable + if (msg->header.is_video()) { + cached_video_count++; + } + + // no acceptable video or pure audio, disable the cache. + if (cached_video_count == 0) { + srs_verbose("ignore any frame util got a h264 video frame."); + return ret; + } + + // clear gop cache when got key frame + if (msg->header.is_video() && codec->video_is_keyframe(msg->payload, msg->size)) { + srs_info("clear gop cache when got keyframe. vcount=%d, count=%d", + cached_video_count, (int)gop_cache.size()); + + clear_gop_cache(); + + // curent msg is video frame, so we set to 1. + cached_video_count = 1; + } + + // cache the frame. + gop_cache.push_back(msg->copy()); + + return ret; +} + +void SrsSource::clear_gop_cache() +{ + std::vector::iterator it; + for (it = gop_cache.begin(); it != gop_cache.end(); ++it) { + SrsSharedPtrMessage* msg = *it; + srs_freep(msg); + } + gop_cache.clear(); + + cached_video_count = 0; +} + diff --git a/trunk/src/core/srs_core_source.hpp b/trunk/src/core/srs_core_source.hpp index 838bc3e57..3779599f3 100755 --- a/trunk/src/core/srs_core_source.hpp +++ b/trunk/src/core/srs_core_source.hpp @@ -1,165 +1,165 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_SOURCE_HPP -#define SRS_CORE_SOURCE_HPP - -/* -#include -*/ - -#include - -#include -#include -#include - -class SrsCodec; -class SrsSource; -class SrsCommonMessage; -class SrsOnMetaDataPacket; -class SrsSharedPtrMessage; - -/** -* the consumer for SrsSource, that is a play client. -*/ -class SrsConsumer -{ -private: - int32_t last_pkt_time; - int32_t last_pkt_correct_time; - SrsSource* source; - std::vector msgs; - bool paused; - SrsCodec* codec; -public: - SrsConsumer(SrsSource* _source); - virtual ~SrsConsumer(); -public: - /** - * get current client time, the last packet time. - */ - virtual int get_time(); - /** - * enqueue an shared ptr message. - * @param audio_sample_rate used to calc the audio time delta if time-jitter detected. - * @param video_frame_rate used to calc the video time delta if time-jitter detected. - */ - virtual int enqueue(SrsSharedPtrMessage* msg, int audio_sample_rate, int video_frame_rate); - /** - * get packets in consumer queue. - * @pmsgs SrsMessages*[], output the prt array. - * @count the count in array. - * @max_count the max count to dequeue, 0 to dequeue all. - */ - virtual int get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count); - /** - * when client send the pause message. - */ - virtual int on_play_client_pause(bool is_pause); -private: - /** - * when paused, shrink the cache queue, - * remove to cache only one gop. - */ - virtual void shrink(); - /** - * detect the time jitter and correct it. - */ - virtual int jitter_correct(SrsSharedPtrMessage* msg, int audio_sample_rate, int video_frame_rate); - virtual void clear(); -}; - -/** -* live streaming source. -*/ -class SrsSource -{ -private: - static std::map pool; -public: - /** - * find stream by vhost/app/stream. - * @stream_url the stream url, for example, myserver.xxx.com/app/stream - * @return the matched source, never be NULL. - * @remark stream_url should without port and schema. - */ - static SrsSource* find(std::string stream_url); -private: - SrsCodec* codec; - std::string stream_url; - std::vector consumers; -// gop cache for client fast startup. -private: - /** - * if disabled the gop cache, - * the client will wait for the next keyframe for h264, - * and will be black-screen. - */ - bool enable_gop_cache; - /** - * the video frame count, avoid cache for pure audio stream. - */ - int cached_video_count; - /** - * cached gop. - */ - std::vector gop_cache; -private: - /** - * the sample rate of audio in metadata. - */ - int audio_sample_rate; - /** - * the video frame rate in metadata. - */ - int video_frame_rate; -private: - SrsSharedPtrMessage* cache_metadata; - // the cached video sequence header. - SrsSharedPtrMessage* cache_sh_video; - // the cached audio sequence header. - SrsSharedPtrMessage* cache_sh_audio; -public: - SrsSource(std::string _stream_url); - virtual ~SrsSource(); -public: - virtual int on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata); - virtual int on_audio(SrsCommonMessage* audio); - virtual int on_video(SrsCommonMessage* video); -public: - virtual int create_consumer(SrsConsumer*& consumer); - virtual void on_consumer_destroy(SrsConsumer* consumer); - virtual void on_unpublish(); - virtual void set_cache(bool enabled); -private: - /** - * only for h264 codec - * 1. cache the gop when got h264 video packet. - * 2. clear gop when got keyframe. - */ - virtual int cache_last_gop(SrsSharedPtrMessage* msg); - virtual void clear_gop_cache(); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_SOURCE_HPP +#define SRS_CORE_SOURCE_HPP + +/* +#include +*/ + +#include + +#include +#include +#include + +class SrsCodec; +class SrsSource; +class SrsCommonMessage; +class SrsOnMetaDataPacket; +class SrsSharedPtrMessage; + +/** +* the consumer for SrsSource, that is a play client. +*/ +class SrsConsumer +{ +private: + int32_t last_pkt_time; + int32_t last_pkt_correct_time; + SrsSource* source; + std::vector msgs; + bool paused; + SrsCodec* codec; +public: + SrsConsumer(SrsSource* _source); + virtual ~SrsConsumer(); +public: + /** + * get current client time, the last packet time. + */ + virtual int get_time(); + /** + * enqueue an shared ptr message. + * @param audio_sample_rate used to calc the audio time delta if time-jitter detected. + * @param video_frame_rate used to calc the video time delta if time-jitter detected. + */ + virtual int enqueue(SrsSharedPtrMessage* msg, int audio_sample_rate, int video_frame_rate); + /** + * get packets in consumer queue. + * @pmsgs SrsMessages*[], output the prt array. + * @count the count in array. + * @max_count the max count to dequeue, 0 to dequeue all. + */ + virtual int get_packets(int max_count, SrsSharedPtrMessage**& pmsgs, int& count); + /** + * when client send the pause message. + */ + virtual int on_play_client_pause(bool is_pause); +private: + /** + * when paused, shrink the cache queue, + * remove to cache only one gop. + */ + virtual void shrink(); + /** + * detect the time jitter and correct it. + */ + virtual int jitter_correct(SrsSharedPtrMessage* msg, int audio_sample_rate, int video_frame_rate); + virtual void clear(); +}; + +/** +* live streaming source. +*/ +class SrsSource +{ +private: + static std::map pool; +public: + /** + * find stream by vhost/app/stream. + * @stream_url the stream url, for example, myserver.xxx.com/app/stream + * @return the matched source, never be NULL. + * @remark stream_url should without port and schema. + */ + static SrsSource* find(std::string stream_url); +private: + SrsCodec* codec; + std::string stream_url; + std::vector consumers; +// gop cache for client fast startup. +private: + /** + * if disabled the gop cache, + * the client will wait for the next keyframe for h264, + * and will be black-screen. + */ + bool enable_gop_cache; + /** + * the video frame count, avoid cache for pure audio stream. + */ + int cached_video_count; + /** + * cached gop. + */ + std::vector gop_cache; +private: + /** + * the sample rate of audio in metadata. + */ + int audio_sample_rate; + /** + * the video frame rate in metadata. + */ + int video_frame_rate; +private: + SrsSharedPtrMessage* cache_metadata; + // the cached video sequence header. + SrsSharedPtrMessage* cache_sh_video; + // the cached audio sequence header. + SrsSharedPtrMessage* cache_sh_audio; +public: + SrsSource(std::string _stream_url); + virtual ~SrsSource(); +public: + virtual int on_meta_data(SrsCommonMessage* msg, SrsOnMetaDataPacket* metadata); + virtual int on_audio(SrsCommonMessage* audio); + virtual int on_video(SrsCommonMessage* video); +public: + virtual int create_consumer(SrsConsumer*& consumer); + virtual void on_consumer_destroy(SrsConsumer* consumer); + virtual void on_unpublish(); + virtual void set_cache(bool enabled); +private: + /** + * only for h264 codec + * 1. cache the gop when got h264 video packet. + * 2. clear gop when got keyframe. + */ + virtual int cache_last_gop(SrsSharedPtrMessage* msg); + virtual void clear_gop_cache(); +}; + #endif \ No newline at end of file diff --git a/trunk/src/core/srs_core_stream.cpp b/trunk/src/core/srs_core_stream.cpp index fb05b7536..4b8930315 100755 --- a/trunk/src/core/srs_core_stream.cpp +++ b/trunk/src/core/srs_core_stream.cpp @@ -1,202 +1,202 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include - -#include -#include - -SrsStream::SrsStream() -{ - p = bytes = NULL; - size = 0; -} - -SrsStream::~SrsStream() -{ -} - -int SrsStream::initialize(char* _bytes, int _size) -{ - int ret = ERROR_SUCCESS; - - if (!_bytes) { - ret = ERROR_SYSTEM_STREAM_INIT; - srs_error("stream param bytes must not be NULL. ret=%d", ret); - return ret; - } - - if (_size <= 0) { - ret = ERROR_SYSTEM_STREAM_INIT; - srs_error("stream param size must be positive. ret=%d", ret); - return ret; - } - - size = _size; - p = bytes = _bytes; - - return ret; -} - -void SrsStream::reset() -{ - p = bytes; -} - -bool SrsStream::empty() -{ - return !p || !bytes || (p >= bytes + size); -} - -bool SrsStream::require(int required_size) -{ - return !empty() && (required_size <= bytes + size - p); -} - -void SrsStream::skip(int size) -{ - p += size; -} - -int SrsStream::pos() -{ - if (empty()) { - return 0; - } - - return p - bytes; -} - -int8_t SrsStream::read_1bytes() -{ - srs_assert(require(1)); - - return (int8_t)*p++; -} - -int16_t SrsStream::read_2bytes() -{ - srs_assert(require(2)); - - int16_t value; - pp = (char*)&value; - pp[1] = *p++; - pp[0] = *p++; - - return value; -} - -int32_t SrsStream::read_4bytes() -{ - srs_assert(require(4)); - - int32_t value; - pp = (char*)&value; - pp[3] = *p++; - pp[2] = *p++; - pp[1] = *p++; - pp[0] = *p++; - - return value; -} - -int64_t SrsStream::read_8bytes() -{ - srs_assert(require(8)); - - int64_t value; - pp = (char*)&value; - pp[7] = *p++; - pp[6] = *p++; - pp[5] = *p++; - pp[4] = *p++; - pp[3] = *p++; - pp[2] = *p++; - pp[1] = *p++; - pp[0] = *p++; - - return value; -} - -std::string SrsStream::read_string(int len) -{ - srs_assert(require(len)); - - std::string value; - value.append(p, len); - - p += len; - - return value; -} - -void SrsStream::write_1bytes(int8_t value) -{ - srs_assert(require(1)); - - *p++ = value; -} - -void SrsStream::write_2bytes(int16_t value) -{ - srs_assert(require(2)); - - pp = (char*)&value; - *p++ = pp[1]; - *p++ = pp[0]; -} - -void SrsStream::write_4bytes(int32_t value) -{ - srs_assert(require(4)); - - pp = (char*)&value; - *p++ = pp[3]; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; -} - -void SrsStream::write_8bytes(int64_t value) -{ - srs_assert(require(8)); - - pp = (char*)&value; - *p++ = pp[7]; - *p++ = pp[6]; - *p++ = pp[5]; - *p++ = pp[4]; - *p++ = pp[3]; - *p++ = pp[2]; - *p++ = pp[1]; - *p++ = pp[0]; -} - -void SrsStream::write_string(std::string value) -{ - srs_assert(require(value.length())); - - memcpy(p, value.data(), value.length()); - p += value.length(); -} - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include + +#include +#include + +SrsStream::SrsStream() +{ + p = bytes = NULL; + size = 0; +} + +SrsStream::~SrsStream() +{ +} + +int SrsStream::initialize(char* _bytes, int _size) +{ + int ret = ERROR_SUCCESS; + + if (!_bytes) { + ret = ERROR_SYSTEM_STREAM_INIT; + srs_error("stream param bytes must not be NULL. ret=%d", ret); + return ret; + } + + if (_size <= 0) { + ret = ERROR_SYSTEM_STREAM_INIT; + srs_error("stream param size must be positive. ret=%d", ret); + return ret; + } + + size = _size; + p = bytes = _bytes; + + return ret; +} + +void SrsStream::reset() +{ + p = bytes; +} + +bool SrsStream::empty() +{ + return !p || !bytes || (p >= bytes + size); +} + +bool SrsStream::require(int required_size) +{ + return !empty() && (required_size <= bytes + size - p); +} + +void SrsStream::skip(int size) +{ + p += size; +} + +int SrsStream::pos() +{ + if (empty()) { + return 0; + } + + return p - bytes; +} + +int8_t SrsStream::read_1bytes() +{ + srs_assert(require(1)); + + return (int8_t)*p++; +} + +int16_t SrsStream::read_2bytes() +{ + srs_assert(require(2)); + + int16_t value; + pp = (char*)&value; + pp[1] = *p++; + pp[0] = *p++; + + return value; +} + +int32_t SrsStream::read_4bytes() +{ + srs_assert(require(4)); + + int32_t value; + pp = (char*)&value; + pp[3] = *p++; + pp[2] = *p++; + pp[1] = *p++; + pp[0] = *p++; + + return value; +} + +int64_t SrsStream::read_8bytes() +{ + srs_assert(require(8)); + + int64_t value; + pp = (char*)&value; + pp[7] = *p++; + pp[6] = *p++; + pp[5] = *p++; + pp[4] = *p++; + pp[3] = *p++; + pp[2] = *p++; + pp[1] = *p++; + pp[0] = *p++; + + return value; +} + +std::string SrsStream::read_string(int len) +{ + srs_assert(require(len)); + + std::string value; + value.append(p, len); + + p += len; + + return value; +} + +void SrsStream::write_1bytes(int8_t value) +{ + srs_assert(require(1)); + + *p++ = value; +} + +void SrsStream::write_2bytes(int16_t value) +{ + srs_assert(require(2)); + + pp = (char*)&value; + *p++ = pp[1]; + *p++ = pp[0]; +} + +void SrsStream::write_4bytes(int32_t value) +{ + srs_assert(require(4)); + + pp = (char*)&value; + *p++ = pp[3]; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; +} + +void SrsStream::write_8bytes(int64_t value) +{ + srs_assert(require(8)); + + pp = (char*)&value; + *p++ = pp[7]; + *p++ = pp[6]; + *p++ = pp[5]; + *p++ = pp[4]; + *p++ = pp[3]; + *p++ = pp[2]; + *p++ = pp[1]; + *p++ = pp[0]; +} + +void SrsStream::write_string(std::string value) +{ + srs_assert(require(value.length())); + + memcpy(p, value.data(), value.length()); + p += value.length(); +} + diff --git a/trunk/src/core/srs_core_stream.hpp b/trunk/src/core/srs_core_stream.hpp index f48fd08b4..9c0ee0769 100755 --- a/trunk/src/core/srs_core_stream.hpp +++ b/trunk/src/core/srs_core_stream.hpp @@ -1,121 +1,121 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#ifndef SRS_CORE_STREAM_HPP -#define SRS_CORE_STREAM_HPP - -/* -#include -*/ - -#include - -#include -#include - -class SrsStream -{ -private: - char* p; - char* pp; - char* bytes; - int size; -public: - SrsStream(); - virtual ~SrsStream(); -public: - /** - * initialize the stream from bytes. - * @_bytes, must not be NULL, or return error. - * @_size, must be positive, or return error. - * @remark, stream never free the _bytes, user must free it. - */ - virtual int initialize(char* _bytes, int _size); - /** - * reset the position to beginning. - */ - virtual void reset(); - /** - * whether stream is empty. - * if empty, never read or write. - */ - virtual bool empty(); - /** - * whether required size is ok. - * @return true if stream can read/write specified required_size bytes. - */ - virtual bool require(int required_size); - /** - * to skip some size. - * @size can be any value. positive to forward; nagetive to backward. - */ - virtual void skip(int size); - /** - * tell the current pos. - */ - virtual int pos(); -public: - /** - * get 1bytes char from stream. - */ - virtual int8_t read_1bytes(); - /** - * get 2bytes int from stream. - */ - virtual int16_t read_2bytes(); - /** - * get 4bytes int from stream. - */ - virtual int32_t read_4bytes(); - /** - * get 8bytes int from stream. - */ - virtual int64_t read_8bytes(); - /** - * get string from stream, length specifies by param len. - */ - virtual std::string read_string(int len); -public: - /** - * write 1bytes char to stream. - */ - virtual void write_1bytes(int8_t value); - /** - * write 2bytes int to stream. - */ - virtual void write_2bytes(int16_t value); - /** - * write 4bytes int to stream. - */ - virtual void write_4bytes(int32_t value); - /** - * write 8bytes int to stream. - */ - virtual void write_8bytes(int64_t value); - /** - * write string to stream - */ - virtual void write_string(std::string value); -}; - +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#ifndef SRS_CORE_STREAM_HPP +#define SRS_CORE_STREAM_HPP + +/* +#include +*/ + +#include + +#include +#include + +class SrsStream +{ +private: + char* p; + char* pp; + char* bytes; + int size; +public: + SrsStream(); + virtual ~SrsStream(); +public: + /** + * initialize the stream from bytes. + * @_bytes, must not be NULL, or return error. + * @_size, must be positive, or return error. + * @remark, stream never free the _bytes, user must free it. + */ + virtual int initialize(char* _bytes, int _size); + /** + * reset the position to beginning. + */ + virtual void reset(); + /** + * whether stream is empty. + * if empty, never read or write. + */ + virtual bool empty(); + /** + * whether required size is ok. + * @return true if stream can read/write specified required_size bytes. + */ + virtual bool require(int required_size); + /** + * to skip some size. + * @size can be any value. positive to forward; nagetive to backward. + */ + virtual void skip(int size); + /** + * tell the current pos. + */ + virtual int pos(); +public: + /** + * get 1bytes char from stream. + */ + virtual int8_t read_1bytes(); + /** + * get 2bytes int from stream. + */ + virtual int16_t read_2bytes(); + /** + * get 4bytes int from stream. + */ + virtual int32_t read_4bytes(); + /** + * get 8bytes int from stream. + */ + virtual int64_t read_8bytes(); + /** + * get string from stream, length specifies by param len. + */ + virtual std::string read_string(int len); +public: + /** + * write 1bytes char to stream. + */ + virtual void write_1bytes(int8_t value); + /** + * write 2bytes int to stream. + */ + virtual void write_2bytes(int16_t value); + /** + * write 4bytes int to stream. + */ + virtual void write_4bytes(int32_t value); + /** + * write 8bytes int to stream. + */ + virtual void write_8bytes(int64_t value); + /** + * write string to stream + */ + virtual void write_string(std::string value); +}; + #endif \ No newline at end of file diff --git a/trunk/src/main/srs_main_server.cpp b/trunk/src/main/srs_main_server.cpp index bd6635211..766ffa13a 100755 --- a/trunk/src/main/srs_main_server.cpp +++ b/trunk/src/main/srs_main_server.cpp @@ -1,60 +1,60 @@ -/* -The MIT License (MIT) - -Copyright (c) 2013 winlin - -Permission is hereby granted, free of charge, to any person obtaining a copy of -this software and associated documentation files (the "Software"), to deal in -the Software without restriction, including without limitation the rights to -use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of -the Software, and to permit persons to whom the Software is furnished to do so, -subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS -FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR -COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER -IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN -CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -*/ - -#include -#include -#include -#include - -#include -#include - -void handler(int signo) -{ - srs_trace("get a signal, signo=%d", signo); - _server()->on_signal(signo); -} - -int main(int argc, char** argv){ - int ret = ERROR_SUCCESS; - - signal(SIGNAL_RELOAD, handler); - - if ((ret = config->parse_options(argc, argv)) != ERROR_SUCCESS) { - return ret; - } - - if ((ret = _server()->initialize()) != ERROR_SUCCESS) { - return ret; - } - - if ((ret = _server()->listen()) != ERROR_SUCCESS) { - return ret; - } - - if ((ret = _server()->cycle()) != ERROR_SUCCESS) { - return ret; - } - - return 0; -} +/* +The MIT License (MIT) + +Copyright (c) 2013 winlin + +Permission is hereby granted, free of charge, to any person obtaining a copy of +this software and associated documentation files (the "Software"), to deal in +the Software without restriction, including without limitation the rights to +use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of +the Software, and to permit persons to whom the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS +FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR +COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER +IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ + +#include +#include +#include +#include + +#include +#include + +void handler(int signo) +{ + srs_trace("get a signal, signo=%d", signo); + _server()->on_signal(signo); +} + +int main(int argc, char** argv){ + int ret = ERROR_SUCCESS; + + signal(SIGNAL_RELOAD, handler); + + if ((ret = config->parse_options(argc, argv)) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = _server()->initialize()) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = _server()->listen()) != ERROR_SUCCESS) { + return ret; + } + + if ((ret = _server()->cycle()) != ERROR_SUCCESS) { + return ret; + } + + return 0; +} diff --git a/trunk/src/srs/srs.upp b/trunk/src/srs/srs.upp index f7517cd8c..52d235d89 100755 --- a/trunk/src/srs/srs.upp +++ b/trunk/src/srs/srs.upp @@ -1,49 +1,49 @@ -file - main readonly separator, - ..\main\srs_main_server.cpp, - core readonly separator, - ..\core\srs_core.hpp, - ..\core\srs_core.cpp, - ..\core\srs_core_error.hpp, - ..\core\srs_core_error.cpp, - ..\core\srs_core_autofree.hpp, - ..\core\srs_core_autofree.cpp, - ..\core\srs_core_server.hpp, - ..\core\srs_core_server.cpp, - ..\core\srs_core_reload.hpp, - ..\core\srs_core_reload.cpp, - ..\core\srs_core_config.hpp, - ..\core\srs_core_config.cpp, - ..\core\srs_core_refer.hpp, - ..\core\srs_core_refer.cpp, - ..\core\srs_core_conn.hpp, - ..\core\srs_core_conn.cpp, - ..\core\srs_core_client.hpp, - ..\core\srs_core_client.cpp, - ..\core\srs_core_source.hpp, - ..\core\srs_core_source.cpp, - ..\core\srs_core_codec.hpp, - ..\core\srs_core_codec.cpp, - ..\core\srs_core_rtmp.hpp, - ..\core\srs_core_rtmp.cpp, - ..\core\srs_core_handshake.hpp, - ..\core\srs_core_handshake.cpp, - ..\core\srs_core_protocol.hpp, - ..\core\srs_core_protocol.cpp, - ..\core\srs_core_amf0.hpp, - ..\core\srs_core_amf0.cpp, - ..\core\srs_core_stream.hpp, - ..\core\srs_core_stream.cpp, - ..\core\srs_core_socket.hpp, - ..\core\srs_core_socket.cpp, - ..\core\srs_core_buffer.hpp, - ..\core\srs_core_buffer.cpp, - ..\core\srs_core_pithy_print.hpp, - ..\core\srs_core_pithy_print.cpp, - ..\core\srs_core_log.hpp, - ..\core\srs_core_log.cpp, - research readonly separator, - ..\..\research\ts_info.cc; -mainconfig - "" = "MAIN"; - +file + main readonly separator, + ..\main\srs_main_server.cpp, + core readonly separator, + ..\core\srs_core.hpp, + ..\core\srs_core.cpp, + ..\core\srs_core_error.hpp, + ..\core\srs_core_error.cpp, + ..\core\srs_core_autofree.hpp, + ..\core\srs_core_autofree.cpp, + ..\core\srs_core_server.hpp, + ..\core\srs_core_server.cpp, + ..\core\srs_core_reload.hpp, + ..\core\srs_core_reload.cpp, + ..\core\srs_core_config.hpp, + ..\core\srs_core_config.cpp, + ..\core\srs_core_refer.hpp, + ..\core\srs_core_refer.cpp, + ..\core\srs_core_conn.hpp, + ..\core\srs_core_conn.cpp, + ..\core\srs_core_client.hpp, + ..\core\srs_core_client.cpp, + ..\core\srs_core_source.hpp, + ..\core\srs_core_source.cpp, + ..\core\srs_core_codec.hpp, + ..\core\srs_core_codec.cpp, + ..\core\srs_core_rtmp.hpp, + ..\core\srs_core_rtmp.cpp, + ..\core\srs_core_handshake.hpp, + ..\core\srs_core_handshake.cpp, + ..\core\srs_core_protocol.hpp, + ..\core\srs_core_protocol.cpp, + ..\core\srs_core_amf0.hpp, + ..\core\srs_core_amf0.cpp, + ..\core\srs_core_stream.hpp, + ..\core\srs_core_stream.cpp, + ..\core\srs_core_socket.hpp, + ..\core\srs_core_socket.cpp, + ..\core\srs_core_buffer.hpp, + ..\core\srs_core_buffer.cpp, + ..\core\srs_core_pithy_print.hpp, + ..\core\srs_core_pithy_print.cpp, + ..\core\srs_core_log.hpp, + ..\core\srs_core_log.cpp, + research readonly separator, + ..\..\research\ts_info.cc; +mainconfig + "" = "MAIN"; +