diff --git a/src/app/htl_app_rtmp_protocol.cpp b/src/app/htl_app_rtmp_protocol.cpp index 84a1c8b..aa4c613 100644 --- a/src/app/htl_app_rtmp_protocol.cpp +++ b/src/app/htl_app_rtmp_protocol.cpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -24,49 +24,20 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include - - - - - - - -/* -The MIT License (MIT) - -Copyright (c) 2013-2015 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 "srs_librtmp.h" // auto generated by configure #ifndef SRS_AUTO_HEADER_HPP #define SRS_AUTO_HEADER_HPP -#define SRS_AUTO_BUILD_TS "1433829743" -#define SRS_AUTO_BUILD_DATE "2015-06-09 14:02:23" -#define SRS_AUTO_UNAME "Darwin winlin.lan 14.3.0 Darwin Kernel Version 14.3.0: Mon Mar 23 11:59:05 PDT 2015; root:xnu-2782.20.48~5/RELEASE_X86_64 x86_64" -#define SRS_AUTO_USER_CONFIGURE "--x86-x64 --export-librtmp-single=/Users/winlin/git/srs.librtmp/src/srs" +#define SRS_AUTO_BUILD_TS "1456973825" +#define SRS_AUTO_BUILD_DATE "2016-03-03 10:57:05" +#define SRS_AUTO_UNAME "Darwin winlin.local 15.3.0 Darwin Kernel Version 15.3.0: Thu Dec 10 18:40:58 PST 2015; root:xnu-3248.30.4~1/RELEASE_X86_64 x86_64" +#define SRS_AUTO_USER_CONFIGURE "--x86-x64 --export-librtmp-single=/Users/winlin/git/srs-bench/src/app/temp" #define SRS_AUTO_CONFIGURE "--prefix=/usr/local/srs --without-hls --without-hds --without-dvr --without-nginx --without-ssl --without-ffmpeg --without-transcode --without-ingest --without-stat --without-http-callback --without-http-server --without-stream-caster --without-http-api --with-librtmp --with-research --without-utest --without-gperf --without-gmc --without-gmp --without-gcp --without-gprof --without-arm-ubuntu12 --without-mips-ubuntu12 --log-trace" #define SRS_X86_X64 +#define SRS_EXPORT_LIBRTMP #define SRS_AUTO_EMBEDED_TOOL_CHAIN "normal x86/x64 gcc" @@ -80,6 +51,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #undef SRS_AUTO_HDS #undef SRS_AUTO_HTTP_CALLBACK #undef SRS_AUTO_SSL +#undef SRS_AUTO_MEM_WATCH #undef SRS_AUTO_FFMPEG_TOOL #define SRS_AUTO_FFMPEG_STUB #undef SRS_AUTO_TRANSCODE @@ -116,6 +88,11 @@ tufang14 \ allspace \ niesongsong \ rudeb0t \ +CallMeNP \ +synote \ +lovecat \ +panda1986<542638787@qq.com> \ +YueHonghui \ " #endif @@ -124,7 +101,7 @@ rudeb0t \ /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -154,24 +131,29 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // current release version #define VERSION_MAJOR 2 #define VERSION_MINOR 0 -#define VERSION_REVISION 173 +#define VERSION_REVISION 209 -// server info. +// generated by configure, only macros. +//#include + +// provider info. #define RTMP_SIG_SRS_KEY "SRS" #define RTMP_SIG_SRS_CODE "ZhouGuowen" -#define RTMP_SIG_SRS_ROLE "origin/edge server" -#define RTMP_SIG_SRS_NAME RTMP_SIG_SRS_KEY"(Simple RTMP Server)" -#define RTMP_SIG_SRS_URL_SHORT "github.com/simple-rtmp-server/srs" -#define RTMP_SIG_SRS_URL "https://"RTMP_SIG_SRS_URL_SHORT +#define RTMP_SIG_SRS_AUTHROS "winlin,wenjie.zhao" +// contact info. #define RTMP_SIG_SRS_WEB "http://ossrs.net" #define RTMP_SIG_SRS_EMAIL "winlin@vip.126.com" +// debug info. +#define RTMP_SIG_SRS_ROLE "cluster" +#define RTMP_SIG_SRS_NAME RTMP_SIG_SRS_KEY"(Simple RTMP Server)" +#define RTMP_SIG_SRS_URL_SHORT "github.com/ossrs/srs" +#define RTMP_SIG_SRS_URL "https://"RTMP_SIG_SRS_URL_SHORT #define RTMP_SIG_SRS_LICENSE "The MIT License (MIT)" -#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013-2015 SRS(simple-rtmp-server)" -#define RTMP_SIG_SRS_PRIMARY "SRS/"VERSION_STABLE_BRANCH -#define RTMP_SIG_SRS_AUTHROS "winlin,wenjie.zhao" +#define RTMP_SIG_SRS_COPYRIGHT "Copyright (c) 2013-2015 SRS(ossrs)" +#define RTMP_SIG_SRS_PRIMARY RTMP_SIG_SRS_KEY"/"VERSION_STABLE_BRANCH #define RTMP_SIG_SRS_CONTRIBUTORS_URL RTMP_SIG_SRS_URL"/blob/master/AUTHORS.txt" #define RTMP_SIG_SRS_HANDSHAKE RTMP_SIG_SRS_KEY"("RTMP_SIG_SRS_VERSION")" -#define RTMP_SIG_SRS_RELEASE RTMP_SIG_SRS_URL"/tree/1.0release" +#define RTMP_SIG_SRS_RELEASE RTMP_SIG_SRS_URL"/tree/"VERSION_STABLE_BRANCH".0release" #define RTMP_SIG_SRS_ISSUES(id) RTMP_SIG_SRS_URL"/issues/"#id #define RTMP_SIG_SRS_VERSION SRS_XSTR(VERSION_MAJOR)"."SRS_XSTR(VERSION_MINOR)"."SRS_XSTR(VERSION_REVISION) #define RTMP_SIG_SRS_SERVER RTMP_SIG_SRS_KEY"/"RTMP_SIG_SRS_VERSION"("RTMP_SIG_SRS_CODE")" @@ -202,7 +184,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define __STDC_FORMAT_MACROS #endif -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -213,8 +195,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -// generated by configure. -//#include // important performance options. //#include @@ -226,9 +206,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. p = NULL; \ } \ (void)0 -// sometimes, the freepa is useless, -// it's recomments to free each elem explicit. -// so we remove the srs_freepa utility. +// please use the freepa(T[]) to free an array, +// or the behavior is undefined. +#define srs_freepa(pa) \ + if (pa) { \ + delete[] pa; \ + pa = NULL; \ + } \ + (void)0 /** * disable copy constructor of class, @@ -242,13 +227,25 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. className(const className&); \ className& operator= (const className&) +/** + * important check for st(state-threads), + * only support the following cpus: + * 1. i386/amd64/x86_64 + * 2. arm, glibc <= 2.15 + */ +#if !defined(__amd64__) && !defined(__x86_64__) && !defined(__i386__) && !defined(__arm__) + #error "only support i386/amd64/x86_64/arm cpu" +#endif +#if defined(__arm__) && (__GLIBC__ != 2 || __GLIBC_MINOR__ > 15) + #error "for arm, only support glibc <= 2.15" #endif +#endif // following is generated by src/core/srs_core_autofree.hpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -278,28 +275,41 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include /** -* auto free the instance in the current scope, for instance, MyClass* ptr, -* which is a ptr and this class will: -* 1. free the ptr. -* 2. set ptr to NULL. -* Usage: -* MyClass* po = new MyClass(); -* // ...... use po -* SrsAutoFree(MyClass, po); -*/ + * auto free the instance in the current scope, for instance, MyClass* ptr, + * which is a ptr and this class will: + * 1. free the ptr. + * 2. set ptr to NULL. + * + * Usage: + * MyClass* po = new MyClass(); + * // ...... use po + * SrsAutoFree(MyClass, po); + * + * Usage for array: + * MyClass** pa = new MyClass*[size]; + * // ....... use pa + * SrsAutoFreeA(MyClass*, pa); + * + * @remark the MyClass can be basic type, for instance, SrsAutoFreeA(char, pstr), + * where the char* pstr = new char[size]. + */ #define SrsAutoFree(className, instance) \ - impl__SrsAutoFree _auto_free_##instance(&instance) +impl__SrsAutoFree _auto_free_##instance(&instance, false) +#define SrsAutoFreeA(className, instance) \ +impl__SrsAutoFree _auto_free_array_##instance(&instance, true) template class impl__SrsAutoFree { private: T** ptr; + bool is_array; public: /** - * auto delete the ptr. - */ - impl__SrsAutoFree(T** _ptr) { - ptr = _ptr; + * auto delete the ptr. + */ + impl__SrsAutoFree(T** p, bool array) { + ptr = p; + is_array = array; } virtual ~impl__SrsAutoFree() { @@ -307,7 +317,11 @@ class impl__SrsAutoFree return; } - delete *ptr; + if (is_array) { + delete[] *ptr; + } else { + delete *ptr; + } *ptr = NULL; } @@ -318,7 +332,7 @@ class impl__SrsAutoFree /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -357,12 +371,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * that is, we merge some data to read together. * @see SrsConfig::get_mr_enabled() * @see SrsConfig::get_mr_sleep_ms() -* @see https://github.com/simple-rtmp-server/srs/issues/241 +* @see https://github.com/ossrs/srs/issues/241 * @example, for the default settings, this algorithm will use: * that is, when got nread bytes smaller than 4KB, sleep(780ms). */ /** -* https://github.com/simple-rtmp-server/srs/issues/241#issuecomment-65554690 +* https://github.com/ossrs/srs/issues/241#issuecomment-65554690 * The merged read algorithm is ok and can be simplified for: * 1. Suppose the client network is ok. All algorithm go wrong when netowrk is not ok. * 2. Suppose the client send each packet one by one. Although send some together, it's same. @@ -388,7 +402,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * @remark this largely improve performance, from 3.5k+ to 7.5k+. * the latency+ when cache+. * @remark the socket send buffer default to 185KB, it large enough. -* @see https://github.com/simple-rtmp-server/srs/issues/194 +* @see https://github.com/ossrs/srs/issues/194 * @see SrsConfig::get_mw_sleep_ms() * @remark the mw sleep and msgs to send, maybe: * mw_sleep msgs iovs @@ -423,24 +437,24 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * whether set the socket send buffer size. -* @see https://github.com/simple-rtmp-server/srs/issues/251 +* @see https://github.com/ossrs/srs/issues/251 */ #define SRS_PERF_MW_SO_SNDBUF /** * whether set the socket recv buffer size. -* @see https://github.com/simple-rtmp-server/srs/issues/251 +* @see https://github.com/ossrs/srs/issues/251 */ #undef SRS_PERF_MW_SO_RCVBUF /** * whether enable the fast vector for qeueue. -* @see https://github.com/simple-rtmp-server/srs/issues/251 +* @see https://github.com/ossrs/srs/issues/251 */ #define SRS_PERF_QUEUE_FAST_VECTOR /** * whether use cond wait to send messages. * @remark this improve performance for large connectios. -* @see https://github.com/simple-rtmp-server/srs/issues/251 +* @see https://github.com/ossrs/srs/issues/251 */ #define SRS_PERF_QUEUE_COND_WAIT #ifdef SRS_PERF_QUEUE_COND_WAIT @@ -452,14 +466,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * for min latence mode: * 1. disable the mr for vhost. * 2. use timeout for cond wait for consumer queue. -* @see https://github.com/simple-rtmp-server/srs/issues/257 +* @see https://github.com/ossrs/srs/issues/257 */ #define SRS_PERF_MIN_LATENCY_ENABLED false /** * how many chunk stream to cache, [0, N]. * to imporove about 10% performance when chunk size small, and 5% for large chunk. -* @see https://github.com/simple-rtmp-server/srs/issues/249 +* @see https://github.com/ossrs/srs/issues/249 * @remark 0 to disable the chunk stream cache. */ #define SRS_PERF_CHUNK_STREAM_CACHE 16 @@ -475,23 +489,23 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * whether always use complex send algorithm. * for some network does not support the complex send, -* @see https://github.com/simple-rtmp-server/srs/issues/320 +* @see https://github.com/ossrs/srs/issues/320 */ //#undef SRS_PERF_COMPLEX_SEND #define SRS_PERF_COMPLEX_SEND /** -* whether enable the TCP_NODELAY -* user maybe need send small tcp packet for some network. -* @see https://github.com/simple-rtmp-server/srs/issues/320 -*/ -//#define SRS_PERF_TCP_NODELAY + * whether enable the TCP_NODELAY + * user maybe need send small tcp packet for some network. + * @see https://github.com/ossrs/srs/issues/320 + */ #undef SRS_PERF_TCP_NODELAY +#define SRS_PERF_TCP_NODELAY /** * set the socket send buffer, * to force the server to send smaller tcp packet. -* @see https://github.com/simple-rtmp-server/srs/issues/320 +* @see https://github.com/ossrs/srs/issues/320 * @remark undef it to auto calc it by merged write sleep ms. -* @remark only apply it when SRS_PERF_MW_SO_RCVBUF is defined. +* @remark only apply it when SRS_PERF_MW_SO_SNDBUF is defined. */ #ifdef SRS_PERF_MW_SO_SNDBUF //#define SRS_PERF_SO_SNDBUF_SIZE 1024 @@ -500,25 +514,18 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * define the following macro to enable the fast flv encoder. - * @see https://github.com/simple-rtmp-server/srs/issues/405 + * @see https://github.com/ossrs/srs/issues/405 */ #undef SRS_PERF_FAST_FLV_ENCODER #define SRS_PERF_FAST_FLV_ENCODER -/** - * whether enable the special memory watcher. - * which used for memory leak debug and hurts performance. - */ -#define SRS_MEM_WATCH -#undef SRS_MEM_WATCH - #endif // following is generated by src/core/srs_core_mem_watch.hpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -547,7 +554,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -#ifdef SRS_MEM_WATCH +#ifdef SRS_AUTO_MEM_WATCH #include @@ -568,7 +575,7 @@ extern void srs_memory_report(); /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -597,7 +604,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #define ERROR_SUCCESS 0 #endif @@ -670,7 +677,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /////////////////////////////////////////////////////// #define ERROR_RTMP_PLAIN_REQUIRED 2000 #define ERROR_RTMP_CHUNK_START 2001 -#define ERROR_RTMP_MSG_INVLIAD_SIZE 2002 +#define ERROR_RTMP_MSG_INVALID_SIZE 2002 #define ERROR_RTMP_AMF0_DECODE 2003 #define ERROR_RTMP_AMF0_INVALID 2004 #define ERROR_RTMP_REQ_CONNECT 2005 @@ -716,6 +723,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_RTP_TYPE96_CORRUPT 2045 #define ERROR_RTP_TYPE97_CORRUPT 2046 #define ERROR_RTSP_AUDIO_CONFIG 2047 +#define ERROR_RTMP_STREAM_NOT_FOUND 2048 +#define ERROR_RTMP_CLIENT_NOT_FOUND 2049 // // system control message, // not an error, but special control logic. @@ -735,7 +744,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_HLS_AAC_FRAME_LENGTH 3005 #define ERROR_HLS_AVC_SAMPLE_SIZE 3006 #define ERROR_HTTP_PARSE_URI 3007 -#define ERROR_HTTP_DATA_INVLIAD 3008 +#define ERROR_HTTP_DATA_INVALID 3008 #define ERROR_HTTP_PARSE_HEADER 3009 #define ERROR_HTTP_HANDLER_MATCH_URL 3010 #define ERROR_HTTP_HANDLER_INVALID 3011 @@ -792,6 +801,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_HLS_NO_STREAM 3062 #define ERROR_JSON_LOADS 3063 #define ERROR_RESPONSE_CODE 3064 +#define ERROR_RESPONSE_DATA 3065 +#define ERROR_REQUEST_DATA 3066 /////////////////////////////////////////////////////// // HTTP/StreamCaster protocol error. @@ -801,7 +812,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_HTTP_URL_NOT_CLEAN 4002 #define ERROR_HTTP_CONTENT_LENGTH 4003 #define ERROR_HTTP_LIVE_STREAM_EXT 4004 -#define ERROR_HTTP_STATUS_INVLIAD 4005 +#define ERROR_HTTP_STATUS_INVALID 4005 #define ERROR_KERNEL_AAC_STREAM_CLOSED 4006 #define ERROR_AAC_DECODE_ERROR 4007 #define ERROR_KERNEL_MP3_STREAM_CLOSED 4008 @@ -827,6 +838,11 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define ERROR_AAC_BYTES_INVALID 4028 #define ERROR_HTTP_REQUEST_EOF 4029 +/////////////////////////////////////////////////////// +// HTTP API error. +/////////////////////////////////////////////////////// +//#define ERROR_API_METHOD_NOT_ALLOWD + /////////////////////////////////////////////////////// // user-define error. /////////////////////////////////////////////////////// @@ -861,7 +877,7 @@ extern bool srs_is_client_gracefully_close(int error_code); /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -955,6 +971,13 @@ class ISrsLog virtual void error(const char* tag, int context_id, const char* fmt, ...); }; +/** + * the context id manager to identify context, for instance, the green-thread. + * usage: + * _srs_context->generate_id(); // when thread start. + * _srs_context->get_id(); // get current generated id. + * int old_id = _srs_context->set_id(1000); // set context id if need to merge thread context. + */ // the context for multiple clients. class ISrsThreadContext { @@ -962,8 +985,19 @@ class ISrsThreadContext ISrsThreadContext(); virtual ~ISrsThreadContext(); public: + /** + * generate the id for current context. + */ virtual int generate_id(); + /** + * get the generated id of current context. + */ virtual int get_id(); + /** + * set the id of current context. + * @return the previous id value; 0 if no context. + */ + virtual int set_id(int v); }; // user must provides a log object @@ -1015,7 +1049,7 @@ extern ISrsThreadContext* _srs_context; /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -1055,23 +1089,25 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsStream { private: + // current position at bytes. char* p; - char* pp; - char* _bytes; - int _size; + // the bytes data for stream to read or write. + char* bytes; + // the total number of bytes. + int nb_bytes; public: SrsStream(); virtual ~SrsStream(); public: /** * initialize the stream from bytes. - * @bytes, the bytes to convert from/to basic types. - * @size, the size of bytes. + * @b, the bytes to convert from/to basic types. + * @nb, the size of bytes, total number of bytes for stream. * @remark, stream never free the bytes, user must free it. * @remark, return error when bytes NULL. * @remark, return error when size is not positive. */ - virtual int initialize(char* bytes, int size); + virtual int initialize(char* b, int nb); // get the status of stream public: /** @@ -1191,7 +1227,7 @@ class SrsBitStream /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -1257,8 +1293,11 @@ extern std::string srs_string_remove(std::string str, std::string remove_chars); extern bool srs_string_ends_with(std::string str, std::string flag); // whether string starts with extern bool srs_string_starts_with(std::string str, std::string flag); +extern bool srs_string_starts_with(std::string str, std::string flag0, std::string flag1); // whether string contains with extern bool srs_string_contains(std::string str, std::string flag); +extern bool srs_string_contains(std::string str, std::string flag0, std::string flag1); +extern bool srs_string_contains(std::string str, std::string flag0, std::string flag1, std::string flag2); // create dir recursively extern int srs_create_dir_recursively(std::string dir); @@ -1357,7 +1396,7 @@ extern int srs_chunk_header_c3( /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -1387,7 +1426,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -1687,7 +1726,7 @@ class SrsSharedPtrMessage // 4.1. Message Header public: // the header can shared, only set the timestamp and stream id. - // @see https://github.com/simple-rtmp-server/srs/issues/251 + // @see https://github.com/ossrs/srs/issues/251 //SrsSharedMessageHeader header; /** * Four-byte field that contains a timestamp of the message. @@ -1722,7 +1761,7 @@ class SrsSharedPtrMessage { public: // shared message header. - // @see https://github.com/simple-rtmp-server/srs/issues/251 + // @see https://github.com/ossrs/srs/issues/251 SrsSharedMessageHeader header; // actual shared payload. char* payload; @@ -1790,7 +1829,7 @@ class SrsSharedPtrMessage class SrsFlvEncoder { private: - SrsFileWriter* _fs; + SrsFileWriter* reader; private: SrsStream* tag_stream; char tag_header[SRS_FLV_TAG_HEADER_SIZE]; @@ -1801,9 +1840,9 @@ class SrsFlvEncoder /** * initialize the underlayer file stream. * @remark user can initialize multiple times to encode multiple flv files. - * @remark, user must free the fs, flv encoder never close/free it. + * @remark, user must free the @param fr, flv encoder never close/free it. */ - virtual int initialize(SrsFileWriter* fs); + virtual int initialize(SrsFileWriter* fr); public: /** * write flv header. @@ -1868,7 +1907,7 @@ class SrsFlvEncoder class SrsFlvDecoder { private: - SrsFileReader* _fs; + SrsFileReader* reader; private: SrsStream* tag_stream; public: @@ -1878,9 +1917,9 @@ class SrsFlvDecoder /** * initialize the underlayer file stream * @remark user can initialize multiple times to decode multiple flv files. - * @remark, user must free the fs, flv decoder never close/free it. + * @remark user must free the @param fr, flv decoder never close/free it. */ - virtual int initialize(SrsFileReader* fs); + virtual int initialize(SrsFileReader* fr); public: /** * read the flv header, donot including the 4bytes previous tag size. @@ -1912,7 +1951,7 @@ class SrsFlvDecoder class SrsFlvVodStreamDecoder { private: - SrsFileReader* _fs; + SrsFileReader* reader; private: SrsStream* tag_stream; public: @@ -1922,9 +1961,9 @@ class SrsFlvVodStreamDecoder /** * initialize the underlayer file stream * @remark user can initialize multiple times to decode multiple flv files. - * @remark, user must free the fs, flv decoder never close/free it. + * @remark user must free the @param fr, flv decoder never close/free it. */ - virtual int initialize(SrsFileReader* fs); + virtual int initialize(SrsFileReader* fr); public: /** * read the flv header and its size. @@ -1953,7 +1992,7 @@ class SrsFlvVodStreamDecoder /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -2198,12 +2237,6 @@ extern int aac_sample_rates[]; #define SRS_SRS_MAX_CODEC_SAMPLE 128 #define SRS_AAC_SAMPLE_RATE_UNSET 15 -// in ms, for HLS aac flush the audio -#define SRS_CONF_DEFAULT_AAC_DELAY 60 - -// max PES packets size to flush the video. -#define SRS_AUTO_HLS_AUDIO_CACHE_SIZE 128 * 1024 - /** * the FLV/RTMP supported audio sample size. * Size of each audio sample. This parameter only pertains to @@ -2387,7 +2420,7 @@ enum SrsAvcPayloadFormat /** * the aac profile, for ADTS(HLS/TS) -* @see https://github.com/simple-rtmp-server/srs/issues/310 +* @see https://github.com/ossrs/srs/issues/310 */ enum SrsAacProfile { @@ -2477,6 +2510,8 @@ enum SrsAvcLevel }; std::string srs_codec_avc_level2str(SrsAvcLevel level); +#if !defined(SRS_EXPORT_LIBRTMP) + /** * the h264/avc and aac codec, for media stream. * @@ -2557,9 +2592,16 @@ class SrsAvcAacCodec */ int aac_extra_size; char* aac_extra_data; +public: + // for sequence header, whether parse the h.264 sps. + bool avc_parse_sps; public: SrsAvcAacCodec(); virtual ~SrsAvcAacCodec(); +public: + // whether avc or aac codec sequence header or extra data is decoded ok. + virtual bool is_avc_codec_ok(); + virtual bool is_aac_codec_ok(); // the following function used for hls to build the sample and codec. public: /** @@ -2608,11 +2650,14 @@ class SrsAvcAacCodec }; #endif + +#endif + // following is generated by src/kernel/srs_kernel_file.hpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -2642,7 +2687,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -2653,20 +2698,26 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. class SrsFileWriter { private: - std::string _file; + std::string path; int fd; public: SrsFileWriter(); virtual ~SrsFileWriter(); public: /** - * open file writer, can open then close then open... - */ - virtual int open(std::string file); + * open file writer, in truncate mode. + * @param p a string indicates the path of file to open. + */ + virtual int open(std::string p); /** - * open file writer in append mode. - */ - virtual int open_append(std::string file); + * open file writer, in append mode. + * @param p a string indicates the path of file to open. + */ + virtual int open_append(std::string p); + /** + * close current writer. + * @remark user can reopen again. + */ virtual void close(); public: virtual bool is_open(); @@ -2680,7 +2731,7 @@ class SrsFileWriter virtual int write(void* buf, size_t count, ssize_t* pnwrite); /** * for the HTTP FLV, to writev to improve performance. - * @see https://github.com/simple-rtmp-server/srs/issues/405 + * @see https://github.com/ossrs/srs/issues/405 */ virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite); }; @@ -2691,16 +2742,21 @@ class SrsFileWriter class SrsFileReader { private: - std::string _file; + std::string path; int fd; public: SrsFileReader(); virtual ~SrsFileReader(); public: /** - * open file reader, can open then close then open... - */ - virtual int open(std::string file); + * open file reader. + * @param p a string indicates the path of file to open. + */ + virtual int open(std::string p); + /** + * close current reader. + * @remark user can reopen again. + */ virtual void close(); public: // TODO: FIXME: extract interface. @@ -2723,7 +2779,7 @@ class SrsFileReader /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -2796,10 +2852,6 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // the timeout to wait client data, // if timeout, close the connection. #define SRS_CONSTS_RTMP_RECV_TIMEOUT_US (int64_t)(30*1000*1000LL) -// the timeout for publish recv. -// we must use more smaller timeout, for the recv never know the status -// of underlayer socket. -#define SRS_CONSTS_RTMP_PUBLISHER_RECV_TIMEOUT_US (int64_t)(3*1000*1000LL) // the timeout to wait for client control message, // if timeout, we generally ignore and send the data to client, @@ -2826,7 +2878,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /** * for performance issue, -* the iovs cache, @see https://github.com/simple-rtmp-server/srs/issues/194 +* the iovs cache, @see https://github.com/ossrs/srs/issues/194 * iovs cache for multiple messages for each connections. * suppose the chunk size is 64k, each message send in a chunk which needs only 2 iovec, * so the iovs max should be (SRS_PERF_MW_MSGS * 2) @@ -2836,7 +2888,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SRS_CONSTS_IOVS_MAX (SRS_PERF_MW_MSGS * 2) /** * for performance issue, -* the c0c3 cache, @see https://github.com/simple-rtmp-server/srs/issues/194 +* the c0c3 cache, @see https://github.com/ossrs/srs/issues/194 * c0c3 cache for multiple messages for each connections. * each c0 <= 16byes, suppose the chunk size is 64k, * each message send in a chunk which needs only a c0 header, @@ -3113,7 +3165,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -3141,6 +3193,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //#include +#if !defined(SRS_EXPORT_LIBRTMP) + #include //#include @@ -3183,11 +3237,13 @@ class SrsAacEncoder #endif +#endif + // following is generated by src/kernel/srs_kernel_mp3.hpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -3215,11 +3271,12 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //#include +#if !defined(SRS_EXPORT_LIBRTMP) + #include class SrsStream; class SrsFileWriter; -class SrsFileReader; /** * encode data to aac file. @@ -3227,7 +3284,7 @@ class SrsFileReader; class SrsMp3Encoder { private: - SrsFileWriter* _fs; + SrsFileWriter* writer; private: SrsStream* tag_stream; public: @@ -3237,9 +3294,9 @@ class SrsMp3Encoder /** * initialize the underlayer file stream. * @remark user can initialize multiple times to encode multiple mp3 files. - * @remark, user must free the fs, mp3 encoder never close/free it. + * @remark, user must free the @param fw, mp3 encoder never close/free it. */ - virtual int initialize(SrsFileWriter* fs); + virtual int initialize(SrsFileWriter* fw); public: /** * write mp3 id3 v2.3 header. @@ -3255,11 +3312,13 @@ class SrsMp3Encoder #endif +#endif + // following is generated by src/kernel/srs_kernel_ts.hpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -3287,6 +3346,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //#include +#if !defined(SRS_EXPORT_LIBRTMP) + #include #include #include @@ -3310,6 +3371,9 @@ class SrsTsContext; // Transport Stream packets are 188 bytes in length. #define SRS_TS_PACKET_SIZE 188 +// the aggregate pure audio for hls, in ts tbn(ms * 90). +#define SRS_CONSTS_HLS_PURE_AUDIO_AGGREGATE 720 * 90 + /** * the pid of ts packet, * Table 2-3 - PID table, hls-mpeg-ts-iso13818-1.pdf, page 37 @@ -3615,6 +3679,7 @@ class SrsTsContext /** * whether the hls stream is pure audio stream. */ + // TODO: FIXME: merge with muxer codec detect. virtual bool is_pure_audio(); /** * when PMT table parsed, we know some info about stream. @@ -4818,15 +4883,16 @@ class SrsTSMuxer virtual ~SrsTSMuxer(); public: /** - * open the writer, donot write the PSI of ts. - */ - virtual int open(std::string _path); + * open the writer, donot write the PSI of ts. + * @param p a string indicates the path of ts file to mux to. + */ + virtual int open(std::string p); /** * when open ts, we donot write the header(PSI), * for user may need to update the acodec to mp3 or others, * so we use delay write PSI, when write audio or video. * @remark for audio aac codec, for example, SRS1, it's ok to write PSI when open ts. - * @see https://github.com/simple-rtmp-server/srs/issues/301 + * @see https://github.com/ossrs/srs/issues/301 */ virtual int update_acodec(SrsCodecAudio ac); /** @@ -4841,6 +4907,11 @@ class SrsTSMuxer * close the writer. */ virtual void close(); +public: + /** + * get the video codec of ts muxer. + */ + virtual SrsCodecVideo video_codec(); }; /** @@ -4883,7 +4954,7 @@ class SrsTsCache class SrsTsEncoder { private: - SrsFileWriter* _fs; + SrsFileWriter* writer; private: SrsAvcAacCodec* codec; SrsCodecSample* sample; @@ -4895,9 +4966,10 @@ class SrsTsEncoder virtual ~SrsTsEncoder(); public: /** - * initialize the underlayer file stream. - */ - virtual int initialize(SrsFileWriter* fs); + * initialize the underlayer file stream. + * @param fw the writer to use for ts encoder, user must free it. + */ + virtual int initialize(SrsFileWriter* fw); public: /** * write audio/video packet. @@ -4912,11 +4984,13 @@ class SrsTsEncoder #endif +#endif + // following is generated by src/kernel/srs_kernel_buffer.hpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -4989,7 +5063,7 @@ class SrsSimpleBuffer /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -5393,6 +5467,10 @@ class SrsAmf0Object : public SrsAmf0Any * @remark user should never free the returned value, copy it if needed. */ virtual SrsAmf0Any* ensure_property_number(std::string name); + /** + * remove the property specified by name. + */ + virtual void remove(std::string name); }; /** @@ -5683,7 +5761,7 @@ namespace _srs_internal * 2.13 Date Type * time-zone = S16 ; reserved, not supported should be set to 0x0000 * date-type = date-marker DOUBLE time-zone - * @see: https://github.com/simple-rtmp-server/srs/issues/185 + * @see: https://github.com/ossrs/srs/issues/185 */ class SrsAmf0Date : public SrsAmf0Any { @@ -5791,6 +5869,7 @@ namespace _srs_internal virtual SrsAmf0Any* get_property(std::string name); virtual SrsAmf0Any* ensure_property_string(std::string name); virtual SrsAmf0Any* ensure_property_number(std::string name); + virtual void remove(std::string name); public: virtual void copy(SrsUnSortedHashtable* src); }; @@ -5834,7 +5913,7 @@ namespace _srs_internal /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -5863,7 +5942,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -6019,7 +6098,7 @@ class ISrsProtocolReaderWriter : public virtual ISrsProtocolReader, public virtu /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -6052,7 +6131,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include #include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -6075,6 +6154,19 @@ class SrsChunkStream; class SrsSharedPtrMessage; class IMergeReadHandler; +class SrsProtocol; +class ISrsProtocolReaderWriter; +class SrsCommonMessage; +class SrsCreateStreamPacket; +class SrsFMLEStartPacket; +class SrsPublishPacket; +class SrsOnMetaDataPacket; +class SrsPlayPacket; +class SrsCommonMessage; +class SrsPacket; +class SrsAmf0Object; +class IMergeReadHandler; + /**************************************************************************** ***************************************************************************** ****************************************************************************/ @@ -6096,6 +6188,36 @@ class IMergeReadHandler; #define RTMP_AMF0_COMMAND_PUBLISH "publish" #define RTMP_AMF0_DATA_SAMPLE_ACCESS "|RtmpSampleAccess" +/** + * 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" +// status error +#define StatusLevelError "error" +// code value +#define StatusCodeConnectSuccess "NetConnection.Connect.Success" +#define StatusCodeConnectRejected "NetConnection.Connect.Rejected" +#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" + /**************************************************************************** ***************************************************************************** ****************************************************************************/ @@ -6191,7 +6313,7 @@ class SrsProtocol /** * cache some frequently used chunk header. * cs_cache, the chunk stream cache. - * @see https://github.com/simple-rtmp-server/srs/issues/249 + * @see https://github.com/ossrs/srs/issues/249 */ SrsChunkStream** cs_cache; /** @@ -6209,7 +6331,7 @@ class SrsProtocol /** * whether auto response when recv messages. * default to true for it's very easy to use the protocol stack. - * @see: https://github.com/simple-rtmp-server/srs/issues/217 + * @see: https://github.com/ossrs/srs/issues/217 */ bool auto_response_when_recv; /** @@ -6247,7 +6369,7 @@ class SrsProtocol /** * set the auto response message when recv for protocol stack. * @param v, whether auto response message when recv message. - * @see: https://github.com/simple-rtmp-server/srs/issues/217 + * @see: https://github.com/ossrs/srs/issues/217 */ virtual void set_auto_response(bool v); /** @@ -6265,7 +6387,7 @@ class SrsProtocol * that is, we merge some data to read together. * @param v true to ename merged read. * @param handler the handler when merge read is enabled. - * @see https://github.com/simple-rtmp-server/srs/issues/241 + * @see https://github.com/ossrs/srs/issues/241 */ virtual void set_merge_read(bool v, IMergeReadHandler* handler); /** @@ -6273,7 +6395,7 @@ class SrsProtocol * @param buffer the size of buffer. * @remark when MR(SRS_PERF_MERGED_READ) disabled, always set to 8K. * @remark when buffer changed, the previous ptr maybe invalid. - * @see https://github.com/simple-rtmp-server/srs/issues/241 + * @see https://github.com/ossrs/srs/issues/241 */ virtual void set_recv_buffer(int buffer_size); #endif @@ -6340,20 +6462,22 @@ class SrsProtocol virtual int send_and_free_packet(SrsPacket* packet, int stream_id); public: /** - * 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. - * for example: - SrsCommonMessage* msg = NULL; - SrsConnectAppResPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - return ret; - } - // use pkt - * user should never recv message and convert it, use this method instead. - * if need to set timeout, use set timeout of SrsProtocol. - */ + * expect a specified message, drop others util got specified one. + * @pmsg, user must free it. NULL if not success. + * @ppacket, user must free it, which decode from payload of message. NULL if not success. + * @remark, only when success, user can use and must free the pmsg and ppacket. + * for example: + * SrsCommonMessage* msg = NULL; + * SrsConnectAppResPacket* pkt = NULL; + * if ((ret = protocol->expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + * return ret; + * } + * // use then free msg and pkt + * srs_freep(msg); + * srs_freep(pkt); + * user should never recv message and convert it, use this method instead. + * if need to set timeout, use set timeout of SrsProtocol. + */ template int expect_message(SrsCommonMessage** pmsg, T** ppacket) { @@ -6462,43 +6586,536 @@ class SrsProtocol }; /** -* incoming chunk stream maybe interlaced, -* use the chunk stream to cache the input RTMP chunk streams. -*/ + * 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. - */ + * 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. - */ + * represents the basic header cid, + * which is the chunk stream id. + */ int cid; /** - * cached message header - */ + * cached message header + */ SrsMessageHeader header; /** - * whether the chunk message header has extended timestamp. - */ + * whether the chunk message header has extended timestamp. + */ bool extended_timestamp; /** - * partially read message. - */ + * partially read message. + */ SrsCommonMessage* msg; /** - * decoded msg count, to identify whether the chunk stream is fresh. - */ + * decoded msg count, to identify whether the chunk stream is fresh. + */ int64_t msg_count; public: SrsChunkStream(int _cid); virtual ~SrsChunkStream(); }; +/** + * the original request from client. + */ +class SrsRequest +{ +public: + // client ip. + std::string ip; +public: + /** + * tcUrl: rtmp://request_vhost:port/app/stream + * support pass vhost in query string, such as: + * rtmp://ip:port/app?vhost=request_vhost/stream + * rtmp://ip:port/app...vhost...request_vhost/stream + */ + std::string tcUrl; + std::string pageUrl; + std::string swfUrl; + double objectEncoding; + // data discovery from request. +public: + // discovery from tcUrl and play/publish. + std::string schema; + // the vhost in tcUrl. + std::string vhost; + // the host in tcUrl. + std::string host; + // the port in tcUrl. + std::string port; + // the app in tcUrl, without param. + std::string app; + // the param in tcUrl(app). + std::string param; + // the stream in play/publish + std::string stream; + // for play live stream, + // used to specified the stop when exceed the duration. + // @see https://github.com/ossrs/srs/issues/45 + // in ms. + double duration; + // the token in the connect request, + // used for edge traverse to origin authentication, + // @see https://github.com/ossrs/srs/issues/104 + SrsAmf0Object* args; +public: + SrsRequest(); + virtual ~SrsRequest(); +public: + /** + * deep copy the request, for source to use it to support reload, + * for when initialize the source, the request is valid, + * when reload it, the request maybe invalid, so need to copy it. + */ + virtual SrsRequest* copy(); + /** + * update the auth info of request, + * to keep the current request ptr is ok, + * for many components use the ptr of request. + */ + virtual void update_auth(SrsRequest* req); + /** + * get the stream identify, vhost/app/stream. + */ + virtual std::string get_stream_url(); + /** + * strip url, user must strip when update the url. + */ + virtual void strip(); +}; + +/** + * the response to client. + */ +class SrsResponse +{ +public: + /** + * the stream id to response client createStream. + */ + int stream_id; +public: + SrsResponse(); + virtual ~SrsResponse(); +}; + +/** + * the rtmp client type. + */ +enum SrsRtmpConnType +{ + SrsRtmpConnUnknown, + SrsRtmpConnPlay, + SrsRtmpConnFMLEPublish, + SrsRtmpConnFlashPublish, +}; +std::string srs_client_type_string(SrsRtmpConnType type); +bool srs_client_type_is_publish(SrsRtmpConnType type); + +/** + * store the handshake bytes, + * for smart switch between complex and simple handshake. + */ +class SrsHandshakeBytes +{ +public: + // [1+1536] + char* c0c1; + // [1+1536+1536] + char* s0s1s2; + // [1536] + char* c2; +public: + SrsHandshakeBytes(); + virtual ~SrsHandshakeBytes(); +public: + virtual int read_c0c1(ISrsProtocolReaderWriter* io); + virtual int read_s0s1s2(ISrsProtocolReaderWriter* io); + virtual int read_c2(ISrsProtocolReaderWriter* io); + virtual int create_c0c1(); + virtual int create_s0s1s2(const char* c1 = NULL); + virtual int create_c2(); +}; + +/** + * implements the client role protocol. + */ +class SrsRtmpClient +{ +private: + SrsHandshakeBytes* hs_bytes; +protected: + SrsProtocol* protocol; + ISrsProtocolReaderWriter* io; +public: + SrsRtmpClient(ISrsProtocolReaderWriter* skt); + virtual ~SrsRtmpClient(); + // protocol methods proxy +public: + /** + * set the recv timeout in us. + * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. + */ + virtual void set_recv_timeout(int64_t timeout_us); + /** + * set the send timeout in us. + * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. + */ + virtual void set_send_timeout(int64_t timeout_us); + /** + * get recv/send bytes. + */ + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + /** + * recv a RTMP message, which is bytes oriented. + * user can use decode_message to get the decoded RTMP packet. + * @param pmsg, set the received message, + * always NULL if error, + * NULL for unknown packet but return success. + * never NULL if decode success. + * @remark, drop message when msg is empty or payload length is empty. + */ + virtual int recv_message(SrsCommonMessage** pmsg); + /** + * decode bytes oriented RTMP message to RTMP packet, + * @param ppacket, output decoded packet, + * always NULL if error, never NULL if success. + * @return error when unknown packet, error when decode failed. + */ + virtual int decode_message(SrsCommonMessage* msg, SrsPacket** ppacket); + /** + * send the RTMP message and always free it. + * user must never free or use the msg after this method, + * for it will always free the msg. + * @param msg, the msg to send out, never be NULL. + * @param stream_id, the stream id of packet to send over, 0 for control message. + */ + virtual int send_and_free_message(SrsSharedPtrMessage* msg, int stream_id); + /** + * send the RTMP message and always free it. + * user must never free or use the msg after this method, + * for it will always free the msg. + * @param msgs, the msgs to send out, never be NULL. + * @param nb_msgs, the size of msgs to send out. + * @param stream_id, the stream id of packet to send over, 0 for control message. + */ + virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id); + /** + * send the RTMP packet and always free it. + * user must never free or use the packet after this method, + * for it will always free the packet. + * @param packet, the packet to send out, never be NULL. + * @param stream_id, the stream id of packet to send over, 0 for control message. + */ + virtual int send_and_free_packet(SrsPacket* packet, int stream_id); +public: + /** + * handshake with server, try complex, then simple handshake. + */ + virtual int handshake(); + /** + * only use simple handshake + */ + virtual int simple_handshake(); + /** + * only use complex handshake + */ + virtual int complex_handshake(); + /** + * set req to use the original request of client: + * pageUrl and swfUrl for refer antisuck. + * args for edge to origin traverse auth, @see SrsRequest.args + */ + virtual int connect_app(std::string app, std::string tc_url, SrsRequest* req, bool debug_srs_upnode); + /** + * connect to server, get the debug srs info. + * + * @param app, the app to connect at. + * @param tc_url, the tcUrl to connect at. + * @param req, the optional req object, use the swfUrl/pageUrl if specified. NULL to ignore. + * + * SRS debug info: + * @param srs_server_ip, debug info, server ip client connected at. + * @param srs_server, server info. + * @param srs_primary, primary authors. + * @param srs_authors, authors. + * @param srs_id, int, debug info, client id in server log. + * @param srs_pid, int, debug info, server pid in log. + */ + virtual int connect_app2( + std::string app, std::string tc_url, SrsRequest* req, bool debug_srs_upnode, + std::string& srs_server_ip, std::string& srs_server, std::string& srs_primary, + std::string& srs_authors, std::string& srs_version, int& srs_id, + int& srs_pid + ); + /** + * create a stream, then play/publish data over this stream. + */ + virtual int create_stream(int& stream_id); + /** + * start play stream. + */ + virtual int play(std::string stream, int stream_id); + /** + * start publish stream. use flash publish workflow: + * connect-app => create-stream => flash-publish + */ + virtual int publish(std::string stream, int stream_id); + /** + * start publish stream. use FMLE publish workflow: + * connect-app => FMLE publish + */ + virtual int fmle_publish(std::string stream, int& stream_id); +public: + /** + * expect a specified message, drop others util got specified one. + * @pmsg, user must free it. NULL if not success. + * @ppacket, user must free it, which decode from payload of message. NULL if not success. + * @remark, only when success, user can use and must free the pmsg and ppacket. + * for example: + * SrsCommonMessage* msg = NULL; + * SrsConnectAppResPacket* pkt = NULL; + * if ((ret = client->expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + * return ret; + * } + * // use then free msg and pkt + * srs_freep(msg); + * srs_freep(pkt); + * user should never recv message and convert it, use this method instead. + * if need to set timeout, use set timeout of SrsProtocol. + */ + template + int expect_message(SrsCommonMessage** pmsg, T** ppacket) + { + return protocol->expect_message(pmsg, ppacket); + } +}; + +/** + * 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 SrsRtmpServer +{ +private: + SrsHandshakeBytes* hs_bytes; + SrsProtocol* protocol; + ISrsProtocolReaderWriter* io; +public: + SrsRtmpServer(ISrsProtocolReaderWriter* skt); + virtual ~SrsRtmpServer(); + // protocol methods proxy +public: + /** + * set the auto response message when recv for protocol stack. + * @param v, whether auto response message when recv message. + * @see: https://github.com/ossrs/srs/issues/217 + */ + virtual void set_auto_response(bool v); +#ifdef SRS_PERF_MERGED_READ + /** + * to improve read performance, merge some packets then read, + * when it on and read small bytes, we sleep to wait more data., + * that is, we merge some data to read together. + * @param v true to ename merged read. + * @param handler the handler when merge read is enabled. + * @see https://github.com/ossrs/srs/issues/241 + */ + virtual void set_merge_read(bool v, IMergeReadHandler* handler); + /** + * create buffer with specifeid size. + * @param buffer the size of buffer. + * @remark when MR(SRS_PERF_MERGED_READ) disabled, always set to 8K. + * @remark when buffer changed, the previous ptr maybe invalid. + * @see https://github.com/ossrs/srs/issues/241 + */ + virtual void set_recv_buffer(int buffer_size); +#endif + /** + * set/get the recv 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(); + /** + * set/get the send timeout in us. + * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. + */ + virtual void set_send_timeout(int64_t timeout_us); + virtual int64_t get_send_timeout(); + /** + * get recv/send bytes. + */ + virtual int64_t get_recv_bytes(); + virtual int64_t get_send_bytes(); + /** + * recv a RTMP message, which is bytes oriented. + * user can use decode_message to get the decoded RTMP packet. + * @param pmsg, set the received message, + * always NULL if error, + * NULL for unknown packet but return success. + * never NULL if decode success. + * @remark, drop message when msg is empty or payload length is empty. + */ + virtual int recv_message(SrsCommonMessage** pmsg); + /** + * decode bytes oriented RTMP message to RTMP packet, + * @param ppacket, output decoded packet, + * always NULL if error, never NULL if success. + * @return error when unknown packet, error when decode failed. + */ + virtual int decode_message(SrsCommonMessage* msg, SrsPacket** ppacket); + /** + * send the RTMP message and always free it. + * user must never free or use the msg after this method, + * for it will always free the msg. + * @param msg, the msg to send out, never be NULL. + * @param stream_id, the stream id of packet to send over, 0 for control message. + */ + virtual int send_and_free_message(SrsSharedPtrMessage* msg, int stream_id); + /** + * send the RTMP message and always free it. + * user must never free or use the msg after this method, + * for it will always free the msg. + * @param msgs, the msgs to send out, never be NULL. + * @param nb_msgs, the size of msgs to send out. + * @param stream_id, the stream id of packet to send over, 0 for control message. + * + * @remark performance issue, to support 6k+ 250kbps client, + * @see https://github.com/ossrs/srs/issues/194 + */ + virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id); + /** + * send the RTMP packet and always free it. + * user must never free or use the packet after this method, + * for it will always free the packet. + * @param packet, the packet to send out, never be NULL. + * @param stream_id, the stream id of packet to send over, 0 for control message. + */ + virtual int send_and_free_packet(SrsPacket* packet, int stream_id); +public: + /** + * handshake with client, try complex then simple. + */ + virtual int handshake(); + /** + * do connect app with client, to discovery tcUrl. + */ + virtual int connect_app(SrsRequest* req); + /** + * set ack size to client, client will send ack-size for each ack window + */ + 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); + /** + * @param server_ip the ip of server. + */ + virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL); + /** + * reject the connect app request. + */ + virtual void response_connect_reject(SrsRequest* req, const char* desc); + /** + * response client the onBWDone message. + */ + 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. + * @stream_name, output the client publish/play stream name. @see: SrsRequest.stream + * @duration, output the play client duration. @see: SrsRequest.duration + */ + virtual int identify_client(int stream_id, SrsRtmpConnType& type, std::string& stream_name, double& duration); + /** + * 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); +public: + /** + * expect a specified message, drop others util got specified one. + * @pmsg, user must free it. NULL if not success. + * @ppacket, user must free it, which decode from payload of message. NULL if not success. + * @remark, only when success, user can use and must free the pmsg and ppacket. + * for example: + * SrsCommonMessage* msg = NULL; + * SrsConnectAppResPacket* pkt = NULL; + * if ((ret = server->expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { + * return ret; + * } + * // use then free msg and pkt + * srs_freep(msg); + * srs_freep(pkt); + * user should never recv message and convert it, use this method instead. + * if need to set timeout, use set timeout of SrsProtocol. + */ + template + int expect_message(SrsCommonMessage** pmsg, T** ppacket) + { + return protocol->expect_message(pmsg, ppacket); + } +private: + virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsRtmpConnType& type, std::string& stream_name, double& duration); + virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsRtmpConnType& type, std::string& stream_name); + virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsRtmpConnType& type, std::string& stream_name); +private: + virtual int identify_play_client(SrsPlayPacket* req, SrsRtmpConnType& type, std::string& stream_name, double& duration); +}; + /** * 4.1.1. connect * The client sends the connect command to the server to request @@ -7237,13 +7854,13 @@ class SrsSampleAccessPacket : public SrsPacket std::string command_name; /** * whether allow access the sample of video. - * @see: https://github.com/simple-rtmp-server/srs/issues/49 + * @see: https://github.com/ossrs/srs/issues/49 * @see: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#videoSampleAccess */ bool video_sample_access; /** * whether allow access the sample of audio. - * @see: https://github.com/simple-rtmp-server/srs/issues/49 + * @see: https://github.com/ossrs/srs/issues/49 * @see: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#audioSampleAccess */ bool audio_sample_access; @@ -7472,6 +8089,13 @@ enum SrcPCUCEventType * kMsgPingRequest request. */ SrcPCUCPingResponse = 0x07, + + /** + * for PCUC size=3, the payload is "00 1A 01", + * where we think the event is 0x001a, fms defined msg, + * which has only 1bytes event data. + */ + SrsPCUCFmsEvent0 = 0x1a, }; /** @@ -7500,6 +8124,11 @@ class SrsUserControlPacket : public SrsPacket * @see: SrcPCUCEventType */ int16_t event_type; + /** + * the event data generally in 4bytes. + * @remark for event type is 0x001a, only 1bytes. + * @see SrsPCUCFmsEvent0 + */ int32_t event_data; /** * 4bytes if event_type is SetBufferLength; otherwise 0. @@ -7522,583 +8151,11 @@ class SrsUserControlPacket : public SrsPacket #endif -// following is generated by src/protocol/srs_rtmp_sdk.hpp -/* -The MIT License (MIT) - -Copyright (c) 2013-2015 SRS(simple-rtmp-server) - -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_RTMP_PROTOCOL_RTMP_HPP -#define SRS_RTMP_PROTOCOL_RTMP_HPP - -/* -//#include -*/ - -//#include - -#include - -//#include -//#include - -class SrsProtocol; -class ISrsProtocolReaderWriter; -class SrsCommonMessage; -class SrsCreateStreamPacket; -class SrsFMLEStartPacket; -class SrsPublishPacket; -class SrsOnMetaDataPacket; -class SrsPlayPacket; -class SrsCommonMessage; -class SrsPacket; -class SrsAmf0Object; -class IMergeReadHandler; - -/** - * 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" -// status error -#define StatusLevelError "error" -// code value -#define StatusCodeConnectSuccess "NetConnection.Connect.Success" -#define StatusCodeConnectRejected "NetConnection.Connect.Rejected" -#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" - -/** -* the original request from client. -*/ -class SrsRequest -{ -public: - // client ip. - std::string ip; -public: - /** - * tcUrl: rtmp://request_vhost:port/app/stream - * support pass vhost in query string, such as: - * rtmp://ip:port/app?vhost=request_vhost/stream - * rtmp://ip:port/app...vhost...request_vhost/stream - */ - std::string tcUrl; - std::string pageUrl; - std::string swfUrl; - double objectEncoding; -// data discovery from request. -public: - // discovery from tcUrl and play/publish. - std::string schema; - // the vhost in tcUrl. - std::string vhost; - // the host in tcUrl. - std::string host; - // the port in tcUrl. - std::string port; - // the app in tcUrl, without param. - std::string app; - // the param in tcUrl(app). - std::string param; - // the stream in play/publish - std::string stream; - // for play live stream, - // used to specified the stop when exceed the duration. - // @see https://github.com/simple-rtmp-server/srs/issues/45 - // in ms. - double duration; - // the token in the connect request, - // used for edge traverse to origin authentication, - // @see https://github.com/simple-rtmp-server/srs/issues/104 - SrsAmf0Object* args; -public: - SrsRequest(); - virtual ~SrsRequest(); -public: - /** - * deep copy the request, for source to use it to support reload, - * for when initialize the source, the request is valid, - * when reload it, the request maybe invalid, so need to copy it. - */ - virtual SrsRequest* copy(); - /** - * update the auth info of request, - * to keep the current request ptr is ok, - * for many components use the ptr of request. - */ - virtual void update_auth(SrsRequest* req); - /** - * get the stream identify, vhost/app/stream. - */ - virtual std::string get_stream_url(); - /** - * strip url, user must strip when update the url. - */ - virtual void strip(); -}; - -/** -* the response to client. -*/ -class SrsResponse -{ -public: - /** - * the stream id to response client createStream. - */ - int stream_id; -public: - SrsResponse(); - virtual ~SrsResponse(); -}; - -/** -* the rtmp client type. -*/ -enum SrsRtmpConnType -{ - SrsRtmpConnUnknown, - SrsRtmpConnPlay, - SrsRtmpConnFMLEPublish, - SrsRtmpConnFlashPublish, -}; -std::string srs_client_type_string(SrsRtmpConnType type); - -/** -* store the handshake bytes, -* for smart switch between complex and simple handshake. -*/ -class SrsHandshakeBytes -{ -public: - // [1+1536] - char* c0c1; - // [1+1536+1536] - char* s0s1s2; - // [1536] - char* c2; -public: - SrsHandshakeBytes(); - virtual ~SrsHandshakeBytes(); -public: - virtual int read_c0c1(ISrsProtocolReaderWriter* io); - virtual int read_s0s1s2(ISrsProtocolReaderWriter* io); - virtual int read_c2(ISrsProtocolReaderWriter* io); - virtual int create_c0c1(); - virtual int create_s0s1s2(const char* c1 = NULL); - virtual int create_c2(); -}; - -/** -* implements the client role protocol. -*/ -class SrsRtmpClient -{ -private: - SrsHandshakeBytes* hs_bytes; -protected: - SrsProtocol* protocol; - ISrsProtocolReaderWriter* io; -public: - SrsRtmpClient(ISrsProtocolReaderWriter* skt); - virtual ~SrsRtmpClient(); -// protocol methods proxy -public: - /** - * set the recv timeout in us. - * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. - */ - virtual void set_recv_timeout(int64_t timeout_us); - /** - * set the send timeout in us. - * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. - */ - virtual void set_send_timeout(int64_t timeout_us); - /** - * get recv/send bytes. - */ - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - /** - * recv a RTMP message, which is bytes oriented. - * user can use decode_message to get the decoded RTMP packet. - * @param pmsg, set the received message, - * always NULL if error, - * NULL for unknown packet but return success. - * never NULL if decode success. - * @remark, drop message when msg is empty or payload length is empty. - */ - virtual int recv_message(SrsCommonMessage** pmsg); - /** - * decode bytes oriented RTMP message to RTMP packet, - * @param ppacket, output decoded packet, - * always NULL if error, never NULL if success. - * @return error when unknown packet, error when decode failed. - */ - virtual int decode_message(SrsCommonMessage* msg, SrsPacket** ppacket); - /** - * send the RTMP message and always free it. - * user must never free or use the msg after this method, - * for it will always free the msg. - * @param msg, the msg to send out, never be NULL. - * @param stream_id, the stream id of packet to send over, 0 for control message. - */ - virtual int send_and_free_message(SrsSharedPtrMessage* msg, int stream_id); - /** - * send the RTMP message and always free it. - * user must never free or use the msg after this method, - * for it will always free the msg. - * @param msgs, the msgs to send out, never be NULL. - * @param nb_msgs, the size of msgs to send out. - * @param stream_id, the stream id of packet to send over, 0 for control message. - */ - virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id); - /** - * send the RTMP packet and always free it. - * user must never free or use the packet after this method, - * for it will always free the packet. - * @param packet, the packet to send out, never be NULL. - * @param stream_id, the stream id of packet to send over, 0 for control message. - */ - virtual int send_and_free_packet(SrsPacket* packet, int stream_id); -public: - /** - * handshake with server, try complex, then simple handshake. - */ - virtual int handshake(); - /** - * only use simple handshake - */ - virtual int simple_handshake(); - /** - * only use complex handshake - */ - virtual int complex_handshake(); - /** - * set req to use the original request of client: - * pageUrl and swfUrl for refer antisuck. - * args for edge to origin traverse auth, @see SrsRequest.args - */ - virtual int connect_app(std::string app, std::string tc_url, - SrsRequest* req, bool debug_srs_upnode); - /** - * connect to server, get the debug srs info. - * - * @param app, the app to connect at. - * @param tc_url, the tcUrl to connect at. - * @param req, the optional req object, use the swfUrl/pageUrl if specified. NULL to ignore. - * - * SRS debug info: - * @param srs_server_ip, debug info, server ip client connected at. - * @param srs_server, server info. - * @param srs_primary, primary authors. - * @param srs_authors, authors. - * @param srs_id, int, debug info, client id in server log. - * @param srs_pid, int, debug info, server pid in log. - */ - virtual int connect_app2( - std::string app, std::string tc_url, SrsRequest* req, bool debug_srs_upnode, - std::string& srs_server_ip, std::string& srs_server, std::string& srs_primary, - std::string& srs_authors, std::string& srs_version, int& srs_id, - int& srs_pid - ); - /** - * create a stream, then play/publish data over this stream. - */ - virtual int create_stream(int& stream_id); - /** - * start play stream. - */ - virtual int play(std::string stream, int stream_id); - /** - * start publish stream. use flash publish workflow: - * connect-app => create-stream => flash-publish - */ - virtual int publish(std::string stream, int stream_id); - /** - * start publish stream. use FMLE publish workflow: - * connect-app => FMLE publish - */ - virtual int fmle_publish(std::string stream, int& stream_id); -public: - /** - * 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. - * for example: - SrsCommonMessage* msg = NULL; - SrsConnectAppResPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - return ret; - } - // use pkt - * user should never recv message and convert it, use this method instead. - * if need to set timeout, use set timeout of SrsProtocol. - */ - template - int expect_message(SrsCommonMessage** pmsg, T** ppacket) - { - return protocol->expect_message(pmsg, ppacket); - } -}; - -/** -* 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 SrsRtmpServer -{ -private: - SrsHandshakeBytes* hs_bytes; - SrsProtocol* protocol; - ISrsProtocolReaderWriter* io; -public: - SrsRtmpServer(ISrsProtocolReaderWriter* skt); - virtual ~SrsRtmpServer(); -// protocol methods proxy -public: - /** - * set the auto response message when recv for protocol stack. - * @param v, whether auto response message when recv message. - * @see: https://github.com/simple-rtmp-server/srs/issues/217 - */ - virtual void set_auto_response(bool v); -#ifdef SRS_PERF_MERGED_READ - /** - * to improve read performance, merge some packets then read, - * when it on and read small bytes, we sleep to wait more data., - * that is, we merge some data to read together. - * @param v true to ename merged read. - * @param handler the handler when merge read is enabled. - * @see https://github.com/simple-rtmp-server/srs/issues/241 - */ - virtual void set_merge_read(bool v, IMergeReadHandler* handler); - /** - * create buffer with specifeid size. - * @param buffer the size of buffer. - * @remark when MR(SRS_PERF_MERGED_READ) disabled, always set to 8K. - * @remark when buffer changed, the previous ptr maybe invalid. - * @see https://github.com/simple-rtmp-server/srs/issues/241 - */ - virtual void set_recv_buffer(int buffer_size); -#endif - /** - * set/get the recv 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(); - /** - * set/get the send timeout in us. - * if timeout, recv/send message return ERROR_SOCKET_TIMEOUT. - */ - virtual void set_send_timeout(int64_t timeout_us); - virtual int64_t get_send_timeout(); - /** - * get recv/send bytes. - */ - virtual int64_t get_recv_bytes(); - virtual int64_t get_send_bytes(); - /** - * recv a RTMP message, which is bytes oriented. - * user can use decode_message to get the decoded RTMP packet. - * @param pmsg, set the received message, - * always NULL if error, - * NULL for unknown packet but return success. - * never NULL if decode success. - * @remark, drop message when msg is empty or payload length is empty. - */ - virtual int recv_message(SrsCommonMessage** pmsg); - /** - * decode bytes oriented RTMP message to RTMP packet, - * @param ppacket, output decoded packet, - * always NULL if error, never NULL if success. - * @return error when unknown packet, error when decode failed. - */ - virtual int decode_message(SrsCommonMessage* msg, SrsPacket** ppacket); - /** - * send the RTMP message and always free it. - * user must never free or use the msg after this method, - * for it will always free the msg. - * @param msg, the msg to send out, never be NULL. - * @param stream_id, the stream id of packet to send over, 0 for control message. - */ - virtual int send_and_free_message(SrsSharedPtrMessage* msg, int stream_id); - /** - * send the RTMP message and always free it. - * user must never free or use the msg after this method, - * for it will always free the msg. - * @param msgs, the msgs to send out, never be NULL. - * @param nb_msgs, the size of msgs to send out. - * @param stream_id, the stream id of packet to send over, 0 for control message. - * - * @remark performance issue, to support 6k+ 250kbps client, - * @see https://github.com/simple-rtmp-server/srs/issues/194 - */ - virtual int send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id); - /** - * send the RTMP packet and always free it. - * user must never free or use the packet after this method, - * for it will always free the packet. - * @param packet, the packet to send out, never be NULL. - * @param stream_id, the stream id of packet to send over, 0 for control message. - */ - virtual int send_and_free_packet(SrsPacket* packet, int stream_id); -public: - /** - * handshake with client, try complex then simple. - */ - virtual int handshake(); - /** - * do connect app with client, to discovery tcUrl. - */ - virtual int connect_app(SrsRequest* req); - /** - * set ack size to client, client will send ack-size for each ack window - */ - 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); - /** - * @param server_ip the ip of server. - */ - virtual int response_connect_app(SrsRequest* req, const char* server_ip = NULL); - /** - * reject the connect app request. - */ - virtual void response_connect_reject(SrsRequest* req, const char* desc); - /** - * response client the onBWDone message. - */ - 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. - * @stream_name, output the client publish/play stream name. @see: SrsRequest.stream - * @duration, output the play client duration. @see: SrsRequest.duration - */ - virtual int identify_client(int stream_id, SrsRtmpConnType& type, std::string& stream_name, double& duration); - /** - * 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); -public: - /** - * 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. - * for example: - SrsCommonMessage* msg = NULL; - SrsConnectAppResPacket* pkt = NULL; - if ((ret = srs_rtmp_expect_message(protocol, &msg, &pkt)) != ERROR_SUCCESS) { - return ret; - } - // use pkt - * user should never recv message and convert it, use this method instead. - * if need to set timeout, use set timeout of SrsProtocol. - */ - template - int expect_message(SrsCommonMessage** pmsg, T** ppacket) - { - return protocol->expect_message(pmsg, ppacket); - } -private: - virtual int identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsRtmpConnType& type, std::string& stream_name, double& duration); - virtual int identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsRtmpConnType& type, std::string& stream_name); - virtual int identify_flash_publish_client(SrsPublishPacket* req, SrsRtmpConnType& type, std::string& stream_name); -private: - virtual int identify_play_client(SrsPlayPacket* req, SrsRtmpConnType& type, std::string& stream_name, double& duration); -}; - -#endif - // following is generated by src/protocol/srs_rtmp_handshake.hpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -8645,7 +8702,7 @@ class SrsComplexHandshake /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -8673,7 +8730,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -8759,7 +8816,7 @@ extern int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -8836,7 +8893,7 @@ class SrsMessageArray /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -8874,7 +8931,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * to improve read performance, merge some packets then read, * when it on and read small bytes, we sleep to wait more data., * that is, we merge some data to read together. -* @see https://github.com/simple-rtmp-server/srs/issues/241 +* @see https://github.com/ossrs/srs/issues/241 */ class IMergeReadHandler { @@ -8912,7 +8969,7 @@ class SrsFastBuffer #endif // the user-space buffer to fill by reader, // which use fast index and reset when chunk body read ok. - // @see https://github.com/simple-rtmp-server/srs/issues/248 + // @see https://github.com/ossrs/srs/issues/248 // ptr to the current read position. char* p; // ptr to the content end. @@ -8941,7 +8998,7 @@ class SrsFastBuffer * @param buffer the size of buffer. ignore when smaller than SRS_MAX_SOCKET_BUFFER. * @remark when MR(SRS_PERF_MERGED_READ) disabled, always set to 8K. * @remark when buffer changed, the previous ptr maybe invalid. - * @see https://github.com/simple-rtmp-server/srs/issues/241 + * @see https://github.com/ossrs/srs/issues/241 */ virtual void set_buffer(int buffer_size); public: @@ -8982,7 +9039,7 @@ class SrsFastBuffer * that is, we merge some data to read together. * @param v true to ename merged read. * @param handler the handler when merge read is enabled. - * @see https://github.com/simple-rtmp-server/srs/issues/241 + * @see https://github.com/ossrs/srs/issues/241 * @remark the merged read is optional, ignore if not specifies. */ virtual void set_merge_read(bool v, IMergeReadHandler* handler); @@ -8994,7 +9051,7 @@ class SrsFastBuffer /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -9139,7 +9196,7 @@ class SrsRawAacStream /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -9168,6 +9225,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include +#if !defined(SRS_EXPORT_LIBRTMP) + #include #include @@ -9852,11 +9911,13 @@ class SrsRtspStack #endif +#endif + // following is generated by src/protocol/srs_http_stack.hpp /* The MIT License (MIT) - Copyright (c) 2013-2015 SRS(simple-rtmp-server) + Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -9880,15 +9941,17 @@ class SrsRtspStack #define SRS_PROTOCOL_HTTP_HPP /* - //#include - */ +//#include +*/ //#include +#if !defined(SRS_EXPORT_LIBRTMP) + #include #include #include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -9931,8 +9994,10 @@ class ISrsHttpResponseWriter; #define SRS_CONSTS_HTTP_PUT HTTP_PUT #define SRS_CONSTS_HTTP_DELETE HTTP_DELETE -// helper function: response in json format. -extern int srs_http_response_json(ISrsHttpResponseWriter* w, std::string data); +// Error replies to the request with the specified error message and HTTP code. +// The error message should be plain text. +extern int srs_go_http_error(ISrsHttpResponseWriter* w, int code); +extern int srs_go_http_error(ISrsHttpResponseWriter* w, int code, std::string error); // get the status text of code. extern std::string srs_generate_http_status_text(int status); @@ -10050,7 +10115,7 @@ class ISrsHttpResponseWriter virtual int write(char* data, int size) = 0; /** * for the HTTP FLV, to writev to improve performance. - * @see https://github.com/simple-rtmp-server/srs/issues/405 + * @see https://github.com/ossrs/srs/issues/405 */ virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite) = 0; @@ -10082,6 +10147,12 @@ class ISrsHttpResponseReader * @param nb_data, the max size of data buffer. * @param nb_read, the actual read size of bytes. NULL to ignore. * @remark when eof(), return error. + * @remark for some server, the content-length not specified and not chunked, + * which is actually the infinite chunked encoding, which after http header + * is http response data, it's ok for browser. that is, + * when user call this read, please ensure there is data to read(by content-length + * or by chunked), because the sdk never know whether there is no data or + * infinite chunked. */ virtual int read(char* data, int nb_data, int* nb_read) = 0; }; @@ -10102,6 +10173,7 @@ class ISrsHttpHandler ISrsHttpHandler(); virtual ~ISrsHttpHandler(); public: + virtual bool is_not_found(); virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) = 0; }; @@ -10125,6 +10197,7 @@ class SrsHttpNotFoundHandler : public ISrsHttpHandler SrsHttpNotFoundHandler(); virtual ~SrsHttpNotFoundHandler(); public: + virtual bool is_not_found(); virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); }; @@ -10202,6 +10275,18 @@ class ISrsHttpMatchHijacker virtual int hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph) = 0; }; +/** + * the server mux, all http server should implements it. + */ +class ISrsHttpServeMux +{ +public: + ISrsHttpServeMux(); + virtual ~ISrsHttpServeMux(); +public: + virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) = 0; +}; + // ServeMux is an HTTP request multiplexer. // It matches the URL of each incoming request against a list of registered // patterns and calls the handler for the pattern that @@ -10229,7 +10314,7 @@ class ISrsHttpMatchHijacker // ServeMux also takes care of sanitizing the URL request path, // redirecting any request containing . or .. elements to an // equivalent .- and ..-free URL. -class SrsHttpServeMux +class SrsHttpServeMux : public ISrsHttpServeMux { private: // the pattern handler, to handle the http request. @@ -10261,7 +10346,10 @@ class SrsHttpServeMux // Handle registers the handler for the given pattern. // If a handler already exists for pattern, Handle panics. virtual int handle(std::string pattern, ISrsHttpHandler* handler); - // interface ISrsHttpHandler + // whether the http muxer can serve the specified message, + // if not, user can try next muxer. + virtual bool can_serve(ISrsHttpMessage* r); +// interface ISrsHttpServeMux public: virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r); private: @@ -10279,9 +10367,19 @@ typedef std::pair SrsHttpHeaderField; // The field semantics differ slightly between client and server // usage. In addition to the notes on the fields below, see the // documentation for Request.Write and RoundTripper. -/** - * the http message, request or response. - */ +// +// There are some modes to determine the length of body: +// 1. content-length and chunked. +// 2. user confirmed infinite chunked. +// 3. no body or user not confirmed infinite chunked. +// For example: +// ISrsHttpMessage* r = ...; +// while (!r->eof()) r->read(); // read in mode 1 or 3. +// For some server, we can confirm the body is infinite chunked: +// ISrsHttpMessage* r = ...; +// r->enter_infinite_chunked(); +// while (!r->eof()) r->read(); // read in mode 2 +// @rmark for mode 2, the infinite chunked, all left data is body. class ISrsHttpMessage { private: @@ -10325,8 +10423,23 @@ class ISrsHttpMessage virtual std::string url() = 0; virtual std::string host() = 0; virtual std::string path() = 0; + virtual std::string query() = 0; virtual std::string ext() = 0; + /** + * get the RESTful id, + * for example, pattern is /api/v1/streams, path is /api/v1/streams/100, + * then the rest id is 100. + * @param pattern the handler pattern which will serve the request. + * @return the REST id; -1 if not matched. + */ + virtual int parse_rest_id(std::string pattern) = 0; public: + /** + * the left all data is chunked body, the infinite chunked mode, + * which is chunked encoding without chunked header. + * @remark error when message is in chunked or content-length specified. + */ + virtual int enter_infinite_chunked() = 0; /** * read body to string. * @remark for small http body. @@ -10354,14 +10467,23 @@ class ISrsHttpMessage virtual int request_header_count() = 0; virtual std::string request_header_key_at(int index) = 0; virtual std::string request_header_value_at(int index) = 0; +public: + /** + * whether the current request is JSONP, + * which has a "callback=xxx" in QueryString. + */ + virtual bool is_jsonp() = 0; }; #endif + +#endif + // following is generated by src/protocol/srs_protocol_kbps.hpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -10594,12 +10716,242 @@ class SrsKbps : public virtual ISrsProtocolStatistic, public virtual IKbpsDelta virtual void sample(); }; +#endif +// following is generated by src/protocol/srs_protocol_json.hpp +/* +The MIT License (MIT) + +Copyright (c) 2013-2015 SRS(ossrs) + +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_PROTOCOL_JSON_HPP +#define SRS_PROTOCOL_JSON_HPP + +/* +//#include +*/ +//#include + +#include +#include + +// whether use nxjson +// @see: https://bitbucket.org/yarosla/nxjson +#undef SRS_JSON_USE_NXJSON +#define SRS_JSON_USE_NXJSON + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// json decode +// 1. SrsJsonAny: read any from str:char* +// SrsJsonAny* pany = NULL; +// if ((ret = srs_json_read_any(str, &pany)) != ERROR_SUCCESS) { +// return ret; +// } +// srs_assert(pany); // if success, always valid object. +// 2. SrsJsonAny: convert to specifid type, for instance, string +// SrsJsonAny* pany = ... +// if (pany->is_string()) { +// string v = pany->to_str(); +// } +// +// for detail usage, see interfaces of each object. +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +// @see: https://bitbucket.org/yarosla/nxjson +// @see: https://github.com/udp/json-parser + +class SrsJsonArray; +class SrsJsonObject; + +class SrsJsonAny +{ +public: + char marker; +// donot directly create this object, +// instead, for examle, use SrsJsonAny::str() to create a concreated one. +protected: + SrsJsonAny(); +public: + virtual ~SrsJsonAny(); +public: + virtual bool is_string(); + virtual bool is_boolean(); + virtual bool is_integer(); + virtual bool is_number(); + virtual bool is_object(); + virtual bool is_array(); + virtual bool is_null(); +public: + /** + * get the string of any when is_string() indicates true. + * user must ensure the type is a string, or assert failed. + */ + virtual std::string to_str(); + /** + * get the boolean of any when is_boolean() indicates true. + * user must ensure the type is a boolean, or assert failed. + */ + virtual bool to_boolean(); + /** + * get the integer of any when is_integer() indicates true. + * user must ensure the type is a integer, or assert failed. + */ + virtual int64_t to_integer(); + /** + * get the number of any when is_number() indicates true. + * user must ensure the type is a number, or assert failed. + */ + virtual double to_number(); + /** + * get the object of any when is_object() indicates true. + * user must ensure the type is a object, or assert failed. + */ + virtual SrsJsonObject* to_object(); + /** + * get the ecma array of any when is_ecma_array() indicates true. + * user must ensure the type is a ecma array, or assert failed. + */ + virtual SrsJsonArray* to_array(); +public: + static SrsJsonAny* str(const char* value = NULL); + static SrsJsonAny* boolean(bool value = false); + static SrsJsonAny* ingeter(int64_t value = 0); + static SrsJsonAny* number(double value = 0.0); + static SrsJsonAny* null(); + static SrsJsonObject* object(); + static SrsJsonArray* array(); +public: + /** + * read json tree from str:char* + * @return json object. NULL if error. + */ + static SrsJsonAny* loads(char* str); +}; + +class SrsJsonObject : public SrsJsonAny +{ +private: + typedef std::pair SrsJsonObjectPropertyType; + std::vector properties; +private: + // use SrsJsonAny::object() to create it. + friend class SrsJsonAny; + SrsJsonObject(); +public: + virtual ~SrsJsonObject(); +public: + virtual int count(); + // @remark: max index is count(). + virtual std::string key_at(int index); + // @remark: max index is count(). + virtual SrsJsonAny* value_at(int index); +public: + virtual void set(std::string key, SrsJsonAny* value); + virtual SrsJsonAny* get_property(std::string name); + virtual SrsJsonAny* ensure_property_string(std::string name); + virtual SrsJsonAny* ensure_property_integer(std::string name); + virtual SrsJsonAny* ensure_property_boolean(std::string name); +}; + +class SrsJsonArray : public SrsJsonAny +{ +private: + std::vector properties; + +private: + // use SrsJsonAny::array() to create it. + friend class SrsJsonAny; + SrsJsonArray(); +public: + virtual ~SrsJsonArray(); +public: + virtual int count(); + // @remark: max index is count(). + virtual SrsJsonAny* at(int index); + virtual void add(SrsJsonAny* value); +}; + +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +/* json encode + cout<< SRS_JOBJECT_START + << SRS_JFIELD_STR("name", "srs") << SRS_JFIELD_CONT + << SRS_JFIELD_ORG("version", 100) << SRS_JFIELD_CONT + << SRS_JFIELD_NAME("features") << SRS_JOBJECT_START + << SRS_JFIELD_STR("rtmp", "released") << SRS_JFIELD_CONT + << SRS_JFIELD_STR("hls", "released") << SRS_JFIELD_CONT + << SRS_JFIELD_STR("dash", "plan") + << SRS_JOBJECT_END << SRS_JFIELD_CONT + << SRS_JFIELD_STR("author", "srs team") + << SRS_JOBJECT_END +it's: + cont<< "{" + << "name:" << "srs" << "," + << "version:" << 100 << "," + << "features:" << "{" + << "rtmp:" << "released" << "," + << "hls:" << "released" << "," + << "dash:" << "plan" + << "}" << "," + << "author:" << "srs team" + << "}" +that is: + """ + { + "name": "srs", + "version": 100, + "features": { + "rtmp": "released", + "hls": "released", + "dash": "plan" + }, + "author": "srs team" + } + """ +*/ +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////// +#define SRS_JOBJECT_START "{" +#define SRS_JFIELD_NAME(k) "\"" << k << "\":" +#define SRS_JFIELD_OBJ(k) SRS_JFIELD_NAME(k) << SRS_JOBJECT_START +#define SRS_JFIELD_STR(k, v) SRS_JFIELD_NAME(k) << "\"" << v << "\"" +#define SRS_JFIELD_ORG(k, v) SRS_JFIELD_NAME(k) << std::dec << v +#define SRS_JFIELD_BOOL(k, v) SRS_JFIELD_ORG(k, (v? "true":"false")) +#define SRS_JFIELD_NULL(k) SRS_JFIELD_NAME(k) << "null" +#define SRS_JFIELD_ERROR(ret) "\"" << "code" << "\":" << ret +#define SRS_JFIELD_CONT "," +#define SRS_JOBJECT_END "}" +#define SRS_JARRAY_START "[" +#define SRS_JARRAY_END "]" + #endif // following is generated by src/libs/srs_librtmp.hpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -10639,7 +10991,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * Windows SRS-LIBRTMP pre-declare ************************************************************** *************************************************************/ -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifdef _WIN32 // include windows first. #include @@ -10647,6 +10999,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. typedef unsigned long long u_int64_t; typedef long long int64_t; typedef unsigned int u_int32_t; + typedef u_int32_t uint32_t; typedef int int32_t; typedef unsigned char u_int8_t; typedef char int8_t; @@ -10918,7 +11271,7 @@ extern srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size); * @remark for aac, only support profile 1-4, AAC main/LC/SSR/LTP, * @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 23, 1.5.1.1 Audio object type * -* @see https://github.com/simple-rtmp-server/srs/issues/212 +* @see https://github.com/ossrs/srs/issues/212 * @see E.4.2.1 AUDIODATA of video_file_format_spec_v10_1.pdf * * @return 0, success; otherswise, failed. @@ -10974,7 +11327,7 @@ extern int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size); * @remark, cts = pts - dts * @remark, use srs_h264_startswith_annexb to check whether frame is annexb format. * @example /trunk/research/librtmp/srs_h264_raw_publish.c -* @see https://github.com/simple-rtmp-server/srs/issues/66 +* @see https://github.com/ossrs/srs/issues/66 * * @return 0, success; otherswise, failed. * for dvbsp error, @see srs_h264_is_dvbsp_error(). @@ -11016,7 +11369,7 @@ extern int srs_h264_write_raw_frames(srs_rtmp_t rtmp, /** * whether error_code is dvbsp(drop video before sps/pps/sequence-header) error. * -* @see https://github.com/simple-rtmp-server/srs/issues/203 +* @see https://github.com/ossrs/srs/issues/203 * @example /trunk/research/librtmp/srs_h264_raw_publish.c * @remark why drop video? * some encoder, for example, ipcamera, will send sps/pps before each IFrame, @@ -11027,14 +11380,14 @@ extern srs_bool srs_h264_is_dvbsp_error(int error_code); /** * whether error_code is duplicated sps error. * -* @see https://github.com/simple-rtmp-server/srs/issues/204 +* @see https://github.com/ossrs/srs/issues/204 * @example /trunk/research/librtmp/srs_h264_raw_publish.c */ extern srs_bool srs_h264_is_duplicated_sps_error(int error_code); /** * whether error_code is duplicated pps error. * -* @see https://github.com/simple-rtmp-server/srs/issues/204 +* @see https://github.com/ossrs/srs/issues/204 * @example /trunk/research/librtmp/srs_h264_raw_publish.c */ extern srs_bool srs_h264_is_duplicated_pps_error(int error_code); @@ -11501,7 +11854,19 @@ extern const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_ty * @return an error code for parse the timetstamp to dts and pts. */ extern int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size); +/** + * @param pre_timestamp the previous timestamp in ms to calc the diff. + */ extern int srs_human_print_rtmp_packet2(char type, u_int32_t timestamp, char* data, int size, u_int32_t pre_timestamp); +/** + * @param pre_now the previous system time in ms to calc the ndiff. + */ +extern int srs_human_print_rtmp_packet3(char type, u_int32_t timestamp, char* data, int size, u_int32_t pre_timestamp, int64_t pre_now); +/** + * @param starttime the rtmpdump starttime in ms. + * @param nb_packets the number of packets received, to calc the packets interval in ms. + */ +extern int srs_human_print_rtmp_packet4(char type, u_int32_t timestamp, char* data, int size, u_int32_t pre_timestamp, int64_t pre_now, int64_t starttime, int64_t nb_packets); // log to console, for use srs-librtmp application. extern const char* srs_human_format_time(); @@ -11620,22 +11985,33 @@ typedef void* srs_hijack_io_t; * Windows SRS-LIBRTMP solution ************************************************************** *************************************************************/ -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifdef _WIN32 + // for time. #define _CRT_SECURE_NO_WARNINGS #include int gettimeofday(struct timeval* tv, struct timezone* tz); #define PRId64 "lld" + // for inet helpers. typedef int socklen_t; const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); + + // for mkdir(). + #include + + // for open(). typedef int mode_t; #define S_IRUSR 0 #define S_IWUSR 0 + #define S_IXUSR 0 #define S_IRGRP 0 #define S_IWGRP 0 + #define S_IXGRP 0 #define S_IROTH 0 + #define S_IXOTH 0 + // for file seek. #include #include #define open _open @@ -11644,14 +12020,19 @@ typedef void* srs_hijack_io_t; #define write _write #define read _read + // for pid. typedef int pid_t; pid_t getpid(void); - #define snprintf _snprintf + + // for socket. ssize_t writev(int fd, const struct iovec *iov, int iovcnt); typedef int64_t useconds_t; int usleep(useconds_t usec); int socket_setup(); int socket_cleanup(); + + // others. + #define snprintf _snprintf #endif #ifdef __cplusplus @@ -11664,7 +12045,7 @@ typedef void* srs_hijack_io_t; /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -11696,7 +12077,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #define SOCKET int #endif @@ -11743,7 +12124,7 @@ class SimpleSocketStream : public ISrsProtocolReaderWriter /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -11834,7 +12215,7 @@ class SrsBandwidthClient /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -11861,7 +12242,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -11887,7 +12268,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -11913,7 +12294,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -11935,7 +12316,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -#ifdef SRS_MEM_WATCH +#ifdef SRS_AUTO_MEM_WATCH #include #include @@ -11980,7 +12361,7 @@ void srs_memory_unwatch(void* ptr) void srs_memory_report() { - printf("srs memory leak report:\n"); + printf("srs memory watch leak report:\n"); int total = 0; std::map::iterator it; @@ -11991,6 +12372,7 @@ void srs_memory_report() } printf("%d objects leak %dKB.\n", (int)_srs_ptrs.size(), total / 1024); + printf("@remark use script to cleanup for memory watch: ./etc/init.d/srs stop\n"); } #endif @@ -11999,7 +12381,7 @@ void srs_memory_report() /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -12039,7 +12421,7 @@ bool srs_is_client_gracefully_close(int error_code) /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -12114,12 +12496,17 @@ int ISrsThreadContext::get_id() return 0; } +int ISrsThreadContext::set_id(int /*v*/) +{ + return 0; +} + // following is generated by src/kernel/srs_kernel_stream.cpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -12149,8 +12536,8 @@ using namespace std; SrsStream::SrsStream() { - p = _bytes = NULL; - _size = 0; + p = bytes = NULL; + nb_bytes = 0; // TODO: support both little and big endian. srs_assert(srs_is_little_endian()); @@ -12160,54 +12547,54 @@ SrsStream::~SrsStream() { } -int SrsStream::initialize(char* bytes, int size) +int SrsStream::initialize(char* b, int nb) { int ret = ERROR_SUCCESS; - if (!bytes) { + if (!b) { ret = ERROR_KERNEL_STREAM_INIT; srs_error("stream param bytes must not be NULL. ret=%d", ret); return ret; } - if (size <= 0) { + if (nb <= 0) { ret = ERROR_KERNEL_STREAM_INIT; srs_error("stream param size must be positive. ret=%d", ret); return ret; } - _size = size; - p = _bytes = bytes; - srs_info("init stream ok, size=%d", size); + nb_bytes = nb; + p = bytes = b; + srs_info("init stream ok, size=%d", size()); return ret; } char* SrsStream::data() { - return _bytes; + return bytes; } int SrsStream::size() { - return _size; + return nb_bytes; } int SrsStream::pos() { - return p - _bytes; + return (int)(p - bytes); } bool SrsStream::empty() { - return !_bytes || (p >= _bytes + _size); + return !bytes || (p >= bytes + nb_bytes); } bool SrsStream::require(int required_size) { - srs_assert(required_size > 0); + srs_assert(required_size >= 0); - return required_size <= _size - (p - _bytes); + return required_size <= nb_bytes - (p - bytes); } void SrsStream::skip(int size) @@ -12229,7 +12616,7 @@ int16_t SrsStream::read_2bytes() srs_assert(require(2)); int16_t value; - pp = (char*)&value; + char* pp = (char*)&value; pp[1] = *p++; pp[0] = *p++; @@ -12241,7 +12628,7 @@ int32_t SrsStream::read_3bytes() srs_assert(require(3)); int32_t value = 0x00; - pp = (char*)&value; + char* pp = (char*)&value; pp[2] = *p++; pp[1] = *p++; pp[0] = *p++; @@ -12254,7 +12641,7 @@ int32_t SrsStream::read_4bytes() srs_assert(require(4)); int32_t value; - pp = (char*)&value; + char* pp = (char*)&value; pp[3] = *p++; pp[2] = *p++; pp[1] = *p++; @@ -12268,7 +12655,7 @@ int64_t SrsStream::read_8bytes() srs_assert(require(8)); int64_t value; - pp = (char*)&value; + char* pp = (char*)&value; pp[7] = *p++; pp[6] = *p++; pp[5] = *p++; @@ -12313,7 +12700,7 @@ void SrsStream::write_2bytes(int16_t value) { srs_assert(require(2)); - pp = (char*)&value; + char* pp = (char*)&value; *p++ = pp[1]; *p++ = pp[0]; } @@ -12322,7 +12709,7 @@ void SrsStream::write_4bytes(int32_t value) { srs_assert(require(4)); - pp = (char*)&value; + char* pp = (char*)&value; *p++ = pp[3]; *p++ = pp[2]; *p++ = pp[1]; @@ -12333,7 +12720,7 @@ void SrsStream::write_3bytes(int32_t value) { srs_assert(require(3)); - pp = (char*)&value; + char* pp = (char*)&value; *p++ = pp[2]; *p++ = pp[1]; *p++ = pp[0]; @@ -12343,7 +12730,7 @@ void SrsStream::write_8bytes(int64_t value) { srs_assert(require(8)); - pp = (char*)&value; + char* pp = (char*)&value; *p++ = pp[7]; *p++ = pp[6]; *p++ = pp[5]; @@ -12356,7 +12743,7 @@ void SrsStream::write_8bytes(int64_t value) void SrsStream::write_string(string value) { - srs_assert(require(value.length())); + srs_assert(require((int)value.length())); memcpy(p, value.data(), value.length()); p += value.length(); @@ -12409,7 +12796,7 @@ int8_t SrsBitStream::read_bit() { /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -12431,7 +12818,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #include @@ -12530,7 +12917,7 @@ int64_t srs_update_system_time_ms() return -1; } - // @see: https://github.com/simple-rtmp-server/srs/issues/35 + // @see: https://github.com/ossrs/srs/issues/35 // we must convert the tv_sec/tv_usec to int64_t. int64_t now_us = ((int64_t)now.tv_sec) * 1000 * 1000 + (int64_t)now.tv_usec; @@ -12552,7 +12939,7 @@ int64_t srs_update_system_time_ms() if (diff < 0 || diff > 1000 * SYS_TIME_RESOLUTION_US) { srs_warn("system time jump, history=%"PRId64"us, now=%"PRId64"us, diff=%"PRId64"us", _srs_system_time_us_cache, now_us, diff); - // @see: https://github.com/simple-rtmp-server/srs/issues/109 + // @see: https://github.com/ossrs/srs/issues/109 _srs_system_time_startup_time += diff; } @@ -12687,11 +13074,26 @@ bool srs_string_starts_with(string str, string flag) return str.find(flag) == 0; } +bool srs_string_starts_with(string str, string flag0, string flag1) +{ + return srs_string_starts_with(str, flag0) || srs_string_starts_with(str, flag1); +} + bool srs_string_contains(string str, string flag) { return str.find(flag) != string::npos; } +bool srs_string_contains(string str, string flag0, string flag1) +{ + return str.find(flag0) != string::npos || str.find(flag1) != string::npos; +} + +bool srs_string_contains(string str, string flag0, string flag1, string flag2) +{ + return str.find(flag0) != string::npos || str.find(flag1) != string::npos || str.find(flag2) != string::npos; +} + int srs_do_create_dir_recursively(string dir) { int ret = ERROR_SUCCESS; @@ -12715,8 +13117,13 @@ int srs_do_create_dir_recursively(string dir) } // create curren dir. + // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 +#ifndef _WIN32 mode_t mode = S_IRUSR|S_IWUSR|S_IXUSR|S_IRGRP|S_IWGRP|S_IXGRP|S_IROTH|S_IXOTH; if (::mkdir(dir.c_str(), mode) < 0) { +#else + if (::mkdir(dir.c_str()) < 0) { +#endif if (errno == EEXIST) { return ERROR_SYSTEM_DIR_EXISTS; } @@ -13300,7 +13707,7 @@ int srs_chunk_header_c3( /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -13322,7 +13729,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -13459,20 +13866,20 @@ SrsCommonMessage::SrsCommonMessage() SrsCommonMessage::~SrsCommonMessage() { -#ifdef SRS_MEM_WATCH +#ifdef SRS_AUTO_MEM_WATCH srs_memory_unwatch(payload); #endif - srs_freep(payload); + srs_freepa(payload); } void SrsCommonMessage::create_payload(int size) { - srs_freep(payload); + srs_freepa(payload); payload = new char[size]; srs_verbose("create payload for RTMP message. size=%d", size); -#ifdef SRS_MEM_WATCH +#ifdef SRS_AUTO_MEM_WATCH srs_memory_watch(payload, "RTMP.msg.payload", size); #endif } @@ -13486,10 +13893,10 @@ SrsSharedPtrMessage::SrsSharedPtrPayload::SrsSharedPtrPayload() SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload() { -#ifdef SRS_MEM_WATCH +#ifdef SRS_AUTO_MEM_WATCH srs_memory_unwatch(payload); #endif - srs_freep(payload); + srs_freepa(payload); } SrsSharedPtrMessage::SrsSharedPtrMessage() @@ -13631,7 +14038,7 @@ SrsSharedPtrMessage* SrsSharedPtrMessage::copy() SrsFlvEncoder::SrsFlvEncoder() { - _fs = NULL; + reader = NULL; tag_stream = new SrsStream(); #ifdef SRS_PERF_FAST_FLV_ENCODER @@ -13649,25 +14056,25 @@ SrsFlvEncoder::~SrsFlvEncoder() srs_freep(tag_stream); #ifdef SRS_PERF_FAST_FLV_ENCODER - srs_freep(tag_headers); - srs_freep(iovss_cache); - srs_freep(ppts); + srs_freepa(tag_headers); + srs_freepa(iovss_cache); + srs_freepa(ppts); #endif } -int SrsFlvEncoder::initialize(SrsFileWriter* fs) +int SrsFlvEncoder::initialize(SrsFileWriter* fr) { int ret = ERROR_SUCCESS; - srs_assert(fs); + srs_assert(fr); - if (!fs->is_open()) { + if (!fr->is_open()) { ret = ERROR_KERNEL_FLV_STREAM_CLOSED; srs_warn("stream is not open for encoder. ret=%d", ret); return ret; } - _fs = fs; + reader = fr; return ret; } @@ -13680,7 +14087,7 @@ int SrsFlvEncoder::write_header() char flv_header[] = { 'F', 'L', 'V', // Signatures "FLV" (char)0x01, // File version (for example, 0x01 for FLV version 1) - (char)0x00, // 4, audio; 1, video; 5 audio+video. + (char)0x05, // 4, audio; 1, video; 5 audio+video. (char)0x00, (char)0x00, (char)0x00, (char)0x09 // DataOffset UI32 The length of this header in bytes }; @@ -13701,14 +14108,14 @@ int SrsFlvEncoder::write_header(char flv_header[9]) int ret = ERROR_SUCCESS; // write data. - if ((ret = _fs->write(flv_header, 9, NULL)) != ERROR_SUCCESS) { + if ((ret = reader->write(flv_header, 9, NULL)) != ERROR_SUCCESS) { srs_error("write flv header failed. ret=%d", ret); return ret; } // previous tag size. char pts[] = { (char)0x00, (char)0x00, (char)0x00, (char)0x00 }; - if ((ret = _fs->write(pts, 4, NULL)) != ERROR_SUCCESS) { + if ((ret = reader->write(pts, 4, NULL)) != ERROR_SUCCESS) { return ret; } @@ -13788,7 +14195,7 @@ int SrsFlvEncoder::write_tags(SrsSharedPtrMessage** msgs, int count) int nb_iovss = 3 * count; iovec* iovss = iovss_cache; if (nb_iovss_cache < nb_iovss) { - srs_freep(iovss_cache); + srs_freepa(iovss_cache); nb_iovss_cache = nb_iovss; iovss = iovss_cache = new iovec[nb_iovss]; @@ -13797,7 +14204,7 @@ int SrsFlvEncoder::write_tags(SrsSharedPtrMessage** msgs, int count) // realloc the tag headers. char* cache = tag_headers; if (nb_tag_headers < count) { - srs_freep(tag_headers); + srs_freepa(tag_headers); nb_tag_headers = count; cache = tag_headers = new char[SRS_FLV_TAG_HEADER_SIZE * count]; @@ -13806,7 +14213,7 @@ int SrsFlvEncoder::write_tags(SrsSharedPtrMessage** msgs, int count) // realloc the pts. char* pts = ppts; if (nb_ppts < count) { - srs_freep(ppts); + srs_freepa(ppts); nb_ppts = count; pts = ppts = new char[SRS_FLV_PREVIOUS_TAG_SIZE * count]; @@ -13851,7 +14258,7 @@ int SrsFlvEncoder::write_tags(SrsSharedPtrMessage** msgs, int count) iovs += 3; } - if ((ret = _fs->writev(iovss, nb_iovss, NULL)) != ERROR_SUCCESS) { + if ((ret = reader->writev(iovss, nb_iovss, NULL)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("write flv tags failed. ret=%d", ret); } @@ -13982,7 +14389,7 @@ int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_s iovs[2].iov_base = pre_size; iovs[2].iov_len = SRS_FLV_PREVIOUS_TAG_SIZE; - if ((ret = _fs->writev(iovs, 3, NULL)) != ERROR_SUCCESS) { + if ((ret = reader->writev(iovs, 3, NULL)) != ERROR_SUCCESS) { if (!srs_is_client_gracefully_close(ret)) { srs_error("write flv tag failed. ret=%d", ret); } @@ -13994,7 +14401,7 @@ int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_s SrsFlvDecoder::SrsFlvDecoder() { - _fs = NULL; + reader = NULL; tag_stream = new SrsStream(); } @@ -14003,19 +14410,19 @@ SrsFlvDecoder::~SrsFlvDecoder() srs_freep(tag_stream); } -int SrsFlvDecoder::initialize(SrsFileReader* fs) +int SrsFlvDecoder::initialize(SrsFileReader* fr) { int ret = ERROR_SUCCESS; - srs_assert(fs); + srs_assert(fr); - if (!fs->is_open()) { + if (!fr->is_open()) { ret = ERROR_KERNEL_FLV_STREAM_CLOSED; srs_warn("stream is not open for decoder. ret=%d", ret); return ret; } - _fs = fs; + reader = fr; return ret; } @@ -14026,7 +14433,7 @@ int SrsFlvDecoder::read_header(char header[9]) srs_assert(header); - if ((ret = _fs->read(header, 9, NULL)) != ERROR_SUCCESS) { + if ((ret = reader->read(header, 9, NULL)) != ERROR_SUCCESS) { return ret; } @@ -14051,7 +14458,7 @@ int SrsFlvDecoder::read_tag_header(char* ptype, int32_t* pdata_size, u_int32_t* char th[11]; // tag header // read tag header - if ((ret = _fs->read(th, 11, NULL)) != ERROR_SUCCESS) { + if ((ret = reader->read(th, 11, NULL)) != ERROR_SUCCESS) { if (ret != ERROR_SYSTEM_FILE_EOF) { srs_error("read flv tag header failed. ret=%d", ret); } @@ -14088,7 +14495,7 @@ int SrsFlvDecoder::read_tag_data(char* data, int32_t size) srs_assert(data); - if ((ret = _fs->read(data, size, NULL)) != ERROR_SUCCESS) { + if ((ret = reader->read(data, size, NULL)) != ERROR_SUCCESS) { if (ret != ERROR_SYSTEM_FILE_EOF) { srs_error("read flv tag header failed. ret=%d", ret); } @@ -14106,7 +14513,7 @@ int SrsFlvDecoder::read_previous_tag_size(char previous_tag_size[4]) srs_assert(previous_tag_size); // ignore 4bytes tag size. - if ((ret = _fs->read(previous_tag_size, 4, NULL)) != ERROR_SUCCESS) { + if ((ret = reader->read(previous_tag_size, 4, NULL)) != ERROR_SUCCESS) { if (ret != ERROR_SYSTEM_FILE_EOF) { srs_error("read flv previous tag size failed. ret=%d", ret); } @@ -14118,7 +14525,7 @@ int SrsFlvDecoder::read_previous_tag_size(char previous_tag_size[4]) SrsFlvVodStreamDecoder::SrsFlvVodStreamDecoder() { - _fs = NULL; + reader = NULL; tag_stream = new SrsStream(); } @@ -14127,19 +14534,19 @@ SrsFlvVodStreamDecoder::~SrsFlvVodStreamDecoder() srs_freep(tag_stream); } -int SrsFlvVodStreamDecoder::initialize(SrsFileReader* fs) +int SrsFlvVodStreamDecoder::initialize(SrsFileReader* fr) { int ret = ERROR_SUCCESS; - srs_assert(fs); + srs_assert(fr); - if (!fs->is_open()) { + if (!fr->is_open()) { ret = ERROR_KERNEL_FLV_STREAM_CLOSED; srs_warn("stream is not open for decoder. ret=%d", ret); return ret; } - _fs = fs; + reader = fr; return ret; } @@ -14156,7 +14563,7 @@ int SrsFlvVodStreamDecoder::read_header_ext(char header[13]) // 9bytes header and 4bytes first previous-tag-size int size = 13; - if ((ret = _fs->read(header, size, NULL)) != ERROR_SUCCESS) { + if ((ret = reader->read(header, size, NULL)) != ERROR_SUCCESS) { return ret; } @@ -14190,7 +14597,7 @@ int SrsFlvVodStreamDecoder::read_sequence_header_summary(int64_t* pstart, int* p int64_t av_sequence_offset_start = -1; int64_t av_sequence_offset_end = -1; for (;;) { - if ((ret = _fs->read(tag_header, SRS_FLV_TAG_HEADER_SIZE, NULL)) != ERROR_SUCCESS) { + if ((ret = reader->read(tag_header, SRS_FLV_TAG_HEADER_SIZE, NULL)) != ERROR_SUCCESS) { return ret; } @@ -14206,7 +14613,7 @@ int SrsFlvVodStreamDecoder::read_sequence_header_summary(int64_t* pstart, int* p bool is_not_av = !is_video && !is_audio; if (is_not_av) { // skip body and tag size. - _fs->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); + reader->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); continue; } @@ -14225,10 +14632,10 @@ int SrsFlvVodStreamDecoder::read_sequence_header_summary(int64_t* pstart, int* p got_video = true; if (av_sequence_offset_start < 0) { - av_sequence_offset_start = _fs->tellg() - SRS_FLV_TAG_HEADER_SIZE; + av_sequence_offset_start = reader->tellg() - SRS_FLV_TAG_HEADER_SIZE; } - av_sequence_offset_end = _fs->tellg() + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; - _fs->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); + av_sequence_offset_end = reader->tellg() + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; + reader->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); } // audio @@ -14237,16 +14644,16 @@ int SrsFlvVodStreamDecoder::read_sequence_header_summary(int64_t* pstart, int* p got_audio = true; if (av_sequence_offset_start < 0) { - av_sequence_offset_start = _fs->tellg() - SRS_FLV_TAG_HEADER_SIZE; + av_sequence_offset_start = reader->tellg() - SRS_FLV_TAG_HEADER_SIZE; } - av_sequence_offset_end = _fs->tellg() + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; - _fs->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); + av_sequence_offset_end = reader->tellg() + data_size + SRS_FLV_PREVIOUS_TAG_SIZE; + reader->skip(data_size + SRS_FLV_PREVIOUS_TAG_SIZE); } } // seek to the sequence header start offset. if (av_sequence_offset_start > 0) { - _fs->lseek(av_sequence_offset_start); + reader->lseek(av_sequence_offset_start); *pstart = av_sequence_offset_start; *psize = (int)(av_sequence_offset_end - av_sequence_offset_start); } @@ -14258,19 +14665,19 @@ int SrsFlvVodStreamDecoder::lseek(int64_t offset) { int ret = ERROR_SUCCESS; - if (offset >= _fs->filesize()) { + if (offset >= reader->filesize()) { ret = ERROR_SYSTEM_FILE_EOF; srs_warn("flv fast decoder seek overflow file, " "size=%"PRId64", offset=%"PRId64", ret=%d", - _fs->filesize(), offset, ret); + reader->filesize(), offset, ret); return ret; } - if (_fs->lseek(offset) < 0) { + if (reader->lseek(offset) < 0) { ret = ERROR_SYSTEM_FILE_SEEK; srs_warn("flv fast decoder seek error, " "size=%"PRId64", offset=%"PRId64", ret=%d", - _fs->filesize(), offset, ret); + reader->filesize(), offset, ret); return ret; } @@ -14282,7 +14689,7 @@ int SrsFlvVodStreamDecoder::lseek(int64_t offset) /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -14661,8 +15068,12 @@ int SrsCodecSample::add_sample_unit(char* bytes, int size) return ret; } +#if !defined(SRS_EXPORT_LIBRTMP) + SrsAvcAacCodec::SrsAvcAacCodec() { + avc_parse_sps = true; + width = 0; height = 0; duration = 0; @@ -14696,12 +15107,22 @@ SrsAvcAacCodec::SrsAvcAacCodec() SrsAvcAacCodec::~SrsAvcAacCodec() { - srs_freep(avc_extra_data); - srs_freep(aac_extra_data); + srs_freepa(avc_extra_data); + srs_freepa(aac_extra_data); srs_freep(stream); - srs_freep(sequenceParameterSetNALUnit); - srs_freep(pictureParameterSetNALUnit); + srs_freepa(sequenceParameterSetNALUnit); + srs_freepa(pictureParameterSetNALUnit); +} + +bool SrsAvcAacCodec::is_avc_codec_ok() +{ + return avc_extra_size > 0 && avc_extra_data; +} + +bool SrsAvcAacCodec::is_aac_codec_ok() +{ + return aac_extra_size > 0 && aac_extra_data; } int SrsAvcAacCodec::audio_aac_demux(char* data, int size, SrsCodecSample* sample) @@ -14722,7 +15143,7 @@ int SrsAvcAacCodec::audio_aac_demux(char* data, int size, SrsCodecSample* sample // audio decode if (!stream->require(1)) { ret = ERROR_HLS_DECODE_ERROR; - srs_error("audio codec decode sound_format failed. ret=%d", ret); + srs_error("aac decode sound_format failed. ret=%d", ret); return ret; } @@ -14749,13 +15170,13 @@ int SrsAvcAacCodec::audio_aac_demux(char* data, int size, SrsCodecSample* sample // only support aac if (audio_codec_id != SrsCodecAudioAAC) { ret = ERROR_HLS_DECODE_ERROR; - srs_error("audio codec only support mp3/aac codec. actual=%d, ret=%d", audio_codec_id, ret); + srs_error("aac only support mp3/aac codec. actual=%d, ret=%d", audio_codec_id, ret); return ret; } if (!stream->require(1)) { ret = ERROR_HLS_DECODE_ERROR; - srs_error("audio codec decode aac_packet_type failed. ret=%d", ret); + srs_error("aac decode aac_packet_type failed. ret=%d", ret); return ret; } @@ -14767,7 +15188,7 @@ int SrsAvcAacCodec::audio_aac_demux(char* data, int size, SrsCodecSample* sample // 1.6.2.1 AudioSpecificConfig, in aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 33. aac_extra_size = stream->size() - stream->pos(); if (aac_extra_size > 0) { - srs_freep(aac_extra_data); + srs_freepa(aac_extra_data); aac_extra_data = new char[aac_extra_size]; memcpy(aac_extra_data, stream->data() + stream->pos(), aac_extra_size); @@ -14778,16 +15199,15 @@ int SrsAvcAacCodec::audio_aac_demux(char* data, int size, SrsCodecSample* sample } } else if (aac_packet_type == SrsCodecAudioTypeRawData) { // ensure the sequence header demuxed - if (aac_extra_size <= 0 || !aac_extra_data) { - ret = ERROR_HLS_DECODE_ERROR; - srs_error("audio codec decode aac failed, sequence header not found. ret=%d", ret); + if (!is_aac_codec_ok()) { + srs_warn("aac ignore type=%d for no sequence header. ret=%d", aac_packet_type, ret); return ret; } // Raw AAC frame data in UI8 [] // 6.3 Raw Data, aac-iso-13818-7.pdf, page 28 if ((ret = sample->add_sample_unit(stream->data() + stream->pos(), stream->size() - stream->pos())) != ERROR_SUCCESS) { - srs_error("audio codec add sample failed. ret=%d", ret); + srs_error("aac add sample failed. ret=%d", ret); return ret; } } else { @@ -14817,7 +15237,7 @@ int SrsAvcAacCodec::audio_aac_demux(char* data, int size, SrsCodecSample* sample }; } - srs_info("audio decoded, type=%d, codec=%d, asize=%d, rate=%d, format=%d, size=%d", + srs_info("aac decoded, type=%d, codec=%d, asize=%d, rate=%d, format=%d, size=%d", sound_type, audio_codec_id, sound_size, sound_rate, sound_format, size); return ret; @@ -14887,7 +15307,7 @@ int SrsAvcAacCodec::audio_aac_sequence_header_demux(char* data, int size) // TODO: FIXME: to support aac he/he-v2, see: ngx_rtmp_codec_parse_aac_header // @see: https://github.com/winlinvip/nginx-rtmp-module/commit/3a5f9eea78fc8d11e8be922aea9ac349b9dcbfc2 // - // donot force to LC, @see: https://github.com/simple-rtmp-server/srs/issues/81 + // donot force to LC, @see: https://github.com/ossrs/srs/issues/81 // the source will print the sequence header info. //if (aac_profile > 3) { // Mark all extended profiles as LC @@ -14917,7 +15337,7 @@ int SrsAvcAacCodec::video_avc_demux(char* data, int size, SrsCodecSample* sample // video decode if (!stream->require(1)) { ret = ERROR_HLS_DECODE_ERROR; - srs_error("video codec decode frame_type failed. ret=%d", ret); + srs_error("avc decode frame_type failed. ret=%d", ret); return ret; } @@ -14929,23 +15349,23 @@ int SrsAvcAacCodec::video_avc_demux(char* data, int size, SrsCodecSample* sample sample->frame_type = (SrsCodecVideoAVCFrame)frame_type; // ignore info frame without error, - // @see https://github.com/simple-rtmp-server/srs/issues/288#issuecomment-69863909 + // @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909 if (sample->frame_type == SrsCodecVideoAVCFrameVideoInfoFrame) { - srs_warn("video codec igone the info frame, ret=%d", ret); + srs_warn("avc igone the info frame, ret=%d", ret); return ret; } // only support h.264/avc if (codec_id != SrsCodecVideoAVC) { ret = ERROR_HLS_DECODE_ERROR; - srs_error("video codec only support video h.264/avc codec. actual=%d, ret=%d", codec_id, ret); + srs_error("avc only support video h.264/avc codec. actual=%d, ret=%d", codec_id, ret); return ret; } video_codec_id = codec_id; if (!stream->require(4)) { ret = ERROR_HLS_DECODE_ERROR; - srs_error("video codec decode avc_packet_type failed. ret=%d", ret); + srs_error("avc decode avc_packet_type failed. ret=%d", ret); return ret; } int8_t avc_packet_type = stream->read_1bytes(); @@ -14961,9 +15381,8 @@ int SrsAvcAacCodec::video_avc_demux(char* data, int size, SrsCodecSample* sample } } else if (avc_packet_type == SrsCodecVideoAVCTypeNALU){ // ensure the sequence header demuxed - if (avc_extra_size <= 0 || !avc_extra_data) { - ret = ERROR_HLS_DECODE_ERROR; - srs_error("avc decode failed, sequence header not found. ret=%d", ret); + if (!is_avc_codec_ok()) { + srs_warn("avc ignore type=%d for no sequence header. ret=%d", avc_packet_type, ret); return ret; } @@ -15019,7 +15438,7 @@ int SrsAvcAacCodec::video_avc_demux(char* data, int size, SrsCodecSample* sample // ignored. } - srs_info("video decoded, type=%d, codec=%d, avc=%d, cts=%d, size=%d", + srs_info("avc decoded, type=%d, codec=%d, avc=%d, cts=%d, size=%d", frame_type, video_codec_id, avc_packet_type, composition_time, size); return ret; @@ -15033,7 +15452,7 @@ int SrsAvcAacCodec::avc_demux_sps_pps(SrsStream* stream) // 5.2.4.1.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16 avc_extra_size = stream->size() - stream->pos(); if (avc_extra_size > 0) { - srs_freep(avc_extra_data); + srs_freepa(avc_extra_data); avc_extra_data = new char[avc_extra_size]; memcpy(avc_extra_data, stream->data() + stream->pos(), avc_extra_size); } @@ -15094,7 +15513,7 @@ int SrsAvcAacCodec::avc_demux_sps_pps(SrsStream* stream) return ret; } if (sequenceParameterSetLength > 0) { - srs_freep(sequenceParameterSetNALUnit); + srs_freepa(sequenceParameterSetNALUnit); sequenceParameterSetNALUnit = new char[sequenceParameterSetLength]; stream->read_bytes(sequenceParameterSetNALUnit, sequenceParameterSetLength); } @@ -15123,7 +15542,7 @@ int SrsAvcAacCodec::avc_demux_sps_pps(SrsStream* stream) return ret; } if (pictureParameterSetLength > 0) { - srs_freep(pictureParameterSetNALUnit); + srs_freepa(pictureParameterSetNALUnit); pictureParameterSetNALUnit = new char[pictureParameterSetLength]; stream->read_bytes(pictureParameterSetNALUnit, pictureParameterSetLength); } @@ -15183,7 +15602,7 @@ int SrsAvcAacCodec::avc_demux_sps() // decode the rbsp from sps. // rbsp[ i ] a raw byte sequence payload is specified as an ordered sequence of bytes. int8_t* rbsp = new int8_t[sequenceParameterSetLength]; - SrsAutoFree(int8_t, rbsp); + SrsAutoFreeA(int8_t, rbsp); int nb_rbsp = 0; while (!stream.empty()) { @@ -15212,6 +15631,12 @@ int SrsAvcAacCodec::avc_demux_sps_rbsp(char* rbsp, int nb_rbsp) { int ret = ERROR_SUCCESS; + // we donot parse the detail of sps. + // @see https://github.com/ossrs/srs/issues/474 + if (!avc_parse_sps) { + return ret; + } + // reparse the rbsp. SrsStream stream; if ((ret = stream.initialize(rbsp, nb_rbsp)) != ERROR_SUCCESS) { @@ -15262,11 +15687,11 @@ int SrsAvcAacCodec::avc_demux_sps_rbsp(char* rbsp, int nb_rbsp) } srs_info("sps parse profile=%d, level=%d, sps_id=%d", profile_idc, level_idc, seq_parameter_set_id); + int32_t chroma_format_idc = -1; if (profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 244 || profile_idc == 44 || profile_idc == 83 || profile_idc == 86 || profile_idc == 118 || profile_idc == 128 ) { - int32_t chroma_format_idc = -1; if ((ret = srs_avc_nalu_read_uev(&bs, chroma_format_idc)) != ERROR_SUCCESS) { return ret; } @@ -15297,9 +15722,18 @@ int SrsAvcAacCodec::avc_demux_sps_rbsp(char* rbsp, int nb_rbsp) return ret; } if (seq_scaling_matrix_present_flag) { - ret = ERROR_HLS_DECODE_ERROR; - srs_error("sps the seq_scaling_matrix_present_flag invalid. ret=%d", ret); - return ret; + int nb_scmpfs = ((chroma_format_idc != 3)? 8:12); + for (int i = 0; i < nb_scmpfs; i++) { + int8_t seq_scaling_matrix_present_flag_i = -1; + if ((ret = srs_avc_nalu_read_bit(&bs, seq_scaling_matrix_present_flag_i)) != ERROR_SUCCESS) { + return ret; + } + if (seq_scaling_matrix_present_flag_i) { + ret = ERROR_HLS_DECODE_ERROR; + srs_error("sps the seq_scaling_matrix_present_flag invalid, i=%d, nb_scmpfs=%d. ret=%d", i, nb_scmpfs, ret); + return ret; + } + } } } @@ -15455,7 +15889,7 @@ int SrsAvcAacCodec::avc_demux_ibmf_format(SrsStream* stream, SrsCodecSample* sam } // maybe stream is invalid format. - // see: https://github.com/simple-rtmp-server/srs/issues/183 + // see: https://github.com/ossrs/srs/issues/183 if (NALUnitLength < 0) { ret = ERROR_HLS_DECODE_ERROR; srs_error("maybe stream is AnnexB format. ret=%d", ret); @@ -15481,11 +15915,13 @@ int SrsAvcAacCodec::avc_demux_ibmf_format(SrsStream* stream, SrsCodecSample* sam return ret; } +#endif + // following is generated by src/kernel/srs_kernel_file.cpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -15507,14 +15943,14 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include +#include #endif #include #include -#include using namespace std; //#include @@ -15530,50 +15966,50 @@ SrsFileWriter::~SrsFileWriter() close(); } -int SrsFileWriter::open(string file) +int SrsFileWriter::open(string p) { int ret = ERROR_SUCCESS; if (fd > 0) { ret = ERROR_SYSTEM_FILE_ALREADY_OPENED; - srs_error("file %s already opened. ret=%d", _file.c_str(), ret); + srs_error("file %s already opened. ret=%d", path.c_str(), ret); return ret; } int flags = O_CREAT|O_WRONLY|O_TRUNC; mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH; - if ((fd = ::open(file.c_str(), flags, mode)) < 0) { + if ((fd = ::open(p.c_str(), flags, mode)) < 0) { ret = ERROR_SYSTEM_FILE_OPENE; - srs_error("open file %s failed. ret=%d", file.c_str(), ret); + srs_error("open file %s failed. ret=%d", p.c_str(), ret); return ret; } - _file = file; + path = p; return ret; } -int SrsFileWriter::open_append(string file) +int SrsFileWriter::open_append(string p) { int ret = ERROR_SUCCESS; if (fd > 0) { ret = ERROR_SYSTEM_FILE_ALREADY_OPENED; - srs_error("file %s already opened. ret=%d", _file.c_str(), ret); + srs_error("file %s already opened. ret=%d", path.c_str(), ret); return ret; } int flags = O_APPEND|O_WRONLY; mode_t mode = S_IRUSR|S_IWUSR|S_IRGRP|S_IWGRP|S_IROTH; - if ((fd = ::open(file.c_str(), flags, mode)) < 0) { + if ((fd = ::open(p.c_str(), flags, mode)) < 0) { ret = ERROR_SYSTEM_FILE_OPENE; - srs_error("open file %s failed. ret=%d", file.c_str(), ret); + srs_error("open file %s failed. ret=%d", p.c_str(), ret); return ret; } - _file = file; + path = p; return ret; } @@ -15588,7 +16024,7 @@ void SrsFileWriter::close() if (::close(fd) < 0) { ret = ERROR_SYSTEM_FILE_CLOSE; - srs_error("close file %s failed. ret=%d", _file.c_str(), ret); + srs_error("close file %s failed. ret=%d", path.c_str(), ret); return; } fd = -1; @@ -15619,7 +16055,7 @@ int SrsFileWriter::write(void* buf, size_t count, ssize_t* pnwrite) // TODO: FIXME: use st_write. if ((nwrite = ::write(fd, buf, count)) < 0) { ret = ERROR_SYSTEM_FILE_WRITE; - srs_error("write to file %s failed. ret=%d", _file.c_str(), ret); + srs_error("write to file %s failed. ret=%d", path.c_str(), ret); return ret; } @@ -15661,23 +16097,23 @@ SrsFileReader::~SrsFileReader() close(); } -int SrsFileReader::open(string file) +int SrsFileReader::open(string p) { int ret = ERROR_SUCCESS; if (fd > 0) { ret = ERROR_SYSTEM_FILE_ALREADY_OPENED; - srs_error("file %s already opened. ret=%d", _file.c_str(), ret); + srs_error("file %s already opened. ret=%d", path.c_str(), ret); return ret; } - if ((fd = ::open(file.c_str(), O_RDONLY)) < 0) { + if ((fd = ::open(p.c_str(), O_RDONLY)) < 0) { ret = ERROR_SYSTEM_FILE_OPENE; - srs_error("open file %s failed. ret=%d", file.c_str(), ret); + srs_error("open file %s failed. ret=%d", p.c_str(), ret); return ret; } - _file = file; + path = p; return ret; } @@ -15692,7 +16128,7 @@ void SrsFileReader::close() if (::close(fd) < 0) { ret = ERROR_SYSTEM_FILE_CLOSE; - srs_error("close file %s failed. ret=%d", _file.c_str(), ret); + srs_error("close file %s failed. ret=%d", path.c_str(), ret); return; } fd = -1; @@ -15736,7 +16172,7 @@ int SrsFileReader::read(void* buf, size_t count, ssize_t* pnread) // TODO: FIXME: use st_read. if ((nread = ::read(fd, buf, count)) < 0) { ret = ERROR_SYSTEM_FILE_READ; - srs_error("read from file %s failed. ret=%d", _file.c_str(), ret); + srs_error("read from file %s failed. ret=%d", path.c_str(), ret); return ret; } @@ -15756,7 +16192,7 @@ int SrsFileReader::read(void* buf, size_t count, ssize_t* pnread) /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -15783,7 +16219,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -15805,7 +16241,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +#if !defined(SRS_EXPORT_LIBRTMP) + +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -15932,7 +16370,7 @@ int SrsAacEncoder::write_audio(int64_t timestamp, char* data, int size) // write the ADTS header. // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, // 1.A.2.2 Audio_Data_Transport_Stream frame, ADTS - // @see https://github.com/simple-rtmp-server/srs/issues/212#issuecomment-64145885 + // @see https://github.com/ossrs/srs/issues/212#issuecomment-64145885 // byte_alignment() // adts_fixed_header: @@ -16000,11 +16438,13 @@ int SrsAacEncoder::write_audio(int64_t timestamp, char* data, int size) return ret; } +#endif + // following is generated by src/kernel/srs_kernel_mp3.cpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -16026,7 +16466,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +#if !defined(SRS_EXPORT_LIBRTMP) + +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -16043,7 +16485,7 @@ using namespace std; SrsMp3Encoder::SrsMp3Encoder() { - _fs = NULL; + writer = NULL; tag_stream = new SrsStream(); } @@ -16052,19 +16494,19 @@ SrsMp3Encoder::~SrsMp3Encoder() srs_freep(tag_stream); } -int SrsMp3Encoder::initialize(SrsFileWriter* fs) +int SrsMp3Encoder::initialize(SrsFileWriter* fw) { int ret = ERROR_SUCCESS; - srs_assert(fs); + srs_assert(fw); - if (!fs->is_open()) { + if (!fw->is_open()) { ret = ERROR_KERNEL_MP3_STREAM_CLOSED; srs_warn("stream is not open for encoder. ret=%d", ret); return ret; } - _fs = fs; + writer = fw; return ret; } @@ -16081,7 +16523,7 @@ int SrsMp3Encoder::write_header() (char)0x00, (char)0x00, (char)0x00, (char)0x00, // FrameSize (char)0x00, (char)0x00 // Flags }; - return _fs->write(id3, sizeof(id3), NULL); + return writer->write(id3, sizeof(id3), NULL); } int SrsMp3Encoder::write_audio(int64_t timestamp, char* data, int size) @@ -16125,14 +16567,16 @@ int SrsMp3Encoder::write_audio(int64_t timestamp, char* data, int size) return ret; } - return _fs->write(data + stream->pos(), size - stream->pos(), NULL); + return writer->write(data + stream->pos(), size - stream->pos(), NULL); } +#endif + // following is generated by src/kernel/srs_kernel_ts.cpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -16154,7 +16598,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +#if !defined(SRS_EXPORT_LIBRTMP) + +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -16516,7 +16962,7 @@ int SrsTsContext::encode_pat_pmt(SrsFileWriter* writer, int16_t vpid, SrsTsStrea SrsAutoFree(SrsTsPacket, pkt); char* buf = new char[SRS_TS_PACKET_SIZE]; - SrsAutoFree(char, buf); + SrsAutoFreeA(char, buf); // set the left bytes with 0xFF. int nb_buf = pkt->size(); @@ -16541,7 +16987,7 @@ int SrsTsContext::encode_pat_pmt(SrsFileWriter* writer, int16_t vpid, SrsTsStrea SrsAutoFree(SrsTsPacket, pkt); char* buf = new char[SRS_TS_PACKET_SIZE]; - SrsAutoFree(char, buf); + SrsAutoFreeA(char, buf); // set the left bytes with 0xFF. int nb_buf = pkt->size(); @@ -16588,14 +17034,17 @@ int SrsTsContext::encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t p while (p < end) { SrsTsPacket* pkt = NULL; if (p == start) { - // for pure audio stream, always write pcr. + // write pcr according to message. bool write_pcr = msg->write_pcr; + + // for pure audio, always write pcr. + // TODO: FIXME: maybe only need to write at begin and end of ts. if (pure_audio && msg->is_audio()) { write_pcr = true; } // it's ok to set pcr equals to dts, - // @see https://github.com/simple-rtmp-server/srs/issues/311 + // @see https://github.com/ossrs/srs/issues/311 int64_t pcr = write_pcr? msg->dts : -1; // TODO: FIXME: finger it why use discontinuity of msg. @@ -16611,7 +17060,7 @@ int SrsTsContext::encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t p SrsAutoFree(SrsTsPacket, pkt); char* buf = new char[SRS_TS_PACKET_SIZE]; - SrsAutoFree(char, buf); + SrsAutoFreeA(char, buf); // set the left bytes with 0xFF. int nb_buf = pkt->size(); @@ -17041,7 +17490,7 @@ SrsTsAdaptationField::SrsTsAdaptationField(SrsTsPacket* pkt) SrsTsAdaptationField::~SrsTsAdaptationField() { - srs_freep(transport_private_data); + srs_freepa(transport_private_data); } int SrsTsAdaptationField::decode(SrsStream* stream) @@ -17110,7 +17559,7 @@ int SrsTsAdaptationField::decode(SrsStream* stream) pp[0] = *p++; // @remark, use pcr base and ignore the extension - // @see https://github.com/simple-rtmp-server/srs/issues/250#issuecomment-71349370 + // @see https://github.com/ossrs/srs/issues/250#issuecomment-71349370 program_clock_reference_extension = pcrv & 0x1ff; const1_value0 = (pcrv >> 9) & 0x3F; program_clock_reference_base = (pcrv >> 15) & 0x1ffffffffLL; @@ -17137,7 +17586,7 @@ int SrsTsAdaptationField::decode(SrsStream* stream) pp[0] = *p++; // @remark, use pcr base and ignore the extension - // @see https://github.com/simple-rtmp-server/srs/issues/250#issuecomment-71349370 + // @see https://github.com/ossrs/srs/issues/250#issuecomment-71349370 original_program_clock_reference_extension = opcrv & 0x1ff; const1_value2 = (opcrv >> 9) & 0x3F; original_program_clock_reference_base = (opcrv >> 15) & 0x1ffffffffLL; @@ -17166,7 +17615,7 @@ int SrsTsAdaptationField::decode(SrsStream* stream) srs_error("ts: demux af transport_private_data_flag failed. ret=%d", ret); return ret; } - srs_freep(transport_private_data); + srs_freepa(transport_private_data); transport_private_data = new char[transport_private_data_length]; stream->read_bytes(transport_private_data, transport_private_data_length); } @@ -17317,7 +17766,7 @@ int SrsTsAdaptationField::encode(SrsStream* stream) stream->skip(6); // @remark, use pcr base and ignore the extension - // @see https://github.com/simple-rtmp-server/srs/issues/250#issuecomment-71349370 + // @see https://github.com/ossrs/srs/issues/250#issuecomment-71349370 int64_t pcrv = program_clock_reference_extension & 0x1ff; pcrv |= (const1_value0 << 9) & 0x7E00; pcrv |= (program_clock_reference_base << 15) & 0x1FFFFFFFF000000LL; @@ -17451,9 +17900,9 @@ SrsTsPayloadPES::SrsTsPayloadPES(SrsTsPacket* p) : SrsTsPayload(p) SrsTsPayloadPES::~SrsTsPayloadPES() { - srs_freep(PES_private_data); - srs_freep(pack_field); - srs_freep(PES_extension_field); + srs_freepa(PES_private_data); + srs_freepa(pack_field); + srs_freepa(PES_extension_field); } int SrsTsPayloadPES::decode(SrsStream* stream, SrsTsMessage** ppmsg) @@ -17730,7 +18179,7 @@ int SrsTsPayloadPES::decode(SrsStream* stream, SrsTsMessage** ppmsg) // 16B if (PES_private_data_flag) { - srs_freep(PES_private_data); + srs_freepa(PES_private_data); PES_private_data = new char[16]; stream->read_bytes(PES_private_data, 16); } @@ -17746,7 +18195,7 @@ int SrsTsPayloadPES::decode(SrsStream* stream, SrsTsMessage** ppmsg) srs_error("ts: demux PSE ext pack failed. ret=%d", ret); return ret; } - srs_freep(pack_field); + srs_freepa(pack_field); pack_field = new char[pack_field_length]; stream->read_bytes(pack_field, pack_field_length); } @@ -17784,7 +18233,7 @@ int SrsTsPayloadPES::decode(SrsStream* stream, SrsTsMessage** ppmsg) srs_error("ts: demux PSE ext field failed. ret=%d", ret); return ret; } - srs_freep(PES_extension_field); + srs_freepa(PES_extension_field); PES_extension_field = new char[PES_extension_field_length]; stream->read_bytes(PES_extension_field, PES_extension_field_length); } @@ -18547,7 +18996,7 @@ SrsTsPayloadPMTESInfo::SrsTsPayloadPMTESInfo(SrsTsStream st, int16_t epid) SrsTsPayloadPMTESInfo::~SrsTsPayloadPMTESInfo() { - srs_freep(ES_info); + srs_freepa(ES_info); } int SrsTsPayloadPMTESInfo::decode(SrsStream* stream) @@ -18577,7 +19026,7 @@ int SrsTsPayloadPMTESInfo::decode(SrsStream* stream) srs_error("ts: demux PMT es info data failed. ret=%d", ret); return ret; } - srs_freep(ES_info); + srs_freepa(ES_info); ES_info = new char[ES_info_length]; stream->read_bytes(ES_info, ES_info_length); } @@ -18634,7 +19083,7 @@ SrsTsPayloadPMT::SrsTsPayloadPMT(SrsTsPacket* p) : SrsTsPayloadPSI(p) SrsTsPayloadPMT::~SrsTsPayloadPMT() { - srs_freep(program_info_desc); + srs_freepa(program_info_desc); std::vector::iterator it; for (it = infos.begin(); it != infos.end(); ++it) { @@ -18688,7 +19137,7 @@ int SrsTsPayloadPMT::psi_decode(SrsStream* stream) return ret; } - srs_freep(program_info_desc); + srs_freepa(program_info_desc); program_info_desc = new char[program_info_length]; stream->read_bytes(program_info_desc, program_info_length); } @@ -18828,11 +19277,11 @@ SrsTSMuxer::~SrsTSMuxer() close(); } -int SrsTSMuxer::open(string _path) +int SrsTSMuxer::open(string p) { int ret = ERROR_SUCCESS; - path = _path; + path = p; close(); @@ -18889,6 +19338,11 @@ void SrsTSMuxer::close() writer->close(); } +SrsCodecVideo SrsTSMuxer::video_codec() +{ + return vcodec; +} + SrsTsCache::SrsTsCache() { audio = NULL; @@ -18909,11 +19363,12 @@ int SrsTsCache::cache_audio(SrsAvcAacCodec* codec, int64_t dts, SrsCodecSample* if (!audio) { audio = new SrsTsMessage(); audio->write_pcr = false; - audio->start_pts = dts; + audio->dts = audio->pts = audio->start_pts = dts; } - audio->dts = dts; - audio->pts = audio->dts; + // TODO: FIXME: refine code. + //audio->dts = dts; + //audio->pts = audio->dts; audio->sid = SrsTsPESStreamIdAudioCommon; // must be aac or mp3 @@ -19179,7 +19634,7 @@ int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample) SrsTsEncoder::SrsTsEncoder() { - _fs = NULL; + writer = NULL; codec = new SrsAvcAacCodec(); sample = new SrsCodecSample(); cache = new SrsTsCache(); @@ -19196,22 +19651,22 @@ SrsTsEncoder::~SrsTsEncoder() srs_freep(context); } -int SrsTsEncoder::initialize(SrsFileWriter* fs) +int SrsTsEncoder::initialize(SrsFileWriter* fw) { int ret = ERROR_SUCCESS; - srs_assert(fs); + srs_assert(fw); - if (!fs->is_open()) { + if (!fw->is_open()) { ret = ERROR_KERNEL_FLV_STREAM_CLOSED; srs_warn("stream is not open for encoder. ret=%d", ret); return ret; } - _fs = fs; + writer = fw; srs_freep(muxer); - muxer = new SrsTSMuxer(fs, context, SrsCodecAudioAAC, SrsCodecVideoAVC); + muxer = new SrsTSMuxer(fw, context, SrsCodecAudioAAC, SrsCodecVideoAVC); if ((ret = muxer->open("")) != ERROR_SUCCESS) { return ret; @@ -19263,20 +19718,11 @@ int SrsTsEncoder::write_audio(int64_t timestamp, char* data, int size) return ret; } - // flush if buffer exceed max size. - if (cache->audio->payload->length() > SRS_AUTO_HLS_AUDIO_CACHE_SIZE) { - return flush_video(); - } - - // TODO: config it. - // in ms, audio delay to flush the audios. - int64_t audio_delay = SRS_CONF_DEFAULT_AAC_DELAY; - // flush if audio delay exceed - if (dts - cache->audio->start_pts > audio_delay * 90) { - return flush_audio(); - } - - return ret; + // TODO: FIXME: for pure audio, aggregate some frame to one. + + // always flush audio frame by frame. + // @see https://github.com/ossrs/srs/issues/512 + return flush_audio(); } int SrsTsEncoder::write_video(int64_t timestamp, char* data, int size) @@ -19290,7 +19736,7 @@ int SrsTsEncoder::write_video(int64_t timestamp, char* data, int size) } // ignore info frame, - // @see https://github.com/simple-rtmp-server/srs/issues/288#issuecomment-69863909 + // @see https://github.com/ossrs/srs/issues/288#issuecomment-69863909 if (sample->frame_type == SrsCodecVideoAVCFrameVideoInfoFrame) { return ret; } @@ -19343,12 +19789,13 @@ int SrsTsEncoder::flush_video() return ret; } +#endif // following is generated by src/kernel/srs_kernel_buffer.cpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -19419,7 +19866,7 @@ void SrsSimpleBuffer::append(const char* bytes, int size) /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -19931,6 +20378,24 @@ SrsAmf0Any* SrsUnSortedHashtable::ensure_property_number(string name) return prop; } +void SrsUnSortedHashtable::remove(string name) +{ + std::vector::iterator it; + + for (it = properties.begin(); it != properties.end();) { + std::string key = it->first; + SrsAmf0Any* any = it->second; + + if (key == name) { + srs_freep(any); + + it = properties.erase(it); + } else { + ++it; + } + } +} + void SrsUnSortedHashtable::copy(SrsUnSortedHashtable* src) { std::vector::iterator it; @@ -20205,6 +20670,11 @@ SrsAmf0Any* SrsAmf0Object::ensure_property_number(string name) return properties->ensure_property_number(name); } +void SrsAmf0Object::remove(string name) +{ + properties->remove(name); +} + SrsAmf0EcmaArray::SrsAmf0EcmaArray() { _count = 0; @@ -21311,7 +21781,7 @@ namespace _srs_internal /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -21385,7 +21855,7 @@ ISrsProtocolReaderWriter::~ISrsProtocolReaderWriter() /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -21414,8 +21884,9 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include //#include //#include +//#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -21423,6 +21894,13 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include using namespace std; +// 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 + // when got a messae header, there must be some data, // increase recv timeout to got an entire message. #define SRS_MIN_RECV_TIMEOUT_US (int64_t)(60*1000*1000LL) @@ -21527,14 +22005,14 @@ int SrsPacket::encode(int& psize, char*& ppayload) if ((ret = stream.initialize(payload, size)) != ERROR_SUCCESS) { srs_error("initialize the stream failed. ret=%d", ret); - srs_freep(payload); + srs_freepa(payload); return ret; } } if ((ret = encode_packet(&stream)) != ERROR_SUCCESS) { srs_error("encode the packet failed. ret=%d", ret); - srs_freep(payload); + srs_freepa(payload); return ret; } @@ -21655,7 +22133,7 @@ SrsProtocol::~SrsProtocol() SrsChunkStream* cs = cs_cache[i]; srs_freep(cs); } - srs_freep(cs_cache); + srs_freepa(cs_cache); } void SrsProtocol::set_auto_response(bool v) @@ -22008,7 +22486,7 @@ int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id) header.perfer_cid = packet->get_prefer_cid(); ret = do_simple_send(&header, payload, size); - srs_freep(payload); + srs_freepa(payload); if (ret == ERROR_SUCCESS) { ret = on_send_packet(&header, packet); } @@ -22313,7 +22791,7 @@ int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg) SrsChunkStream* chunk = NULL; // use chunk stream cache to get the chunk info. - // @see https://github.com/simple-rtmp-server/srs/issues/249 + // @see https://github.com/ossrs/srs/issues/249 if (cid < SRS_PERF_CHUNK_STREAM_CACHE) { // chunk stream cache hit. srs_verbose("cs-cache hit, cid=%d", cid); @@ -22524,7 +23002,7 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt) // 0x04 where: message_type=4(protocol control user-control message) // 0x00 0x06 where: event Ping(0x06) // 0x00 0x00 0x0d 0x0f where: event data 4bytes ping timestamp. - // @see: https://github.com/simple-rtmp-server/srs/issues/98 + // @see: https://github.com/ossrs/srs/issues/98 if (chunk->cid == RTMP_CID_ProtocolControl && fmt == RTMP_FMT_TYPE1) { srs_warn("accept cid=2, fmt=1 to make librtmp happy."); } else { @@ -22694,7 +23172,7 @@ int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt) pp[0] = *p++; // always use 31bits timestamp, for some server may use 32bits extended timestamp. - // @see https://github.com/simple-rtmp-server/srs/issues/111 + // @see https://github.com/ossrs/srs/issues/111 timestamp &= 0x7fffffff; /** @@ -22884,16 +23362,23 @@ int SrsProtocol::on_recv_message(SrsCommonMessage* msg) // for some server, the actual chunk size can greater than the max value(65536), // so we just warning the invalid chunk size, and actually use it is ok, - // @see: https://github.com/simple-rtmp-server/srs/issues/160 + // @see: https://github.com/ossrs/srs/issues/160 if (pkt->chunk_size < SRS_CONSTS_RTMP_MIN_CHUNK_SIZE || pkt->chunk_size > SRS_CONSTS_RTMP_MAX_CHUNK_SIZE) { srs_warn("accept chunk size %d, but should in [%d, %d], " - "@see: https://github.com/simple-rtmp-server/srs/issues/160", - pkt->chunk_size, SRS_CONSTS_RTMP_MIN_CHUNK_SIZE, - SRS_CONSTS_RTMP_MAX_CHUNK_SIZE); + "@see: https://github.com/ossrs/srs/issues/160", + pkt->chunk_size, SRS_CONSTS_RTMP_MIN_CHUNK_SIZE, SRS_CONSTS_RTMP_MAX_CHUNK_SIZE); } + // @see: https://github.com/ossrs/srs/issues/541 + if (pkt->chunk_size < SRS_CONSTS_RTMP_MIN_CHUNK_SIZE) { + ret = ERROR_RTMP_CHUNK_SIZE; + srs_error("chunk size should be %d+, value=%d. ret=%d", + SRS_CONSTS_RTMP_MIN_CHUNK_SIZE, pkt->chunk_size, ret); + return ret; + } + in_chunk_size = pkt->chunk_size; srs_trace("input chunk size to %d", pkt->chunk_size); @@ -23036,1358 +23521,1562 @@ SrsChunkStream::~SrsChunkStream() srs_freep(msg); } -SrsConnectAppPacket::SrsConnectAppPacket() +SrsRequest::SrsRequest() { - command_name = RTMP_AMF0_COMMAND_CONNECT; - transaction_id = 1; - command_object = SrsAmf0Any::object(); - // optional + objectEncoding = RTMP_SIG_AMF0_VER; + duration = -1; args = NULL; } -SrsConnectAppPacket::~SrsConnectAppPacket() +SrsRequest::~SrsRequest() { - srs_freep(command_object); srs_freep(args); } -int SrsConnectAppPacket::decode(SrsStream* stream) +SrsRequest* SrsRequest::copy() { - 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; - } + SrsRequest* cp = new SrsRequest(); - // some client donot send id=1.0, so we only warn user if not match. - if (transaction_id != 1.0) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_warn("amf0 decode connect transaction_id failed. " - "required=%.1f, actual=%.1f, ret=%d", 1.0, transaction_id, ret); - ret = ERROR_SUCCESS; + cp->ip = ip; + cp->app = app; + cp->objectEncoding = objectEncoding; + cp->pageUrl = pageUrl; + cp->host = host; + cp->port = port; + cp->param = param; + cp->schema = schema; + cp->stream = stream; + cp->swfUrl = swfUrl; + cp->tcUrl = tcUrl; + cp->vhost = vhost; + cp->duration = duration; + if (args) { + cp->args = args->copy()->to_object(); } - if ((ret = command_object->read(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode connect command_object failed. ret=%d", ret); - return ret; - } + return cp; +} + +void SrsRequest::update_auth(SrsRequest* req) +{ + pageUrl = req->pageUrl; + swfUrl = req->swfUrl; + tcUrl = req->tcUrl; - if (!stream->empty()) { + if (args) { srs_freep(args); - - // see: https://github.com/simple-rtmp-server/srs/issues/186 - // the args maybe any amf0, for instance, a string. we should drop if not object. - SrsAmf0Any* any = NULL; - if ((ret = SrsAmf0Any::discovery(stream, &any)) != ERROR_SUCCESS) { - srs_error("amf0 find connect args failed. ret=%d", ret); - return ret; - } - srs_assert(any); - - // read the instance - if ((ret = any->read(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode connect args failed. ret=%d", ret); - srs_freep(any); - return ret; - } - - // drop when not an AMF0 object. - if (!any->is_object()) { - srs_warn("drop the args, see: '4.1.1. connect', marker=%#x", any->marker); - srs_freep(any); - } else { - args = any->to_object(); - } + } + if (req->args) { + args = req->args->copy()->to_object(); } - srs_info("amf0 decode connect packet success"); + srs_info("update req of soruce for auth ok"); +} + +string SrsRequest::get_stream_url() +{ + return srs_generate_stream_url(vhost, app, stream); +} + +void SrsRequest::strip() +{ + // remove the unsupported chars in names. + host = srs_string_remove(host, "/ \n\r\t"); + vhost = srs_string_remove(vhost, "/ \n\r\t"); + app = srs_string_remove(app, " \n\r\t"); + stream = srs_string_remove(stream, " \n\r\t"); - return ret; + // remove end slash of app/stream + app = srs_string_trim_end(app, "/"); + stream = srs_string_trim_end(stream, "/"); + + // remove start slash of app/stream + app = srs_string_trim_start(app, "/"); + stream = srs_string_trim_start(stream, "/"); } -int SrsConnectAppPacket::get_prefer_cid() +SrsResponse::SrsResponse() { - return RTMP_CID_OverConnection; + stream_id = SRS_DEFAULT_SID; } -int SrsConnectAppPacket::get_message_type() +SrsResponse::~SrsResponse() { - return RTMP_MSG_AMF0CommandMessage; } -int SrsConnectAppPacket::get_size() +string srs_client_type_string(SrsRtmpConnType type) { - int size = 0; - - size += SrsAmf0Size::str(command_name); - size += SrsAmf0Size::number(); - size += SrsAmf0Size::object(command_object); - if (args) { - size += SrsAmf0Size::object(args); + switch (type) { + case SrsRtmpConnPlay: return "Play"; + case SrsRtmpConnFlashPublish: return "flash-publish)"; + case SrsRtmpConnFMLEPublish: return "fmle-publish"; + default: return "Unknown"; } - - return size; } -int SrsConnectAppPacket::encode_packet(SrsStream* stream) +bool srs_client_type_is_publish(SrsRtmpConnType type) +{ + return type != SrsRtmpConnPlay; +} + +SrsHandshakeBytes::SrsHandshakeBytes() +{ + c0c1 = s0s1s2 = c2 = NULL; +} + +SrsHandshakeBytes::~SrsHandshakeBytes() +{ + srs_freepa(c0c1); + srs_freepa(s0s1s2); + srs_freepa(c2); +} + +int SrsHandshakeBytes::read_c0c1(ISrsProtocolReaderWriter* io) { int ret = ERROR_SUCCESS; - if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("encode command_name failed. ret=%d", ret); + if (c0c1) { 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); + ssize_t nsize; + + c0c1 = new char[1537]; + if ((ret = io->read_fully(c0c1, 1537, &nsize)) != ERROR_SUCCESS) { + srs_warn("read c0c1 failed. ret=%d", ret); return ret; } - srs_verbose("encode transaction_id success."); + srs_verbose("read c0c1 success."); - if ((ret = command_object->write(stream)) != ERROR_SUCCESS) { - srs_error("encode command_object failed. ret=%d", ret); + return ret; +} + +int SrsHandshakeBytes::read_s0s1s2(ISrsProtocolReaderWriter* io) +{ + int ret = ERROR_SUCCESS; + + if (s0s1s2) { return ret; } - srs_verbose("encode command_object success."); - if (args && (ret = args->write(stream)) != ERROR_SUCCESS) { - srs_error("encode args failed. ret=%d", ret); + ssize_t nsize; + + s0s1s2 = new char[3073]; + if ((ret = io->read_fully(s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) { + srs_warn("read s0s1s2 failed. ret=%d", ret); return ret; } - srs_verbose("encode args success."); - - srs_info("encode connect app request packet success."); + srs_verbose("read s0s1s2 success."); return ret; } -SrsConnectAppResPacket::SrsConnectAppResPacket() -{ - command_name = RTMP_AMF0_COMMAND_RESULT; - transaction_id = 1; - props = SrsAmf0Any::object(); - info = SrsAmf0Any::object(); -} - -SrsConnectAppResPacket::~SrsConnectAppResPacket() -{ - srs_freep(props); - srs_freep(info); -} - -int SrsConnectAppResPacket::decode(SrsStream* stream) +int SrsHandshakeBytes::read_c2(ISrsProtocolReaderWriter* io) { 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_RESULT) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 decode connect command_name failed. " - "command_name=%s, ret=%d", command_name.c_str(), ret); + + if (c2) { return ret; } - if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode connect transaction_id failed. ret=%d", ret); + ssize_t nsize; + + c2 = new char[1536]; + if ((ret = io->read_fully(c2, 1536, &nsize)) != ERROR_SUCCESS) { + srs_warn("read c2 failed. ret=%d", ret); return ret; } + srs_verbose("read c2 success."); - // some client donot send id=1.0, so we only warn user if not match. - if (transaction_id != 1.0) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_warn("amf0 decode connect transaction_id failed. " - "required=%.1f, actual=%.1f, ret=%d", 1.0, transaction_id, ret); - ret = ERROR_SUCCESS; - } + return ret; +} + +int SrsHandshakeBytes::create_c0c1() +{ + int ret = ERROR_SUCCESS; - if ((ret = props->read(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode connect props failed. ret=%d", ret); + if (c0c1) { return ret; } - if ((ret = info->read(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode connect info failed. ret=%d", ret); + c0c1 = new char[1537]; + srs_random_generate(c0c1, 1537); + + // plain text required. + SrsStream stream; + if ((ret = stream.initialize(c0c1, 9)) != ERROR_SUCCESS) { return ret; } - - srs_info("amf0 decode connect response packet success"); + stream.write_1bytes(0x03); + stream.write_4bytes((int32_t)::time(NULL)); + stream.write_4bytes(0x00); return ret; } -int SrsConnectAppResPacket::get_prefer_cid() -{ - return RTMP_CID_OverConnection; -} - -int SrsConnectAppResPacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsConnectAppResPacket::get_size() -{ - return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() - + SrsAmf0Size::object(props) + SrsAmf0Size::object(info); -} - -int SrsConnectAppResPacket::encode_packet(SrsStream* stream) +int SrsHandshakeBytes::create_s0s1s2(const char* c1) { int ret = ERROR_SUCCESS; - if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("encode command_name failed. ret=%d", ret); + if (s0s1s2) { 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."); + s0s1s2 = new char[3073]; + srs_random_generate(s0s1s2, 3073); - if ((ret = props->write(stream)) != ERROR_SUCCESS) { - srs_error("encode props failed. ret=%d", ret); + // plain text required. + SrsStream stream; + if ((ret = stream.initialize(s0s1s2, 9)) != ERROR_SUCCESS) { return ret; } - - srs_verbose("encode props success."); - - if ((ret = info->write(stream)) != ERROR_SUCCESS) { - srs_error("encode info failed. ret=%d", ret); - return ret; + stream.write_1bytes(0x03); + stream.write_4bytes((int32_t)::time(NULL)); + // s1 time2 copy from c1 + if (c0c1) { + stream.write_bytes(c0c1 + 1, 4); } - - srs_verbose("encode info success."); - srs_info("encode connect app response packet success."); + // if c1 specified, copy c1 to s2. + // @see: https://github.com/ossrs/srs/issues/46 + if (c1) { + memcpy(s0s1s2 + 1537, c1, 1536); + } return ret; } -SrsCallPacket::SrsCallPacket() -{ - command_name = ""; - transaction_id = 0; - command_object = NULL; - arguments = NULL; -} - -SrsCallPacket::~SrsCallPacket() -{ - srs_freep(command_object); - srs_freep(arguments); -} - -int SrsCallPacket::decode(SrsStream* stream) +int SrsHandshakeBytes::create_c2() { int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode call command_name failed. ret=%d", ret); - return ret; - } - if (command_name.empty()) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 decode call 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 call transaction_id failed. ret=%d", ret); + if (c2) { return ret; } - srs_freep(command_object); - if ((ret = SrsAmf0Any::discovery(stream, &command_object)) != ERROR_SUCCESS) { - srs_error("amf0 discovery call command_object failed. ret=%d", ret); - return ret; - } - if ((ret = command_object->read(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode call command_object failed. ret=%d", ret); + c2 = new char[1536]; + srs_random_generate(c2, 1536); + + // time + SrsStream stream; + if ((ret = stream.initialize(c2, 8)) != ERROR_SUCCESS) { return ret; } - - if (!stream->empty()) { - srs_freep(arguments); - if ((ret = SrsAmf0Any::discovery(stream, &arguments)) != ERROR_SUCCESS) { - srs_error("amf0 discovery call arguments failed. ret=%d", ret); - return ret; - } - if ((ret = arguments->read(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode call arguments failed. ret=%d", ret); - return ret; - } + stream.write_4bytes((int32_t)::time(NULL)); + // c2 time2 copy from s1 + if (s0s1s2) { + stream.write_bytes(s0s1s2 + 1, 4); } - srs_info("amf0 decode call packet success"); - return ret; } -int SrsCallPacket::get_prefer_cid() +SrsRtmpClient::SrsRtmpClient(ISrsProtocolReaderWriter* skt) { - return RTMP_CID_OverConnection; + io = skt; + protocol = new SrsProtocol(skt); + hs_bytes = new SrsHandshakeBytes(); } -int SrsCallPacket::get_message_type() +SrsRtmpClient::~SrsRtmpClient() { - return RTMP_MSG_AMF0CommandMessage; + srs_freep(protocol); + srs_freep(hs_bytes); } -int SrsCallPacket::get_size() +void SrsRtmpClient::set_recv_timeout(int64_t timeout_us) { - int size = 0; - - size += SrsAmf0Size::str(command_name) + SrsAmf0Size::number(); - - if (command_object) { - size += command_object->total_size(); - } - - if (arguments) { - size += arguments->total_size(); - } - - return size; + protocol->set_recv_timeout(timeout_us); } -int SrsCallPacket::encode_packet(SrsStream* stream) +void SrsRtmpClient::set_send_timeout(int64_t timeout_us) { - 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 (command_object && (ret = command_object->write(stream)) != ERROR_SUCCESS) { - srs_error("encode command_object failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_object success."); - - if (arguments && (ret = arguments->write(stream)) != ERROR_SUCCESS) { - srs_error("encode arguments failed. ret=%d", ret); - return ret; - } - srs_verbose("encode arguments success."); - - srs_info("encode create stream request packet success."); - - return ret; + protocol->set_send_timeout(timeout_us); } -SrsCallResPacket::SrsCallResPacket(double _transaction_id) +int64_t SrsRtmpClient::get_recv_bytes() { - command_name = RTMP_AMF0_COMMAND_RESULT; - transaction_id = _transaction_id; - command_object = NULL; - response = NULL; + return protocol->get_recv_bytes(); } -SrsCallResPacket::~SrsCallResPacket() +int64_t SrsRtmpClient::get_send_bytes() { - srs_freep(command_object); - srs_freep(response); + return protocol->get_send_bytes(); } -int SrsCallResPacket::get_prefer_cid() +int SrsRtmpClient::recv_message(SrsCommonMessage** pmsg) { - return RTMP_CID_OverConnection; + return protocol->recv_message(pmsg); } -int SrsCallResPacket::get_message_type() +int SrsRtmpClient::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket) { - return RTMP_MSG_AMF0CommandMessage; + return protocol->decode_message(msg, ppacket); } -int SrsCallResPacket::get_size() +int SrsRtmpClient::send_and_free_message(SrsSharedPtrMessage* msg, int stream_id) { - int size = 0; + return protocol->send_and_free_message(msg, stream_id); +} + +int SrsRtmpClient::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id) +{ + return protocol->send_and_free_messages(msgs, nb_msgs, stream_id); +} + +int SrsRtmpClient::send_and_free_packet(SrsPacket* packet, int stream_id) +{ + return protocol->send_and_free_packet(packet, stream_id); +} + +int SrsRtmpClient::handshake() +{ + int ret = ERROR_SUCCESS; - size += SrsAmf0Size::str(command_name) + SrsAmf0Size::number(); + srs_assert(hs_bytes); - if (command_object) { - size += command_object->total_size(); - } + // maybe st has problem when alloc object on stack, always alloc object at heap. + // @see https://github.com/ossrs/srs/issues/509 + SrsComplexHandshake* complex_hs = new SrsComplexHandshake(); + SrsAutoFree(SrsComplexHandshake, complex_hs); - if (response) { - size += response->total_size(); + if ((ret = complex_hs->handshake_with_server(hs_bytes, io)) != ERROR_SUCCESS) { + if (ret == ERROR_RTMP_TRY_SIMPLE_HS) { + // always alloc object at heap. + // @see https://github.com/ossrs/srs/issues/509 + SrsSimpleHandshake* simple_hs = new SrsSimpleHandshake(); + SrsAutoFree(SrsSimpleHandshake, simple_hs); + + if ((ret = simple_hs->handshake_with_server(hs_bytes, io)) != ERROR_SUCCESS) { + return ret; + } + } + return ret; } - return size; + srs_freep(hs_bytes); + + return ret; } -int SrsCallResPacket::encode_packet(SrsStream* stream) +int SrsRtmpClient::simple_handshake() { 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 (command_object && (ret = command_object->write(stream)) != ERROR_SUCCESS) { - srs_error("encode command_object failed. ret=%d", ret); - return ret; - } - srs_verbose("encode command_object success."); + srs_assert(hs_bytes); - if (response && (ret = response->write(stream)) != ERROR_SUCCESS) { - srs_error("encode response failed. ret=%d", ret); + SrsSimpleHandshake simple_hs; + if ((ret = simple_hs.handshake_with_server(hs_bytes, io)) != ERROR_SUCCESS) { return ret; } - srs_verbose("encode response success."); - - srs_info("encode call response packet success."); + srs_freep(hs_bytes); return ret; } -SrsCreateStreamPacket::SrsCreateStreamPacket() -{ - command_name = RTMP_AMF0_COMMAND_CREATE_STREAM; - transaction_id = 2; - command_object = SrsAmf0Any::null(); -} - -SrsCreateStreamPacket::~SrsCreateStreamPacket() -{ - srs_freep(command_object); -} - -int SrsCreateStreamPacket::decode(SrsStream* stream) +int SrsRtmpClient::complex_handshake() { 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; - } + srs_assert(hs_bytes); - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode createStream command_object failed. ret=%d", ret); + SrsComplexHandshake complex_hs; + if ((ret = complex_hs.handshake_with_server(hs_bytes, io)) != ERROR_SUCCESS) { return ret; } - srs_info("amf0 decode createStream packet success"); + srs_freep(hs_bytes); return ret; } -int SrsCreateStreamPacket::get_prefer_cid() -{ - return RTMP_CID_OverConnection; -} - -int SrsCreateStreamPacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsCreateStreamPacket::get_size() +int SrsRtmpClient::connect_app(string app, string tc_url, SrsRequest* req, bool debug_srs_upnode) { - return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() - + SrsAmf0Size::null(); + std::string srs_server_ip; + std::string srs_server; + std::string srs_primary; + std::string srs_authors; + std::string srs_version; + int srs_id = 0; + int srs_pid = 0; + + return connect_app2(app, tc_url, req, debug_srs_upnode, + srs_server_ip, srs_server, srs_primary, srs_authors, + srs_version, srs_id, srs_pid); } -int SrsCreateStreamPacket::encode_packet(SrsStream* stream) -{ +int SrsRtmpClient::connect_app2( + string app, string tc_url, SrsRequest* req, bool debug_srs_upnode, + string& srs_server_ip, string& srs_server, string& srs_primary, + string& srs_authors, string& srs_version, int& srs_id, + int& srs_pid +){ 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; + // Connect(vhost, app) + if (true) { + SrsConnectAppPacket* pkt = new SrsConnectAppPacket(); + + pkt->command_object->set("app", SrsAmf0Any::str(app.c_str())); + pkt->command_object->set("flashVer", SrsAmf0Any::str("WIN 15,0,0,239")); + if (req) { + pkt->command_object->set("swfUrl", SrsAmf0Any::str(req->swfUrl.c_str())); + } else { + pkt->command_object->set("swfUrl", SrsAmf0Any::str()); + } + if (req && req->tcUrl != "") { + pkt->command_object->set("tcUrl", SrsAmf0Any::str(req->tcUrl.c_str())); + } else { + pkt->command_object->set("tcUrl", SrsAmf0Any::str(tc_url.c_str())); + } + pkt->command_object->set("fpad", SrsAmf0Any::boolean(false)); + pkt->command_object->set("capabilities", SrsAmf0Any::number(239)); + pkt->command_object->set("audioCodecs", SrsAmf0Any::number(3575)); + pkt->command_object->set("videoCodecs", SrsAmf0Any::number(252)); + pkt->command_object->set("videoFunction", SrsAmf0Any::number(1)); + if (req) { + pkt->command_object->set("pageUrl", SrsAmf0Any::str(req->pageUrl.c_str())); + } else { + pkt->command_object->set("pageUrl", SrsAmf0Any::str()); + } + pkt->command_object->set("objectEncoding", SrsAmf0Any::number(0)); + + // @see https://github.com/ossrs/srs/issues/160 + // the debug_srs_upnode is config in vhost and default to true. + if (debug_srs_upnode && req && req->args) { + srs_freep(pkt->args); + pkt->args = req->args->copy()->to_object(); + } + + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + 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; + // Set Window Acknowledgement size(2500000) + if (true) { + SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); + pkt->ackowledgement_window_size = 2500000; + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + 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); + // expect connect _result + SrsCommonMessage* msg = NULL; + SrsConnectAppResPacket* pkt = NULL; + if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect connect app response message failed. ret=%d", ret); return ret; } - srs_verbose("encode command_object success."); + SrsAutoFree(SrsCommonMessage, msg); + SrsAutoFree(SrsConnectAppResPacket, pkt); - srs_info("encode create stream request packet success."); + // server info + SrsAmf0Any* data = pkt->info->get_property("data"); + if (data && data->is_ecma_array()) { + SrsAmf0EcmaArray* arr = data->to_ecma_array(); + + SrsAmf0Any* prop = NULL; + if ((prop = arr->ensure_property_string("srs_primary")) != NULL) { + srs_primary = prop->to_str(); + } + if ((prop = arr->ensure_property_string("srs_authors")) != NULL) { + srs_authors = prop->to_str(); + } + if ((prop = arr->ensure_property_string("srs_version")) != NULL) { + srs_version = prop->to_str(); + } + if ((prop = arr->ensure_property_string("srs_server_ip")) != NULL) { + srs_server_ip = prop->to_str(); + } + if ((prop = arr->ensure_property_string("srs_server")) != NULL) { + srs_server = prop->to_str(); + } + if ((prop = arr->ensure_property_number("srs_id")) != NULL) { + srs_id = (int)prop->to_number(); + } + if ((prop = arr->ensure_property_number("srs_pid")) != NULL) { + srs_pid = (int)prop->to_number(); + } + } + srs_trace("connected, version=%s, ip=%s, pid=%d, id=%d, dsu=%d", + srs_version.c_str(), srs_server_ip.c_str(), srs_pid, srs_id, debug_srs_upnode); return ret; } -SrsCreateStreamResPacket::SrsCreateStreamResPacket(double _transaction_id, double _stream_id) -{ - command_name = RTMP_AMF0_COMMAND_RESULT; - transaction_id = _transaction_id; - command_object = SrsAmf0Any::null(); - stream_id = _stream_id; -} - -SrsCreateStreamResPacket::~SrsCreateStreamResPacket() +int SrsRtmpClient::create_stream(int& stream_id) { - srs_freep(command_object); + int ret = ERROR_SUCCESS; + + // CreateStream + if (true) { + SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket(); + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + return ret; + } + } + + // CreateStream _result. + if (true) { + SrsCommonMessage* msg = NULL; + SrsCreateStreamResPacket* pkt = NULL; + if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect create stream response message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg); + SrsAutoFree(SrsCreateStreamResPacket, pkt); + srs_info("get create stream response message"); + + stream_id = (int)pkt->stream_id; + } + + return ret; } -int SrsCreateStreamResPacket::decode(SrsStream* stream) +int SrsRtmpClient::play(string stream, int stream_id) { 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_RESULT) { - 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; + // Play(stream) + if (true) { + SrsPlayPacket* pkt = new SrsPlayPacket(); + pkt->stream_name = stream; + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { + srs_error("send play stream failed. " + "stream=%s, stream_id=%d, ret=%d", + stream.c_str(), stream_id, 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; + // SetBufferLength(1000ms) + int buffer_length_ms = 1000; + if (true) { + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCSetBufferLength; + pkt->event_data = stream_id; + pkt->extra_data = buffer_length_ms; + + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send set buffer length failed. " + "stream=%s, stream_id=%d, bufferLength=%d, ret=%d", + stream.c_str(), stream_id, buffer_length_ms, ret); + return ret; + } } - if ((ret = srs_amf0_read_number(stream, stream_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode createStream stream_id failed. ret=%d", ret); - return ret; + // SetChunkSize + if (true) { + SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); + pkt->chunk_size = SRS_CONSTS_RTMP_SRS_CHUNK_SIZE; + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send set chunk size failed. " + "stream=%s, chunk_size=%d, ret=%d", + stream.c_str(), SRS_CONSTS_RTMP_SRS_CHUNK_SIZE, ret); + return ret; + } } - srs_info("amf0 decode createStream response packet success"); - return ret; } -int SrsCreateStreamResPacket::get_prefer_cid() -{ - return RTMP_CID_OverConnection; -} - -int SrsCreateStreamResPacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsCreateStreamResPacket::get_size() +int SrsRtmpClient::publish(string stream, int stream_id) { - return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() - + SrsAmf0Size::null() + SrsAmf0Size::number(); + int ret = ERROR_SUCCESS; + + // SetChunkSize + if (true) { + SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); + pkt->chunk_size = SRS_CONSTS_RTMP_SRS_CHUNK_SIZE; + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send set chunk size failed. " + "stream=%s, chunk_size=%d, ret=%d", + stream.c_str(), SRS_CONSTS_RTMP_SRS_CHUNK_SIZE, ret); + return ret; + } + } + + // publish(stream) + if (true) { + SrsPublishPacket* pkt = new SrsPublishPacket(); + pkt->stream_name = stream; + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { + srs_error("send publish message failed. " + "stream=%s, stream_id=%d, ret=%d", + stream.c_str(), stream_id, ret); + return ret; + } + } + + return ret; } -int SrsCreateStreamResPacket::encode_packet(SrsStream* stream) +int SrsRtmpClient::fmle_publish(string stream, int& stream_id) { + stream_id = 0; + 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; + // SrsFMLEStartPacket + if (true) { + SrsFMLEStartPacket* pkt = SrsFMLEStartPacket::create_release_stream(stream); + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send FMLE publish " + "release stream failed. stream=%s, ret=%d", stream.c_str(), 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; + // FCPublish + if (true) { + SrsFMLEStartPacket* pkt = SrsFMLEStartPacket::create_FC_publish(stream); + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send FMLE publish " + "FCPublish failed. stream=%s, ret=%d", stream.c_str(), 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; + // CreateStream + if (true) { + SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket(); + pkt->transaction_id = 4; + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send FMLE publish " + "createStream failed. stream=%s, ret=%d", stream.c_str(), 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; + // expect result of CreateStream + if (true) { + SrsCommonMessage* msg = NULL; + SrsCreateStreamResPacket* pkt = NULL; + if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect create stream response message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg); + SrsAutoFree(SrsCreateStreamResPacket, pkt); + srs_info("get create stream response message"); + + stream_id = (int)pkt->stream_id; } - srs_verbose("encode stream_id success."); - - srs_info("encode createStream response packet success."); + // publish(stream) + if (true) { + SrsPublishPacket* pkt = new SrsPublishPacket(); + pkt->stream_name = stream; + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { + srs_error("send FMLE publish publish failed. " + "stream=%s, stream_id=%d, ret=%d", stream.c_str(), stream_id, ret); + return ret; + } + } return ret; } -SrsCloseStreamPacket::SrsCloseStreamPacket() +SrsRtmpServer::SrsRtmpServer(ISrsProtocolReaderWriter* skt) { - command_name = RTMP_AMF0_COMMAND_CLOSE_STREAM; - transaction_id = 0; - command_object = SrsAmf0Any::null(); + io = skt; + protocol = new SrsProtocol(skt); + hs_bytes = new SrsHandshakeBytes(); } -SrsCloseStreamPacket::~SrsCloseStreamPacket() +SrsRtmpServer::~SrsRtmpServer() { - srs_freep(command_object); + srs_freep(protocol); + srs_freep(hs_bytes); } -int SrsCloseStreamPacket::decode(SrsStream* stream) +void SrsRtmpServer::set_auto_response(bool v) { - int ret = ERROR_SUCCESS; + protocol->set_auto_response(v); +} - if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode closeStream command_name failed. ret=%d", ret); - return ret; - } +#ifdef SRS_PERF_MERGED_READ +void SrsRtmpServer::set_merge_read(bool v, IMergeReadHandler* handler) +{ + protocol->set_merge_read(v, handler); +} - if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode closeStream transaction_id failed. ret=%d", ret); - return ret; - } +void SrsRtmpServer::set_recv_buffer(int buffer_size) +{ + protocol->set_recv_buffer(buffer_size); +} +#endif - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode closeStream command_object failed. ret=%d", ret); - return ret; - } - srs_info("amf0 decode closeStream packet success"); +void SrsRtmpServer::set_recv_timeout(int64_t timeout_us) +{ + protocol->set_recv_timeout(timeout_us); +} - return ret; +int64_t SrsRtmpServer::get_recv_timeout() +{ + return protocol->get_recv_timeout(); } -SrsFMLEStartPacket::SrsFMLEStartPacket() +void SrsRtmpServer::set_send_timeout(int64_t timeout_us) { - command_name = RTMP_AMF0_COMMAND_RELEASE_STREAM; - transaction_id = 0; - command_object = SrsAmf0Any::null(); + protocol->set_send_timeout(timeout_us); } -SrsFMLEStartPacket::~SrsFMLEStartPacket() +int64_t SrsRtmpServer::get_send_timeout() { - srs_freep(command_object); + return protocol->get_send_timeout(); } -int SrsFMLEStartPacket::decode(SrsStream* stream) +int64_t SrsRtmpServer::get_recv_bytes() { - int ret = ERROR_SUCCESS; + return protocol->get_recv_bytes(); +} - 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; +int64_t SrsRtmpServer::get_send_bytes() +{ + return protocol->get_send_bytes(); } -int SrsFMLEStartPacket::get_prefer_cid() +int SrsRtmpServer::recv_message(SrsCommonMessage** pmsg) { - return RTMP_CID_OverConnection; + return protocol->recv_message(pmsg); } -int SrsFMLEStartPacket::get_message_type() +int SrsRtmpServer::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket) { - return RTMP_MSG_AMF0CommandMessage; + return protocol->decode_message(msg, ppacket); } -int SrsFMLEStartPacket::get_size() +int SrsRtmpServer::send_and_free_message(SrsSharedPtrMessage* msg, int stream_id) { - return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() - + SrsAmf0Size::null() + SrsAmf0Size::str(stream_name); + return protocol->send_and_free_message(msg, stream_id); } -int SrsFMLEStartPacket::encode_packet(SrsStream* stream) +int SrsRtmpServer::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id) +{ + return protocol->send_and_free_messages(msgs, nb_msgs, stream_id); +} + +int SrsRtmpServer::send_and_free_packet(SrsPacket* packet, int stream_id) +{ + return protocol->send_and_free_packet(packet, stream_id); +} + +int SrsRtmpServer::handshake() { 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."); + srs_assert(hs_bytes); - if ((ret = srs_amf0_write_string(stream, stream_name)) != ERROR_SUCCESS) { - srs_error("encode stream_name failed. ret=%d", ret); + SrsComplexHandshake complex_hs; + if ((ret = complex_hs.handshake_with_client(hs_bytes, io)) != ERROR_SUCCESS) { + if (ret == ERROR_RTMP_TRY_SIMPLE_HS) { + SrsSimpleHandshake simple_hs; + if ((ret = simple_hs.handshake_with_client(hs_bytes, io)) != ERROR_SUCCESS) { + return ret; + } + } return ret; } - srs_verbose("encode stream_name success."); - - srs_info("encode FMLE start response packet success."); + srs_freep(hs_bytes); return ret; } -SrsFMLEStartPacket* SrsFMLEStartPacket::create_release_stream(string stream) +int SrsRtmpServer::connect_app(SrsRequest* req) { - SrsFMLEStartPacket* pkt = new SrsFMLEStartPacket(); - - pkt->command_name = RTMP_AMF0_COMMAND_RELEASE_STREAM; - pkt->transaction_id = 2; - pkt->stream_name = stream; + int ret = ERROR_SUCCESS; - return pkt; -} - -SrsFMLEStartPacket* SrsFMLEStartPacket::create_FC_publish(string stream) -{ - SrsFMLEStartPacket* pkt = new SrsFMLEStartPacket(); + SrsCommonMessage* msg = NULL; + SrsConnectAppPacket* pkt = NULL; + if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { + srs_error("expect connect app message failed. ret=%d", ret); + return ret; + } + SrsAutoFree(SrsCommonMessage, msg); + SrsAutoFree(SrsConnectAppPacket, pkt); + srs_info("get connect app message"); - pkt->command_name = RTMP_AMF0_COMMAND_FC_PUBLISH; - pkt->transaction_id = 3; - pkt->stream_name = stream; + SrsAmf0Any* prop = NULL; - return pkt; -} - -SrsFMLEStartResPacket::SrsFMLEStartResPacket(double _transaction_id) -{ - command_name = RTMP_AMF0_COMMAND_RESULT; - transaction_id = _transaction_id; - command_object = SrsAmf0Any::null(); - args = SrsAmf0Any::undefined(); -} - -SrsFMLEStartResPacket::~SrsFMLEStartResPacket() -{ - srs_freep(command_object); - srs_freep(args); -} - -int SrsFMLEStartResPacket::decode(SrsStream* stream) -{ - int ret = ERROR_SUCCESS; - - if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode FMLE start response command_name failed. ret=%d", ret); + 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; } - if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_RESULT) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 decode FMLE start response command_name failed. " - "command_name=%s, ret=%d", command_name.c_str(), ret); - return ret; + req->tcUrl = prop->to_str(); + + if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) { + req->pageUrl = prop->to_str(); } - if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode FMLE start response transaction_id failed. ret=%d", ret); - return ret; + if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) { + req->swfUrl = prop->to_str(); } - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode FMLE start response command_object failed. ret=%d", ret); - return ret; + if ((prop = pkt->command_object->ensure_property_number("objectEncoding")) != NULL) { + req->objectEncoding = prop->to_number(); } - if ((ret = srs_amf0_read_undefined(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode FMLE start response stream_id failed. ret=%d", ret); - return ret; + if (pkt->args) { + srs_freep(req->args); + req->args = pkt->args->copy()->to_object(); + srs_info("copy edge traverse to origin auth args."); } - srs_info("amf0 decode FMLE start packet success"); + srs_info("get connect app message params success."); + + srs_discovery_tc_url(req->tcUrl, + req->schema, req->host, req->vhost, req->app, req->port, + req->param); + req->strip(); return ret; } -int SrsFMLEStartResPacket::get_prefer_cid() -{ - return RTMP_CID_OverConnection; -} - -int SrsFMLEStartResPacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsFMLEStartResPacket::get_size() -{ - return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() - + SrsAmf0Size::null() + SrsAmf0Size::undefined(); -} - -int SrsFMLEStartResPacket::encode_packet(SrsStream* stream) +int SrsRtmpServer::set_window_ack_size(int ack_size) { 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); + SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); + pkt->ackowledgement_window_size = ack_size; + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send ack size message failed. ret=%d", ret); return ret; } - srs_verbose("encode transaction_id success."); + srs_info("send ack size message success. ack_size=%d", ack_size); - 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."); + return ret; +} + +int SrsRtmpServer::set_peer_bandwidth(int bandwidth, int type) +{ + int ret = ERROR_SUCCESS; - if ((ret = srs_amf0_write_undefined(stream)) != ERROR_SUCCESS) { - srs_error("encode args failed. ret=%d", ret); + SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket(); + pkt->bandwidth = bandwidth; + pkt->type = type; + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send set bandwidth message failed. ret=%d", ret); return ret; } - srs_verbose("encode args success."); - - - srs_info("encode FMLE start response packet success."); + srs_info("send set bandwidth message " + "success. bandwidth=%d, type=%d", bandwidth, type); return ret; } -SrsPublishPacket::SrsPublishPacket() -{ - command_name = RTMP_AMF0_COMMAND_PUBLISH; - transaction_id = 0; - command_object = SrsAmf0Any::null(); - type = "live"; -} - -SrsPublishPacket::~SrsPublishPacket() -{ - srs_freep(command_object); -} - -int SrsPublishPacket::decode(SrsStream* stream) +int SrsRtmpServer::response_connect_app(SrsRequest *req, const char* server_ip) { 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; - } + SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode publish command_object failed. ret=%d", ret); - return ret; - } + pkt->props->set("fmsVer", SrsAmf0Any::str("FMS/"RTMP_SIG_FMS_VER)); + pkt->props->set("capabilities", SrsAmf0Any::number(127)); + pkt->props->set("mode", SrsAmf0Any::number(1)); - if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode publish stream_name failed. ret=%d", ret); - return ret; + pkt->info->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); + pkt->info->set(StatusCode, SrsAmf0Any::str(StatusCodeConnectSuccess)); + pkt->info->set(StatusDescription, SrsAmf0Any::str("Connection succeeded")); + pkt->info->set("objectEncoding", SrsAmf0Any::number(req->objectEncoding)); + SrsAmf0EcmaArray* data = SrsAmf0Any::ecma_array(); + pkt->info->set("data", data); + + data->set("version", SrsAmf0Any::str(RTMP_SIG_FMS_VER)); + data->set("srs_sig", SrsAmf0Any::str(RTMP_SIG_SRS_KEY)); + data->set("srs_server", SrsAmf0Any::str(RTMP_SIG_SRS_SERVER)); + data->set("srs_license", SrsAmf0Any::str(RTMP_SIG_SRS_LICENSE)); + data->set("srs_role", SrsAmf0Any::str(RTMP_SIG_SRS_ROLE)); + data->set("srs_url", SrsAmf0Any::str(RTMP_SIG_SRS_URL)); + data->set("srs_version", SrsAmf0Any::str(RTMP_SIG_SRS_VERSION)); + data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB)); + data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL)); + data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT)); + data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); + data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); + + if (server_ip) { + data->set("srs_server_ip", SrsAmf0Any::str(server_ip)); } + // for edge to directly get the id of client. + data->set("srs_pid", SrsAmf0Any::number(getpid())); + data->set("srs_id", SrsAmf0Any::number(_srs_context->get_id())); - if (!stream->empty() && (ret = srs_amf0_read_string(stream, type)) != ERROR_SUCCESS) { - srs_error("amf0 decode publish type failed. ret=%d", ret); + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send connect app response message failed. ret=%d", ret); return ret; } - - srs_info("amf0 decode publish packet success"); + srs_info("send connect app response message success."); return ret; } -int SrsPublishPacket::get_prefer_cid() -{ - return RTMP_CID_OverStream; -} - -int SrsPublishPacket::get_message_type() +void SrsRtmpServer::response_connect_reject(SrsRequest* /*req*/, const char* desc) { - return RTMP_MSG_AMF0CommandMessage; -} + int ret = ERROR_SUCCESS; + + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelError)); + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeConnectRejected)); + pkt->data->set(StatusDescription, SrsAmf0Any::str(desc)); + + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send connect app response rejected message failed. ret=%d", ret); + return; + } + srs_info("send connect app response rejected message success."); -int SrsPublishPacket::get_size() -{ - return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() - + SrsAmf0Size::null() + SrsAmf0Size::str(stream_name) - + SrsAmf0Size::str(type); + return; } -int SrsPublishPacket::encode_packet(SrsStream* stream) +int SrsRtmpServer::on_bw_done() { 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_string(stream, stream_name)) != ERROR_SUCCESS) { - srs_error("encode stream_name failed. ret=%d", ret); - return ret; - } - srs_verbose("encode stream_name success."); - - if ((ret = srs_amf0_write_string(stream, type)) != ERROR_SUCCESS) { - srs_error("encode type failed. ret=%d", ret); + SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket(); + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send onBWDone message failed. ret=%d", ret); return ret; } - srs_verbose("encode type success."); - - srs_info("encode play request packet success."); + srs_info("send onBWDone message success."); return ret; } -SrsPausePacket::SrsPausePacket() +int SrsRtmpServer::identify_client(int stream_id, SrsRtmpConnType& type, string& stream_name, double& duration) { - command_name = RTMP_AMF0_COMMAND_PAUSE; - transaction_id = 0; - command_object = SrsAmf0Any::null(); + type = SrsRtmpConnUnknown; + int ret = ERROR_SUCCESS; + + while (true) { + SrsCommonMessage* msg = NULL; + if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("recv identify client message failed. ret=%d", ret); + } + return ret; + } - time_ms = 0; - is_pause = true; + SrsAutoFree(SrsCommonMessage, msg); + SrsMessageHeader& h = msg->header; + + if (h.is_ackledgement() || h.is_set_chunk_size() || h.is_window_ackledgement_size() || h.is_user_control_message()) { + continue; + } + + if (!h.is_amf0_command() && !h.is_amf3_command()) { + srs_trace("identify ignore messages except " + "AMF0/AMF3 command message. type=%#x", h.message_type); + continue; + } + + SrsPacket* pkt = NULL; + if ((ret = protocol->decode_message(msg, &pkt)) != ERROR_SUCCESS) { + srs_error("identify decode message failed. ret=%d", ret); + return ret; + } + + SrsAutoFree(SrsPacket, pkt); + + 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, duration); + } + if (dynamic_cast(pkt)) { + srs_info("identify client by releaseStream, fmle publish."); + return identify_fmle_publish_client(dynamic_cast(pkt), type, stream_name); + } + if (dynamic_cast(pkt)) { + srs_info("level0 identify client by play."); + return identify_play_client(dynamic_cast(pkt), type, stream_name, duration); + } + // call msg, + // support response null first, + // @see https://github.com/ossrs/srs/issues/106 + // TODO: FIXME: response in right way, or forward in edge mode. + SrsCallPacket* call = dynamic_cast(pkt); + if (call) { + SrsCallResPacket* res = new SrsCallResPacket(call->transaction_id); + res->command_object = SrsAmf0Any::null(); + res->response = SrsAmf0Any::null(); + if ((ret = protocol->send_and_free_packet(res, 0)) != ERROR_SUCCESS) { + if (!srs_is_system_control_error(ret) && !srs_is_client_gracefully_close(ret)) { + srs_warn("response call failed. ret=%d", ret); + } + return ret; + } + continue; + } + + srs_trace("ignore AMF0/AMF3 command message."); + } + + return ret; } -SrsPausePacket::~SrsPausePacket() +int SrsRtmpServer::set_chunk_size(int chunk_size) { - srs_freep(command_object); + int ret = ERROR_SUCCESS; + + SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); + pkt->chunk_size = chunk_size; + if ((ret = protocol->send_and_free_packet(pkt, 0)) != 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 SrsPausePacket::decode(SrsStream* stream) +int SrsRtmpServer::start_play(int stream_id) { 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; + + // StreamBegin + if (true) { + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + pkt->event_type = SrcPCUCStreamBegin; + pkt->event_data = stream_id; + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send PCUC(StreamBegin) message failed. ret=%d", ret); + return ret; + } + srs_info("send PCUC(StreamBegin) message success."); } - if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode pause transaction_id failed. ret=%d", ret); - return ret; + // onStatus(NetStream.Play.Reset) + if (true) { + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeStreamReset)); + pkt->data->set(StatusDescription, SrsAmf0Any::str("Playing and resetting stream.")); + pkt->data->set(StatusDetails, SrsAmf0Any::str("stream")); + pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID)); + + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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."); } - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode pause command_object failed. ret=%d", ret); - return ret; + // onStatus(NetStream.Play.Start) + if (true) { + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeStreamStart)); + pkt->data->set(StatusDescription, SrsAmf0Any::str("Started playing stream.")); + pkt->data->set(StatusDetails, SrsAmf0Any::str("stream")); + pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID)); + + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { + srs_error("send onStatus(NetStream.Play.Start) message failed. ret=%d", ret); + return ret; + } + srs_info("send onStatus(NetStream.Play.Start) message success."); } - if ((ret = srs_amf0_read_boolean(stream, is_pause)) != ERROR_SUCCESS) { - srs_error("amf0 decode pause is_pause failed. ret=%d", ret); - return ret; + // |RtmpSampleAccess(false, false) + if (true) { + SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket(); + + // allow audio/video sample. + // @see: https://github.com/ossrs/srs/issues/49 + pkt->audio_sample_access = true; + pkt->video_sample_access = true; + + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { + srs_error("send |RtmpSampleAccess(false, false) message failed. ret=%d", ret); + return ret; + } + srs_info("send |RtmpSampleAccess(false, false) message success."); } - if ((ret = srs_amf0_read_number(stream, time_ms)) != ERROR_SUCCESS) { - srs_error("amf0 decode pause time_ms failed. ret=%d", ret); - return ret; + // onStatus(NetStream.Data.Start) + if (true) { + SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket(); + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeDataStart)); + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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("amf0 decode pause packet success"); + srs_info("start play success."); return ret; } -SrsPlayPacket::SrsPlayPacket() -{ - command_name = RTMP_AMF0_COMMAND_PLAY; - transaction_id = 0; - command_object = SrsAmf0Any::null(); - - start = -2; - duration = -1; - reset = true; -} - -SrsPlayPacket::~SrsPlayPacket() +int SrsRtmpServer::on_play_client_pause(int stream_id, bool is_pause) { - srs_freep(command_object); + int ret = ERROR_SUCCESS; + + if (is_pause) { + // onStatus(NetStream.Pause.Notify) + if (true) { + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeStreamPause)); + pkt->data->set(StatusDescription, SrsAmf0Any::str("Paused stream.")); + + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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) { + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCStreamEOF; + pkt->event_data = stream_id; + + if ((ret = protocol->send_and_free_packet(pkt, 0)) != 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) { + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeStreamUnpause)); + pkt->data->set(StatusDescription, SrsAmf0Any::str("Unpaused stream.")); + + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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) { + SrsUserControlPacket* pkt = new SrsUserControlPacket(); + + pkt->event_type = SrcPCUCStreamBegin; + pkt->event_data = stream_id; + + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send PCUC(StreanBegin) message failed. ret=%d", ret); + return ret; + } + srs_info("send PCUC(StreanBegin) message success."); + } + } + + return ret; } -int SrsPlayPacket::decode(SrsStream* stream) +int SrsRtmpServer::start_fmle_publish(int stream_id) { 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; - } + // FCPublish + double fc_publish_tid = 0; + if (true) { + SrsCommonMessage* msg = NULL; + SrsFMLEStartPacket* pkt = NULL; + if ((ret = expect_message(&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); + SrsAutoFree(SrsFMLEStartPacket, pkt); - if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode play command_object failed. ret=%d", ret); - return ret; + fc_publish_tid = pkt->transaction_id; } - - if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode play stream_name failed. ret=%d", ret); - return ret; + // FCPublish response + if (true) { + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid); + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send FCPublish response message failed. ret=%d", ret); + return ret; + } + srs_info("send FCPublish response message success."); } - 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; + // createStream + double create_stream_tid = 0; + if (true) { + SrsCommonMessage* msg = NULL; + SrsCreateStreamPacket* pkt = NULL; + if ((ret = expect_message(&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); + SrsAutoFree(SrsCreateStreamPacket, pkt); + + create_stream_tid = pkt->transaction_id; } - - if (stream->empty()) { - return ret; + // createStream response + if (true) { + SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id); + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send createStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send createStream response message success."); } - SrsAmf0Any* reset_value = NULL; - if ((ret = srs_amf0_read_any(stream, &reset_value)) != ERROR_SUCCESS) { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 read play reset marker failed. ret=%d", ret); - return ret; + // publish + if (true) { + SrsCommonMessage* msg = NULL; + SrsPublishPacket* pkt = NULL; + if ((ret = expect_message(&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); + SrsAutoFree(SrsPublishPacket, pkt); } - SrsAutoFree(SrsAmf0Any, reset_value); - - if (reset_value) { - // check if the value is bool or number - // An optional Boolean value or number that specifies whether - // to flush any previous playlist - if (reset_value->is_boolean()) { - reset = reset_value->to_boolean(); - } else if (reset_value->is_number()) { - reset = (reset_value->to_number() != 0); - } else { - ret = ERROR_RTMP_AMF0_DECODE; - srs_error("amf0 invalid type=%#x, requires number or bool, ret=%d", reset_value->marker, ret); + // publish response onFCPublish(NetStream.Publish.Start) + if (true) { + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH; + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodePublishStart)); + pkt->data->set(StatusDescription, SrsAmf0Any::str("Started publishing stream.")); + + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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) { + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodePublishStart)); + pkt->data->set(StatusDescription, SrsAmf0Any::str("Started publishing stream.")); + pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID)); + + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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("amf0 decode play packet success"); + + srs_info("FMLE publish success."); return ret; } -int SrsPlayPacket::get_prefer_cid() -{ - return RTMP_CID_OverStream; -} - -int SrsPlayPacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsPlayPacket::get_size() -{ - return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() - + SrsAmf0Size::null() + SrsAmf0Size::str(stream_name) - + SrsAmf0Size::number() + SrsAmf0Size::number() - + SrsAmf0Size::boolean(); -} - -int SrsPlayPacket::encode_packet(SrsStream* stream) +int SrsRtmpServer::fmle_unpublish(int stream_id, double unpublish_tid) { 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_string(stream, stream_name)) != ERROR_SUCCESS) { - srs_error("encode stream_name failed. ret=%d", ret); - return ret; - } - srs_verbose("encode stream_name success."); - - if ((ret = srs_amf0_write_number(stream, start)) != ERROR_SUCCESS) { - srs_error("encode start failed. ret=%d", ret); - return ret; + // publish response onFCUnpublish(NetStream.unpublish.Success) + if (true) { + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH; + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeUnpublishSuccess)); + pkt->data->set(StatusDescription, SrsAmf0Any::str("Stop publishing stream.")); + + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { + if (!srs_is_system_control_error(ret) && !srs_is_client_gracefully_close(ret)) { + srs_error("send onFCUnpublish(NetStream.unpublish.Success) message failed. ret=%d", ret); + } + return ret; + } + srs_info("send onFCUnpublish(NetStream.unpublish.Success) message success."); } - srs_verbose("encode start success."); - - if ((ret = srs_amf0_write_number(stream, duration)) != ERROR_SUCCESS) { - srs_error("encode duration failed. ret=%d", ret); - return ret; + // FCUnpublish response + if (true) { + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(unpublish_tid); + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { + if (!srs_is_system_control_error(ret) && !srs_is_client_gracefully_close(ret)) { + srs_error("send FCUnpublish response message failed. ret=%d", ret); + } + return ret; + } + srs_info("send FCUnpublish response message success."); } - srs_verbose("encode duration success."); - - if ((ret = srs_amf0_write_boolean(stream, reset)) != ERROR_SUCCESS) { - srs_error("encode reset failed. ret=%d", ret); - return ret; + // publish response onStatus(NetStream.Unpublish.Success) + if (true) { + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeUnpublishSuccess)); + pkt->data->set(StatusDescription, SrsAmf0Any::str("Stream is now unpublished")); + pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID)); + + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { + if (!srs_is_system_control_error(ret) && !srs_is_client_gracefully_close(ret)) { + srs_error("send onStatus(NetStream.Unpublish.Success) message failed. ret=%d", ret); + } + return ret; + } + srs_info("send onStatus(NetStream.Unpublish.Success) message success."); } - srs_verbose("encode reset success."); - srs_info("encode play request packet success."); + srs_info("FMLE unpublish success."); return ret; } -SrsPlayResPacket::SrsPlayResPacket() -{ - command_name = RTMP_AMF0_COMMAND_RESULT; - transaction_id = 0; - command_object = SrsAmf0Any::null(); - desc = SrsAmf0Any::object(); -} - -SrsPlayResPacket::~SrsPlayResPacket() -{ - srs_freep(command_object); - srs_freep(desc); -} - -int SrsPlayResPacket::get_prefer_cid() -{ - return RTMP_CID_OverStream; -} - -int SrsPlayResPacket::get_message_type() -{ - return RTMP_MSG_AMF0CommandMessage; -} - -int SrsPlayResPacket::get_size() +int SrsRtmpServer::start_flash_publish(int stream_id) { - return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() - + SrsAmf0Size::null() + SrsAmf0Size::object(desc); + int ret = ERROR_SUCCESS; + + // publish response onStatus(NetStream.Publish.Start) + if (true) { + SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); + + pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); + pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodePublishStart)); + pkt->data->set(StatusDescription, SrsAmf0Any::str("Started publishing stream.")); + pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID)); + + if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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 SrsPlayResPacket::encode_packet(SrsStream* stream) +int SrsRtmpServer::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsRtmpConnType& type, string& stream_name, double& duration) { 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; + if (true) { + SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id); + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send createStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send createStream response message success."); } - 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."); + while (true) { + SrsCommonMessage* msg = NULL; + if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { + if (!srs_is_client_gracefully_close(ret)) { + srs_error("recv identify client message failed. ret=%d", ret); + } + return ret; + } + + SrsAutoFree(SrsCommonMessage, msg); + SrsMessageHeader& h = msg->header; + + if (h.is_ackledgement() || h.is_set_chunk_size() || h.is_window_ackledgement_size() || h.is_user_control_message()) { + continue; + } - if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { - srs_error("encode command_object failed. ret=%d", ret); - return ret; + if (!h.is_amf0_command() && !h.is_amf3_command()) { + srs_trace("identify ignore messages except " + "AMF0/AMF3 command message. type=%#x", h.message_type); + continue; + } + + SrsPacket* pkt = NULL; + if ((ret = protocol->decode_message(msg, &pkt)) != ERROR_SUCCESS) { + srs_error("identify decode message failed. ret=%d", ret); + return ret; + } + + SrsAutoFree(SrsPacket, pkt); + + if (dynamic_cast(pkt)) { + srs_info("level1 identify client by play."); + return identify_play_client(dynamic_cast(pkt), type, stream_name, duration); + } + if (dynamic_cast(pkt)) { + srs_info("identify client by publish, falsh publish."); + return identify_flash_publish_client(dynamic_cast(pkt), type, stream_name); + } + 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, duration); + } + + srs_trace("ignore AMF0/AMF3 command message."); } - srs_verbose("encode command_object success."); - if ((ret = desc->write(stream)) != ERROR_SUCCESS) { - srs_error("encode desc failed. ret=%d", ret); - return ret; - } - srs_verbose("encode desc success."); + return ret; +} + +int SrsRtmpServer::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsRtmpConnType& type, string& stream_name) +{ + int ret = ERROR_SUCCESS; + type = SrsRtmpConnFMLEPublish; + stream_name = req->stream_name; - srs_info("encode play response packet success."); + // releaseStream response + if (true) { + SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id); + if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { + srs_error("send releaseStream response message failed. ret=%d", ret); + return ret; + } + srs_info("send releaseStream response message success."); + } return ret; } -SrsOnBWDonePacket::SrsOnBWDonePacket() +int SrsRtmpServer::identify_flash_publish_client(SrsPublishPacket* req, SrsRtmpConnType& type, string& stream_name) { - command_name = RTMP_AMF0_COMMAND_ON_BW_DONE; - transaction_id = 0; - args = SrsAmf0Any::null(); + int ret = ERROR_SUCCESS; + + type = SrsRtmpConnFlashPublish; + stream_name = req->stream_name; + + return ret; } -SrsOnBWDonePacket::~SrsOnBWDonePacket() +int SrsRtmpServer::identify_play_client(SrsPlayPacket* req, SrsRtmpConnType& type, string& stream_name, double& duration) { - srs_freep(args); -} + int ret = ERROR_SUCCESS; + + type = SrsRtmpConnPlay; + stream_name = req->stream_name; + duration = req->duration; + + srs_info("identity client type=play, stream_name=%s, duration=%.2f", stream_name.c_str(), duration); -int SrsOnBWDonePacket::get_prefer_cid() -{ - return RTMP_CID_OverConnection; + return ret; } -int SrsOnBWDonePacket::get_message_type() +SrsConnectAppPacket::SrsConnectAppPacket() { - return RTMP_MSG_AMF0CommandMessage; + command_name = RTMP_AMF0_COMMAND_CONNECT; + transaction_id = 1; + command_object = SrsAmf0Any::object(); + // optional + args = NULL; } -int SrsOnBWDonePacket::get_size() +SrsConnectAppPacket::~SrsConnectAppPacket() { - return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() - + SrsAmf0Size::null(); + srs_freep(command_object); + srs_freep(args); } -int SrsOnBWDonePacket::encode_packet(SrsStream* stream) +int SrsConnectAppPacket::decode(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); + + 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; } - 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); + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode connect 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); + // some client donot send id=1.0, so we only warn user if not match. + if (transaction_id != 1.0) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_warn("amf0 decode connect transaction_id failed. " + "required=%.1f, actual=%.1f, ret=%d", 1.0, transaction_id, ret); + ret = ERROR_SUCCESS; + } + + if ((ret = command_object->read(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode connect command_object failed. ret=%d", ret); return ret; } - srs_verbose("encode args success."); - srs_info("encode onBWDone packet success."); + if (!stream->empty()) { + srs_freep(args); + + // see: https://github.com/ossrs/srs/issues/186 + // the args maybe any amf0, for instance, a string. we should drop if not object. + SrsAmf0Any* any = NULL; + if ((ret = SrsAmf0Any::discovery(stream, &any)) != ERROR_SUCCESS) { + srs_error("amf0 find connect args failed. ret=%d", ret); + return ret; + } + srs_assert(any); + + // read the instance + if ((ret = any->read(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode connect args failed. ret=%d", ret); + srs_freep(any); + return ret; + } + + // drop when not an AMF0 object. + if (!any->is_object()) { + srs_warn("drop the args, see: '4.1.1. connect', marker=%#x", any->marker); + srs_freep(any); + } else { + args = any->to_object(); + } + } + + srs_info("amf0 decode connect packet success"); return ret; } -SrsOnStatusCallPacket::SrsOnStatusCallPacket() -{ - command_name = RTMP_AMF0_COMMAND_ON_STATUS; - transaction_id = 0; - args = SrsAmf0Any::null(); - data = SrsAmf0Any::object(); -} - -SrsOnStatusCallPacket::~SrsOnStatusCallPacket() -{ - srs_freep(args); - srs_freep(data); -} - -int SrsOnStatusCallPacket::get_prefer_cid() +int SrsConnectAppPacket::get_prefer_cid() { - return RTMP_CID_OverStream; + return RTMP_CID_OverConnection; } -int SrsOnStatusCallPacket::get_message_type() +int SrsConnectAppPacket::get_message_type() { return RTMP_MSG_AMF0CommandMessage; } -int SrsOnStatusCallPacket::get_size() +int SrsConnectAppPacket::get_size() { - return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() - + SrsAmf0Size::null() + SrsAmf0Size::object(data); + int size = 0; + + size += SrsAmf0Size::str(command_name); + size += SrsAmf0Size::number(); + size += SrsAmf0Size::object(command_object); + if (args) { + size += SrsAmf0Size::object(args); + } + + return size; } -int SrsOnStatusCallPacket::encode_packet(SrsStream* stream) +int SrsConnectAppPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; @@ -24403,87 +25092,112 @@ int SrsOnStatusCallPacket::encode_packet(SrsStream* stream) } srs_verbose("encode transaction_id success."); - if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { - srs_error("encode args failed. ret=%d", ret); + if ((ret = command_object->write(stream)) != ERROR_SUCCESS) { + srs_error("encode command_object failed. ret=%d", ret); return ret; } - srs_verbose("encode args success.");; + srs_verbose("encode command_object success."); - if ((ret = data->write(stream)) != ERROR_SUCCESS) { - srs_error("encode data failed. ret=%d", ret); + if (args && (ret = args->write(stream)) != ERROR_SUCCESS) { + srs_error("encode args failed. ret=%d", ret); return ret; } - srs_verbose("encode data success."); + srs_verbose("encode args success."); - srs_info("encode onStatus(Call) packet success."); + srs_info("encode connect app request packet success."); return ret; } -SrsBandwidthPacket::SrsBandwidthPacket() +SrsConnectAppResPacket::SrsConnectAppResPacket() { - command_name = RTMP_AMF0_COMMAND_ON_STATUS; - transaction_id = 0; - args = SrsAmf0Any::null(); - data = SrsAmf0Any::object(); + command_name = RTMP_AMF0_COMMAND_RESULT; + transaction_id = 1; + props = SrsAmf0Any::object(); + info = SrsAmf0Any::object(); } -SrsBandwidthPacket::~SrsBandwidthPacket() +SrsConnectAppResPacket::~SrsConnectAppResPacket() { - srs_freep(args); - srs_freep(data); + srs_freep(props); + srs_freep(info); } -int SrsBandwidthPacket::decode(SrsStream *stream) +int SrsConnectAppResPacket::decode(SrsStream* stream) { int ret = ERROR_SUCCESS; if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { - srs_error("amf0 decode bwtc command_name failed. ret=%d", ret); + srs_error("amf0 decode connect command_name failed. ret=%d", ret); return ret; } - - if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { - srs_error("amf0 decode bwtc transaction_id failed. ret=%d", ret); + if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_RESULT) { + 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_null(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode bwtc command_object failed. ret=%d", 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; } - // @remark, for bandwidth test, ignore the data field. - // only decode the stop-play, start-publish and finish packet. - if (is_stop_play() || is_start_publish() || is_finish()) { - if ((ret = data->read(stream)) != ERROR_SUCCESS) { - srs_error("amf0 decode bwtc command_object failed. ret=%d", ret); + // some client donot send id=1.0, so we only warn user if not match. + if (transaction_id != 1.0) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_warn("amf0 decode connect transaction_id failed. " + "required=%.1f, actual=%.1f, ret=%d", 1.0, transaction_id, ret); + ret = ERROR_SUCCESS; + } + + // for RED5(1.0.6), the props is NULL, we must ignore it. + // @see https://github.com/ossrs/srs/issues/418 + if (!stream->empty()) { + SrsAmf0Any* p = NULL; + if ((ret = srs_amf0_read_any(stream, &p)) != ERROR_SUCCESS) { + srs_error("amf0 decode connect props failed. ret=%d", ret); return ret; } + + // ignore when props is not amf0 object. + if (!p->is_object()) { + srs_warn("ignore connect response props marker=%#x.", (u_int8_t)p->marker); + srs_freep(p); + } else { + srs_freep(props); + props = p->to_object(); + srs_info("accept amf0 object connect response props"); + } } - - srs_info("decode SrsBandwidthPacket success."); - + + if ((ret = info->read(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode connect info failed. ret=%d", ret); + return ret; + } + + srs_info("amf0 decode connect response packet success"); + return ret; } -int SrsBandwidthPacket::get_prefer_cid() +int SrsConnectAppResPacket::get_prefer_cid() { - return RTMP_CID_OverStream; + return RTMP_CID_OverConnection; } -int SrsBandwidthPacket::get_message_type() +int SrsConnectAppResPacket::get_message_type() { return RTMP_MSG_AMF0CommandMessage; } -int SrsBandwidthPacket::get_size() +int SrsConnectAppResPacket::get_size() { - return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() - + SrsAmf0Size::null() + SrsAmf0Size::object(data); + return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() + + SrsAmf0Size::object(props) + SrsAmf0Size::object(info); } -int SrsBandwidthPacket::encode_packet(SrsStream* stream) +int SrsConnectAppResPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; @@ -24499,179 +25213,114 @@ int SrsBandwidthPacket::encode_packet(SrsStream* stream) } srs_verbose("encode transaction_id success."); - if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { - srs_error("encode args failed. ret=%d", ret); + if ((ret = props->write(stream)) != ERROR_SUCCESS) { + srs_error("encode props failed. ret=%d", ret); return ret; } - srs_verbose("encode args success.");; + + srs_verbose("encode props success."); - if ((ret = data->write(stream)) != ERROR_SUCCESS) { - srs_error("encode data failed. ret=%d", ret); + if ((ret = info->write(stream)) != ERROR_SUCCESS) { + srs_error("encode info failed. ret=%d", ret); return ret; } - srs_verbose("encode data success."); + + srs_verbose("encode info success."); - srs_info("encode onStatus(Call) packet success."); + srs_info("encode connect app response packet success."); return ret; } -bool SrsBandwidthPacket::is_start_play() -{ - return command_name == SRS_BW_CHECK_START_PLAY; -} - -bool SrsBandwidthPacket::is_starting_play() -{ - return command_name == SRS_BW_CHECK_STARTING_PLAY; -} - -bool SrsBandwidthPacket::is_stop_play() -{ - return command_name == SRS_BW_CHECK_STOP_PLAY; -} - -bool SrsBandwidthPacket::is_stopped_play() -{ - return command_name == SRS_BW_CHECK_STOPPED_PLAY; -} - -bool SrsBandwidthPacket::is_start_publish() -{ - return command_name == SRS_BW_CHECK_START_PUBLISH; -} - -bool SrsBandwidthPacket::is_starting_publish() -{ - return command_name == SRS_BW_CHECK_STARTING_PUBLISH; -} - -bool SrsBandwidthPacket::is_stop_publish() -{ - return command_name == SRS_BW_CHECK_STOP_PUBLISH; -} - -bool SrsBandwidthPacket::is_stopped_publish() -{ - return command_name == SRS_BW_CHECK_STOPPED_PUBLISH; -} - -bool SrsBandwidthPacket::is_finish() -{ - return command_name == SRS_BW_CHECK_FINISHED; -} - -bool SrsBandwidthPacket::is_final() -{ - return command_name == SRS_BW_CHECK_FINAL; -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_start_play() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_START_PLAY); -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_starting_play() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_STARTING_PLAY); -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_playing() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_PLAYING); -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_stop_play() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_STOP_PLAY); -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_stopped_play() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_STOPPED_PLAY); -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_start_publish() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_START_PUBLISH); -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_starting_publish() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_STARTING_PUBLISH); -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_publishing() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_PUBLISHING); -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_stop_publish() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_STOP_PUBLISH); -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_stopped_publish() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_STOPPED_PUBLISH); -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_finish() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_FINISHED); -} - -SrsBandwidthPacket* SrsBandwidthPacket::create_final() -{ - SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); - return pkt->set_command(SRS_BW_CHECK_FINAL); -} - -SrsBandwidthPacket* SrsBandwidthPacket::set_command(string command) +SrsCallPacket::SrsCallPacket() { - command_name = command; - - return this; + command_name = ""; + transaction_id = 0; + command_object = NULL; + arguments = NULL; } -SrsOnStatusDataPacket::SrsOnStatusDataPacket() +SrsCallPacket::~SrsCallPacket() { - command_name = RTMP_AMF0_COMMAND_ON_STATUS; - data = SrsAmf0Any::object(); + srs_freep(command_object); + srs_freep(arguments); } -SrsOnStatusDataPacket::~SrsOnStatusDataPacket() +int SrsCallPacket::decode(SrsStream* stream) { - srs_freep(data); + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode call command_name failed. ret=%d", ret); + return ret; + } + if (command_name.empty()) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 decode call 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 call transaction_id failed. ret=%d", ret); + return ret; + } + + srs_freep(command_object); + if ((ret = SrsAmf0Any::discovery(stream, &command_object)) != ERROR_SUCCESS) { + srs_error("amf0 discovery call command_object failed. ret=%d", ret); + return ret; + } + if ((ret = command_object->read(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode call command_object failed. ret=%d", ret); + return ret; + } + + if (!stream->empty()) { + srs_freep(arguments); + if ((ret = SrsAmf0Any::discovery(stream, &arguments)) != ERROR_SUCCESS) { + srs_error("amf0 discovery call arguments failed. ret=%d", ret); + return ret; + } + if ((ret = arguments->read(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode call arguments failed. ret=%d", ret); + return ret; + } + } + + srs_info("amf0 decode call packet success"); + + return ret; } -int SrsOnStatusDataPacket::get_prefer_cid() +int SrsCallPacket::get_prefer_cid() { - return RTMP_CID_OverStream; + return RTMP_CID_OverConnection; } -int SrsOnStatusDataPacket::get_message_type() +int SrsCallPacket::get_message_type() { - return RTMP_MSG_AMF0DataMessage; + return RTMP_MSG_AMF0CommandMessage; } -int SrsOnStatusDataPacket::get_size() +int SrsCallPacket::get_size() { - return SrsAmf0Size::str(command_name) + SrsAmf0Size::object(data); + int size = 0; + + size += SrsAmf0Size::str(command_name) + SrsAmf0Size::number(); + + if (command_object) { + size += command_object->total_size(); + } + + if (arguments) { + size += arguments->total_size(); + } + + return size; } -int SrsOnStatusDataPacket::encode_packet(SrsStream* stream) +int SrsCallPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; @@ -24681,45 +25330,71 @@ int SrsOnStatusDataPacket::encode_packet(SrsStream* stream) } srs_verbose("encode command_name success."); - if ((ret = data->write(stream)) != ERROR_SUCCESS) { - srs_error("encode data failed. ret=%d", ret); + 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 data success."); + srs_verbose("encode transaction_id success."); - srs_info("encode onStatus(Data) packet success."); + if (command_object && (ret = command_object->write(stream)) != ERROR_SUCCESS) { + srs_error("encode command_object failed. ret=%d", ret); + return ret; + } + srs_verbose("encode command_object success."); + + if (arguments && (ret = arguments->write(stream)) != ERROR_SUCCESS) { + srs_error("encode arguments failed. ret=%d", ret); + return ret; + } + srs_verbose("encode arguments success."); + + srs_info("encode create stream request packet success."); return ret; } -SrsSampleAccessPacket::SrsSampleAccessPacket() +SrsCallResPacket::SrsCallResPacket(double _transaction_id) { - command_name = RTMP_AMF0_DATA_SAMPLE_ACCESS; - video_sample_access = false; - audio_sample_access = false; + command_name = RTMP_AMF0_COMMAND_RESULT; + transaction_id = _transaction_id; + command_object = NULL; + response = NULL; } -SrsSampleAccessPacket::~SrsSampleAccessPacket() +SrsCallResPacket::~SrsCallResPacket() { + srs_freep(command_object); + srs_freep(response); } -int SrsSampleAccessPacket::get_prefer_cid() +int SrsCallResPacket::get_prefer_cid() { - return RTMP_CID_OverStream; + return RTMP_CID_OverConnection; } -int SrsSampleAccessPacket::get_message_type() +int SrsCallResPacket::get_message_type() { - return RTMP_MSG_AMF0DataMessage; + return RTMP_MSG_AMF0CommandMessage; } -int SrsSampleAccessPacket::get_size() +int SrsCallResPacket::get_size() { - return SrsAmf0Size::str(command_name) - + SrsAmf0Size::boolean() + SrsAmf0Size::boolean(); + int size = 0; + + size += SrsAmf0Size::str(command_name) + SrsAmf0Size::number(); + + if (command_object) { + size += command_object->total_size(); + } + + if (response) { + size += response->total_size(); + } + + return size; } -int SrsSampleAccessPacket::encode_packet(SrsStream* stream) +int SrsCallResPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; @@ -24729,1881 +25404,1717 @@ int SrsSampleAccessPacket::encode_packet(SrsStream* stream) } 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); + 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 video_sample_access success."); + srs_verbose("encode transaction_id success."); - if ((ret = srs_amf0_write_boolean(stream, audio_sample_access)) != ERROR_SUCCESS) { - srs_error("encode audio_sample_access failed. ret=%d", ret); + if (command_object && (ret = command_object->write(stream)) != ERROR_SUCCESS) { + srs_error("encode command_object failed. ret=%d", ret); return ret; } - srs_verbose("encode audio_sample_access success.");; + srs_verbose("encode command_object success."); + + if (response && (ret = response->write(stream)) != ERROR_SUCCESS) { + srs_error("encode response failed. ret=%d", ret); + return ret; + } + srs_verbose("encode response success."); - srs_info("encode |RtmpSampleAccess packet success."); + + srs_info("encode call response packet success."); return ret; } -SrsOnMetaDataPacket::SrsOnMetaDataPacket() +SrsCreateStreamPacket::SrsCreateStreamPacket() { - name = SRS_CONSTS_RTMP_ON_METADATA; - metadata = SrsAmf0Any::object(); + command_name = RTMP_AMF0_COMMAND_CREATE_STREAM; + transaction_id = 2; + command_object = SrsAmf0Any::null(); } -SrsOnMetaDataPacket::~SrsOnMetaDataPacket() +SrsCreateStreamPacket::~SrsCreateStreamPacket() { - srs_freep(metadata); + srs_freep(command_object); } -int SrsOnMetaDataPacket::decode(SrsStream* stream) +int SrsCreateStreamPacket::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); + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode createStream command_name failed. ret=%d", ret); return ret; } - - // ignore the @setDataFrame - if (name == SRS_CONSTS_RTMP_SET_DATAFRAME) { - if ((ret = srs_amf0_read_string(stream, name)) != ERROR_SUCCESS) { - srs_error("decode metadata 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; } - 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); + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode createStream transaction_id failed. ret=%d", ret); return ret; } - srs_assert(any); - if (any->is_object()) { - srs_freep(metadata); - metadata = any->to_object(); - srs_info("decode metadata object success"); + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode createStream command_object failed. ret=%d", ret); return ret; } - SrsAutoFree(SrsAmf0Any, any); - - if (any->is_ecma_array()) { - SrsAmf0EcmaArray* arr = any->to_ecma_array(); - - // if ecma array, copy to object. - for (int i = 0; i < arr->count(); i++) { - metadata->set(arr->key_at(i), arr->value_at(i)->copy()); - } - - srs_info("decode metadata array success"); - } + srs_info("amf0 decode createStream packet success"); return ret; } -int SrsOnMetaDataPacket::get_prefer_cid() +int SrsCreateStreamPacket::get_prefer_cid() { - return RTMP_CID_OverConnection2; + return RTMP_CID_OverConnection; } -int SrsOnMetaDataPacket::get_message_type() +int SrsCreateStreamPacket::get_message_type() { - return RTMP_MSG_AMF0DataMessage; + return RTMP_MSG_AMF0CommandMessage; } -int SrsOnMetaDataPacket::get_size() +int SrsCreateStreamPacket::get_size() { - return SrsAmf0Size::str(name) + SrsAmf0Size::object(metadata); + return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() + + SrsAmf0Size::null(); } -int SrsOnMetaDataPacket::encode_packet(SrsStream* stream) +int SrsCreateStreamPacket::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); + 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 name success."); + srs_verbose("encode command_name success."); - if ((ret = metadata->write(stream)) != ERROR_SUCCESS) { - srs_error("encode metadata failed. ret=%d", ret); + 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 metadata success."); + 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."); + + srs_info("encode create stream request packet success."); - srs_info("encode onMetaData packet success."); return ret; } -SrsSetWindowAckSizePacket::SrsSetWindowAckSizePacket() +SrsCreateStreamResPacket::SrsCreateStreamResPacket(double _transaction_id, double _stream_id) { - ackowledgement_window_size = 0; + command_name = RTMP_AMF0_COMMAND_RESULT; + transaction_id = _transaction_id; + command_object = SrsAmf0Any::null(); + stream_id = _stream_id; } -SrsSetWindowAckSizePacket::~SrsSetWindowAckSizePacket() +SrsCreateStreamResPacket::~SrsCreateStreamResPacket() { + srs_freep(command_object); } -int SrsSetWindowAckSizePacket::decode(SrsStream* stream) +int SrsCreateStreamResPacket::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_RESULT) { + 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 (!stream->require(4)) { - ret = ERROR_RTMP_MESSAGE_DECODE; - srs_error("decode ack window size failed. ret=%d", 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; } - ackowledgement_window_size = stream->read_4bytes(); - srs_info("decode ack window size success"); + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode createStream command_object failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_number(stream, stream_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode createStream stream_id failed. ret=%d", ret); + return ret; + } + + srs_info("amf0 decode createStream response packet success"); return ret; } -int SrsSetWindowAckSizePacket::get_prefer_cid() +int SrsCreateStreamResPacket::get_prefer_cid() { - return RTMP_CID_ProtocolControl; + return RTMP_CID_OverConnection; } -int SrsSetWindowAckSizePacket::get_message_type() +int SrsCreateStreamResPacket::get_message_type() { - return RTMP_MSG_WindowAcknowledgementSize; + return RTMP_MSG_AMF0CommandMessage; } -int SrsSetWindowAckSizePacket::get_size() +int SrsCreateStreamResPacket::get_size() { - return 4; + return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() + + SrsAmf0Size::null() + SrsAmf0Size::number(); } -int SrsSetWindowAckSizePacket::encode_packet(SrsStream* stream) +int SrsCreateStreamResPacket::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); + 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."); - stream->write_4bytes(ackowledgement_window_size); + 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."); - srs_verbose("encode ack size packet " - "success. ack_size=%d", ackowledgement_window_size); + 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; } -SrsAcknowledgementPacket::SrsAcknowledgementPacket() +SrsCloseStreamPacket::SrsCloseStreamPacket() { - sequence_number = 0; + command_name = RTMP_AMF0_COMMAND_CLOSE_STREAM; + transaction_id = 0; + command_object = SrsAmf0Any::null(); } -SrsAcknowledgementPacket::~SrsAcknowledgementPacket() +SrsCloseStreamPacket::~SrsCloseStreamPacket() { + srs_freep(command_object); } -int SrsAcknowledgementPacket::get_prefer_cid() +int SrsCloseStreamPacket::decode(SrsStream* stream) { - return RTMP_CID_ProtocolControl; + int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode closeStream command_name failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode closeStream transaction_id failed. ret=%d", ret); + return ret; + } + + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode closeStream command_object failed. ret=%d", ret); + return ret; + } + srs_info("amf0 decode closeStream packet success"); + + return ret; } -int SrsAcknowledgementPacket::get_message_type() +SrsFMLEStartPacket::SrsFMLEStartPacket() { - return RTMP_MSG_Acknowledgement; + command_name = RTMP_AMF0_COMMAND_RELEASE_STREAM; + transaction_id = 0; + command_object = SrsAmf0Any::null(); } -int SrsAcknowledgementPacket::get_size() +SrsFMLEStartPacket::~SrsFMLEStartPacket() { - return 4; + srs_freep(command_object); } -int SrsAcknowledgementPacket::encode_packet(SrsStream* stream) +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 (!stream->require(4)) { - ret = ERROR_RTMP_MESSAGE_ENCODE; - srs_error("encode acknowledgement packet failed. ret=%d", 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; } - stream->write_4bytes(sequence_number); + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode FMLE start command_object failed. ret=%d", ret); + return ret; + } - srs_verbose("encode acknowledgement packet " - "success. sequence_number=%d", sequence_number); + 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; } -SrsSetChunkSizePacket::SrsSetChunkSizePacket() +int SrsFMLEStartPacket::get_prefer_cid() { - chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE; + return RTMP_CID_OverConnection; } -SrsSetChunkSizePacket::~SrsSetChunkSizePacket() +int SrsFMLEStartPacket::get_message_type() { + return RTMP_MSG_AMF0CommandMessage; } -int SrsSetChunkSizePacket::decode(SrsStream* stream) +int SrsFMLEStartPacket::get_size() +{ + return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() + + SrsAmf0Size::null() + SrsAmf0Size::str(stream_name); +} + +int SrsFMLEStartPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; - if (!stream->require(4)) { - ret = ERROR_RTMP_MESSAGE_DECODE; - srs_error("decode chunk size failed. ret=%d", ret); + 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."); - chunk_size = stream->read_4bytes(); - srs_info("decode chunk size success. chunk_size=%d", chunk_size); + 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_string(stream, stream_name)) != ERROR_SUCCESS) { + srs_error("encode stream_name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode stream_name success."); + + + srs_info("encode FMLE start response packet success."); return ret; } -int SrsSetChunkSizePacket::get_prefer_cid() +SrsFMLEStartPacket* SrsFMLEStartPacket::create_release_stream(string stream) { - return RTMP_CID_ProtocolControl; + SrsFMLEStartPacket* pkt = new SrsFMLEStartPacket(); + + pkt->command_name = RTMP_AMF0_COMMAND_RELEASE_STREAM; + pkt->transaction_id = 2; + pkt->stream_name = stream; + + return pkt; } -int SrsSetChunkSizePacket::get_message_type() +SrsFMLEStartPacket* SrsFMLEStartPacket::create_FC_publish(string stream) { - return RTMP_MSG_SetChunkSize; + SrsFMLEStartPacket* pkt = new SrsFMLEStartPacket(); + + pkt->command_name = RTMP_AMF0_COMMAND_FC_PUBLISH; + pkt->transaction_id = 3; + pkt->stream_name = stream; + + return pkt; } -int SrsSetChunkSizePacket::get_size() +SrsFMLEStartResPacket::SrsFMLEStartResPacket(double _transaction_id) { - return 4; + command_name = RTMP_AMF0_COMMAND_RESULT; + transaction_id = _transaction_id; + command_object = SrsAmf0Any::null(); + args = SrsAmf0Any::undefined(); } -int SrsSetChunkSizePacket::encode_packet(SrsStream* stream) +SrsFMLEStartResPacket::~SrsFMLEStartResPacket() +{ + srs_freep(command_object); + srs_freep(args); +} + +int SrsFMLEStartResPacket::decode(SrsStream* stream) { int ret = ERROR_SUCCESS; + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode FMLE start response command_name failed. ret=%d", ret); + return ret; + } + if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_RESULT) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 decode FMLE start response 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 response transaction_id failed. ret=%d", ret); + return ret; + } - if (!stream->require(4)) { - ret = ERROR_RTMP_MESSAGE_ENCODE; - srs_error("encode chunk packet failed. ret=%d", ret); + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode FMLE start response command_object failed. ret=%d", ret); return ret; } - stream->write_4bytes(chunk_size); + if ((ret = srs_amf0_read_undefined(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode FMLE start response stream_id failed. ret=%d", ret); + return ret; + } - srs_verbose("encode chunk packet success. ack_size=%d", chunk_size); + srs_info("amf0 decode FMLE start packet success"); return ret; } -SrsSetPeerBandwidthPacket::SrsSetPeerBandwidthPacket() -{ - bandwidth = 0; - type = SrsPeerBandwidthDynamic; -} - -SrsSetPeerBandwidthPacket::~SrsSetPeerBandwidthPacket() -{ -} - -int SrsSetPeerBandwidthPacket::get_prefer_cid() +int SrsFMLEStartResPacket::get_prefer_cid() { - return RTMP_CID_ProtocolControl; + return RTMP_CID_OverConnection; } -int SrsSetPeerBandwidthPacket::get_message_type() +int SrsFMLEStartResPacket::get_message_type() { - return RTMP_MSG_SetPeerBandwidth; + return RTMP_MSG_AMF0CommandMessage; } -int SrsSetPeerBandwidthPacket::get_size() +int SrsFMLEStartResPacket::get_size() { - return 5; + return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() + + SrsAmf0Size::null() + SrsAmf0Size::undefined(); } -int SrsSetPeerBandwidthPacket::encode_packet(SrsStream* stream) +int SrsFMLEStartResPacket::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); + 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."); - stream->write_4bytes(bandwidth); - stream->write_1bytes(type); + 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."); - srs_verbose("encode set bandwidth packet " - "success. bandwidth=%d, type=%d", bandwidth, type); + 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; } -SrsUserControlPacket::SrsUserControlPacket() +SrsPublishPacket::SrsPublishPacket() { - event_type = 0; - event_data = 0; - extra_data = 0; + command_name = RTMP_AMF0_COMMAND_PUBLISH; + transaction_id = 0; + command_object = SrsAmf0Any::null(); + type = "live"; } -SrsUserControlPacket::~SrsUserControlPacket() +SrsPublishPacket::~SrsPublishPacket() { + srs_freep(command_object); } -int SrsUserControlPacket::decode(SrsStream* stream) +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 (!stream->require(6)) { - ret = ERROR_RTMP_MESSAGE_DECODE; - srs_error("decode user control failed. ret=%d", 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; } - event_type = stream->read_2bytes(); - event_data = stream->read_4bytes(); + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode publish command_object failed. ret=%d", ret); + return ret; + } - 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(); + if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode publish stream_name failed. ret=%d", ret); + return ret; } - srs_info("decode user control success. " - "event_type=%d, event_data=%d, extra_data=%d", - event_type, event_data, extra_data); + 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; } -int SrsUserControlPacket::get_prefer_cid() +int SrsPublishPacket::get_prefer_cid() { - return RTMP_CID_ProtocolControl; + return RTMP_CID_OverStream; } -int SrsUserControlPacket::get_message_type() +int SrsPublishPacket::get_message_type() { - return RTMP_MSG_UserControlMessage; + return RTMP_MSG_AMF0CommandMessage; } -int SrsUserControlPacket::get_size() +int SrsPublishPacket::get_size() { - if (event_type == SrcPCUCSetBufferLength) { - return 2 + 4 + 4; - } else { - return 2 + 4; - } + return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() + + SrsAmf0Size::null() + SrsAmf0Size::str(stream_name) + + SrsAmf0Size::str(type); } -int SrsUserControlPacket::encode_packet(SrsStream* stream) +int SrsPublishPacket::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); + 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."); - stream->write_2bytes(event_type); - stream->write_4bytes(event_data); - - // when event type is set buffer length, - // write the extra buffer length. - if (event_type == SrcPCUCSetBufferLength) { - stream->write_4bytes(extra_data); - srs_verbose("user control message, buffer_length=%d", extra_data); + 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."); - srs_verbose("encode user control packet success. " - "event_type=%d, event_data=%d", event_type, event_data); + 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_string(stream, stream_name)) != ERROR_SUCCESS) { + srs_error("encode stream_name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode stream_name success."); + + if ((ret = srs_amf0_write_string(stream, type)) != ERROR_SUCCESS) { + srs_error("encode type failed. ret=%d", ret); + return ret; + } + srs_verbose("encode type success."); + + srs_info("encode play request packet success."); return ret; } - -// following is generated by src/protocol/srs_rtmp_sdk.cpp -/* -The MIT License (MIT) - -Copyright (c) 2013-2015 SRS(simple-rtmp-server) - -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 - -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 -#ifndef _WIN32 -#include -#endif - -using namespace std; - -// 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() +SrsPausePacket::SrsPausePacket() { - objectEncoding = RTMP_SIG_AMF0_VER; - duration = -1; - args = NULL; + command_name = RTMP_AMF0_COMMAND_PAUSE; + transaction_id = 0; + command_object = SrsAmf0Any::null(); + + time_ms = 0; + is_pause = true; } -SrsRequest::~SrsRequest() +SrsPausePacket::~SrsPausePacket() { - srs_freep(args); + srs_freep(command_object); } -SrsRequest* SrsRequest::copy() +int SrsPausePacket::decode(SrsStream* stream) { - SrsRequest* cp = new SrsRequest(); - - cp->ip = ip; - cp->app = app; - cp->objectEncoding = objectEncoding; - cp->pageUrl = pageUrl; - cp->host = host; - cp->port = port; - cp->param = param; - cp->schema = schema; - cp->stream = stream; - cp->swfUrl = swfUrl; - cp->tcUrl = tcUrl; - cp->vhost = vhost; - cp->duration = duration; - if (args) { - cp->args = args->copy()->to_object(); + 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; } - return cp; -} - -void SrsRequest::update_auth(SrsRequest* req) -{ - pageUrl = req->pageUrl; - swfUrl = req->swfUrl; - tcUrl = req->tcUrl; + 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 (args) { - srs_freep(args); + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode pause command_object failed. ret=%d", ret); + return ret; } - if (req->args) { - args = req->args->copy()->to_object(); + + if ((ret = srs_amf0_read_boolean(stream, is_pause)) != ERROR_SUCCESS) { + srs_error("amf0 decode pause is_pause failed. ret=%d", ret); + return ret; } - srs_info("update req of soruce for auth ok"); -} - -string SrsRequest::get_stream_url() -{ - return srs_generate_stream_url(vhost, app, stream); -} - -void SrsRequest::strip() -{ - // remove the unsupported chars in names. - host = srs_string_remove(host, "/ \n\r\t"); - vhost = srs_string_remove(vhost, "/ \n\r\t"); - app = srs_string_remove(app, " \n\r\t"); - stream = srs_string_remove(stream, " \n\r\t"); + if ((ret = srs_amf0_read_number(stream, time_ms)) != ERROR_SUCCESS) { + srs_error("amf0 decode pause time_ms failed. ret=%d", ret); + return ret; + } - // remove end slash of app/stream - app = srs_string_trim_end(app, "/"); - stream = srs_string_trim_end(stream, "/"); + srs_info("amf0 decode pause packet success"); - // remove start slash of app/stream - app = srs_string_trim_start(app, "/"); - stream = srs_string_trim_start(stream, "/"); -} - -SrsResponse::SrsResponse() -{ - stream_id = SRS_DEFAULT_SID; -} - -SrsResponse::~SrsResponse() -{ + return ret; } -string srs_client_type_string(SrsRtmpConnType type) +SrsPlayPacket::SrsPlayPacket() { - switch (type) { - case SrsRtmpConnPlay: return "Play"; - case SrsRtmpConnFlashPublish: return "publish(FlashPublish)"; - case SrsRtmpConnFMLEPublish: return "publish(FMLEPublish)"; - default: return "Unknown"; - } -} + command_name = RTMP_AMF0_COMMAND_PLAY; + transaction_id = 0; + command_object = SrsAmf0Any::null(); -SrsHandshakeBytes::SrsHandshakeBytes() -{ - c0c1 = s0s1s2 = c2 = NULL; + start = -2; + duration = -1; + reset = true; } -SrsHandshakeBytes::~SrsHandshakeBytes() +SrsPlayPacket::~SrsPlayPacket() { - srs_freep(c0c1); - srs_freep(s0s1s2); - srs_freep(c2); + srs_freep(command_object); } -int SrsHandshakeBytes::read_c0c1(ISrsProtocolReaderWriter* io) +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 (c0c1) { + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode play transaction_id failed. ret=%d", ret); return ret; } - ssize_t nsize; + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode play command_object failed. ret=%d", ret); + return ret; + } - c0c1 = new char[1537]; - if ((ret = io->read_fully(c0c1, 1537, &nsize)) != ERROR_SUCCESS) { - srs_warn("read c0c1 failed. ret=%d", 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; } - srs_verbose("read c0c1 success."); - 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; + } -int SrsHandshakeBytes::read_s0s1s2(ISrsProtocolReaderWriter* io) -{ - int ret = ERROR_SUCCESS; - - if (s0s1s2) { + if (stream->empty()) { return ret; } - ssize_t nsize; - - s0s1s2 = new char[3073]; - if ((ret = io->read_fully(s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) { - srs_warn("read s0s1s2 failed. ret=%d", ret); + SrsAmf0Any* reset_value = NULL; + if ((ret = srs_amf0_read_any(stream, &reset_value)) != ERROR_SUCCESS) { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 read play reset marker failed. ret=%d", ret); return ret; } - srs_verbose("read s0s1s2 success."); + SrsAutoFree(SrsAmf0Any, reset_value); + + if (reset_value) { + // check if the value is bool or number + // An optional Boolean value or number that specifies whether + // to flush any previous playlist + if (reset_value->is_boolean()) { + reset = reset_value->to_boolean(); + } else if (reset_value->is_number()) { + reset = (reset_value->to_number() != 0); + } else { + ret = ERROR_RTMP_AMF0_DECODE; + srs_error("amf0 invalid type=%#x, requires number or bool, ret=%d", reset_value->marker, ret); + return ret; + } + } + + srs_info("amf0 decode play packet success"); return ret; } -int SrsHandshakeBytes::read_c2(ISrsProtocolReaderWriter* io) +int SrsPlayPacket::get_prefer_cid() { - int ret = ERROR_SUCCESS; + return RTMP_CID_OverStream; +} + +int SrsPlayPacket::get_message_type() +{ + return RTMP_MSG_AMF0CommandMessage; +} + +int SrsPlayPacket::get_size() +{ + int size = SrsAmf0Size::str(command_name) + SrsAmf0Size::number() + + SrsAmf0Size::null() + SrsAmf0Size::str(stream_name); - if (c2) { - return ret; + if (start != -2 || duration != -1 || !reset) { + size += SrsAmf0Size::number(); } - ssize_t nsize; + if (duration != -1 || !reset) { + size += SrsAmf0Size::number(); + } - c2 = new char[1536]; - if ((ret = io->read_fully(c2, 1536, &nsize)) != ERROR_SUCCESS) { - srs_warn("read c2 failed. ret=%d", ret); - return ret; + if (!reset) { + size += SrsAmf0Size::boolean(); } - srs_verbose("read c2 success."); - return ret; + return size; } -int SrsHandshakeBytes::create_c0c1() +int SrsPlayPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; - if (c0c1) { + 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."); - c0c1 = new char[1537]; - srs_random_generate(c0c1, 1537); - - // plain text required. - SrsStream stream; - if ((ret = stream.initialize(c0c1, 9)) != ERROR_SUCCESS) { + if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("encode transaction_id failed. ret=%d", ret); return ret; } - stream.write_1bytes(0x03); - stream.write_4bytes((int32_t)::time(NULL)); - stream.write_4bytes(0x00); - - return ret; -} - -int SrsHandshakeBytes::create_s0s1s2(const char* c1) -{ - int ret = ERROR_SUCCESS; + srs_verbose("encode transaction_id success."); - if (s0s1s2) { + 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."); - s0s1s2 = new char[3073]; - srs_random_generate(s0s1s2, 3073); + if ((ret = srs_amf0_write_string(stream, stream_name)) != ERROR_SUCCESS) { + srs_error("encode stream_name failed. ret=%d", ret); + return ret; + } + srs_verbose("encode stream_name success."); - // plain text required. - SrsStream stream; - if ((ret = stream.initialize(s0s1s2, 9)) != ERROR_SUCCESS) { + if ((start != -2 || duration != -1 || !reset) && (ret = srs_amf0_write_number(stream, start)) != ERROR_SUCCESS) { + srs_error("encode start failed. ret=%d", ret); return ret; } - stream.write_1bytes(0x03); - stream.write_4bytes((int32_t)::time(NULL)); - // s1 time2 copy from c1 - if (c0c1) { - stream.write_bytes(c0c1 + 1, 4); + srs_verbose("encode start success."); + + if ((duration != -1 || !reset) && (ret = srs_amf0_write_number(stream, duration)) != ERROR_SUCCESS) { + srs_error("encode duration failed. ret=%d", ret); + return ret; } + srs_verbose("encode duration success."); - // if c1 specified, copy c1 to s2. - // @see: https://github.com/simple-rtmp-server/srs/issues/46 - if (c1) { - memcpy(s0s1s2 + 1537, c1, 1536); + if (!reset && (ret = srs_amf0_write_boolean(stream, reset)) != ERROR_SUCCESS) { + srs_error("encode reset failed. ret=%d", ret); + return ret; } + srs_verbose("encode reset success."); + + srs_info("encode play request packet success."); return ret; } -int SrsHandshakeBytes::create_c2() +SrsPlayResPacket::SrsPlayResPacket() +{ + command_name = RTMP_AMF0_COMMAND_RESULT; + transaction_id = 0; + command_object = SrsAmf0Any::null(); + desc = SrsAmf0Any::object(); +} + +SrsPlayResPacket::~SrsPlayResPacket() +{ + srs_freep(command_object); + srs_freep(desc); +} + +int SrsPlayResPacket::get_prefer_cid() +{ + return RTMP_CID_OverStream; +} + +int SrsPlayResPacket::get_message_type() +{ + return RTMP_MSG_AMF0CommandMessage; +} + +int SrsPlayResPacket::get_size() +{ + return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() + + SrsAmf0Size::null() + SrsAmf0Size::object(desc); +} + +int SrsPlayResPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; - if (c2) { + 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."); - c2 = new char[1536]; - srs_random_generate(c2, 1536); + 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."); - // time - SrsStream stream; - if ((ret = stream.initialize(c2, 8)) != ERROR_SUCCESS) { + if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { + srs_error("encode command_object failed. ret=%d", ret); return ret; } - stream.write_4bytes((int32_t)::time(NULL)); - // c2 time2 copy from s1 - if (s0s1s2) { - stream.write_bytes(s0s1s2 + 1, 4); + srs_verbose("encode command_object success."); + + if ((ret = desc->write(stream)) != 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; } -SrsRtmpClient::SrsRtmpClient(ISrsProtocolReaderWriter* skt) +SrsOnBWDonePacket::SrsOnBWDonePacket() { - io = skt; - protocol = new SrsProtocol(skt); - hs_bytes = new SrsHandshakeBytes(); + command_name = RTMP_AMF0_COMMAND_ON_BW_DONE; + transaction_id = 0; + args = SrsAmf0Any::null(); } -SrsRtmpClient::~SrsRtmpClient() +SrsOnBWDonePacket::~SrsOnBWDonePacket() { - srs_freep(protocol); - srs_freep(hs_bytes); + srs_freep(args); } -void SrsRtmpClient::set_recv_timeout(int64_t timeout_us) +int SrsOnBWDonePacket::get_prefer_cid() { - protocol->set_recv_timeout(timeout_us); + return RTMP_CID_OverConnection; } -void SrsRtmpClient::set_send_timeout(int64_t timeout_us) +int SrsOnBWDonePacket::get_message_type() { - protocol->set_send_timeout(timeout_us); + return RTMP_MSG_AMF0CommandMessage; } -int64_t SrsRtmpClient::get_recv_bytes() +int SrsOnBWDonePacket::get_size() { - return protocol->get_recv_bytes(); + return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() + + SrsAmf0Size::null(); } -int64_t SrsRtmpClient::get_send_bytes() +int SrsOnBWDonePacket::encode_packet(SrsStream* stream) { - return protocol->get_send_bytes(); + 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; } -int SrsRtmpClient::recv_message(SrsCommonMessage** pmsg) +SrsOnStatusCallPacket::SrsOnStatusCallPacket() { - return protocol->recv_message(pmsg); + command_name = RTMP_AMF0_COMMAND_ON_STATUS; + transaction_id = 0; + args = SrsAmf0Any::null(); + data = SrsAmf0Any::object(); } -int SrsRtmpClient::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket) +SrsOnStatusCallPacket::~SrsOnStatusCallPacket() { - return protocol->decode_message(msg, ppacket); + srs_freep(args); + srs_freep(data); } -int SrsRtmpClient::send_and_free_message(SrsSharedPtrMessage* msg, int stream_id) +int SrsOnStatusCallPacket::get_prefer_cid() { - return protocol->send_and_free_message(msg, stream_id); + return RTMP_CID_OverStream; } -int SrsRtmpClient::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id) +int SrsOnStatusCallPacket::get_message_type() { - return protocol->send_and_free_messages(msgs, nb_msgs, stream_id); + return RTMP_MSG_AMF0CommandMessage; } -int SrsRtmpClient::send_and_free_packet(SrsPacket* packet, int stream_id) +int SrsOnStatusCallPacket::get_size() { - return protocol->send_and_free_packet(packet, stream_id); + return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() + + SrsAmf0Size::null() + SrsAmf0Size::object(data); } -int SrsRtmpClient::handshake() +int SrsOnStatusCallPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; - srs_assert(hs_bytes); - - SrsComplexHandshake complex_hs; - if ((ret = complex_hs.handshake_with_server(hs_bytes, io)) != ERROR_SUCCESS) { - if (ret == ERROR_RTMP_TRY_SIMPLE_HS) { - SrsSimpleHandshake simple_hs; - if ((ret = simple_hs.handshake_with_server(hs_bytes, io)) != ERROR_SUCCESS) { - return ret; - } - } + 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."); - srs_freep(hs_bytes); - - return ret; -} - -int SrsRtmpClient::simple_handshake() -{ - int ret = ERROR_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."); - srs_assert(hs_bytes); + if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { + srs_error("encode args failed. ret=%d", ret); + return ret; + } + srs_verbose("encode args success.");; - SrsSimpleHandshake simple_hs; - if ((ret = simple_hs.handshake_with_server(hs_bytes, io)) != ERROR_SUCCESS) { + if ((ret = data->write(stream)) != ERROR_SUCCESS) { + srs_error("encode data failed. ret=%d", ret); return ret; } + srs_verbose("encode data success."); - srs_freep(hs_bytes); + srs_info("encode onStatus(Call) packet success."); return ret; } -int SrsRtmpClient::complex_handshake() +SrsBandwidthPacket::SrsBandwidthPacket() { - int ret = ERROR_SUCCESS; - - srs_assert(hs_bytes); - - SrsComplexHandshake complex_hs; - if ((ret = complex_hs.handshake_with_server(hs_bytes, io)) != ERROR_SUCCESS) { - return ret; - } - - srs_freep(hs_bytes); - - return ret; + command_name = RTMP_AMF0_COMMAND_ON_STATUS; + transaction_id = 0; + args = SrsAmf0Any::null(); + data = SrsAmf0Any::object(); } -int SrsRtmpClient::connect_app(string app, string tc_url, - SrsRequest* req, bool debug_srs_upnode) +SrsBandwidthPacket::~SrsBandwidthPacket() { - std::string srs_server_ip; - std::string srs_server; - std::string srs_primary; - std::string srs_authors; - std::string srs_version; - int srs_id = 0; - int srs_pid = 0; - - return connect_app2(app, tc_url, req, debug_srs_upnode, - srs_server_ip, srs_server, srs_primary, srs_authors, - srs_version, srs_id, srs_pid); + srs_freep(args); + srs_freep(data); } -int SrsRtmpClient::connect_app2( - string app, string tc_url, SrsRequest* req, bool debug_srs_upnode, - string& srs_server_ip, string& srs_server, string& srs_primary, - string& srs_authors, string& srs_version, int& srs_id, - int& srs_pid -){ +int SrsBandwidthPacket::decode(SrsStream *stream) +{ int ret = ERROR_SUCCESS; - - // Connect(vhost, app) - if (true) { - SrsConnectAppPacket* pkt = new SrsConnectAppPacket(); - - pkt->command_object->set("app", SrsAmf0Any::str(app.c_str())); - pkt->command_object->set("flashVer", SrsAmf0Any::str("WIN 15,0,0,239")); - if (req) { - pkt->command_object->set("swfUrl", SrsAmf0Any::str(req->swfUrl.c_str())); - } else { - pkt->command_object->set("swfUrl", SrsAmf0Any::str()); - } - if (req && req->tcUrl != "") { - pkt->command_object->set("tcUrl", SrsAmf0Any::str(req->tcUrl.c_str())); - } else { - pkt->command_object->set("tcUrl", SrsAmf0Any::str(tc_url.c_str())); - } - pkt->command_object->set("fpad", SrsAmf0Any::boolean(false)); - pkt->command_object->set("capabilities", SrsAmf0Any::number(239)); - pkt->command_object->set("audioCodecs", SrsAmf0Any::number(3575)); - pkt->command_object->set("videoCodecs", SrsAmf0Any::number(252)); - pkt->command_object->set("videoFunction", SrsAmf0Any::number(1)); - if (req) { - pkt->command_object->set("pageUrl", SrsAmf0Any::str(req->pageUrl.c_str())); - } else { - pkt->command_object->set("pageUrl", SrsAmf0Any::str()); - } - pkt->command_object->set("objectEncoding", SrsAmf0Any::number(0)); - - // @see https://github.com/simple-rtmp-server/srs/issues/160 - // the debug_srs_upnode is config in vhost and default to true. - if (debug_srs_upnode && req && req->args) { - srs_freep(pkt->args); - pkt->args = req->args->copy()->to_object(); - } - - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - return ret; - } + + if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("amf0 decode bwtc command_name failed. ret=%d", ret); + return ret; } - - // Set Window Acknowledgement size(2500000) - if (true) { - SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); - pkt->ackowledgement_window_size = 2500000; - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - return ret; - } + + if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) { + srs_error("amf0 decode bwtc transaction_id failed. ret=%d", ret); + return ret; } - - // expect connect _result - SrsCommonMessage* msg = NULL; - SrsConnectAppResPacket* pkt = NULL; - if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect connect app response message failed. ret=%d", ret); + + if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode bwtc command_object failed. ret=%d", ret); return ret; } - SrsAutoFree(SrsCommonMessage, msg); - SrsAutoFree(SrsConnectAppResPacket, pkt); - // server info - SrsAmf0Any* data = pkt->info->get_property("data"); - if (data && data->is_ecma_array()) { - SrsAmf0EcmaArray* arr = data->to_ecma_array(); - - SrsAmf0Any* prop = NULL; - if ((prop = arr->ensure_property_string("srs_primary")) != NULL) { - srs_primary = prop->to_str(); - } - if ((prop = arr->ensure_property_string("srs_authors")) != NULL) { - srs_authors = prop->to_str(); - } - if ((prop = arr->ensure_property_string("srs_version")) != NULL) { - srs_version = prop->to_str(); - } - if ((prop = arr->ensure_property_string("srs_server_ip")) != NULL) { - srs_server_ip = prop->to_str(); - } - if ((prop = arr->ensure_property_string("srs_server")) != NULL) { - srs_server = prop->to_str(); - } - if ((prop = arr->ensure_property_number("srs_id")) != NULL) { - srs_id = (int)prop->to_number(); - } - if ((prop = arr->ensure_property_number("srs_pid")) != NULL) { - srs_pid = (int)prop->to_number(); + // @remark, for bandwidth test, ignore the data field. + // only decode the stop-play, start-publish and finish packet. + if (is_stop_play() || is_start_publish() || is_finish()) { + if ((ret = data->read(stream)) != ERROR_SUCCESS) { + srs_error("amf0 decode bwtc command_object failed. ret=%d", ret); + return ret; } } - srs_trace("connected, version=%s, ip=%s, pid=%d, id=%d, dsu=%d", - srs_version.c_str(), srs_server_ip.c_str(), srs_pid, srs_id, debug_srs_upnode); - + + srs_info("decode SrsBandwidthPacket success."); + return ret; } -int SrsRtmpClient::create_stream(int& stream_id) +int SrsBandwidthPacket::get_prefer_cid() { - int ret = ERROR_SUCCESS; - - // CreateStream - if (true) { - SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket(); - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - return ret; - } - } + return RTMP_CID_OverStream; +} - // CreateStream _result. - if (true) { - SrsCommonMessage* msg = NULL; - SrsCreateStreamResPacket* pkt = NULL; - if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect create stream response message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg); - SrsAutoFree(SrsCreateStreamResPacket, pkt); - srs_info("get create stream response message"); +int SrsBandwidthPacket::get_message_type() +{ + return RTMP_MSG_AMF0CommandMessage; +} - stream_id = (int)pkt->stream_id; - } - - return ret; +int SrsBandwidthPacket::get_size() +{ + return SrsAmf0Size::str(command_name) + SrsAmf0Size::number() + + SrsAmf0Size::null() + SrsAmf0Size::object(data); } -int SrsRtmpClient::play(string stream, int stream_id) +int SrsBandwidthPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; - - // Play(stream) - if (true) { - SrsPlayPacket* pkt = new SrsPlayPacket(); - pkt->stream_name = stream; - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { - srs_error("send play stream failed. " - "stream=%s, stream_id=%d, ret=%d", - stream.c_str(), stream_id, ret); - return ret; - } + + 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."); - // SetBufferLength(1000ms) - int buffer_length_ms = 1000; - if (true) { - SrsUserControlPacket* pkt = new SrsUserControlPacket(); + 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."); - pkt->event_type = SrcPCUCSetBufferLength; - pkt->event_data = stream_id; - pkt->extra_data = buffer_length_ms; - - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send set buffer length failed. " - "stream=%s, stream_id=%d, bufferLength=%d, ret=%d", - stream.c_str(), stream_id, buffer_length_ms, ret); - return ret; - } + if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) { + srs_error("encode args failed. ret=%d", ret); + return ret; } + srs_verbose("encode args success.");; - // SetChunkSize - if (true) { - SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); - pkt->chunk_size = SRS_CONSTS_RTMP_SRS_CHUNK_SIZE; - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send set chunk size failed. " - "stream=%s, chunk_size=%d, ret=%d", - stream.c_str(), SRS_CONSTS_RTMP_SRS_CHUNK_SIZE, ret); - return ret; - } + if ((ret = data->write(stream)) != 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; } -int SrsRtmpClient::publish(string stream, int stream_id) +bool SrsBandwidthPacket::is_start_play() { - int ret = ERROR_SUCCESS; - - // SetChunkSize - if (true) { - SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); - pkt->chunk_size = SRS_CONSTS_RTMP_SRS_CHUNK_SIZE; - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send set chunk size failed. " - "stream=%s, chunk_size=%d, ret=%d", - stream.c_str(), SRS_CONSTS_RTMP_SRS_CHUNK_SIZE, ret); - return ret; - } - } + return command_name == SRS_BW_CHECK_START_PLAY; +} - // publish(stream) - if (true) { - SrsPublishPacket* pkt = new SrsPublishPacket(); - pkt->stream_name = stream; - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { - srs_error("send publish message failed. " - "stream=%s, stream_id=%d, ret=%d", - stream.c_str(), stream_id, ret); - return ret; - } - } - - return ret; +bool SrsBandwidthPacket::is_starting_play() +{ + return command_name == SRS_BW_CHECK_STARTING_PLAY; } -int SrsRtmpClient::fmle_publish(string stream, int& stream_id) +bool SrsBandwidthPacket::is_stop_play() { - stream_id = 0; - - int ret = ERROR_SUCCESS; - - // SrsFMLEStartPacket - if (true) { - SrsFMLEStartPacket* pkt = SrsFMLEStartPacket::create_release_stream(stream); - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send FMLE publish " - "release stream failed. stream=%s, ret=%d", stream.c_str(), ret); - return ret; - } - } - - // FCPublish - if (true) { - SrsFMLEStartPacket* pkt = SrsFMLEStartPacket::create_FC_publish(stream); - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send FMLE publish " - "FCPublish failed. stream=%s, ret=%d", stream.c_str(), ret); - return ret; - } - } - - // CreateStream - if (true) { - SrsCreateStreamPacket* pkt = new SrsCreateStreamPacket(); - pkt->transaction_id = 4; - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send FMLE publish " - "createStream failed. stream=%s, ret=%d", stream.c_str(), ret); - return ret; - } - } - - // expect result of CreateStream - if (true) { - SrsCommonMessage* msg = NULL; - SrsCreateStreamResPacket* pkt = NULL; - if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect create stream response message failed. ret=%d", ret); - return ret; - } - SrsAutoFree(SrsCommonMessage, msg); - SrsAutoFree(SrsCreateStreamResPacket, pkt); - srs_info("get create stream response message"); + return command_name == SRS_BW_CHECK_STOP_PLAY; +} + +bool SrsBandwidthPacket::is_stopped_play() +{ + return command_name == SRS_BW_CHECK_STOPPED_PLAY; +} + +bool SrsBandwidthPacket::is_start_publish() +{ + return command_name == SRS_BW_CHECK_START_PUBLISH; +} + +bool SrsBandwidthPacket::is_starting_publish() +{ + return command_name == SRS_BW_CHECK_STARTING_PUBLISH; +} + +bool SrsBandwidthPacket::is_stop_publish() +{ + return command_name == SRS_BW_CHECK_STOP_PUBLISH; +} + +bool SrsBandwidthPacket::is_stopped_publish() +{ + return command_name == SRS_BW_CHECK_STOPPED_PUBLISH; +} + +bool SrsBandwidthPacket::is_finish() +{ + return command_name == SRS_BW_CHECK_FINISHED; +} + +bool SrsBandwidthPacket::is_final() +{ + return command_name == SRS_BW_CHECK_FINAL; +} - stream_id = (int)pkt->stream_id; - } +SrsBandwidthPacket* SrsBandwidthPacket::create_start_play() +{ + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_START_PLAY); +} - // publish(stream) - if (true) { - SrsPublishPacket* pkt = new SrsPublishPacket(); - pkt->stream_name = stream; - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { - srs_error("send FMLE publish publish failed. " - "stream=%s, stream_id=%d, ret=%d", stream.c_str(), stream_id, ret); - return ret; - } - } - - return ret; +SrsBandwidthPacket* SrsBandwidthPacket::create_starting_play() +{ + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_STARTING_PLAY); } -SrsRtmpServer::SrsRtmpServer(ISrsProtocolReaderWriter* skt) +SrsBandwidthPacket* SrsBandwidthPacket::create_playing() { - io = skt; - protocol = new SrsProtocol(skt); - hs_bytes = new SrsHandshakeBytes(); + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_PLAYING); } -SrsRtmpServer::~SrsRtmpServer() +SrsBandwidthPacket* SrsBandwidthPacket::create_stop_play() { - srs_freep(protocol); - srs_freep(hs_bytes); + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_STOP_PLAY); } -void SrsRtmpServer::set_auto_response(bool v) +SrsBandwidthPacket* SrsBandwidthPacket::create_stopped_play() { - protocol->set_auto_response(v); + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_STOPPED_PLAY); } -#ifdef SRS_PERF_MERGED_READ -void SrsRtmpServer::set_merge_read(bool v, IMergeReadHandler* handler) +SrsBandwidthPacket* SrsBandwidthPacket::create_start_publish() { - protocol->set_merge_read(v, handler); + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_START_PUBLISH); } -void SrsRtmpServer::set_recv_buffer(int buffer_size) +SrsBandwidthPacket* SrsBandwidthPacket::create_starting_publish() { - protocol->set_recv_buffer(buffer_size); + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_STARTING_PUBLISH); } -#endif -void SrsRtmpServer::set_recv_timeout(int64_t timeout_us) +SrsBandwidthPacket* SrsBandwidthPacket::create_publishing() { - protocol->set_recv_timeout(timeout_us); + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_PUBLISHING); } -int64_t SrsRtmpServer::get_recv_timeout() +SrsBandwidthPacket* SrsBandwidthPacket::create_stop_publish() { - return protocol->get_recv_timeout(); + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_STOP_PUBLISH); } -void SrsRtmpServer::set_send_timeout(int64_t timeout_us) +SrsBandwidthPacket* SrsBandwidthPacket::create_stopped_publish() { - protocol->set_send_timeout(timeout_us); + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_STOPPED_PUBLISH); } -int64_t SrsRtmpServer::get_send_timeout() +SrsBandwidthPacket* SrsBandwidthPacket::create_finish() { - return protocol->get_send_timeout(); + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_FINISHED); } -int64_t SrsRtmpServer::get_recv_bytes() +SrsBandwidthPacket* SrsBandwidthPacket::create_final() { - return protocol->get_recv_bytes(); + SrsBandwidthPacket* pkt = new SrsBandwidthPacket(); + return pkt->set_command(SRS_BW_CHECK_FINAL); } -int64_t SrsRtmpServer::get_send_bytes() +SrsBandwidthPacket* SrsBandwidthPacket::set_command(string command) { - return protocol->get_send_bytes(); + command_name = command; + + return this; } -int SrsRtmpServer::recv_message(SrsCommonMessage** pmsg) +SrsOnStatusDataPacket::SrsOnStatusDataPacket() { - return protocol->recv_message(pmsg); + command_name = RTMP_AMF0_COMMAND_ON_STATUS; + data = SrsAmf0Any::object(); } -int SrsRtmpServer::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket) +SrsOnStatusDataPacket::~SrsOnStatusDataPacket() { - return protocol->decode_message(msg, ppacket); + srs_freep(data); } -int SrsRtmpServer::send_and_free_message(SrsSharedPtrMessage* msg, int stream_id) +int SrsOnStatusDataPacket::get_prefer_cid() { - return protocol->send_and_free_message(msg, stream_id); + return RTMP_CID_OverStream; } -int SrsRtmpServer::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id) +int SrsOnStatusDataPacket::get_message_type() { - return protocol->send_and_free_messages(msgs, nb_msgs, stream_id); + return RTMP_MSG_AMF0DataMessage; } -int SrsRtmpServer::send_and_free_packet(SrsPacket* packet, int stream_id) +int SrsOnStatusDataPacket::get_size() { - return protocol->send_and_free_packet(packet, stream_id); + return SrsAmf0Size::str(command_name) + SrsAmf0Size::object(data); } -int SrsRtmpServer::handshake() +int SrsOnStatusDataPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; - srs_assert(hs_bytes); + 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."); - SrsComplexHandshake complex_hs; - if ((ret = complex_hs.handshake_with_client(hs_bytes, io)) != ERROR_SUCCESS) { - if (ret == ERROR_RTMP_TRY_SIMPLE_HS) { - SrsSimpleHandshake simple_hs; - if ((ret = simple_hs.handshake_with_client(hs_bytes, io)) != ERROR_SUCCESS) { - return ret; - } - } + if ((ret = data->write(stream)) != ERROR_SUCCESS) { + srs_error("encode data failed. ret=%d", ret); return ret; } + srs_verbose("encode data success."); - srs_freep(hs_bytes); + srs_info("encode onStatus(Data) packet success."); return ret; } -int SrsRtmpServer::connect_app(SrsRequest* req) +SrsSampleAccessPacket::SrsSampleAccessPacket() +{ + command_name = RTMP_AMF0_DATA_SAMPLE_ACCESS; + video_sample_access = false; + audio_sample_access = false; +} + +SrsSampleAccessPacket::~SrsSampleAccessPacket() +{ +} + +int SrsSampleAccessPacket::get_prefer_cid() +{ + return RTMP_CID_OverStream; +} + +int SrsSampleAccessPacket::get_message_type() +{ + return RTMP_MSG_AMF0DataMessage; +} + +int SrsSampleAccessPacket::get_size() +{ + return SrsAmf0Size::str(command_name) + + SrsAmf0Size::boolean() + SrsAmf0Size::boolean(); +} + +int SrsSampleAccessPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; - SrsCommonMessage* msg = NULL; - SrsConnectAppPacket* pkt = NULL; - if ((ret = expect_message(&msg, &pkt)) != ERROR_SUCCESS) { - srs_error("expect connect app message failed. ret=%d", ret); + if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) { + srs_error("encode command_name failed. ret=%d", ret); return ret; } - SrsAutoFree(SrsCommonMessage, msg); - SrsAutoFree(SrsConnectAppPacket, pkt); - srs_info("get connect app message"); - - SrsAmf0Any* prop = NULL; + srs_verbose("encode command_name success."); - 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); + if ((ret = srs_amf0_write_boolean(stream, video_sample_access)) != ERROR_SUCCESS) { + srs_error("encode video_sample_access failed. ret=%d", ret); return ret; } - req->tcUrl = prop->to_str(); - - if ((prop = pkt->command_object->ensure_property_string("pageUrl")) != NULL) { - req->pageUrl = prop->to_str(); - } - - if ((prop = pkt->command_object->ensure_property_string("swfUrl")) != NULL) { - req->swfUrl = prop->to_str(); - } - - if ((prop = pkt->command_object->ensure_property_number("objectEncoding")) != NULL) { - req->objectEncoding = prop->to_number(); - } + srs_verbose("encode video_sample_access success."); - if (pkt->args) { - srs_freep(req->args); - req->args = pkt->args->copy()->to_object(); - srs_info("copy edge traverse to origin auth args."); + 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("get connect app message params success."); - - srs_discovery_tc_url(req->tcUrl, - req->schema, req->host, req->vhost, req->app, req->port, - req->param); - req->strip(); + srs_info("encode |RtmpSampleAccess packet success."); return ret; } -int SrsRtmpServer::set_window_ack_size(int ack_size) +SrsOnMetaDataPacket::SrsOnMetaDataPacket() { - int ret = ERROR_SUCCESS; - - SrsSetWindowAckSizePacket* pkt = new SrsSetWindowAckSizePacket(); - pkt->ackowledgement_window_size = ack_size; - if ((ret = protocol->send_and_free_packet(pkt, 0)) != 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; + name = SRS_CONSTS_RTMP_ON_METADATA; + metadata = SrsAmf0Any::object(); } -int SrsRtmpServer::set_peer_bandwidth(int bandwidth, int type) +SrsOnMetaDataPacket::~SrsOnMetaDataPacket() +{ + srs_freep(metadata); +} + +int SrsOnMetaDataPacket::decode(SrsStream* stream) { int ret = ERROR_SUCCESS; - SrsSetPeerBandwidthPacket* pkt = new SrsSetPeerBandwidthPacket(); - pkt->bandwidth = bandwidth; - pkt->type = type; - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send set bandwidth message failed. ret=%d", ret); + if ((ret = srs_amf0_read_string(stream, name)) != ERROR_SUCCESS) { + srs_error("decode metadata name failed. ret=%d", ret); return ret; } - srs_info("send set bandwidth message " - "success. bandwidth=%d, type=%d", bandwidth, type); - - return ret; -} -int SrsRtmpServer::response_connect_app(SrsRequest *req, const char* server_ip) -{ - int ret = ERROR_SUCCESS; + // ignore the @setDataFrame + if (name == SRS_CONSTS_RTMP_SET_DATAFRAME) { + if ((ret = srs_amf0_read_string(stream, name)) != ERROR_SUCCESS) { + srs_error("decode metadata name failed. ret=%d", ret); + return ret; + } + } - SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket(); + srs_verbose("decode metadata name success. name=%s", name.c_str()); - pkt->props->set("fmsVer", SrsAmf0Any::str("FMS/"RTMP_SIG_FMS_VER)); - pkt->props->set("capabilities", SrsAmf0Any::number(127)); - pkt->props->set("mode", SrsAmf0Any::number(1)); + // 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; + } - pkt->info->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); - pkt->info->set(StatusCode, SrsAmf0Any::str(StatusCodeConnectSuccess)); - pkt->info->set(StatusDescription, SrsAmf0Any::str("Connection succeeded")); - pkt->info->set("objectEncoding", SrsAmf0Any::number(req->objectEncoding)); - SrsAmf0EcmaArray* data = SrsAmf0Any::ecma_array(); - pkt->info->set("data", data); + srs_assert(any); + if (any->is_object()) { + srs_freep(metadata); + metadata = any->to_object(); + srs_info("decode metadata object success"); + return ret; + } - data->set("version", SrsAmf0Any::str(RTMP_SIG_FMS_VER)); - data->set("srs_sig", SrsAmf0Any::str(RTMP_SIG_SRS_KEY)); - data->set("srs_server", SrsAmf0Any::str(RTMP_SIG_SRS_SERVER)); - data->set("srs_license", SrsAmf0Any::str(RTMP_SIG_SRS_LICENSE)); - data->set("srs_role", SrsAmf0Any::str(RTMP_SIG_SRS_ROLE)); - data->set("srs_url", SrsAmf0Any::str(RTMP_SIG_SRS_URL)); - data->set("srs_version", SrsAmf0Any::str(RTMP_SIG_SRS_VERSION)); - data->set("srs_site", SrsAmf0Any::str(RTMP_SIG_SRS_WEB)); - data->set("srs_email", SrsAmf0Any::str(RTMP_SIG_SRS_EMAIL)); - data->set("srs_copyright", SrsAmf0Any::str(RTMP_SIG_SRS_COPYRIGHT)); - data->set("srs_primary", SrsAmf0Any::str(RTMP_SIG_SRS_PRIMARY)); - data->set("srs_authors", SrsAmf0Any::str(RTMP_SIG_SRS_AUTHROS)); + SrsAutoFree(SrsAmf0Any, any); - if (server_ip) { - data->set("srs_server_ip", SrsAmf0Any::str(server_ip)); - } - // for edge to directly get the id of client. - data->set("srs_pid", SrsAmf0Any::number(getpid())); - data->set("srs_id", SrsAmf0Any::number(_srs_context->get_id())); + if (any->is_ecma_array()) { + SrsAmf0EcmaArray* arr = any->to_ecma_array(); - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send connect app response message failed. ret=%d", ret); - return ret; + // if ecma array, copy to object. + for (int i = 0; i < arr->count(); i++) { + metadata->set(arr->key_at(i), arr->value_at(i)->copy()); + } + + srs_info("decode metadata array success"); } - srs_info("send connect app response message success."); return ret; } -void SrsRtmpServer::response_connect_reject(SrsRequest* /*req*/, const char* desc) +int SrsOnMetaDataPacket::get_prefer_cid() { - int ret = ERROR_SUCCESS; - - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelError)); - pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeConnectRejected)); - pkt->data->set(StatusDescription, SrsAmf0Any::str(desc)); + return RTMP_CID_OverConnection2; +} - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send connect app response rejected message failed. ret=%d", ret); - return; - } - srs_info("send connect app response rejected message success."); +int SrsOnMetaDataPacket::get_message_type() +{ + return RTMP_MSG_AMF0DataMessage; +} - return; +int SrsOnMetaDataPacket::get_size() +{ + return SrsAmf0Size::str(name) + SrsAmf0Size::object(metadata); } -int SrsRtmpServer::on_bw_done() +int SrsOnMetaDataPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; - SrsOnBWDonePacket* pkt = new SrsOnBWDonePacket(); - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send onBWDone message failed. ret=%d", ret); + if ((ret = srs_amf0_write_string(stream, name)) != ERROR_SUCCESS) { + srs_error("encode name failed. ret=%d", ret); return ret; } - srs_info("send onBWDone message success."); + srs_verbose("encode name success."); + + if ((ret = metadata->write(stream)) != 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; } -int SrsRtmpServer::identify_client(int stream_id, SrsRtmpConnType& type, string& stream_name, double& duration) +SrsSetWindowAckSizePacket::SrsSetWindowAckSizePacket() { - type = SrsRtmpConnUnknown; - int ret = ERROR_SUCCESS; - - while (true) { - SrsCommonMessage* msg = NULL; - if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("recv identify client message failed. ret=%d", ret); - } - return ret; - } + ackowledgement_window_size = 0; +} - SrsAutoFree(SrsCommonMessage, msg); - SrsMessageHeader& h = msg->header; - - if (h.is_ackledgement() || h.is_set_chunk_size() || h.is_window_ackledgement_size() || h.is_user_control_message()) { - continue; - } - - if (!h.is_amf0_command() && !h.is_amf3_command()) { - srs_trace("identify ignore messages except " - "AMF0/AMF3 command message. type=%#x", h.message_type); - continue; - } - - SrsPacket* pkt = NULL; - if ((ret = protocol->decode_message(msg, &pkt)) != ERROR_SUCCESS) { - srs_error("identify decode message failed. ret=%d", ret); - return ret; - } - - SrsAutoFree(SrsPacket, pkt); - - 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, duration); - } - if (dynamic_cast(pkt)) { - srs_info("identify client by releaseStream, fmle publish."); - return identify_fmle_publish_client(dynamic_cast(pkt), type, stream_name); - } - if (dynamic_cast(pkt)) { - srs_info("level0 identify client by play."); - return identify_play_client(dynamic_cast(pkt), type, stream_name, duration); - } - // call msg, - // support response null first, - // @see https://github.com/simple-rtmp-server/srs/issues/106 - // TODO: FIXME: response in right way, or forward in edge mode. - SrsCallPacket* call = dynamic_cast(pkt); - if (call) { - SrsCallResPacket* res = new SrsCallResPacket(call->transaction_id); - res->command_object = SrsAmf0Any::null(); - res->response = SrsAmf0Any::null(); - if ((ret = protocol->send_and_free_packet(res, 0)) != ERROR_SUCCESS) { - srs_warn("response call failed. ret=%d", ret); - return ret; - } - continue; - } - - srs_trace("ignore AMF0/AMF3 command message."); - } - - return ret; +SrsSetWindowAckSizePacket::~SrsSetWindowAckSizePacket() +{ } -int SrsRtmpServer::set_chunk_size(int chunk_size) +int SrsSetWindowAckSizePacket::decode(SrsStream* stream) { int ret = ERROR_SUCCESS; - SrsSetChunkSizePacket* pkt = new SrsSetChunkSizePacket(); - pkt->chunk_size = chunk_size; - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send set chunk size message failed. ret=%d", ret); + if (!stream->require(4)) { + ret = ERROR_RTMP_MESSAGE_DECODE; + srs_error("decode ack window size failed. ret=%d", ret); return ret; } - srs_info("send set chunk size message success. chunk_size=%d", chunk_size); + + ackowledgement_window_size = stream->read_4bytes(); + srs_info("decode ack window size success"); return ret; } -int SrsRtmpServer::start_play(int stream_id) +int SrsSetWindowAckSizePacket::get_prefer_cid() { - int ret = ERROR_SUCCESS; - - // StreamBegin - if (true) { - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - pkt->event_type = SrcPCUCStreamBegin; - pkt->event_data = stream_id; - if ((ret = protocol->send_and_free_packet(pkt, 0)) != 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) { - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); - pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeStreamReset)); - pkt->data->set(StatusDescription, SrsAmf0Any::str("Playing and resetting stream.")); - pkt->data->set(StatusDetails, SrsAmf0Any::str("stream")); - pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID)); - - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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) { - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); - pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeStreamStart)); - pkt->data->set(StatusDescription, SrsAmf0Any::str("Started playing stream.")); - pkt->data->set(StatusDetails, SrsAmf0Any::str("stream")); - pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID)); - - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { - srs_error("send onStatus(NetStream.Play.Start) message failed. ret=%d", ret); - return ret; - } - srs_info("send onStatus(NetStream.Play.Start) message success."); - } - - // |RtmpSampleAccess(false, false) - if (true) { - SrsSampleAccessPacket* pkt = new SrsSampleAccessPacket(); + return RTMP_CID_ProtocolControl; +} - // allow audio/video sample. - // @see: https://github.com/simple-rtmp-server/srs/issues/49 - pkt->audio_sample_access = true; - pkt->video_sample_access = true; - - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != ERROR_SUCCESS) { - srs_error("send |RtmpSampleAccess(false, false) message failed. ret=%d", ret); - return ret; - } - srs_info("send |RtmpSampleAccess(false, false) message success."); - } +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; - // onStatus(NetStream.Data.Start) - if (true) { - SrsOnStatusDataPacket* pkt = new SrsOnStatusDataPacket(); - pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeDataStart)); - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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."); + if (!stream->require(4)) { + ret = ERROR_RTMP_MESSAGE_ENCODE; + srs_error("encode ack size packet failed. ret=%d", ret); + return ret; } - srs_info("start play success."); + stream->write_4bytes(ackowledgement_window_size); + + srs_verbose("encode ack size packet " + "success. ack_size=%d", ackowledgement_window_size); return ret; } -int SrsRtmpServer::on_play_client_pause(int stream_id, bool is_pause) +SrsAcknowledgementPacket::SrsAcknowledgementPacket() +{ + sequence_number = 0; +} + +SrsAcknowledgementPacket::~SrsAcknowledgementPacket() +{ +} + +int SrsAcknowledgementPacket::get_prefer_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 (is_pause) { - // onStatus(NetStream.Pause.Notify) - if (true) { - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); - pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeStreamPause)); - pkt->data->set(StatusDescription, SrsAmf0Any::str("Paused stream.")); - - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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) { - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCStreamEOF; - pkt->event_data = stream_id; - - if ((ret = protocol->send_and_free_packet(pkt, 0)) != 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) { - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); - pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeStreamUnpause)); - pkt->data->set(StatusDescription, SrsAmf0Any::str("Unpaused stream.")); - - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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) { - SrsUserControlPacket* pkt = new SrsUserControlPacket(); - - pkt->event_type = SrcPCUCStreamBegin; - pkt->event_data = stream_id; - - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send PCUC(StreanBegin) message failed. ret=%d", ret); - return ret; - } - srs_info("send PCUC(StreanBegin) message 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; } -int SrsRtmpServer::start_fmle_publish(int stream_id) +SrsSetChunkSizePacket::SrsSetChunkSizePacket() +{ + chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE; +} + +SrsSetChunkSizePacket::~SrsSetChunkSizePacket() +{ +} + +int SrsSetChunkSizePacket::decode(SrsStream* stream) { int ret = ERROR_SUCCESS; - // FCPublish - double fc_publish_tid = 0; - if (true) { - SrsCommonMessage* msg = NULL; - SrsFMLEStartPacket* pkt = NULL; - if ((ret = expect_message(&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); - SrsAutoFree(SrsFMLEStartPacket, pkt); - - fc_publish_tid = pkt->transaction_id; - } - // FCPublish response - if (true) { - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(fc_publish_tid); - if ((ret = protocol->send_and_free_packet(pkt, 0)) != 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 = expect_message(&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); - SrsAutoFree(SrsCreateStreamPacket, pkt); - - create_stream_tid = pkt->transaction_id; - } - // createStream response - if (true) { - SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(create_stream_tid, stream_id); - if ((ret = protocol->send_and_free_packet(pkt, 0)) != 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 = expect_message(&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); - SrsAutoFree(SrsPublishPacket, pkt); - } - // publish response onFCPublish(NetStream.Publish.Start) - if (true) { - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_PUBLISH; - pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodePublishStart)); - pkt->data->set(StatusDescription, SrsAmf0Any::str("Started publishing stream.")); - - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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) { - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); - pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodePublishStart)); - pkt->data->set(StatusDescription, SrsAmf0Any::str("Started publishing stream.")); - pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID)); - - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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."); + if (!stream->require(4)) { + ret = ERROR_RTMP_MESSAGE_DECODE; + srs_error("decode chunk size failed. ret=%d", ret); + return ret; } - srs_info("FMLE publish success."); + chunk_size = stream->read_4bytes(); + srs_info("decode chunk size success. chunk_size=%d", chunk_size); return ret; } -int SrsRtmpServer::fmle_unpublish(int stream_id, double unpublish_tid) +int SrsSetChunkSizePacket::get_prefer_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; - // publish response onFCUnpublish(NetStream.unpublish.Success) - if (true) { - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->command_name = RTMP_AMF0_COMMAND_ON_FC_UNPUBLISH; - pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeUnpublishSuccess)); - pkt->data->set(StatusDescription, SrsAmf0Any::str("Stop publishing stream.")); - - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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) { - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(unpublish_tid); - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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) { - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); - pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodeUnpublishSuccess)); - pkt->data->set(StatusDescription, SrsAmf0Any::str("Stream is now unpublished")); - pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID)); - - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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."); + if (!stream->require(4)) { + ret = ERROR_RTMP_MESSAGE_ENCODE; + srs_error("encode chunk packet failed. ret=%d", ret); + return ret; } - srs_info("FMLE unpublish success."); + stream->write_4bytes(chunk_size); + + srs_verbose("encode chunk packet success. ack_size=%d", chunk_size); return ret; } -int SrsRtmpServer::start_flash_publish(int stream_id) +SrsSetPeerBandwidthPacket::SrsSetPeerBandwidthPacket() +{ + bandwidth = 0; + type = SrsPeerBandwidthDynamic; +} + +SrsSetPeerBandwidthPacket::~SrsSetPeerBandwidthPacket() +{ +} + +int SrsSetPeerBandwidthPacket::get_prefer_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; - // publish response onStatus(NetStream.Publish.Start) - if (true) { - SrsOnStatusCallPacket* pkt = new SrsOnStatusCallPacket(); - - pkt->data->set(StatusLevel, SrsAmf0Any::str(StatusLevelStatus)); - pkt->data->set(StatusCode, SrsAmf0Any::str(StatusCodePublishStart)); - pkt->data->set(StatusDescription, SrsAmf0Any::str("Started publishing stream.")); - pkt->data->set(StatusClientId, SrsAmf0Any::str(RTMP_SIG_CLIENT_ID)); - - if ((ret = protocol->send_and_free_packet(pkt, stream_id)) != 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."); + if (!stream->require(5)) { + ret = ERROR_RTMP_MESSAGE_ENCODE; + srs_error("encode set bandwidth packet failed. ret=%d", ret); + return ret; } - srs_info("flash publish success."); + stream->write_4bytes(bandwidth); + stream->write_1bytes(type); + + srs_verbose("encode set bandwidth packet " + "success. bandwidth=%d, type=%d", bandwidth, type); return ret; } -int SrsRtmpServer::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsRtmpConnType& type, string& stream_name, double& duration) +SrsUserControlPacket::SrsUserControlPacket() +{ + event_type = 0; + event_data = 0; + extra_data = 0; +} + +SrsUserControlPacket::~SrsUserControlPacket() +{ +} + +int SrsUserControlPacket::decode(SrsStream* stream) { int ret = ERROR_SUCCESS; - if (true) { - SrsCreateStreamResPacket* pkt = new SrsCreateStreamResPacket(req->transaction_id, stream_id); - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send createStream response message failed. ret=%d", ret); - return ret; - } - srs_info("send createStream response message success."); + if (!stream->require(2)) { + ret = ERROR_RTMP_MESSAGE_DECODE; + srs_error("decode user control failed. ret=%d", ret); + return ret; } - while (true) { - SrsCommonMessage* msg = NULL; - if ((ret = protocol->recv_message(&msg)) != ERROR_SUCCESS) { - if (!srs_is_client_gracefully_close(ret)) { - srs_error("recv identify client message failed. ret=%d", ret); - } + event_type = stream->read_2bytes(); + + if (event_type == SrsPCUCFmsEvent0) { + if (!stream->require(1)) { + ret = ERROR_RTMP_MESSAGE_DECODE; + srs_error("decode user control failed. ret=%d", ret); return ret; } - - SrsAutoFree(SrsCommonMessage, msg); - SrsMessageHeader& h = msg->header; - - if (h.is_ackledgement() || h.is_set_chunk_size() || h.is_window_ackledgement_size() || h.is_user_control_message()) { - continue; + event_data = stream->read_1bytes(); + } else { + if (!stream->require(4)) { + ret = ERROR_RTMP_MESSAGE_DECODE; + srs_error("decode user control failed. ret=%d", ret); + return ret; } + event_data = stream->read_4bytes(); + } - if (!h.is_amf0_command() && !h.is_amf3_command()) { - srs_trace("identify ignore messages except " - "AMF0/AMF3 command message. type=%#x", h.message_type); - continue; - } - - SrsPacket* pkt = NULL; - if ((ret = protocol->decode_message(msg, &pkt)) != ERROR_SUCCESS) { - srs_error("identify decode message failed. ret=%d", ret); + 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; } - - SrsAutoFree(SrsPacket, pkt); - - if (dynamic_cast(pkt)) { - srs_info("level1 identify client by play."); - return identify_play_client(dynamic_cast(pkt), type, stream_name, duration); - } - if (dynamic_cast(pkt)) { - srs_info("identify client by publish, falsh publish."); - return identify_flash_publish_client(dynamic_cast(pkt), type, stream_name); - } - 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, duration); - } - - srs_trace("ignore AMF0/AMF3 command message."); + 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 SrsRtmpServer::identify_fmle_publish_client(SrsFMLEStartPacket* req, SrsRtmpConnType& type, string& stream_name) +int SrsUserControlPacket::get_prefer_cid() { - int ret = ERROR_SUCCESS; + return RTMP_CID_ProtocolControl; +} + +int SrsUserControlPacket::get_message_type() +{ + return RTMP_MSG_UserControlMessage; +} + +int SrsUserControlPacket::get_size() +{ + int size = 2; - type = SrsRtmpConnFMLEPublish; - stream_name = req->stream_name; + if (event_type == SrsPCUCFmsEvent0) { + size += 1; + } else { + size += 4; + } - // releaseStream response - if (true) { - SrsFMLEStartResPacket* pkt = new SrsFMLEStartResPacket(req->transaction_id); - if ((ret = protocol->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) { - srs_error("send releaseStream response message failed. ret=%d", ret); - return ret; - } - srs_info("send releaseStream response message success."); + if (event_type == SrcPCUCSetBufferLength) { + size += 4; } - return ret; + return size; } -int SrsRtmpServer::identify_flash_publish_client(SrsPublishPacket* req, SrsRtmpConnType& type, string& stream_name) +int SrsUserControlPacket::encode_packet(SrsStream* stream) { int ret = ERROR_SUCCESS; - type = SrsRtmpConnFlashPublish; - stream_name = req->stream_name; + if (!stream->require(get_size())) { + ret = ERROR_RTMP_MESSAGE_ENCODE; + srs_error("encode user control packet failed. ret=%d", ret); + return ret; + } - return ret; -} + stream->write_2bytes(event_type); + + if (event_type == SrsPCUCFmsEvent0) { + stream->write_1bytes(event_data); + } else { + stream->write_4bytes(event_data); + } -int SrsRtmpServer::identify_play_client(SrsPlayPacket* req, SrsRtmpConnType& type, string& stream_name, double& duration) -{ - int ret = ERROR_SUCCESS; + // when event type is set buffer length, + // write the extra buffer length. + if (event_type == SrcPCUCSetBufferLength) { + stream->write_4bytes(extra_data); + srs_verbose("user control message, buffer_length=%d", extra_data); + } - type = SrsRtmpConnPlay; - stream_name = req->stream_name; - duration = req->duration; + srs_verbose("encode user control packet success. " + "event_type=%d, event_data=%d", event_type, event_data); - srs_info("identity client type=play, stream_name=%s, duration=%.2f", stream_name.c_str(), duration); - return ret; } @@ -26612,7 +27123,7 @@ int SrsRtmpServer::identify_play_client(SrsPlayPacket* req, SrsRtmpConnType& typ /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -26641,7 +27152,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include //#include //#include -//#include +//#include //#include #ifdef SRS_AUTO_SSL @@ -26811,12 +27322,12 @@ namespace _srs_internal // maybe the key_size is 127, but dh will write all 128bytes pkey, // so, donot need to set/initialize the pkey. - // @see https://github.com/simple-rtmp-server/srs/issues/165 + // @see https://github.com/ossrs/srs/issues/165 key_size = BN_bn2bin(pdh->pub_key, (unsigned char*)pkey); srs_assert(key_size > 0); // output the size of public key. - // @see https://github.com/simple-rtmp-server/srs/issues/165 + // @see https://github.com/ossrs/srs/issues/165 srs_assert(key_size <= pkey_size); pkey_size = key_size; @@ -26836,7 +27347,7 @@ namespace _srs_internal // if failed, donot return, do cleanup, @see ./test/dhtest.c:168 // maybe the key_size is 127, but dh will write all 128bytes skey, // so, donot need to set/initialize the skey. - // @see https://github.com/simple-rtmp-server/srs/issues/165 + // @see https://github.com/ossrs/srs/issues/165 int32_t key_size = DH_compute_key((unsigned char*)skey, ppk, pdh); if (key_size < ppkey_size) { @@ -26930,8 +27441,8 @@ namespace _srs_internal key_block::~key_block() { - srs_freep(random0); - srs_freep(random1); + srs_freepa(random0); + srs_freepa(random1); } int key_block::parse(SrsStream* stream) @@ -26953,7 +27464,7 @@ namespace _srs_internal random0_size = valid_offset; if (random0_size > 0) { - srs_freep(random0); + srs_freepa(random0); random0 = new char[random0_size]; stream->read_bytes(random0, random0_size); } @@ -26962,7 +27473,7 @@ namespace _srs_internal random1_size = 764 - valid_offset - 128 - 4; if (random1_size > 0) { - srs_freep(random1); + srs_freepa(random1); random1 = new char[random1_size]; stream->read_bytes(random1, random1_size); } @@ -27012,8 +27523,8 @@ namespace _srs_internal digest_block::~digest_block() { - srs_freep(random0); - srs_freep(random1); + srs_freepa(random0); + srs_freepa(random1); } int digest_block::parse(SrsStream* stream) @@ -27030,7 +27541,7 @@ namespace _srs_internal random0_size = valid_offset; if (random0_size > 0) { - srs_freep(random0); + srs_freepa(random0); random0 = new char[random0_size]; stream->read_bytes(random0, random0_size); } @@ -27039,7 +27550,7 @@ namespace _srs_internal random1_size = 764 - 4 - valid_offset - 32; if (random1_size > 0) { - srs_freep(random1); + srs_freepa(random1); random1 = new char[random1_size]; stream->read_bytes(random1, random1_size); } @@ -27098,7 +27609,7 @@ namespace _srs_internal } srs_assert(c1_digest != NULL); - SrsAutoFree(char, c1_digest); + SrsAutoFreeA(char, c1_digest); memcpy(digest.digest, c1_digest, 32); @@ -27117,7 +27628,7 @@ namespace _srs_internal } srs_assert(c1_digest != NULL); - SrsAutoFree(char, c1_digest); + SrsAutoFreeA(char, c1_digest); is_valid = srs_bytes_equals(digest.digest, c1_digest, 32); @@ -27136,7 +27647,7 @@ namespace _srs_internal } // directly generate the public key. - // @see: https://github.com/simple-rtmp-server/srs/issues/148 + // @see: https://github.com/ossrs/srs/issues/148 int pkey_size = 128; if ((ret = dh.copy_shared_key(c1->get_key(), 128, key.key, pkey_size)) != ERROR_SUCCESS) { srs_error("calc s1 key failed. ret=%d", ret); @@ -27157,7 +27668,7 @@ namespace _srs_internal srs_verbose("calc s1 digest success."); srs_assert(s1_digest != NULL); - SrsAutoFree(char, s1_digest); + SrsAutoFreeA(char, s1_digest); memcpy(digest.digest, s1_digest, 32); srs_verbose("copy s1 key success."); @@ -27177,7 +27688,7 @@ namespace _srs_internal } srs_assert(s1_digest != NULL); - SrsAutoFree(char, s1_digest); + SrsAutoFreeA(char, s1_digest); is_valid = srs_bytes_equals(digest.digest, s1_digest, 32); @@ -27196,14 +27707,14 @@ namespace _srs_internal * @return a new allocated bytes, user must free it. */ char* c1s1_joined_bytes = new char[1536 -32]; - SrsAutoFree(char, c1s1_joined_bytes); + SrsAutoFreeA(char, c1s1_joined_bytes); if ((ret = copy_to(owner, c1s1_joined_bytes, 1536 - 32, false)) != ERROR_SUCCESS) { return ret; } c1_digest = new char[SRS_OpensslHashSize]; if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 30, c1s1_joined_bytes, 1536 - 32, c1_digest)) != ERROR_SUCCESS) { - srs_freep(c1_digest); + srs_freepa(c1_digest); srs_error("calc digest for c1 failed. ret=%d", ret); return ret; } @@ -27224,14 +27735,14 @@ namespace _srs_internal * @return a new allocated bytes, user must free it. */ char* c1s1_joined_bytes = new char[1536 -32]; - SrsAutoFree(char, c1s1_joined_bytes); + SrsAutoFreeA(char, c1s1_joined_bytes); if ((ret = copy_to(owner, c1s1_joined_bytes, 1536 - 32, false)) != ERROR_SUCCESS) { return ret; } s1_digest = new char[SRS_OpensslHashSize]; if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 36, c1s1_joined_bytes, 1536 - 32, s1_digest)) != ERROR_SUCCESS) { - srs_freep(s1_digest); + srs_freepa(s1_digest); srs_error("calc digest for s1 failed. ret=%d", ret); return ret; } @@ -27516,7 +28027,7 @@ namespace _srs_internal } // client c1 time and version - time = ::time(NULL); + time = (int32_t)::time(NULL); version = 0x80000702; // client c1 version // generate signature by schema @@ -27775,6 +28286,11 @@ int SrsSimpleHandshake::handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrsP if ((ret = hs_bytes->create_c2()) != ERROR_SUCCESS) { return ret; } + + // for simple handshake, copy s1 to c2. + // @see https://github.com/ossrs/srs/issues/418 + memcpy(hs_bytes->c2, hs_bytes->s0s1s2 + 1, 1536); + if ((ret = io->write(hs_bytes->c2, 1536, &nsize)) != ERROR_SUCCESS) { srs_warn("simple handshake write c2 failed. ret=%d", ret); return ret; @@ -27995,7 +28511,7 @@ int SrsComplexHandshake::handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrs /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -28017,7 +28533,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -28211,7 +28727,7 @@ int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, in // only when failed, we must free the data. if ((ret = srs_do_rtmp_create_msg(type, timestamp, data, size, stream_id, ppmsg)) != ERROR_SUCCESS) { - srs_freep(data); + srs_freepa(data); return ret; } @@ -28238,10 +28754,10 @@ int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, s int ret = ERROR_SUCCESS; // the limits of writev iovs. - // for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 + // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 // for linux, generally it's 1024. - static int limits = sysconf(_SC_IOV_MAX); + static int limits = (int)sysconf(_SC_IOV_MAX); #else static int limits = 1024; #endif @@ -28277,7 +28793,7 @@ int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, s /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -28316,7 +28832,7 @@ SrsMessageArray::~SrsMessageArray() // we just free the msgs itself, // both delete and delete[] is ok, // for each msg in msgs is already freed by send_and_free_messages. - srs_freep(msgs); + srs_freepa(msgs); } void SrsMessageArray::free(int count) @@ -28343,7 +28859,7 @@ void SrsMessageArray::zero(int count) /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -28456,8 +28972,9 @@ char SrsFastBuffer::read_1byte() char* SrsFastBuffer::read_slice(int size) { + srs_assert(size >= 0); srs_assert(end - p >= size); - srs_assert(p + size > buffer); + srs_assert(p + size >= buffer); char* ptr = p; p += size; @@ -28468,7 +28985,7 @@ char* SrsFastBuffer::read_slice(int size) void SrsFastBuffer::skip(int size) { srs_assert(end - p >= size); - srs_assert(p + size > buffer); + srs_assert(p + size >= buffer); p += size; } @@ -28529,7 +29046,7 @@ int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size) * to improve read performance, merge some packets then read, * when it on and read small bytes, we sleep to wait more data., * that is, we merge some data to read together. - * @see https://github.com/simple-rtmp-server/srs/issues/241 + * @see https://github.com/ossrs/srs/issues/241 */ if (merged_read && _handler) { _handler->on_read(nread); @@ -28557,7 +29074,7 @@ void SrsFastBuffer::set_merge_read(bool v, IMergeReadHandler* handler) /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -28713,7 +29230,7 @@ int SrsRawH264Stream::mux_sequence_header(string sps, string pps, u_int32_t dts, + 3 + (int)sps.length() + 3 + (int)pps.length(); char* packet = new char[nb_packet]; - SrsAutoFree(char, packet); + SrsAutoFreeA(char, packet); // use stream to generate the h264 packet. SrsStream stream; @@ -28792,7 +29309,7 @@ int SrsRawH264Stream::mux_ipb_frame(char* frame, int nb_frame, string& ibp) // NALUnit int nb_packet = 4 + nb_frame; char* packet = new char[nb_packet]; - SrsAutoFree(char, packet); + SrsAutoFreeA(char, packet); // use stream to generate the h264 packet. SrsStream stream; @@ -28876,7 +29393,7 @@ int SrsRawAacStream::adts_demux(SrsStream* stream, char** pframe, int* pnb_frame // decode the ADTS. // @see aac-iso-13818-7.pdf, page 26 // 6.2 Audio Data Transport Stream, ADTS - // @see https://github.com/simple-rtmp-server/srs/issues/212#issuecomment-64145885 + // @see https://github.com/ossrs/srs/issues/212#issuecomment-64145885 // byte_alignment() // adts_fixed_header: @@ -28939,7 +29456,7 @@ int SrsRawAacStream::adts_demux(SrsStream* stream, char** pframe, int* pnb_frame int8_t channel_configuration = (sfiv >> 6) & 0x07; /*int8_t original = (sfiv >> 5) & 0x01;*/ /*int8_t home = (sfiv >> 4) & 0x01;*/ - //int8_t Emphasis; @remark, Emphasis is removed, @see https://github.com/simple-rtmp-server/srs/issues/212#issuecomment-64154736 + //int8_t Emphasis; @remark, Emphasis is removed, @see https://github.com/ossrs/srs/issues/212#issuecomment-64154736 // 4bits left. // adts_variable_header(), 1.A.2.2.2 Variable Header of ADTS // copyright_identification_bit 1 bslbf @@ -29029,7 +29546,7 @@ int SrsRawAacStream::mux_sequence_header(SrsRawAacStreamCodec* codec, string& sh char samplingFrequencyIndex = codec->sampling_frequency_index; // override the aac samplerate by user specified. - // @see https://github.com/simple-rtmp-server/srs/issues/212#issuecomment-64146899 + // @see https://github.com/ossrs/srs/issues/212#issuecomment-64146899 switch (codec->sound_rate) { case SrsCodecAudioSampleRate11025: samplingFrequencyIndex = 0x0a; break; @@ -29117,7 +29634,7 @@ int SrsRawAacStream::mux_aac2flv(char* frame, int nb_frame, SrsRawAacStreamCodec /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -29139,6 +29656,8 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include +#if !defined(SRS_EXPORT_LIBRTMP) + #include #include using namespace std; @@ -29662,7 +30181,7 @@ int SrsRtspSdp::parse_fmtp_attribute(string attr) } char* tmp_sh = new char[item_value.length()]; - SrsAutoFree(char, tmp_sh); + SrsAutoFreeA(char, tmp_sh); int nb_tmp_sh = ff_hex_to_data((u_int8_t*)tmp_sh, item_value.c_str()); srs_assert(nb_tmp_sh > 0); audio_sh.append(tmp_sh, nb_tmp_sh); @@ -29717,7 +30236,7 @@ string SrsRtspSdp::base64_decode(string value) int nb_output = (int)(value.length() * 2); u_int8_t* output = new u_int8_t[nb_output]; - SrsAutoFree(u_int8_t, output); + SrsAutoFreeA(u_int8_t, output); int ret = srs_av_base64_decode(output, (char*)value.c_str(), nb_output); if (ret <= 0) { @@ -30298,11 +30817,13 @@ int SrsRtspStack::recv_token(std::string& token, SrsRtspTokenState& state, char #endif +#endif + // following is generated by src/protocol/srs_http_stack.cpp /* The MIT License (MIT) - Copyright (c) 2013-2015 SRS(simple-rtmp-server) + Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -30324,6 +30845,8 @@ int SrsRtspStack::recv_token(std::string& token, SrsRtspTokenState& state, char //#include +#if !defined(SRS_EXPORT_LIBRTMP) + #include #include #include @@ -30333,6 +30856,7 @@ using namespace std; //#include //#include //#include +//#include #define SRS_HTTP_DEFAULT_PAGE "index.html" @@ -30420,8 +30944,11 @@ string srs_go_http_detect(char* data, int size) return "application/octet-stream"; // fallback } -// Error replies to the request with the specified error message and HTTP code. -// The error message should be plain text. +int srs_go_http_error(ISrsHttpResponseWriter* w, int code) +{ + return srs_go_http_error(w, code, srs_generate_http_status_text(code)); +} + int srs_go_http_error(ISrsHttpResponseWriter* w, int code, string error) { int ret = ERROR_SUCCESS; @@ -30434,16 +30961,6 @@ int srs_go_http_error(ISrsHttpResponseWriter* w, int code, string error) return ret; } -int srs_http_response_json(ISrsHttpResponseWriter* w, string data) -{ - SrsHttpHeader* h = w->header(); - - h->set_content_length(data.length()); - h->set_content_type("application/json"); - - return w->write((char*)data.data(), (int)data.length()); -} - SrsHttpHeader::SrsHttpHeader() { } @@ -30529,6 +31046,11 @@ ISrsHttpHandler::~ISrsHttpHandler() { } +bool ISrsHttpHandler::is_not_found() +{ + return false; +} + SrsHttpRedirectHandler::SrsHttpRedirectHandler(string u, int c) { url = u; @@ -30542,7 +31064,23 @@ SrsHttpRedirectHandler::~SrsHttpRedirectHandler() int SrsHttpRedirectHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; - // TODO: FIXME: implements it. + + string location = url; + if (!r->query().empty()) { + location += "?" + r->query(); + } + + string msg = "Redirect to" + location; + + w->header()->set_content_type("text/plain; charset=utf-8"); + w->header()->set_content_length(msg.length()); + w->header()->set("Location", location); + w->write_header(code); + + w->write((char*)msg.data(), (int)msg.length()); + w->final_request(); + + srs_info("redirect to %s.", location.c_str()); return ret; } @@ -30554,9 +31092,14 @@ SrsHttpNotFoundHandler::~SrsHttpNotFoundHandler() { } +bool SrsHttpNotFoundHandler::is_not_found() +{ + return true; +} + int SrsHttpNotFoundHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { - return srs_go_http_error(w, SRS_CONSTS_HTTP_NotFound, SRS_CONSTS_HTTP_NotFound_str); + return srs_go_http_error(w, SRS_CONSTS_HTTP_NotFound); } SrsHttpFileServer::SrsHttpFileServer(string root_dir) @@ -30788,6 +31331,14 @@ ISrsHttpMatchHijacker::~ISrsHttpMatchHijacker() { } +ISrsHttpServeMux::ISrsHttpServeMux() +{ +} + +ISrsHttpServeMux::~ISrsHttpServeMux() +{ +} + SrsHttpServeMux::SrsHttpServeMux() { } @@ -30894,7 +31445,7 @@ int SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handler) entry = new SrsHttpMuxEntry(); entry->explicit_match = false; - entry->handler = new SrsHttpRedirectHandler(pattern, SRS_CONSTS_HTTP_MovedPermanently); + entry->handler = new SrsHttpRedirectHandler(pattern, SRS_CONSTS_HTTP_Found); entry->pattern = pattern; entry->handler->entry = entry; @@ -30905,6 +31456,19 @@ int SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handler) return ret; } +bool SrsHttpServeMux::can_serve(ISrsHttpMessage* r) +{ + int ret = ERROR_SUCCESS; + + ISrsHttpHandler* h = NULL; + if ((ret = find_handler(r, &h)) != ERROR_SUCCESS) { + return false; + } + + srs_assert(h); + return !h->is_not_found(); +} + int SrsHttpServeMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) { int ret = ERROR_SUCCESS; @@ -30955,9 +31519,9 @@ int SrsHttpServeMux::find_handler(ISrsHttpMessage* r, ISrsHttpHandler** ph) } } + static ISrsHttpHandler* h404 = new SrsHttpNotFoundHandler(); if (*ph == NULL) { - // TODO: FIXME: memory leak. - *ph = new SrsHttpNotFoundHandler(); + *ph = h404; } return ret; @@ -31032,18 +31596,21 @@ ISrsHttpMessage::ISrsHttpMessage() ISrsHttpMessage::~ISrsHttpMessage() { - srs_freep(_http_ts_send_buffer); + srs_freepa(_http_ts_send_buffer); } char* ISrsHttpMessage::http_ts_send_buffer() { return _http_ts_send_buffer; } + +#endif + // following is generated by src/protocol/srs_protocol_kbps.cpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -31292,11 +31859,936 @@ void SrsKbps::sample() os.sample(); } +// following is generated by src/protocol/srs_protocol_json.cpp +/* +The MIT License (MIT) + +Copyright (c) 2013-2015 SRS(ossrs) + +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 + +using namespace std; + +//#include + +#ifdef SRS_JSON_USE_NXJSON + +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// +/* + * Copyright (c) 2013 Yaroslav Stavnichiy + * + * This file is part of NXJSON. + * + * NXJSON is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * NXJSON is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with NXJSON. If not, see . + */ + +#ifndef NXJSON_H +#define NXJSON_H + +#ifdef __cplusplus +extern "C" { +#endif + + +typedef enum nx_json_type { + NX_JSON_NULL, // this is null value + NX_JSON_OBJECT, // this is an object; properties can be found in child nodes + NX_JSON_ARRAY, // this is an array; items can be found in child nodes + NX_JSON_STRING, // this is a string; value can be found in text_value field + NX_JSON_INTEGER, // this is an integer; value can be found in int_value field + NX_JSON_DOUBLE, // this is a double; value can be found in dbl_value field + NX_JSON_BOOL // this is a boolean; value can be found in int_value field +} nx_json_type; + +typedef struct nx_json { + nx_json_type type; // type of json node, see above + const char* key; // key of the property; for object's children only + const char* text_value; // text value of STRING node + long int_value; // the value of INTEGER or BOOL node + double dbl_value; // the value of DOUBLE node + int length; // number of children of OBJECT or ARRAY + struct nx_json* child; // points to first child + struct nx_json* next; // points to next child + struct nx_json* last_child; +} nx_json; + +typedef int (*nx_json_unicode_encoder)(unsigned int codepoint, char* p, char** endp); + +extern nx_json_unicode_encoder nx_json_unicode_to_utf8; + +const nx_json* nx_json_parse(char* text, nx_json_unicode_encoder encoder); +const nx_json* nx_json_parse_utf8(char* text); +void nx_json_free(const nx_json* js); +const nx_json* nx_json_get(const nx_json* json, const char* key); // get object's property by key +const nx_json* nx_json_item(const nx_json* json, int idx); // get array element by index + + +#ifdef __cplusplus +} +#endif + +#endif /* NXJSON_H */ + +#endif + +// Json marker +#define SRS_JSON_Boolean 0x01 +#define SRS_JSON_String 0x02 +#define SRS_JSON_Object 0x03 +#define SRS_JSON_Integer 0x04 +#define SRS_JSON_Number 0x05 +#define SRS_JSON_Null 0x06 +#define SRS_JSON_Array 0x07 + +class SrsJsonString : public SrsJsonAny +{ +public: + std::string value; + + SrsJsonString(const char* _value) + { + marker = SRS_JSON_String; + if (_value) { + value = _value; + } + } + virtual ~SrsJsonString() + { + } +}; + +class SrsJsonBoolean : public SrsJsonAny +{ +public: + bool value; + + SrsJsonBoolean(bool _value) + { + marker = SRS_JSON_Boolean; + value = _value; + } + virtual ~SrsJsonBoolean() + { + } +}; + +class SrsJsonInteger : public SrsJsonAny +{ +public: + int64_t value; + + SrsJsonInteger(int64_t _value) + { + marker = SRS_JSON_Integer; + value = _value; + } + virtual ~SrsJsonInteger() + { + } +}; + +class SrsJsonNumber : public SrsJsonAny +{ +public: + double value; + + SrsJsonNumber(double _value) + { + marker = SRS_JSON_Number; + value = _value; + } + virtual ~SrsJsonNumber() + { + } +}; + +class SrsJsonNull : public SrsJsonAny +{ +public: + SrsJsonNull() { + marker = SRS_JSON_Null; + } + virtual ~SrsJsonNull() { + } +}; + +SrsJsonAny::SrsJsonAny() +{ + marker = 0; +} + +SrsJsonAny::~SrsJsonAny() +{ +} + +bool SrsJsonAny::is_string() +{ + return marker == SRS_JSON_String; +} + +bool SrsJsonAny::is_boolean() +{ + return marker == SRS_JSON_Boolean; +} + +bool SrsJsonAny::is_number() +{ + return marker == SRS_JSON_Number; +} + +bool SrsJsonAny::is_integer() +{ + return marker == SRS_JSON_Integer; +} + +bool SrsJsonAny::is_object() +{ + return marker == SRS_JSON_Object; +} + +bool SrsJsonAny::is_array() +{ + return marker == SRS_JSON_Array; +} + +bool SrsJsonAny::is_null() +{ + return marker == SRS_JSON_Null; +} + +string SrsJsonAny::to_str() +{ + SrsJsonString* p = dynamic_cast(this); + srs_assert(p != NULL); + return p->value; +} + +bool SrsJsonAny::to_boolean() +{ + SrsJsonBoolean* p = dynamic_cast(this); + srs_assert(p != NULL); + return p->value; +} + +int64_t SrsJsonAny::to_integer() +{ + SrsJsonInteger* p = dynamic_cast(this); + srs_assert(p != NULL); + return p->value; +} + +double SrsJsonAny::to_number() +{ + SrsJsonNumber* p = dynamic_cast(this); + srs_assert(p != NULL); + return p->value; +} + +SrsJsonObject* SrsJsonAny::to_object() +{ + SrsJsonObject* p = dynamic_cast(this); + srs_assert(p != NULL); + return p; +} + +SrsJsonArray* SrsJsonAny::to_array() +{ + SrsJsonArray* p = dynamic_cast(this); + srs_assert(p != NULL); + return p; +} + +SrsJsonAny* SrsJsonAny::str(const char* value) +{ + return new SrsJsonString(value); +} + +SrsJsonAny* SrsJsonAny::boolean(bool value) +{ + return new SrsJsonBoolean(value); +} + +SrsJsonAny* SrsJsonAny::ingeter(int64_t value) +{ + return new SrsJsonInteger(value); +} + +SrsJsonAny* SrsJsonAny::number(double value) +{ + return new SrsJsonNumber(value); +} + +SrsJsonAny* SrsJsonAny::null() +{ + return new SrsJsonNull(); +} + +SrsJsonObject* SrsJsonAny::object() +{ + return new SrsJsonObject(); +} + +SrsJsonArray* SrsJsonAny::array() +{ + return new SrsJsonArray(); +} + +#ifdef SRS_JSON_USE_NXJSON +SrsJsonAny* srs_json_parse_tree_nx_json(const nx_json* node) +{ + if (!node) { + return NULL; + } + + switch (node->type) { + case NX_JSON_NULL: + return SrsJsonAny::null(); + case NX_JSON_STRING: + return SrsJsonAny::str(node->text_value); + case NX_JSON_INTEGER: + return SrsJsonAny::ingeter(node->int_value); + case NX_JSON_DOUBLE: + return SrsJsonAny::number(node->dbl_value); + case NX_JSON_BOOL: + return SrsJsonAny::boolean(node->int_value != 0); + case NX_JSON_OBJECT: { + SrsJsonObject* obj = SrsJsonAny::object(); + for (nx_json* p = node->child; p != NULL; p = p->next) { + SrsJsonAny* value = srs_json_parse_tree_nx_json(p); + if (value) { + obj->set(p->key, value); + } + } + return obj; + } + case NX_JSON_ARRAY: { + SrsJsonArray* arr = SrsJsonAny::array(); + for (nx_json* p = node->child; p != NULL; p = p->next) { + SrsJsonAny* value = srs_json_parse_tree_nx_json(p); + if (value) { + arr->add(value); + } + } + return arr; + } + } + + return NULL; +} + +SrsJsonAny* SrsJsonAny::loads(char* str) +{ + if (!str) { + return NULL; + } + + if (strlen(str) == 0) { + return NULL; + } + + const nx_json* o = nx_json_parse(str, 0); + + SrsJsonAny* json = srs_json_parse_tree_nx_json(o); + + if (o) { + nx_json_free(o); + } + + return json; +} +#endif + +SrsJsonObject::SrsJsonObject() +{ + marker = SRS_JSON_Object; +} + +SrsJsonObject::~SrsJsonObject() +{ + std::vector::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + SrsJsonObjectPropertyType item = *it; + SrsJsonAny* obj = item.second; + srs_freep(obj); + } + properties.clear(); +} + +int SrsJsonObject::count() +{ + return (int)properties.size(); +} + +string SrsJsonObject::key_at(int index) +{ + srs_assert(index < count()); + SrsJsonObjectPropertyType& elem = properties[index]; + return elem.first; +} + +SrsJsonAny* SrsJsonObject::value_at(int index) +{ + srs_assert(index < count()); + SrsJsonObjectPropertyType& elem = properties[index]; + return elem.second; +} + +void SrsJsonObject::set(string key, SrsJsonAny* value) +{ + if (!value) { + srs_warn("add a NULL propertity %s", key.c_str()); + return; + } + + std::vector::iterator it; + + for (it = properties.begin(); it != properties.end(); ++it) { + SrsJsonObjectPropertyType& elem = *it; + std::string name = elem.first; + SrsJsonAny* any = elem.second; + + if (key == name) { + srs_freep(any); + properties.erase(it); + break; + } + } + + properties.push_back(std::make_pair(key, value)); +} + +SrsJsonAny* SrsJsonObject::get_property(string name) +{ + std::vector::iterator it; + + for (it = properties.begin(); it != properties.end(); ++it) { + SrsJsonObjectPropertyType& elem = *it; + std::string key = elem.first; + SrsJsonAny* any = elem.second; + if (key == name) { + return any; + } + } + + return NULL; +} + +SrsJsonAny* SrsJsonObject::ensure_property_string(string name) +{ + SrsJsonAny* prop = get_property(name); + + if (!prop) { + return NULL; + } + + if (!prop->is_string()) { + return NULL; + } + + return prop; +} + +SrsJsonAny* SrsJsonObject::ensure_property_integer(string name) +{ + SrsJsonAny* prop = get_property(name); + + if (!prop) { + return NULL; + } + + if (!prop->is_integer()) { + return NULL; + } + + return prop; +} + +SrsJsonAny* SrsJsonObject::ensure_property_boolean(string name) +{ + SrsJsonAny* prop = get_property(name); + + if (!prop) { + return NULL; + } + + if (!prop->is_boolean()) { + return NULL; + } + + return prop; +} + +SrsJsonArray::SrsJsonArray() +{ + marker = SRS_JSON_Array; +} + +SrsJsonArray::~SrsJsonArray() +{ + std::vector::iterator it; + for (it = properties.begin(); it != properties.end(); ++it) { + SrsJsonAny* item = *it; + srs_freep(item); + } + properties.clear(); +} + +int SrsJsonArray::count() +{ + return (int)properties.size(); +} + +SrsJsonAny* SrsJsonArray::at(int index) +{ + srs_assert(index < count()); + SrsJsonAny* elem = properties[index]; + return elem; +} + +void SrsJsonArray::add(SrsJsonAny* value) +{ + properties.push_back(value); +} + +#ifdef SRS_JSON_USE_NXJSON + +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// +/* + * Copyright (c) 2013 Yaroslav Stavnichiy + * + * This file is part of NXJSON. + * + * NXJSON is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation, either version 3 + * of the License, or (at your option) any later version. + * + * NXJSON is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with NXJSON. If not, see . + */ + +// this file can be #included in your code +#ifndef NXJSON_C +#define NXJSON_C + +#ifdef __cplusplus +extern "C" { +#endif + + +#include +#include +#include +#include + +//#include "nxjson.h" + +// redefine NX_JSON_CALLOC & NX_JSON_FREE to use custom allocator +#ifndef NX_JSON_CALLOC +#define NX_JSON_CALLOC() calloc(1, sizeof(nx_json)) +#define NX_JSON_FREE(json) free((void*)(json)) +#endif + +// redefine NX_JSON_REPORT_ERROR to use custom error reporting +#ifndef NX_JSON_REPORT_ERROR +#define NX_JSON_REPORT_ERROR(msg, p) srs_warn("NXJSON PARSE ERROR (%d): " msg " at %s", __LINE__, p) +#endif + +#define IS_WHITESPACE(c) ((unsigned char)(c)<=(unsigned char)' ') + +static const nx_json dummy={ NX_JSON_NULL }; + +static nx_json* create_json(nx_json_type type, const char* key, nx_json* parent) { + nx_json* js=(nx_json*)NX_JSON_CALLOC(); + memset(js, 0, sizeof(nx_json)); + assert(js); + js->type=type; + js->key=key; + if (!parent->last_child) { + parent->child=parent->last_child=js; + } + else { + parent->last_child->next=js; + parent->last_child=js; + } + parent->length++; + return js; +} + +void nx_json_free(const nx_json* js) { + nx_json* p=js->child; + nx_json* p1; + while (p) { + p1=p->next; + nx_json_free(p); + p=p1; + } + NX_JSON_FREE(js); +} + +static int unicode_to_utf8(unsigned int codepoint, char* p, char** endp) { + // code from http://stackoverflow.com/a/4609989/697313 + if (codepoint<0x80) *p++=codepoint; + else if (codepoint<0x800) *p++=192+codepoint/64, *p++=128+codepoint%64; + else if (codepoint-0xd800u<0x800) return 0; // surrogate must have been treated earlier + else if (codepoint<0x10000) *p++=224+codepoint/4096, *p++=128+codepoint/64%64, *p++=128+codepoint%64; + else if (codepoint<0x110000) *p++=240+codepoint/262144, *p++=128+codepoint/4096%64, *p++=128+codepoint/64%64, *p++=128+codepoint%64; + else return 0; // error + *endp=p; + return 1; +} + +nx_json_unicode_encoder nx_json_unicode_to_utf8=unicode_to_utf8; + +static inline int hex_val(char c) { + if (c>='0' && c<='9') return c-'0'; + if (c>='a' && c<='f') return c-'a'+10; + if (c>='A' && c<='F') return c-'A'+10; + return -1; +} + +static char* unescape_string(char* s, char** end, nx_json_unicode_encoder encoder) { + char* p=s; + char* d=s; + char c; + while ((c=*p++)) { + if (c=='"') { + *d='\0'; + *end=p; + return s; + } + else if (c=='\\') { + switch (*p) { + case '\\': + case '/': + case '"': + *d++=*p++; + break; + case 'b': + *d++='\b'; p++; + break; + case 'f': + *d++='\f'; p++; + break; + case 'n': + *d++='\n'; p++; + break; + case 'r': + *d++='\r'; p++; + break; + case 't': + *d++='\t'; p++; + break; + case 'u': { // unicode + if (!encoder) { + // leave untouched + *d++=c; + break; + } + char* ps=p-1; + int h1, h2, h3, h4; + if ((h1=hex_val(p[1]))<0 || (h2=hex_val(p[2]))<0 || (h3=hex_val(p[3]))<0 || (h4=hex_val(p[4]))<0) { + NX_JSON_REPORT_ERROR("invalid unicode escape", p-1); + return 0; + } + unsigned int codepoint=h1<<12|h2<<8|h3<<4|h4; + if ((codepoint & 0xfc00)==0xd800) { // high surrogate; need one more unicode to succeed + p+=6; + if (p[-1]!='\\' || *p!='u' || (h1=hex_val(p[1]))<0 || (h2=hex_val(p[2]))<0 || (h3=hex_val(p[3]))<0 || (h4=hex_val(p[4]))<0) { + NX_JSON_REPORT_ERROR("invalid unicode surrogate", ps); + return 0; + } + unsigned int codepoint2=h1<<12|h2<<8|h3<<4|h4; + if ((codepoint2 & 0xfc00)!=0xdc00) { + NX_JSON_REPORT_ERROR("invalid unicode surrogate", ps); + return 0; + } + codepoint=0x10000+((codepoint-0xd800)<<10)+(codepoint2-0xdc00); + } + if (!encoder(codepoint, d, &d)) { + NX_JSON_REPORT_ERROR("invalid codepoint", ps); + return 0; + } + p+=5; + break; + } + default: { + // leave untouched + *d++=c; + break; + } + } + } + else { + *d++=c; + } + } + NX_JSON_REPORT_ERROR("no closing quote for string", s); + return 0; +} + +static char* skip_block_comment(char* p) { + // assume p[-2]=='/' && p[-1]=='*' + char* ps=p-2; + if (!*p) { + NX_JSON_REPORT_ERROR("endless comment", ps); + return 0; + } + REPEAT: + p=strchr(p+1, '/'); + if (!p) { + NX_JSON_REPORT_ERROR("endless comment", ps); + return 0; + } + if (p[-1]!='*') goto REPEAT; + return p+1; +} + +static char* parse_key(const char** key, char* p, nx_json_unicode_encoder encoder) { + // on '}' return with *p=='}' + char c; + while ((c=*p++)) { + if (c=='"') { + *key=unescape_string(p, &p, encoder); + if (!*key) return 0; // propagate error + while (*p && IS_WHITESPACE(*p)) p++; + if (*p==':') return p+1; + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; + } + else if (IS_WHITESPACE(c) || c==',') { + // continue + } + else if (c=='}') { + return p-1; + } + else if (c=='/') { + if (*p=='/') { // line comment + char* ps=p-1; + p=strchr(p+1, '\n'); + if (!p) { + NX_JSON_REPORT_ERROR("endless comment", ps); + return 0; // error + } + p++; + } + else if (*p=='*') { // block comment + p=skip_block_comment(p+1); + if (!p) return 0; + } + else { + NX_JSON_REPORT_ERROR("unexpected chars", p-1); + return 0; // error + } + } + else { + NX_JSON_REPORT_ERROR("unexpected chars", p-1); + return 0; // error + } + } + NX_JSON_REPORT_ERROR("unexpected chars", p-1); + return 0; // error +} + +static char* parse_value(nx_json* parent, const char* key, char* p, nx_json_unicode_encoder encoder) { + nx_json* js; + while (1) { + switch (*p) { + case '\0': + NX_JSON_REPORT_ERROR("unexpected end of text", p); + return 0; // error + case ' ': case '\t': case '\n': case '\r': + case ',': + // skip + p++; + break; + case '{': + js=create_json(NX_JSON_OBJECT, key, parent); + p++; + while (1) { + const char* new_key; + p=parse_key(&new_key, p, encoder); + if (!p) return 0; // error + if (*p=='}') return p+1; // end of object + p=parse_value(js, new_key, p, encoder); + if (!p) return 0; // error + } + case '[': + js=create_json(NX_JSON_ARRAY, key, parent); + p++; + while (1) { + p=parse_value(js, 0, p, encoder); + if (!p) return 0; // error + if (*p==']') return p+1; // end of array + } + case ']': + return p; + case '"': + p++; + js=create_json(NX_JSON_STRING, key, parent); + js->text_value=unescape_string(p, &p, encoder); + if (!js->text_value) return 0; // propagate error + return p; + case '-': case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + { + js=create_json(NX_JSON_INTEGER, key, parent); + char* pe; + js->int_value=strtol(p, &pe, 0); + if (pe==p) { + NX_JSON_REPORT_ERROR("invalid number", p); + return 0; // error + } + if (*pe=='.' || *pe=='e' || *pe=='E') { // double value + js->type=NX_JSON_DOUBLE; + js->dbl_value=strtod(p, &pe); + if (pe==p) { + NX_JSON_REPORT_ERROR("invalid number", p); + return 0; // error + } + } + else { + js->dbl_value=js->int_value; + } + return pe; + } + case 't': + if (!strncmp(p, "true", 4)) { + js=create_json(NX_JSON_BOOL, key, parent); + js->int_value=1; + return p+4; + } + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; // error + case 'f': + if (!strncmp(p, "false", 5)) { + js=create_json(NX_JSON_BOOL, key, parent); + js->int_value=0; + return p+5; + } + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; // error + case 'n': + if (!strncmp(p, "null", 4)) { + create_json(NX_JSON_NULL, key, parent); + return p+4; + } + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; // error + case '/': // comment + if (p[1]=='/') { // line comment + char* ps=p; + p=strchr(p+2, '\n'); + if (!p) { + NX_JSON_REPORT_ERROR("endless comment", ps); + return 0; // error + } + p++; + } + else if (p[1]=='*') { // block comment + p=skip_block_comment(p+2); + if (!p) return 0; + } + else { + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; // error + } + break; + default: + NX_JSON_REPORT_ERROR("unexpected chars", p); + return 0; // error + } + } +} + +const nx_json* nx_json_parse_utf8(char* text) { + return nx_json_parse(text, unicode_to_utf8); +} + +const nx_json* nx_json_parse(char* text, nx_json_unicode_encoder encoder) { + nx_json js; + memset(&js, 0, sizeof(nx_json)); + if (!parse_value(&js, 0, text, encoder)) { + if (js.child) nx_json_free(js.child); + return 0; + } + return js.child; +} + +const nx_json* nx_json_get(const nx_json* json, const char* key) { + if (!json || !key) return &dummy; // never return null + nx_json* js; + for (js=json->child; js; js=js->next) { + if (js->key && !strcmp(js->key, key)) return js; + } + return &dummy; // never return null +} + +const nx_json* nx_json_item(const nx_json* json, int idx) { + if (!json) return &dummy; // never return null + nx_json* js; + for (js=json->child; js; js=js->next) { + if (!idx--) return js; + } + return &dummy; // never return null +} + + +#ifdef __cplusplus +} +#endif + +#endif /* NXJSON_C */ + +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// +//////////////////////////////////////////////////////////////////////////////////////////////// + +#endif + + // following is generated by src/libs/srs_librtmp.cpp /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -31320,7 +32812,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -31330,7 +32822,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. using namespace std; //#include -//#include +//#include //#include //#include //#include @@ -31381,20 +32873,20 @@ struct Context SrsRawAacStream aac_raw; // for h264 raw stream, - // @see: https://github.com/simple-rtmp-server/srs/issues/66#issuecomment-62240521 + // @see: https://github.com/ossrs/srs/issues/66#issuecomment-62240521 SrsStream h264_raw_stream; // about SPS, @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62 std::string h264_sps; std::string h264_pps; // whether the sps and pps sent, - // @see https://github.com/simple-rtmp-server/srs/issues/203 + // @see https://github.com/ossrs/srs/issues/203 bool h264_sps_pps_sent; // only send the ssp and pps when both changed. - // @see https://github.com/simple-rtmp-server/srs/issues/204 + // @see https://github.com/ossrs/srs/issues/204 bool h264_sps_changed; bool h264_pps_changed; // for aac raw stream, - // @see: https://github.com/simple-rtmp-server/srs/issues/212#issuecomment-64146250 + // @see: https://github.com/ossrs/srs/issues/212#issuecomment-64146250 SrsStream aac_raw_stream; // the aac sequence header. std::string aac_specific_config; @@ -31422,7 +32914,7 @@ struct Context } }; -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifdef _WIN32 int gettimeofday(struct timeval* tv, struct timezone* tz) { @@ -32534,7 +34026,7 @@ int srs_write_h264_ipb_frame(Context* context, int ret = ERROR_SUCCESS; // when sps or pps not sent, ignore the packet. - // @see https://github.com/simple-rtmp-server/srs/issues/203 + // @see https://github.com/ossrs/srs/issues/203 if (!context->h264_sps_pps_sent) { return ERROR_H264_DROP_BEFORE_SPS_PPS; } @@ -32574,8 +34066,8 @@ int srs_write_h264_sps_pps(Context* context, u_int32_t dts, u_int32_t pts) { int ret = ERROR_SUCCESS; - // only send when both sps and pps changed. - if (!context->h264_sps_changed || !context->h264_pps_changed) { + // send when sps or pps changed. + if (!context->h264_sps_changed && !context->h264_pps_changed) { return ret; } @@ -32625,7 +34117,7 @@ int srs_write_h264_raw_frame(Context* context, context->h264_sps_changed = true; context->h264_sps = sps; - return srs_write_h264_sps_pps(context, dts, pts); + return ret; } // for pps @@ -32641,7 +34133,12 @@ int srs_write_h264_raw_frame(Context* context, context->h264_pps_changed = true; context->h264_pps = pps; - return srs_write_h264_sps_pps(context, dts, pts); + return ret; + } + + // send pps+sps before ipb frames when sps/pps changed. + if ((ret = srs_write_h264_sps_pps(context, dts, pts)) != ERROR_SUCCESS) { + return ret; } // ibp frame. @@ -32667,8 +34164,8 @@ int srs_h264_write_raw_frames(srs_rtmp_t rtmp, } // use the last error - // @see https://github.com/simple-rtmp-server/srs/issues/203 - // @see https://github.com/simple-rtmp-server/srs/issues/204 + // @see https://github.com/ossrs/srs/issues/203 + // @see https://github.com/ossrs/srs/issues/204 int error_code_return = ret; // send each frame. @@ -33210,8 +34707,7 @@ void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value) int64_t srs_utils_time_ms() { - srs_update_system_time_ms(); - return srs_get_system_time_ms(); + return srs_update_system_time_ms(); } int64_t srs_utils_send_bytes(srs_rtmp_t rtmp) @@ -33615,32 +35111,59 @@ int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int } int srs_human_print_rtmp_packet2(char type, u_int32_t timestamp, char* data, int size, u_int32_t pre_timestamp) +{ + return srs_human_print_rtmp_packet3(type, timestamp, data, size, pre_timestamp, 0); +} + +int srs_human_print_rtmp_packet3(char type, u_int32_t timestamp, char* data, int size, u_int32_t pre_timestamp, int64_t pre_now) +{ + return srs_human_print_rtmp_packet4(type, timestamp, data, size, pre_timestamp, pre_now, 0, 0); +} + +int srs_human_print_rtmp_packet4(char type, u_int32_t timestamp, char* data, int size, u_int32_t pre_timestamp, int64_t pre_now, int64_t starttime, int64_t nb_packets) { int ret = ERROR_SUCCESS; + // packets interval in milliseconds. + double pi = 0; + if (pre_now > starttime) { + pi = (pre_now - starttime) / (double)nb_packets; + } + + // global fps(video and audio mixed fps). + double gfps = 0; + if (pi > 0) { + gfps = 1000 / pi; + } + int diff = 0; if (pre_timestamp > 0) { diff = (int)timestamp - (int)pre_timestamp; } + int ndiff = 0; + if (pre_now > 0) { + ndiff = (int)(srs_utils_time_ms() - pre_now); + } + u_int32_t pts; if (srs_utils_parse_timestamp(timestamp, type, data, size, &pts) != 0) { - srs_human_trace("Rtmp packet type=%s, dts=%d, diff=%d, size=%d, DecodeError", - srs_human_flv_tag_type2string(type), timestamp, diff, size + srs_human_trace("Rtmp packet id=%"PRId64"/%.1f/%.1f, type=%s, dts=%d, ndiff=%d, diff=%d, size=%d, DecodeError", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, ndiff, diff, size ); return ret; } if (type == SRS_RTMP_TYPE_VIDEO) { - srs_human_trace("Video packet type=%s, dts=%d, pts=%d, diff=%d, size=%d, %s(%s,%s)", - srs_human_flv_tag_type2string(type), timestamp, pts, diff, size, + srs_human_trace("Video packet id=%"PRId64"/%.1f/%.1f, type=%s, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, %s(%s,%s)", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, pts, ndiff, diff, size, srs_human_flv_video_codec_id2string(srs_utils_flv_video_codec_id(data, size)), srs_human_flv_video_avc_packet_type2string(srs_utils_flv_video_avc_packet_type(data, size)), srs_human_flv_video_frame_type2string(srs_utils_flv_video_frame_type(data, size)) ); } else if (type == SRS_RTMP_TYPE_AUDIO) { - srs_human_trace("Audio packet type=%s, dts=%d, pts=%d, diff=%d, size=%d, %s(%s,%s,%s,%s)", - srs_human_flv_tag_type2string(type), timestamp, pts, diff, size, + srs_human_trace("Audio packet id=%"PRId64"/%.1f/%.1f, type=%s, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d, %s(%s,%s,%s,%s)", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, pts, ndiff, diff, size, srs_human_flv_audio_sound_format2string(srs_utils_flv_audio_sound_format(data, size)), srs_human_flv_audio_sound_rate2string(srs_utils_flv_audio_sound_rate(data, size)), srs_human_flv_audio_sound_size2string(srs_utils_flv_audio_sound_size(data, size)), @@ -33648,8 +35171,8 @@ int srs_human_print_rtmp_packet2(char type, u_int32_t timestamp, char* data, int srs_human_flv_audio_aac_packet_type2string(srs_utils_flv_audio_aac_packet_type(data, size)) ); } else if (type == SRS_RTMP_TYPE_SCRIPT) { - srs_human_verbose("Data packet type=%s, time=%d, diff=%d, size=%d", - srs_human_flv_tag_type2string(type), timestamp, diff, size); + srs_human_verbose("Data packet id=%"PRId64"/%.1f/%.1f, type=%s, time=%d, ndiff=%d, diff=%d, size=%d", + nb_packets, pi, gfps, srs_human_flv_tag_type2string(type), timestamp, ndiff, diff, size); int nparsed = 0; while (nparsed < size) { int nb_parsed_this = 0; @@ -33662,11 +35185,11 @@ int srs_human_print_rtmp_packet2(char type, u_int32_t timestamp, char* data, int char* amf0_str = NULL; srs_human_raw("%s", srs_human_amf0_print(amf0, &amf0_str, NULL)); - srs_freep(amf0_str); + srs_freepa(amf0_str); } } else { - srs_human_trace("Rtmp packet type=%#x, dts=%d, pts=%d, diff=%d, size=%d", - type, timestamp, pts, diff, size); + srs_human_trace("Rtmp packet id=%"PRId64"/%.1f/%.1f, type=%#x, dts=%d, pts=%d, ndiff=%d, diff=%d, size=%d", + nb_packets, pi, gfps, type, timestamp, pts, ndiff, diff, size); } return ret; @@ -33696,7 +35219,7 @@ const char* srs_human_format_time() tm->tm_hour, tm->tm_min, tm->tm_sec, (int)(tv.tv_usec / 1000)); - // for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 + // for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 buf[sizeof(buf) - 1] = 0; return buf; @@ -33727,7 +35250,7 @@ srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp) /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -33751,7 +35274,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #define SOCKET_ETIME ETIME #define SOCKET_ECONNRESET ECONNRESET @@ -33779,7 +35302,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. #define SOCKET_CLEANUP() socket_cleanup() #endif -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #include @@ -33932,7 +35455,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. // the writev() function returns the number of bytes written. On error, -1 is // returned, and errno is set appropriately. if (nb_write <= 0) { - // @see https://github.com/simple-rtmp-server/srs/issues/200 + // @see https://github.com/ossrs/srs/issues/200 if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { return ERROR_SOCKET_TIMEOUT; } @@ -33989,7 +35512,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. } if (nb_write <= 0) { - // @see https://github.com/simple-rtmp-server/srs/issues/200 + // @see https://github.com/ossrs/srs/issues/200 if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) { return ERROR_SOCKET_TIMEOUT; } @@ -34108,7 +35631,7 @@ int SimpleSocketStream::write(void* buf, size_t size, ssize_t* nwrite) /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -34130,7 +35653,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. //#include -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifndef _WIN32 #include #endif @@ -34140,7 +35663,7 @@ using namespace std; //#include //#include -//#include +//#include //#include //#include //#include diff --git a/src/app/htl_app_rtmp_protocol.hpp b/src/app/htl_app_rtmp_protocol.hpp index 67eb77b..5bf16b0 100644 --- a/src/app/htl_app_rtmp_protocol.hpp +++ b/src/app/htl_app_rtmp_protocol.hpp @@ -1,7 +1,7 @@ /* The MIT License (MIT) -Copyright (c) 2013-2015 SRS(simple-rtmp-server) +Copyright (c) 2013-2015 SRS(ossrs) 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 @@ -41,7 +41,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * Windows SRS-LIBRTMP pre-declare ************************************************************** *************************************************************/ -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifdef _WIN32 // include windows first. #include @@ -49,6 +49,7 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. typedef unsigned long long u_int64_t; typedef long long int64_t; typedef unsigned int u_int32_t; + typedef u_int32_t uint32_t; typedef int int32_t; typedef unsigned char u_int8_t; typedef char int8_t; @@ -320,7 +321,7 @@ extern srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size); * @remark for aac, only support profile 1-4, AAC main/LC/SSR/LTP, * @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 23, 1.5.1.1 Audio object type * -* @see https://github.com/simple-rtmp-server/srs/issues/212 +* @see https://github.com/ossrs/srs/issues/212 * @see E.4.2.1 AUDIODATA of video_file_format_spec_v10_1.pdf * * @return 0, success; otherswise, failed. @@ -376,7 +377,7 @@ extern int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size); * @remark, cts = pts - dts * @remark, use srs_h264_startswith_annexb to check whether frame is annexb format. * @example /trunk/research/librtmp/srs_h264_raw_publish.c -* @see https://github.com/simple-rtmp-server/srs/issues/66 +* @see https://github.com/ossrs/srs/issues/66 * * @return 0, success; otherswise, failed. * for dvbsp error, @see srs_h264_is_dvbsp_error(). @@ -418,7 +419,7 @@ extern int srs_h264_write_raw_frames(srs_rtmp_t rtmp, /** * whether error_code is dvbsp(drop video before sps/pps/sequence-header) error. * -* @see https://github.com/simple-rtmp-server/srs/issues/203 +* @see https://github.com/ossrs/srs/issues/203 * @example /trunk/research/librtmp/srs_h264_raw_publish.c * @remark why drop video? * some encoder, for example, ipcamera, will send sps/pps before each IFrame, @@ -429,14 +430,14 @@ extern srs_bool srs_h264_is_dvbsp_error(int error_code); /** * whether error_code is duplicated sps error. * -* @see https://github.com/simple-rtmp-server/srs/issues/204 +* @see https://github.com/ossrs/srs/issues/204 * @example /trunk/research/librtmp/srs_h264_raw_publish.c */ extern srs_bool srs_h264_is_duplicated_sps_error(int error_code); /** * whether error_code is duplicated pps error. * -* @see https://github.com/simple-rtmp-server/srs/issues/204 +* @see https://github.com/ossrs/srs/issues/204 * @example /trunk/research/librtmp/srs_h264_raw_publish.c */ extern srs_bool srs_h264_is_duplicated_pps_error(int error_code); @@ -903,7 +904,19 @@ extern const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_ty * @return an error code for parse the timetstamp to dts and pts. */ extern int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size); +/** + * @param pre_timestamp the previous timestamp in ms to calc the diff. + */ extern int srs_human_print_rtmp_packet2(char type, u_int32_t timestamp, char* data, int size, u_int32_t pre_timestamp); +/** + * @param pre_now the previous system time in ms to calc the ndiff. + */ +extern int srs_human_print_rtmp_packet3(char type, u_int32_t timestamp, char* data, int size, u_int32_t pre_timestamp, int64_t pre_now); +/** + * @param starttime the rtmpdump starttime in ms. + * @param nb_packets the number of packets received, to calc the packets interval in ms. + */ +extern int srs_human_print_rtmp_packet4(char type, u_int32_t timestamp, char* data, int size, u_int32_t pre_timestamp, int64_t pre_now, int64_t starttime, int64_t nb_packets); // log to console, for use srs-librtmp application. extern const char* srs_human_format_time(); @@ -1022,22 +1035,33 @@ typedef void* srs_hijack_io_t; * Windows SRS-LIBRTMP solution ************************************************************** *************************************************************/ -// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213 +// for srs-librtmp, @see https://github.com/ossrs/srs/issues/213 #ifdef _WIN32 + // for time. #define _CRT_SECURE_NO_WARNINGS #include int gettimeofday(struct timeval* tv, struct timezone* tz); #define PRId64 "lld" + // for inet helpers. typedef int socklen_t; const char *inet_ntop(int af, const void *src, char *dst, socklen_t size); + + // for mkdir(). + #include + + // for open(). typedef int mode_t; #define S_IRUSR 0 #define S_IWUSR 0 + #define S_IXUSR 0 #define S_IRGRP 0 #define S_IWGRP 0 + #define S_IXGRP 0 #define S_IROTH 0 + #define S_IXOTH 0 + // for file seek. #include #include #define open _open @@ -1046,14 +1070,19 @@ typedef void* srs_hijack_io_t; #define write _write #define read _read + // for pid. typedef int pid_t; pid_t getpid(void); - #define snprintf _snprintf + + // for socket. ssize_t writev(int fd, const struct iovec *iov, int iovcnt); typedef int64_t useconds_t; int usleep(useconds_t usec); int socket_setup(); int socket_cleanup(); + + // others. + #define snprintf _snprintf #endif #ifdef __cplusplus