/*
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 <htl_stdinc.hpp>
#include <htl_app_rtmp_protocol.hpp>








/*
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_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_AUTO_EMBEDED_TOOL_CHAIN "normal x86/x64 gcc"

#undef SRS_AUTO_HTTP_CORE
#undef SRS_AUTO_HTTP_SERVER
#undef SRS_AUTO_STREAM_CASTER
#undef SRS_AUTO_HTTP_API
#undef SRS_AUTO_NGINX
#undef SRS_AUTO_DVR
#undef SRS_AUTO_HLS
#undef SRS_AUTO_HDS
#undef SRS_AUTO_HTTP_CALLBACK
#undef SRS_AUTO_SSL
#undef SRS_AUTO_FFMPEG_TOOL
#define SRS_AUTO_FFMPEG_STUB
#undef SRS_AUTO_TRANSCODE
#undef SRS_AUTO_INGEST
#undef SRS_AUTO_STAT
#undef SRS_AUTO_GPERF
#undef SRS_AUTO_GPERF_MC
#undef SRS_AUTO_GPERF_MP
#undef SRS_AUTO_GPERF_CP
#undef SRS_AUTO_EMBEDED_CPU
#undef SRS_AUTO_ARM_UBUNTU12
#undef SRS_AUTO_MIPS_UBUNTU12

#undef SRS_AUTO_VERBOSE
#undef SRS_AUTO_INFO
#define SRS_AUTO_TRACE

#define SRS_AUTO_PREFIX "/usr/local/srs"

#define SRS_AUTO_CONSTRIBUTORS "\
winlin<winlin@vip.126.com> \
wenjie.zhao<740936897@qq.com> \
xiangcheng.liu<liuxc0116@foxmail.com> \
naijia.liu<youngcow@youngcow.net> \
alcoholyi<alcoholyi@qq.com> \
byteman<wangchen2011@gmail.com> \
chad.wang<chad.wang.cn@gmail.com> \
suhetao<suhetao@gmail.com> \
Johnny<fengjihu@163.com> \
karthikeyan<keyanmca@gmail.com> \
StevenLiu<lq@chinaffmpeg.org> \
zhengfl<zhengfl_1989@126.com> \
tufang14<breadbean1449@gmail.com> \
allspace<allspace@gmail.com> \
niesongsong<nie950@gmail.com> \
rudeb0t<nimrod@themanxgroup.tw> \
"

#endif

// following is generated by src/core/srs_core.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_CORE_HPP
#define SRS_CORE_HPP

/*
//#include <srs_core.hpp>
*/

// current release version
#define VERSION_MAJOR       2
#define VERSION_MINOR       0
#define VERSION_REVISION    173

// server 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_WEB "http://ossrs.net"
#define RTMP_SIG_SRS_EMAIL "winlin@vip.126.com"
#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_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_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")"

// stable major version
#define VERSION_STABLE 1
#define VERSION_STABLE_BRANCH SRS_XSTR(VERSION_STABLE)".0release"

// internal macros, covert macro values to str,
// see: read https://gcc.gnu.org/onlinedocs/cpp/Stringification.html#Stringification
#define SRS_XSTR(v) SRS_INTERNAL_STR(v)
#define SRS_INTERNAL_STR(v) #v

/**
* the core provides the common defined macros, utilities,
* user must include the srs_core.hpp before any header, or maybe 
* build failed.
*/

// for 32bit os, 2G big file limit for unistd io, 
// ie. read/write/lseek to use 64bits size for huge file.
#ifndef _FILE_OFFSET_BITS
    #define _FILE_OFFSET_BITS 64
#endif

// for int64_t print using PRId64 format.
#ifndef __STDC_FORMAT_MACROS
    #define __STDC_FORMAT_MACROS
#endif

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <inttypes.h>
#endif

#include <assert.h>
#define srs_assert(expression) assert(expression)

#include <stddef.h>
#include <sys/types.h>

// generated by configure.
//#include <srs_auto_headers.hpp>
// important performance options.
//#include <srs_core_performance.hpp>

// free the p and set to NULL.
// p must be a T*.
#define srs_freep(p) \
    if (p) { \
        delete p; \
        p = NULL; \
    } \
    (void)0
// sometimes, the freepa is useless,
// it's recomments to free each elem explicit.
// so we remove the srs_freepa utility.

/**
* disable copy constructor of class,
* to avoid the memory leak or corrupt by copy instance.
*/
#define disable_default_copy(className)\
    private:\
        /** \
        * disable the copy constructor and operator=, donot allow directly copy. \
        */ \
        className(const className&); \
        className& operator= (const className&)

#endif

// following is generated by src/core/srs_core_autofree.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_CORE_AUTO_FREE_HPP
#define SRS_CORE_AUTO_FREE_HPP

/*
//#include <srs_core_autofree.hpp>
*/

//#include <srs_core.hpp>

/**
* 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);
*/
#define SrsAutoFree(className, instance) \
    impl__SrsAutoFree<className> _auto_free_##instance(&instance)
template<class T>
class impl__SrsAutoFree
{
private:
    T** ptr;
public:
    /**
    * auto delete the ptr.
    */
    impl__SrsAutoFree(T** _ptr) {
        ptr = _ptr;
    }
    
    virtual ~impl__SrsAutoFree() {
        if (ptr == NULL || *ptr == NULL) {
            return;
        }
        
        delete *ptr;
        
        *ptr = NULL;
    }
};

#endif
// following is generated by src/core/srs_core_performance.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_CORE_PERFORMANCE_HPP
#define SRS_CORE_PERFORMANCE_HPP

/*
//#include <srs_core_performance.hpp>
*/

//#include <srs_core.hpp>

/**
* this file defines the perfromance options.
*/

/**
* 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 SrsConfig::get_mr_enabled()
* @see SrsConfig::get_mr_sleep_ms()
* @see https://github.com/simple-rtmp-server/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
* 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.
*   3. SRS MR algorithm will read all data then sleep.
* So, the MR algorithm is:
*   while true:
*       read all data from socket.
*       sleep a while
* For example, sleep 120ms. Then there is, and always 120ms data in buffer.
* That is, the latency is 120ms(the sleep time).
*/
#define SRS_PERF_MERGED_READ
// the default config of mr.
#define SRS_PERF_MR_ENABLED false
#define SRS_PERF_MR_SLEEP 350

/**
* the MW(merged-write) send cache time in ms.
* the default value, user can override it in config.
* to improve send performance, cache msgs and send in a time.
* for example, cache 500ms videos and audios, then convert all these
* msgs to iovecs, finally use writev to send.
* @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 SrsConfig::get_mw_sleep_ms()
* @remark the mw sleep and msgs to send, maybe:
*       mw_sleep        msgs        iovs
*       350             43          86
*       400             44          88
*       500             46          92
*       600             46          92
*       700             82          164
*       800             81          162
*       900             80          160
*       1000            88          176
*       1100            91          182
*       1200            89          178
*       1300            119         238
*       1400            120         240
*       1500            119         238
*       1600            131         262
*       1700            131         262
*       1800            133         266
*       1900            141         282
*       2000            150         300
*/
// the default config of mw.
#define SRS_PERF_MW_SLEEP 350
/**
* how many msgs can be send entirely.
* for play clients to get msgs then totally send out.
* for the mw sleep set to 1800, the msgs is about 133.
* @remark, recomment to 128.
*/
#define SRS_PERF_MW_MSGS 128

/**
* whether set the socket send buffer size.
* @see https://github.com/simple-rtmp-server/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
*/
#undef SRS_PERF_MW_SO_RCVBUF
/**
* whether enable the fast vector for qeueue.
* @see https://github.com/simple-rtmp-server/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
*/
#define SRS_PERF_QUEUE_COND_WAIT
#ifdef SRS_PERF_QUEUE_COND_WAIT
    #define SRS_PERF_MW_MIN_MSGS 8
#endif
/**
* the default value of vhost for
* SRS whether use the min latency mode.
* 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
*/
#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
* @remark 0 to disable the chunk stream cache.
*/
#define SRS_PERF_CHUNK_STREAM_CACHE 16

/**
* the gop cache and play cache queue.
*/
// whether gop cache is on.
#define SRS_PERF_GOP_CACHE true
// in seconds, the live queue length.
#define SRS_PERF_PLAY_QUEUE 30

/**
* 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
*/
//#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
#undef 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
* @remark undef it to auto calc it by merged write sleep ms.
* @remark only apply it when SRS_PERF_MW_SO_RCVBUF is defined.
*/
#ifdef SRS_PERF_MW_SO_SNDBUF
    //#define SRS_PERF_SO_SNDBUF_SIZE 1024
    #undef SRS_PERF_SO_SNDBUF_SIZE
#endif

/**
 * define the following macro to enable the fast flv encoder.
 * @see https://github.com/simple-rtmp-server/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)

Permission is hereby granted, free of charge, to any person obtaining a copy of
this software and associated documentation files (the "Software"), to deal in
the Software without restriction, including without limitation the rights to
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:

The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
*/

#ifndef SRS_CORE_MEM_WATCH_HPP
#define SRS_CORE_MEM_WATCH_HPP

/*
//#include <srs_core_mem_watch.hpp>
*/

//#include <srs_core.hpp>

#ifdef SRS_MEM_WATCH

#include <string>

// watch the specified memory.
extern void srs_memory_watch(void* ptr, std::string category, int size);

// unwatch the specified memory.
extern void srs_memory_unwatch(void* ptr);

// report the memory watch.
extern void srs_memory_report();

#endif

#endif

// following is generated by src/kernel/srs_kernel_error.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_KERNEL_ERROR_HPP
#define SRS_KERNEL_ERROR_HPP

/*
//#include <srs_kernel_error.hpp>
*/

//#include <srs_core.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#define ERROR_SUCCESS                       0
#endif

///////////////////////////////////////////////////////
// system error.
///////////////////////////////////////////////////////
#define ERROR_SOCKET_CREATE                 1000
#define ERROR_SOCKET_SETREUSE               1001
#define ERROR_SOCKET_BIND                   1002
#define ERROR_SOCKET_LISTEN                 1003
#define ERROR_SOCKET_CLOSED                 1004
#define ERROR_SOCKET_GET_PEER_NAME          1005
#define ERROR_SOCKET_GET_PEER_IP            1006
#define ERROR_SOCKET_READ                   1007
#define ERROR_SOCKET_READ_FULLY             1008
#define ERROR_SOCKET_WRITE                  1009
#define ERROR_SOCKET_WAIT                   1010
#define ERROR_SOCKET_TIMEOUT                1011
#define ERROR_SOCKET_CONNECT                1012
#define ERROR_ST_SET_EPOLL                  1013
#define ERROR_ST_INITIALIZE                 1014
#define ERROR_ST_OPEN_SOCKET                1015
#define ERROR_ST_CREATE_LISTEN_THREAD       1016
#define ERROR_ST_CREATE_CYCLE_THREAD        1017
#define ERROR_ST_CONNECT                    1018
#define ERROR_SYSTEM_PACKET_INVALID         1019
#define ERROR_SYSTEM_CLIENT_INVALID         1020
#define ERROR_SYSTEM_ASSERT_FAILED          1021
#define ERROR_READER_BUFFER_OVERFLOW        1022
#define ERROR_SYSTEM_CONFIG_INVALID         1023
#define ERROR_SYSTEM_CONFIG_DIRECTIVE       1024
#define ERROR_SYSTEM_CONFIG_BLOCK_START     1025
#define ERROR_SYSTEM_CONFIG_BLOCK_END       1026
#define ERROR_SYSTEM_CONFIG_EOF             1027
#define ERROR_SYSTEM_STREAM_BUSY            1028
#define ERROR_SYSTEM_IP_INVALID             1029
#define ERROR_SYSTEM_FORWARD_LOOP           1030
#define ERROR_SYSTEM_WAITPID                1031
#define ERROR_SYSTEM_BANDWIDTH_KEY          1032
#define ERROR_SYSTEM_BANDWIDTH_DENIED       1033
#define ERROR_SYSTEM_PID_ACQUIRE            1034
#define ERROR_SYSTEM_PID_ALREADY_RUNNING    1035
#define ERROR_SYSTEM_PID_LOCK               1036
#define ERROR_SYSTEM_PID_TRUNCATE_FILE      1037
#define ERROR_SYSTEM_PID_WRITE_FILE         1038
#define ERROR_SYSTEM_PID_GET_FILE_INFO      1039
#define ERROR_SYSTEM_PID_SET_FILE_INFO      1040
#define ERROR_SYSTEM_FILE_ALREADY_OPENED    1041
#define ERROR_SYSTEM_FILE_OPENE             1042
#define ERROR_SYSTEM_FILE_CLOSE             1043
#define ERROR_SYSTEM_FILE_READ              1044
#define ERROR_SYSTEM_FILE_WRITE             1045
#define ERROR_SYSTEM_FILE_EOF               1046
#define ERROR_SYSTEM_FILE_RENAME            1047
#define ERROR_SYSTEM_CREATE_PIPE            1048
#define ERROR_SYSTEM_FILE_SEEK              1049
#define ERROR_SYSTEM_IO_INVALID             1050
#define ERROR_ST_EXCEED_THREADS             1051
#define ERROR_SYSTEM_SECURITY               1052
#define ERROR_SYSTEM_SECURITY_DENY          1053
#define ERROR_SYSTEM_SECURITY_ALLOW         1054
#define ERROR_SYSTEM_TIME                   1055
#define ERROR_SYSTEM_DIR_EXISTS             1056
#define ERROR_SYSTEM_CREATE_DIR             1057
#define ERROR_SYSTEM_KILL                   1058

///////////////////////////////////////////////////////
// RTMP protocol error.
///////////////////////////////////////////////////////
#define ERROR_RTMP_PLAIN_REQUIRED           2000
#define ERROR_RTMP_CHUNK_START              2001
#define ERROR_RTMP_MSG_INVLIAD_SIZE         2002
#define ERROR_RTMP_AMF0_DECODE              2003
#define ERROR_RTMP_AMF0_INVALID             2004
#define ERROR_RTMP_REQ_CONNECT              2005
#define ERROR_RTMP_REQ_TCURL                2006
#define ERROR_RTMP_MESSAGE_DECODE           2007
#define ERROR_RTMP_MESSAGE_ENCODE           2008
#define ERROR_RTMP_AMF0_ENCODE              2009
#define ERROR_RTMP_CHUNK_SIZE               2010
#define ERROR_RTMP_TRY_SIMPLE_HS            2011
#define ERROR_RTMP_CH_SCHEMA                2012
#define ERROR_RTMP_PACKET_SIZE              2013
#define ERROR_RTMP_VHOST_NOT_FOUND          2014
#define ERROR_RTMP_ACCESS_DENIED            2015
#define ERROR_RTMP_HANDSHAKE                2016
#define ERROR_RTMP_NO_REQUEST               2017
#define ERROR_RTMP_HS_SSL_REQUIRE           2018
#define ERROR_RTMP_DURATION_EXCEED          2019
#define ERROR_RTMP_EDGE_PLAY_STATE          2020
#define ERROR_RTMP_EDGE_PUBLISH_STATE       2021
#define ERROR_RTMP_EDGE_PROXY_PULL          2022
#define ERROR_RTMP_EDGE_RELOAD              2023
#define ERROR_RTMP_AGGREGATE                2024
#define ERROR_RTMP_BWTC_DATA                2025
#define ERROR_OpenSslCreateDH               2026
#define ERROR_OpenSslCreateP                2027
#define ERROR_OpenSslCreateG                2028
#define ERROR_OpenSslParseP1024             2029
#define ERROR_OpenSslSetG                   2030
#define ERROR_OpenSslGenerateDHKeys         2031
#define ERROR_OpenSslCopyKey                2032
#define ERROR_OpenSslSha256Update           2033
#define ERROR_OpenSslSha256Init             2034
#define ERROR_OpenSslSha256Final            2035
#define ERROR_OpenSslSha256EvpDigest        2036
#define ERROR_OpenSslSha256DigestSize       2037
#define ERROR_OpenSslGetPeerPublicKey       2038
#define ERROR_OpenSslComputeSharedKey       2039
#define ERROR_RTMP_MIC_CHUNKSIZE_CHANGED    2040
#define ERROR_RTMP_MIC_CACHE_OVERFLOW       2041
#define ERROR_RTSP_TOKEN_NOT_NORMAL         2042
#define ERROR_RTSP_REQUEST_HEADER_EOF       2043
#define ERROR_RTP_HEADER_CORRUPT            2044
#define ERROR_RTP_TYPE96_CORRUPT            2045
#define ERROR_RTP_TYPE97_CORRUPT            2046
#define ERROR_RTSP_AUDIO_CONFIG             2047
//                                           
// system control message, 
// not an error, but special control logic.
// sys ctl: rtmp close stream, support replay.
#define ERROR_CONTROL_RTMP_CLOSE            2998
// FMLE stop publish and republish.
#define ERROR_CONTROL_REPUBLISH             2999

///////////////////////////////////////////////////////
// application level
///////////////////////////////////////////////////////
#define ERROR_HLS_METADATA                  3000
#define ERROR_HLS_DECODE_ERROR              3001
//#define ERROR_HLS_CREATE_DIR                3002
#define ERROR_HLS_OPEN_FAILED               3003
#define ERROR_HLS_WRITE_FAILED              3004
#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_PARSE_HEADER             3009
#define ERROR_HTTP_HANDLER_MATCH_URL        3010
#define ERROR_HTTP_HANDLER_INVALID          3011
#define ERROR_HTTP_API_LOGS                 3012
#define ERROR_HTTP_REMUX_SEQUENCE_HEADER      3013
#define ERROR_HTTP_REMUX_OFFSET_OVERFLOW      3014
#define ERROR_ENCODER_VCODEC                3015
#define ERROR_ENCODER_OUTPUT                3016
#define ERROR_ENCODER_ACHANNELS             3017
#define ERROR_ENCODER_ASAMPLE_RATE          3018
#define ERROR_ENCODER_ABITRATE              3019
#define ERROR_ENCODER_ACODEC                3020
#define ERROR_ENCODER_VPRESET               3021
#define ERROR_ENCODER_VPROFILE              3022
#define ERROR_ENCODER_VTHREADS              3023
#define ERROR_ENCODER_VHEIGHT               3024
#define ERROR_ENCODER_VWIDTH                3025
#define ERROR_ENCODER_VFPS                  3026
#define ERROR_ENCODER_VBITRATE              3027
#define ERROR_ENCODER_FORK                  3028
#define ERROR_ENCODER_LOOP                  3029
#define ERROR_ENCODER_OPEN                  3030
#define ERROR_ENCODER_DUP2                  3031
#define ERROR_ENCODER_PARSE                 3032
#define ERROR_ENCODER_NO_INPUT              3033
#define ERROR_ENCODER_NO_OUTPUT             3034
#define ERROR_ENCODER_INPUT_TYPE            3035
#define ERROR_KERNEL_FLV_HEADER             3036
#define ERROR_KERNEL_FLV_STREAM_CLOSED      3037
#define ERROR_KERNEL_STREAM_INIT            3038
#define ERROR_EDGE_VHOST_REMOVED            3039
#define ERROR_HLS_AVC_TRY_OTHERS            3040
#define ERROR_H264_API_NO_PREFIXED          3041
#define ERROR_FLV_INVALID_VIDEO_TAG         3042
#define ERROR_H264_DROP_BEFORE_SPS_PPS      3043
#define ERROR_H264_DUPLICATED_SPS           3044
#define ERROR_H264_DUPLICATED_PPS           3045
#define ERROR_AAC_REQUIRED_ADTS             3046
#define ERROR_AAC_ADTS_HEADER               3047
#define ERROR_AAC_DATA_INVALID              3048
#define ERROR_HLS_TRY_MP3                   3049
#define ERROR_HTTP_DVR_DISABLED             3050
#define ERROR_HTTP_DVR_REQUEST              3051
#define ERROR_HTTP_JSON_REQUIRED            3052
#define ERROR_HTTP_DVR_CREATE_REQUEST       3053
#define ERROR_HTTP_DVR_NO_TAEGET            3054
#define ERROR_ADTS_ID_NOT_AAC               3055
#define ERROR_HDS_OPEN_F4M_FAILED           3056
#define ERROR_HDS_WRITE_F4M_FAILED          3057
#define ERROR_HDS_OPEN_BOOTSTRAP_FAILED     3058
#define ERROR_HDS_WRITE_BOOTSTRAP_FAILED    3059
#define ERROR_HDS_OPEN_FRAGMENT_FAILED      3060
#define ERROR_HDS_WRITE_FRAGMENT_FAILED     3061
#define ERROR_HLS_NO_STREAM                 3062
#define ERROR_JSON_LOADS                    3063
#define ERROR_RESPONSE_CODE                 3064

///////////////////////////////////////////////////////
// HTTP/StreamCaster protocol error.
///////////////////////////////////////////////////////
#define ERROR_HTTP_PATTERN_EMPTY            4000
#define ERROR_HTTP_PATTERN_DUPLICATED       4001
#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_KERNEL_AAC_STREAM_CLOSED      4006
#define ERROR_AAC_DECODE_ERROR              4007
#define ERROR_KERNEL_MP3_STREAM_CLOSED      4008
#define ERROR_MP3_DECODE_ERROR              4009
#define ERROR_STREAM_CASTER_ENGINE          4010
#define ERROR_STREAM_CASTER_PORT            4011
#define ERROR_STREAM_CASTER_TS_HEADER       4012
#define ERROR_STREAM_CASTER_TS_SYNC_BYTE    4013
#define ERROR_STREAM_CASTER_TS_AF           4014
#define ERROR_STREAM_CASTER_TS_CRC32        4015
#define ERROR_STREAM_CASTER_TS_PSI          4016
#define ERROR_STREAM_CASTER_TS_PAT          4017
#define ERROR_STREAM_CASTER_TS_PMT          4018
#define ERROR_STREAM_CASTER_TS_PSE          4019
#define ERROR_STREAM_CASTER_TS_ES           4020
#define ERROR_STREAM_CASTER_TS_CODEC        4021
#define ERROR_STREAM_CASTER_AVC_SPS         4022
#define ERROR_STREAM_CASTER_AVC_PPS         4023
#define ERROR_STREAM_CASTER_FLV_TAG         4024
#define ERROR_HTTP_RESPONSE_EOF             4025
#define ERROR_HTTP_INVALID_CHUNK_HEADER     4026
#define ERROR_AVC_NALU_UEV                  4027
#define ERROR_AAC_BYTES_INVALID             4028
#define ERROR_HTTP_REQUEST_EOF              4029

///////////////////////////////////////////////////////
// user-define error.
///////////////////////////////////////////////////////
#define ERROR_USER_START                    9000
#define ERROR_USER_DISCONNECT               9001
#define ERROR_SOURCE_NOT_FOUND              9002
#define ERROR_USER_END                      9999

/**
* whether the error code is an system control error.
*/
extern bool srs_is_system_control_error(int error_code);
extern bool srs_is_client_gracefully_close(int error_code);

/**
@remark: use column copy to generate the new error codes.
01234567890
01234567891
01234567892
01234567893
01234567894
01234567895
01234567896
01234567897
01234567898
01234567899
*/

#endif

// following is generated by src/kernel/srs_kernel_log.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_KERNEL_LOG_HPP
#define SRS_KERNEL_LOG_HPP

/*
//#include <srs_kernel_log.hpp>
*/

//#include <srs_core.hpp>

#include <stdio.h>

#include <errno.h>
#include <string.h>

//#include <srs_kernel_consts.hpp>

/**
* the log level, for example:
* if specified Debug level, all level messages will be logged.
* if specified Warn level, only Warn/Error/Fatal level messages will be logged.
*/
class SrsLogLevel
{
public:
    // only used for very verbose debug, generally, 
    // we compile without this level for high performance.
    static const int Verbose = 0x01;
    static const int Info = 0x02;
    static const int Trace = 0x03;
    static const int Warn = 0x04;
    static const int Error = 0x05;
    // specified the disabled level, no log, for utest.
    static const int Disabled = 0x06;
};

/**
* the log interface provides method to write log.
* but we provides some macro, which enable us to disable the log when compile.
* @see also SmtDebug/SmtTrace/SmtWarn/SmtError which is corresponding to Debug/Trace/Warn/Fatal.
*/ 
class ISrsLog
{
public:
    ISrsLog();
    virtual ~ISrsLog();
public:
    /**
    * initialize log utilities.
    */
    virtual int initialize();
public:
    /**
    * log for verbose, very verbose information.
    */
    virtual void verbose(const char* tag, int context_id, const char* fmt, ...);
    /**
    * log for debug, detail information.
    */
    virtual void info(const char* tag, int context_id, const char* fmt, ...);
    /**
    * log for trace, important information.
    */
    virtual void trace(const char* tag, int context_id, const char* fmt, ...);
    /**
    * log for warn, warn is something should take attention, but not a error.
    */
    virtual void warn(const char* tag, int context_id, const char* fmt, ...);
    /**
    * log for error, something error occur, do something about the error, ie. close the connection,
    * but we will donot abort the program.
    */
    virtual void error(const char* tag, int context_id, const char* fmt, ...);
};

// the context for multiple clients.
class ISrsThreadContext
{
public:
    ISrsThreadContext();
    virtual ~ISrsThreadContext();
public:
    virtual int generate_id();
    virtual int get_id();
};

// user must provides a log object
extern ISrsLog* _srs_log;

// user must implements the LogContext and define a global instance.
extern ISrsThreadContext* _srs_context;

// donot print method
#if 1
    #define srs_verbose(msg, ...) _srs_log->verbose(NULL, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_info(msg, ...)    _srs_log->info(NULL, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_trace(msg, ...)   _srs_log->trace(NULL, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_warn(msg, ...)    _srs_log->warn(NULL, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_error(msg, ...)   _srs_log->error(NULL, _srs_context->get_id(), msg, ##__VA_ARGS__)
// use __FUNCTION__ to print c method
#elif 0
    #define srs_verbose(msg, ...) _srs_log->verbose(__FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_info(msg, ...)    _srs_log->info(__FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_trace(msg, ...)   _srs_log->trace(__FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_warn(msg, ...)    _srs_log->warn(__FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_error(msg, ...)   _srs_log->error(__FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__)
// use __PRETTY_FUNCTION__ to print c++ class:method
#else
    #define srs_verbose(msg, ...) _srs_log->verbose(__PRETTY_FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_info(msg, ...)    _srs_log->info(__PRETTY_FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_trace(msg, ...)   _srs_log->trace(__PRETTY_FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_warn(msg, ...)    _srs_log->warn(__PRETTY_FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__)
    #define srs_error(msg, ...)   _srs_log->error(__PRETTY_FUNCTION__, _srs_context->get_id(), msg, ##__VA_ARGS__)
#endif

// TODO: FIXME: add more verbose and info logs.
#ifndef SRS_AUTO_VERBOSE
    #undef srs_verbose
    #define srs_verbose(msg, ...) (void)0
#endif
#ifndef SRS_AUTO_INFO
    #undef srs_info
    #define srs_info(msg, ...) (void)0
#endif
#ifndef SRS_AUTO_TRACE
    #undef srs_trace
    #define srs_trace(msg, ...) (void)0
#endif

#endif

// following is generated by src/kernel/srs_kernel_stream.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_KERNEL_STREAM_HPP
#define SRS_KERNEL_STREAM_HPP

/*
//#include <srs_kernel_stream.hpp>
*/

//#include <srs_core.hpp>

#include <sys/types.h>
#include <string>

/**
* bytes utility, used to:
* convert basic types to bytes,
* build basic types from bytes.
*/
class SrsStream
{
private:
    char* p;
    char* pp;
    char* _bytes;
    int _size;
public:
    SrsStream();
    virtual ~SrsStream();
public:
    /**
    * initialize the stream from bytes.
    * @bytes, the bytes to convert from/to basic types.
    * @size, the size of bytes.
    * @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);
// get the status of stream
public:
    /**
    * get data of stream, set by initialize.
    * current bytes = data() + pos()
    */
    virtual char* data();
    /**
    * the total stream size, set by initialize.
    * left bytes = size() - pos().
    */
    virtual int size();
    /**
    * tell the current pos.
    */
    virtual int pos();
    /**
    * whether stream is empty.
    * if empty, user should never read or write.
    */
    virtual bool empty();
    /**
    * whether required size is ok.
    * @return true if stream can read/write specified required_size bytes.
    * @remark assert required_size positive.
    */
    virtual bool require(int required_size);
// to change stream.
public:
    /**
    * to skip some size.
    * @param size can be any value. positive to forward; nagetive to backward.
    * @remark to skip(pos()) to reset stream.
    * @remark assert initialized, the data() not NULL.
    */
    virtual void skip(int size);
public:
    /**
    * get 1bytes char from stream.
    */
    virtual int8_t read_1bytes();
    /**
    * get 2bytes int from stream.
    */
    virtual int16_t read_2bytes();
    /**
    * get 3bytes int from stream.
    */
    virtual int32_t read_3bytes();
    /**
    * get 4bytes int from stream.
    */
    virtual int32_t read_4bytes();
    /**
    * get 8bytes int from stream.
    */
    virtual int64_t read_8bytes();
    /**
    * get string from stream, length specifies by param len.
    */
    virtual std::string read_string(int len);
    /**
    * get bytes from stream, length specifies by param len.
    */
    virtual void read_bytes(char* data, int size);
public:
    /**
    * write 1bytes char to stream.
    */
    virtual void write_1bytes(int8_t value);
    /**
    * write 2bytes int to stream.
    */
    virtual void write_2bytes(int16_t value);
    /**
    * write 4bytes int to stream.
    */
    virtual void write_4bytes(int32_t value);
    /**
    * write 3bytes int to stream.
    */
    virtual void write_3bytes(int32_t value);
    /**
    * write 8bytes int to stream.
    */
    virtual void write_8bytes(int64_t value);
    /**
    * write string to stream
    */
    virtual void write_string(std::string value);
    /**
    * write bytes to stream
    */
    virtual void write_bytes(char* data, int size);
};

/**
 * the bit stream.
 */
class SrsBitStream
{
private:
    int8_t cb;
    u_int8_t cb_left;
    SrsStream* stream;
public:
    SrsBitStream();
    virtual ~SrsBitStream();
public:
    virtual int initialize(SrsStream* s);
    virtual bool empty();
    virtual int8_t read_bit();
};

#endif
// following is generated by src/kernel/srs_kernel_utility.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_KERNEL_UTILITY_HPP
#define SRS_KERNEL_UTILITY_HPP

/*
//#include <srs_kernel_utility.hpp>
*/

//#include <srs_core.hpp>

#include <string>

class SrsStream;
class SrsBitStream;

// compare
#define srs_min(a, b) (((a) < (b))? (a) : (b))
#define srs_max(a, b) (((a) < (b))? (b) : (a))

// read nalu uev.
extern int srs_avc_nalu_read_uev(SrsBitStream* stream, int32_t& v);
extern int srs_avc_nalu_read_bit(SrsBitStream* stream, int8_t& v);

// get current system time in ms, use cache to avoid performance problem
extern int64_t srs_get_system_time_ms();
extern int64_t srs_get_system_startup_time_ms();
// the deamon st-thread will update it.
extern int64_t srs_update_system_time_ms();

// dns resolve utility, return the resolved ip address.
extern std::string srs_dns_resolve(std::string host);

// whether system is little endian
extern bool srs_is_little_endian();

// replace old_str to new_str of str
extern std::string srs_string_replace(std::string str, std::string old_str, std::string new_str);
// trim char in trim_chars of str
extern std::string srs_string_trim_end(std::string str, std::string trim_chars);
// trim char in trim_chars of str
extern std::string srs_string_trim_start(std::string str, std::string trim_chars);
// remove char in remove_chars of str
extern std::string srs_string_remove(std::string str, std::string remove_chars);
// whether string end with
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);
// whether string contains with
extern bool srs_string_contains(std::string str, std::string flag);

// create dir recursively
extern int srs_create_dir_recursively(std::string dir);

// whether path exists.
extern bool srs_path_exists(std::string path);
// get the dirname of path
extern std::string srs_path_dirname(std::string path);
// get the basename of path
extern std::string srs_path_basename(std::string path);

/**
* whether stream starts with the avc NALU in "AnnexB" 
* from H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
* start code must be "N[00] 00 00 01" where N>=0
* @param pnb_start_code output the size of start code, must >=3. 
*       NULL to ignore.
*/
extern bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code = NULL);

/**
* whether stream starts with the aac ADTS 
* from aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, 1.A.2.2 ADTS.
* start code must be '1111 1111 1111'B, that is 0xFFF
*/
extern bool srs_aac_startswith_adts(SrsStream* stream);

/**
* cacl the crc32 of bytes in buf.
*/
extern u_int32_t srs_crc32(const void* buf, int size);

/**
* Decode a base64-encoded string.
*
* @param out      buffer for decoded data
* @param in       null-terminated input string
* @param out_size size in bytes of the out buffer, must be at
*                 least 3/4 of the length of in
* @return         number of bytes written, or a negative value in case of
*                 invalid input
*/
extern int srs_av_base64_decode(u_int8_t* out, const char* in, int out_size);

/**
* Encode data to base64 and null-terminate.
*
* @param out      buffer for encoded data
* @param out_size size in bytes of the out buffer (including the
*                 null terminator), must be at least AV_BASE64_SIZE(in_size)
* @param in       input buffer containing the data to encode
* @param in_size  size in bytes of the in buffer
* @return         out or NULL in case of error
*/
extern char* srs_av_base64_encode(char* out, int out_size, const u_int8_t* in, int in_size);

/**
 * Calculate the output size needed to base64-encode x bytes to a
 * null-terminated string.
 */
#define SRS_AV_BASE64_SIZE(x)  (((x)+2) / 3 * 4 + 1)

/**
* convert hex string to data.
* for example, p=config='139056E5A0'
* output hex to data={0x13, 0x90, 0x56, 0xe5, 0xa0}
*/
extern int ff_hex_to_data(u_int8_t* data, const char* p);

/**
 * generate the c0 chunk header for msg.
 * @param cache, the cache to write header.
 * @param nb_cache, the size of cache.
 * @return the size of header. 0 if cache not enough.
 */
extern int srs_chunk_header_c0(
    int perfer_cid, u_int32_t timestamp, int32_t payload_length,
    int8_t message_type, int32_t stream_id,
    char* cache, int nb_cache
    );

/**
 * generate the c3 chunk header for msg.
 * @param cache, the cache to write header.
 * @param nb_cache, the size of cache.
 * @return the size of header. 0 if cache not enough.
 */
extern int srs_chunk_header_c3(
    int perfer_cid, u_int32_t timestamp,
    char* cache, int nb_cache
    );

#endif

// following is generated by src/kernel/srs_kernel_flv.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_KERNEL_FLV_HPP
#define SRS_KERNEL_FLV_HPP

/*
//#include <srs_kernel_flv.hpp>
*/
//#include <srs_core.hpp>

#include <string>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <sys/uio.h>
#endif

class SrsStream;
class SrsFileWriter;
class SrsFileReader;

#define SRS_FLV_TAG_HEADER_SIZE 11
#define SRS_FLV_PREVIOUS_TAG_SIZE 4

/****************************************************************************
 *****************************************************************************
 ****************************************************************************/
/**
 5. Protocol Control Messages
 RTMP reserves message type IDs 1-7 for protocol control messages.
 These messages contain information needed by the RTM Chunk Stream
 protocol or RTMP itself. Protocol messages with IDs 1 & 2 are
 reserved for usage with RTM Chunk Stream protocol. Protocol messages
 with IDs 3-6 are reserved for usage of RTMP. Protocol message with ID
 7 is used between edge server and origin server.
 */
#define RTMP_MSG_SetChunkSize                   0x01
#define RTMP_MSG_AbortMessage                   0x02
#define RTMP_MSG_Acknowledgement                0x03
#define RTMP_MSG_UserControlMessage             0x04
#define RTMP_MSG_WindowAcknowledgementSize      0x05
#define RTMP_MSG_SetPeerBandwidth               0x06
#define RTMP_MSG_EdgeAndOriginServerCommand     0x07
/**
 3. Types of messages
 The server and the client send messages over the network to
 communicate with each other. The messages can be of any type which
 includes audio messages, video messages, command messages, shared
 object messages, data messages, and user control messages.
 3.1. Command message
 Command messages carry the AMF-encoded commands between the client
 and the server. These messages have been assigned message type value
 of 20 for AMF0 encoding and message type value of 17 for AMF3
 encoding. These messages are sent to perform some operations like
 connect, createStream, publish, play, pause on the peer. Command
 messages like onstatus, result etc. are used to inform the sender
 about the status of the requested commands. A command message
 consists of command name, transaction ID, and command object that
 contains related parameters. A client or a server can request Remote
 Procedure Calls (RPC) over streams that are communicated using the
 command messages to the peer.
 */
#define RTMP_MSG_AMF3CommandMessage             17 // 0x11
#define RTMP_MSG_AMF0CommandMessage             20 // 0x14
/**
 3.2. Data message
 The client or the server sends this message to send Metadata or any
 user data to the peer. Metadata includes details about the
 data(audio, video etc.) like creation time, duration, theme and so
 on. These messages have been assigned message type value of 18 for
 AMF0 and message type value of 15 for AMF3.
 */
#define RTMP_MSG_AMF0DataMessage                18 // 0x12
#define RTMP_MSG_AMF3DataMessage                15 // 0x0F
/**
 3.3. Shared object message
 A shared object is a Flash object (a collection of name value pairs)
 that are in synchronization across multiple clients, instances, and
 so on. The message types kMsgContainer=19 for AMF0 and
 kMsgContainerEx=16 for AMF3 are reserved for shared object events.
 Each message can contain multiple events.
 */
#define RTMP_MSG_AMF3SharedObject               16 // 0x10
#define RTMP_MSG_AMF0SharedObject               19 // 0x13
/**
 3.4. Audio message
 The client or the server sends this message to send audio data to the
 peer. The message type value of 8 is reserved for audio messages.
 */
#define RTMP_MSG_AudioMessage                   8 // 0x08
/* *
 3.5. Video message
 The client or the server sends this message to send video data to the
 peer. The message type value of 9 is reserved for video messages.
 These messages are large and can delay the sending of other type of
 messages. To avoid such a situation, the video message is assigned
 the lowest priority.
 */
#define RTMP_MSG_VideoMessage                   9 // 0x09
/**
 3.6. Aggregate message
 An aggregate message is a single message that contains a list of submessages.
 The message type value of 22 is reserved for aggregate
 messages.
 */
#define RTMP_MSG_AggregateMessage               22 // 0x16

/****************************************************************************
 *****************************************************************************
 ****************************************************************************/
/**
 * the chunk stream id used for some under-layer message,
 * for example, the PC(protocol control) message.
 */
#define RTMP_CID_ProtocolControl                0x02
/**
 * the AMF0/AMF3 command message, invoke method and return the result, over NetConnection.
 * generally use 0x03.
 */
#define RTMP_CID_OverConnection                 0x03
/**
 * the AMF0/AMF3 command message, invoke method and return the result, over NetConnection,
 * the midst state(we guess).
 * rarely used, e.g. onStatus(NetStream.Play.Reset).
 */
#define RTMP_CID_OverConnection2                0x04
/**
 * the stream message(amf0/amf3), over NetStream.
 * generally use 0x05.
 */
#define RTMP_CID_OverStream                     0x05
/**
 * the stream message(amf0/amf3), over NetStream, the midst state(we guess).
 * rarely used, e.g. play("mp4:mystram.f4v")
 */
#define RTMP_CID_OverStream2                    0x08
/**
 * the stream message(video), over NetStream
 * generally use 0x06.
 */
#define RTMP_CID_Video                          0x06
/**
 * the stream message(audio), over NetStream.
 * generally use 0x07.
 */
#define RTMP_CID_Audio                          0x07

/**
 * 6.1. Chunk Format
 * Extended timestamp: 0 or 4 bytes
 * This field MUST be sent when the normal timsestamp is set to
 * 0xffffff, it MUST NOT be sent if the normal timestamp is set to
 * anything else. So for values less than 0xffffff the normal
 * timestamp field SHOULD be used in which case the extended timestamp
 * MUST NOT be present. For values greater than or equal to 0xffffff
 * the normal timestamp field MUST NOT be used and MUST be set to
 * 0xffffff and the extended timestamp MUST be sent.
 */
#define RTMP_EXTENDED_TIMESTAMP                 0xFFFFFF

/**
 * 4.1. Message Header
 */
class SrsMessageHeader
{
public:
    /**
     * 3bytes.
     * Three-byte field that contains a timestamp delta of the message.
     * @remark, only used for decoding message from chunk stream.
     */
    int32_t timestamp_delta;
    /**
     * 3bytes.
     * Three-byte field that represents the size of the payload in bytes.
     * It is set in big-endian format.
     */
    int32_t payload_length;
    /**
     * 1byte.
     * One byte field to represent the message type. A range of type IDs
     * (1-7) are reserved for protocol control messages.
     */
    int8_t message_type;
    /**
     * 4bytes.
     * Four-byte field that identifies the stream of the message. These
     * bytes are set in little-endian format.
     */
    int32_t stream_id;
    
    /**
     * Four-byte field that contains a timestamp of the message.
     * The 4 bytes are packed in the big-endian order.
     * @remark, used as calc timestamp when decode and encode time.
     * @remark, we use 64bits for large time for jitter detect and hls.
     */
    int64_t timestamp;
public:
    /**
     * get the perfered cid(chunk stream id) which sendout over.
     * set at decoding, and canbe used for directly send message,
     * for example, dispatch to all connections.
     */
    int perfer_cid;
public:
    SrsMessageHeader();
    virtual ~SrsMessageHeader();
public:
    bool is_audio();
    bool is_video();
    bool is_amf0_command();
    bool is_amf0_data();
    bool is_amf3_command();
    bool is_amf3_data();
    bool is_window_ackledgement_size();
    bool is_ackledgement();
    bool is_set_chunk_size();
    bool is_user_control_message();
    bool is_set_peer_bandwidth();
    bool is_aggregate();
public:
    /**
     * create a amf0 script header, set the size and stream_id.
     */
    void initialize_amf0_script(int size, int stream);
    /**
     * create a audio header, set the size, timestamp and stream_id.
     */
    void initialize_audio(int size, u_int32_t time, int stream);
    /**
     * create a video header, set the size, timestamp and stream_id.
     */
    void initialize_video(int size, u_int32_t time, int stream);
};

/**
 * message is raw data RTMP message, bytes oriented,
 * protcol always recv RTMP message, and can send RTMP message or RTMP packet.
 * the common message is read from underlay protocol sdk.
 * while the shared ptr message used to copy and send.
 */
class SrsCommonMessage
{
    // 4.1. Message Header
public:
    SrsMessageHeader header;
    // 4.2. Message Payload
public:
    /**
     * current message parsed size,
     *       size <= header.payload_length
     * for the payload maybe sent in multiple chunks.
     */
    int size;
    /**
     * the payload of message, the SrsCommonMessage never know about the detail of payload,
     * user must use SrsProtocol.decode_message to get concrete packet.
     * @remark, not all message payload can be decoded to packet. for example,
     *       video/audio packet use raw bytes, no video/audio packet.
     */
    char* payload;
public:
    SrsCommonMessage();
    virtual ~SrsCommonMessage();
public:
    /**
     * alloc the payload to specified size of bytes.
     */
    virtual void create_payload(int size);
};

/**
 * the message header for shared ptr message.
 * only the message for all msgs are same.
 */
struct SrsSharedMessageHeader
{
    /**
     * 3bytes.
     * Three-byte field that represents the size of the payload in bytes.
     * It is set in big-endian format.
     */
    int32_t payload_length;
    /**
     * 1byte.
     * One byte field to represent the message type. A range of type IDs
     * (1-7) are reserved for protocol control messages.
     */
    int8_t message_type;
    /**
     * get the perfered cid(chunk stream id) which sendout over.
     * set at decoding, and canbe used for directly send message,
     * for example, dispatch to all connections.
     */
    int perfer_cid;
};

/**
 * shared ptr message.
 * for audio/video/data message that need less memory copy.
 * and only for output.
 *
 * create first object by constructor and create(),
 * use copy if need reference count message.
 *
 */
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
    //SrsSharedMessageHeader header;
    /**
     * Four-byte field that contains a timestamp of the message.
     * The 4 bytes are packed in the big-endian order.
     * @remark, used as calc timestamp when decode and encode time.
     * @remark, we use 64bits for large time for jitter detect and hls.
     */
    int64_t timestamp;
    /**
     * 4bytes.
     * Four-byte field that identifies the stream of the message. These
     * bytes are set in big-endian format.
     */
    int32_t stream_id;
    // 4.2. Message Payload
public:
    /**
     * current message parsed size,
     *       size <= header.payload_length
     * for the payload maybe sent in multiple chunks.
     */
    int size;
    /**
     * the payload of message, the SrsCommonMessage never know about the detail of payload,
     * user must use SrsProtocol.decode_message to get concrete packet.
     * @remark, not all message payload can be decoded to packet. for example,
     *       video/audio packet use raw bytes, no video/audio packet.
     */
    char* payload;
private:
    class SrsSharedPtrPayload
    {
    public:
        // shared message header.
        // @see https://github.com/simple-rtmp-server/srs/issues/251
        SrsSharedMessageHeader header;
        // actual shared payload.
        char* payload;
        // size of payload.
        int size;
        // the reference count
        int shared_count;
    public:
        SrsSharedPtrPayload();
        virtual ~SrsSharedPtrPayload();
    };
    SrsSharedPtrPayload* ptr;
public:
    SrsSharedPtrMessage();
    virtual ~SrsSharedPtrMessage();
public:
    /**
     * create shared ptr message,
     * copy header, manage the payload of msg,
     * set the payload to NULL to prevent double free.
     * @remark payload of msg set to NULL if success.
     */
    virtual int create(SrsCommonMessage* msg);
    /**
     * create shared ptr message,
     * from the header and payload.
     * @remark user should never free the payload.
     * @param pheader, the header to copy to the message. NULL to ignore.
     */
    virtual int create(SrsMessageHeader* pheader, char* payload, int size);
    /**
     * get current reference count.
     * when this object created, count set to 0.
     * if copy() this object, count increase 1.
     * if this or copy deleted, free payload when count is 0, or count--.
     * @remark, assert object is created.
     */
    virtual int count();
    /**
     * check perfer cid and stream id.
     * @return whether stream id already set.
     */
    virtual bool check(int stream_id);
public:
    virtual bool is_av();
    virtual bool is_audio();
    virtual bool is_video();
public:
    /**
     * generate the chunk header to cache.
     * @return the size of header.
     */
    virtual int chunk_header(char* cache, int nb_cache, bool c0);
public:
    /**
     * copy current shared ptr message, use ref-count.
     * @remark, assert object is created.
     */
    virtual SrsSharedPtrMessage* copy();
};

/**
* encode data to flv file.
*/
class SrsFlvEncoder
{
private:
    SrsFileWriter* _fs;
private:
    SrsStream* tag_stream;
    char tag_header[SRS_FLV_TAG_HEADER_SIZE];
public:
    SrsFlvEncoder();
    virtual ~SrsFlvEncoder();
public:
    /**
    * 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.
    */
    virtual int initialize(SrsFileWriter* fs);
public:
    /**
    * write flv header.
    * write following:
    *   1. E.2 The FLV header
    *   2. PreviousTagSize0 UI32 Always 0
    * that is, 9+4=13bytes.
    */
    virtual int write_header();
    virtual int write_header(char flv_header[9]);
    /**
    * write flv metadata. 
    * @param type, the type of data, or other message type.
    *       @see SrsCodecFlvTag
    * @param data, the amf0 metadata which serialize from:
    *   AMF0 string: onMetaData,
    *   AMF0 object: the metadata object.
    * @remark assert data is not NULL.
    */
    virtual int write_metadata(char type, char* data, int size);
    /**
    * write audio/video packet.
    * @remark assert data is not NULL.
    */
    virtual int write_audio(int64_t timestamp, char* data, int size);
    virtual int write_video(int64_t timestamp, char* data, int size);
public:
    /**
    * get the tag size,
    * including the tag header, body, and 4bytes previous tag size.
    * @remark assert data_size is not negative.
    */
    static int size_tag(int data_size);
#ifdef SRS_PERF_FAST_FLV_ENCODER
private:
    // cache tag header.
    int nb_tag_headers;
    char* tag_headers;
    // cache pps(previous tag size)
    int nb_ppts;
    char* ppts;
    // cache iovss.
    int nb_iovss_cache;
    iovec* iovss_cache;
public:
    /**
     * write the tags in a time.
     */
    virtual int write_tags(SrsSharedPtrMessage** msgs, int count);
#endif
private:
    virtual int write_metadata_to_cache(char type, char* data, int size, char* cache);
    virtual int write_audio_to_cache(int64_t timestamp, char* data, int size, char* cache);
    virtual int write_video_to_cache(int64_t timestamp, char* data, int size, char* cache);
    virtual int write_pts_to_cache(int size, char* cache);
    virtual int write_tag(char* header, int header_size, char* tag, int tag_size);
};

/**
* decode flv file.
*/
class SrsFlvDecoder
{
private:
    SrsFileReader* _fs;
private:
    SrsStream* tag_stream;
public:
    SrsFlvDecoder();
    virtual ~SrsFlvDecoder();
public:
    /**
    * 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.
    */
    virtual int initialize(SrsFileReader* fs);
public:
    /**
    * read the flv header, donot including the 4bytes previous tag size.
    * @remark assert header not NULL.
    */
    virtual int read_header(char header[9]);
    /**
    * read the tag header infos.
    * @remark assert ptype/pdata_size/ptime not NULL.
    */
    virtual int read_tag_header(char* ptype, int32_t* pdata_size, u_int32_t* ptime);
    /**
    * read the tag data.
    * @remark assert data not NULL.
    */
    virtual int read_tag_data(char* data, int32_t size);
    /**
    * read the 4bytes previous tag size.
    * @remark assert previous_tag_size not NULL.
    */
    virtual int read_previous_tag_size(char previous_tag_size[4]);
};

/**
* decode flv fast by only decoding the header and tag.
* used for vod flv stream to read the header and sequence header, 
* then seek to specified offset.
*/
class SrsFlvVodStreamDecoder
{
private:
    SrsFileReader* _fs;
private:
    SrsStream* tag_stream;
public:
    SrsFlvVodStreamDecoder();
    virtual ~SrsFlvVodStreamDecoder();
public:
    /**
    * 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.
    */
    virtual int initialize(SrsFileReader* fs);
public:
    /**
    * read the flv header and its size.
    * @param header, fill it 13bytes(9bytes header, 4bytes previous tag size).
    * @remark assert header not NULL.
    */
    virtual int read_header_ext(char header[13]);
    /**
    * read the sequence header tags offset and its size.
    * @param pstart, the start offset of sequence header.
    * @param psize, output the size, (tag header)+(tag body)+(4bytes previous tag size).
    * @remark we think the first audio/video is sequence header.
    * @remark assert pstart/psize not NULL.
    */
    virtual int read_sequence_header_summary(int64_t* pstart, int* psize);
public:
    /**
    * for start offset, seed to this position and response flv stream.
    */
    virtual int lseek(int64_t offset);
};

#endif

// following is generated by src/kernel/srs_kernel_codec.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_KERNEL_CODEC_HPP
#define SRS_KERNEL_CODEC_HPP

/*
//#include <srs_kernel_codec.hpp>
*/

//#include <srs_core.hpp>

#include <string>

class SrsStream;

// AACPacketType IF SoundFormat == 10 UI8
// The following values are defined:
//     0 = AAC sequence header
//     1 = AAC raw
enum SrsCodecAudioType
{
    // set to the max value to reserved, for array map.
    SrsCodecAudioTypeReserved                        = 2,
    
    SrsCodecAudioTypeSequenceHeader                 = 0,
    SrsCodecAudioTypeRawData                         = 1,
};

// E.4.3.1 VIDEODATA
// Frame Type UB [4]
// Type of video frame. The following values are defined:
//     1 = key frame (for AVC, a seekable frame)
//     2 = inter frame (for AVC, a non-seekable frame)
//     3 = disposable inter frame (H.263 only)
//     4 = generated key frame (reserved for server use only)
//     5 = video info/command frame
enum SrsCodecVideoAVCFrame
{
    // set to the zero to reserved, for array map.
    SrsCodecVideoAVCFrameReserved                    = 0,
    SrsCodecVideoAVCFrameReserved1                    = 6,
    
    SrsCodecVideoAVCFrameKeyFrame                     = 1,
    SrsCodecVideoAVCFrameInterFrame                 = 2,
    SrsCodecVideoAVCFrameDisposableInterFrame         = 3,
    SrsCodecVideoAVCFrameGeneratedKeyFrame            = 4,
    SrsCodecVideoAVCFrameVideoInfoFrame                = 5,
};

// AVCPacketType IF CodecID == 7 UI8
// The following values are defined:
//     0 = AVC sequence header
//     1 = AVC NALU
//     2 = AVC end of sequence (lower level NALU sequence ender is
//         not required or supported)
enum SrsCodecVideoAVCType
{
    // set to the max value to reserved, for array map.
    SrsCodecVideoAVCTypeReserved                    = 3,
    
    SrsCodecVideoAVCTypeSequenceHeader                 = 0,
    SrsCodecVideoAVCTypeNALU                         = 1,
    SrsCodecVideoAVCTypeSequenceHeaderEOF             = 2,
};

// E.4.3.1 VIDEODATA
// CodecID UB [4]
// Codec Identifier. The following values are defined:
//     2 = Sorenson H.263
//     3 = Screen video
//     4 = On2 VP6
//     5 = On2 VP6 with alpha channel
//     6 = Screen video version 2
//     7 = AVC
enum SrsCodecVideo
{
    // set to the zero to reserved, for array map.
    SrsCodecVideoReserved                = 0,
    SrsCodecVideoReserved1                = 1,
    SrsCodecVideoReserved2                = 9,
    
    // for user to disable video, for example, use pure audio hls.
    SrsCodecVideoDisabled                = 8,
    
    SrsCodecVideoSorensonH263             = 2,
    SrsCodecVideoScreenVideo             = 3,
    SrsCodecVideoOn2VP6                 = 4,
    SrsCodecVideoOn2VP6WithAlphaChannel = 5,
    SrsCodecVideoScreenVideoVersion2     = 6,
    SrsCodecVideoAVC                     = 7,
};
std::string srs_codec_video2str(SrsCodecVideo codec);

// SoundFormat UB [4] 
// Format of SoundData. The following values are defined:
//     0 = Linear PCM, platform endian
//     1 = ADPCM
//     2 = MP3
//     3 = Linear PCM, little endian
//     4 = Nellymoser 16 kHz mono
//     5 = Nellymoser 8 kHz mono
//     6 = Nellymoser
//     7 = G.711 A-law logarithmic PCM
//     8 = G.711 mu-law logarithmic PCM
//     9 = reserved
//     10 = AAC
//     11 = Speex
//     14 = MP3 8 kHz
//     15 = Device-specific sound
// Formats 7, 8, 14, and 15 are reserved.
// AAC is supported in Flash Player 9,0,115,0 and higher.
// Speex is supported in Flash Player 10 and higher.
enum SrsCodecAudio
{
    // set to the max value to reserved, for array map.
    SrsCodecAudioReserved1                = 16,
    
    // for user to disable audio, for example, use pure video hls.
    SrsCodecAudioDisabled                   = 17,
    
    SrsCodecAudioLinearPCMPlatformEndian             = 0,
    SrsCodecAudioADPCM                                 = 1,
    SrsCodecAudioMP3                                 = 2,
    SrsCodecAudioLinearPCMLittleEndian                 = 3,
    SrsCodecAudioNellymoser16kHzMono                 = 4,
    SrsCodecAudioNellymoser8kHzMono                 = 5,
    SrsCodecAudioNellymoser                         = 6,
    SrsCodecAudioReservedG711AlawLogarithmicPCM        = 7,
    SrsCodecAudioReservedG711MuLawLogarithmicPCM    = 8,
    SrsCodecAudioReserved                             = 9,
    SrsCodecAudioAAC                                 = 10,
    SrsCodecAudioSpeex                                 = 11,
    SrsCodecAudioReservedMP3_8kHz                     = 14,
    SrsCodecAudioReservedDeviceSpecificSound         = 15,
};
std::string srs_codec_audio2str(SrsCodecAudio codec);

/**
* the FLV/RTMP supported audio sample rate.
* Sampling rate. The following values are defined:
* 0 = 5.5 kHz = 5512 Hz
* 1 = 11 kHz = 11025 Hz
* 2 = 22 kHz = 22050 Hz
* 3 = 44 kHz = 44100 Hz
*/
enum SrsCodecAudioSampleRate
{
    // set to the max value to reserved, for array map.
    SrsCodecAudioSampleRateReserved                 = 4,
    
    SrsCodecAudioSampleRate5512                     = 0,
    SrsCodecAudioSampleRate11025                    = 1,
    SrsCodecAudioSampleRate22050                    = 2,
    SrsCodecAudioSampleRate44100                    = 3,
};

/**
* E.4.1 FLV Tag, page 75
*/
enum SrsCodecFlvTag
{
    // set to the zero to reserved, for array map.
    SrsCodecFlvTagReserved = 0,

    // 8 = audio
    SrsCodecFlvTagAudio = 8,
    // 9 = video
    SrsCodecFlvTagVideo = 9,
    // 18 = script data
    SrsCodecFlvTagScript = 18,
};

/**
* Annex E. The FLV File Format
* @see SrsAvcAacCodec for the media stream codec.
*/
class SrsFlvCodec
{
public:
    SrsFlvCodec();
    virtual ~SrsFlvCodec();
// the following function used to finger out the flv/rtmp packet detail.
public:
    /**
    * only check the frame_type, not check the codec type.
    */
    static bool video_is_keyframe(char* data, int size);
    /**
    * check codec h264, keyframe, sequence header
    */
    static bool video_is_sequence_header(char* data, int size);
    /**
    * check codec aac, sequence header
    */
    static bool audio_is_sequence_header(char* data, int size);
    /**
    * check codec h264.
    */
    static bool video_is_h264(char* data, int size);
    /**
    * check codec aac.
    */
    static bool audio_is_aac(char* data, int size);
    /**
     * check the video RTMP/flv header info,
     * @return true if video RTMP/flv header is ok.
     * @remark all type of audio is possible, no need to check audio.
     */
    static bool video_is_acceptable(char* data, int size);
};

/**
* the public data, event HLS disable, others can use it.
*/
/**
* the flv sample rate map
*/
extern int flv_sample_rates[];

/**
* the aac sample rate map
*/
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
* uncompressed formats. Compressed formats always decode
* to 16 bits internally.
* 0 = 8-bit samples
* 1 = 16-bit samples
*/
enum SrsCodecAudioSampleSize
{
    // set to the max value to reserved, for array map.
    SrsCodecAudioSampleSizeReserved                 = 2,
    
    SrsCodecAudioSampleSize8bit                     = 0,
    SrsCodecAudioSampleSize16bit                    = 1,
};

/**
* the FLV/RTMP supported audio sound type/channel.
* Mono or stereo sound
* 0 = Mono sound
* 1 = Stereo sound
*/
enum SrsCodecAudioSoundType
{
    // set to the max value to reserved, for array map.
    SrsCodecAudioSoundTypeReserved                  = 2, 
    
    SrsCodecAudioSoundTypeMono                      = 0,
    SrsCodecAudioSoundTypeStereo                    = 1,
};

/**
 * Table 7-1 - NAL unit type codes, syntax element categories, and NAL unit type classes
 * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
 */
enum SrsAvcNaluType
{
    // Unspecified
    SrsAvcNaluTypeReserved = 0,
    
    // Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp( )
    SrsAvcNaluTypeNonIDR = 1,
    // Coded slice data partition A slice_data_partition_a_layer_rbsp( )
    SrsAvcNaluTypeDataPartitionA = 2,
    // Coded slice data partition B slice_data_partition_b_layer_rbsp( )
    SrsAvcNaluTypeDataPartitionB = 3,
    // Coded slice data partition C slice_data_partition_c_layer_rbsp( )
    SrsAvcNaluTypeDataPartitionC = 4,
    // Coded slice of an IDR picture slice_layer_without_partitioning_rbsp( )
    SrsAvcNaluTypeIDR = 5,
    // Supplemental enhancement information (SEI) sei_rbsp( )
    SrsAvcNaluTypeSEI = 6,
    // Sequence parameter set seq_parameter_set_rbsp( )
    SrsAvcNaluTypeSPS = 7,
    // Picture parameter set pic_parameter_set_rbsp( )
    SrsAvcNaluTypePPS = 8,
    // Access unit delimiter access_unit_delimiter_rbsp( )
    SrsAvcNaluTypeAccessUnitDelimiter = 9,
    // End of sequence end_of_seq_rbsp( )
    SrsAvcNaluTypeEOSequence = 10,
    // End of stream end_of_stream_rbsp( )
    SrsAvcNaluTypeEOStream = 11,
    // Filler data filler_data_rbsp( )
    SrsAvcNaluTypeFilterData = 12,
    // Sequence parameter set extension seq_parameter_set_extension_rbsp( )
    SrsAvcNaluTypeSPSExt = 13,
    // Prefix NAL unit prefix_nal_unit_rbsp( )
    SrsAvcNaluTypePrefixNALU = 14,
    // Subset sequence parameter set subset_seq_parameter_set_rbsp( )
    SrsAvcNaluTypeSubsetSPS = 15,
    // Coded slice of an auxiliary coded picture without partitioning slice_layer_without_partitioning_rbsp( )
    SrsAvcNaluTypeLayerWithoutPartition = 19,
    // Coded slice extension slice_layer_extension_rbsp( )
    SrsAvcNaluTypeCodedSliceExt = 20,
};
std::string srs_codec_avc_nalu2str(SrsAvcNaluType nalu_type);

/**
* the codec sample unit.
* for h.264 video packet, a NALU is a sample unit.
* for aac raw audio packet, a NALU is the entire aac raw data.
* for sequence header, it's not a sample unit.
*/
class SrsCodecSampleUnit
{
public:
    /**
    * the sample bytes is directly ptr to packet bytes,
    * user should never use it when packet destroyed.
    */
    int size;
    char* bytes;
public:
    SrsCodecSampleUnit();
    virtual ~SrsCodecSampleUnit();
};

/**
* the samples in the flv audio/video packet.
* the sample used to analysis a video/audio packet,
* split the h.264 NALUs to buffers, or aac raw data to a buffer,
* and decode the video/audio specified infos.
* 
* the sample unit:
*       a video packet codec in h.264 contains many NALUs, each is a sample unit.
*       a audio packet codec in aac is a sample unit.
* @remark, the video/audio sequence header is not sample unit,
*       all sequence header stores as extra data, 
*       @see SrsAvcAacCodec.avc_extra_data and SrsAvcAacCodec.aac_extra_data
* @remark, user must clear all samples before decode a new video/audio packet.
*/
class SrsCodecSample
{
public:
    /**
    * each audio/video raw data packet will dumps to one or multiple buffers,
    * the buffers will write to hls and clear to reset.
    * generally, aac audio packet corresponding to one buffer,
    * where avc/h264 video packet may contains multiple buffer.
    */
    int nb_sample_units;
    SrsCodecSampleUnit sample_units[SRS_SRS_MAX_CODEC_SAMPLE];
public:
    /**
    * whether the sample is video sample which demux from video packet.
    */
    bool is_video;
    /**
    * CompositionTime, video_file_format_spec_v10_1.pdf, page 78.
    * cts = pts - dts, where dts = flvheader->timestamp.
    */
    int32_t cts;
public:
    // video specified
    SrsCodecVideoAVCFrame frame_type;
    SrsCodecVideoAVCType avc_packet_type;
    // whether sample_units contains IDR frame.
    bool has_idr;
    SrsAvcNaluType first_nalu_type;
public:
    // audio specified
    SrsCodecAudio acodec;
    // audio aac specified.
    SrsCodecAudioSampleRate sound_rate;
    SrsCodecAudioSampleSize sound_size;
    SrsCodecAudioSoundType sound_type;
    SrsCodecAudioType aac_packet_type;
public:
    SrsCodecSample();
    virtual ~SrsCodecSample();
public:
    /**
    * clear all samples.
    * the sample units never copy the bytes, it directly use the ptr,
    * so when video/audio packet is destroyed, the sample must be clear.
    * in a word, user must clear sample before demux it.
    * @remark demux sample use SrsAvcAacCodec.audio_aac_demux or video_avc_demux.
    */
    void clear();
    /**
    * add the a sample unit, it's a h.264 NALU or aac raw data.
    * the sample unit directly use the ptr of packet bytes,
    * so user must never use sample unit when packet is destroyed.
    * in a word, user must clear sample before demux it.
    */
    int add_sample_unit(char* bytes, int size);
};

/**
* the avc payload format, must be ibmf or annexb format.
* we guess by annexb first, then ibmf for the first time,
* and we always use the guessed format for the next time.
*/
enum SrsAvcPayloadFormat
{
    SrsAvcPayloadFormatGuess = 0,
    SrsAvcPayloadFormatAnnexb,
    SrsAvcPayloadFormatIbmf,
};

/**
* the aac profile, for ADTS(HLS/TS)
* @see https://github.com/simple-rtmp-server/srs/issues/310
*/
enum SrsAacProfile
{
    SrsAacProfileReserved = 3,
    
    // @see 7.1 Profiles, aac-iso-13818-7.pdf, page 40
    SrsAacProfileMain = 0,
    SrsAacProfileLC = 1,
    SrsAacProfileSSR = 2,
};
std::string srs_codec_aac_profile2str(SrsAacProfile aac_profile);

/**
* the aac object type, for RTMP sequence header
* for AudioSpecificConfig, @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 33
* for audioObjectType, @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 23
*/
enum SrsAacObjectType
{
    SrsAacObjectTypeReserved = 0,
    
    // Table 1.1 - Audio Object Type definition
    // @see @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 23
    SrsAacObjectTypeAacMain = 1,
    SrsAacObjectTypeAacLC = 2,
    SrsAacObjectTypeAacSSR = 3,
    
    // AAC HE = LC+SBR
    SrsAacObjectTypeAacHE = 5,
    // AAC HEv2 = LC+SBR+PS
    SrsAacObjectTypeAacHEV2 = 29,
};
std::string srs_codec_aac_object2str(SrsAacObjectType aac_object);
// ts/hls/adts audio header profile to RTMP sequence header object type.
SrsAacObjectType srs_codec_aac_ts2rtmp(SrsAacProfile profile);
// RTMP sequence header object type to ts/hls/adts audio header profile.
SrsAacProfile srs_codec_aac_rtmp2ts(SrsAacObjectType object_type);

/**
* the profile for avc/h.264.
* @see Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205.
*/
enum SrsAvcProfile
{
    SrsAvcProfileReserved = 0,
    
    // @see ffmpeg, libavcodec/avcodec.h:2713
    SrsAvcProfileBaseline = 66,
    // FF_PROFILE_H264_CONSTRAINED  (1<<9)  // 8+1; constraint_set1_flag
    // FF_PROFILE_H264_CONSTRAINED_BASELINE (66|FF_PROFILE_H264_CONSTRAINED)
    SrsAvcProfileConstrainedBaseline = 578,
    SrsAvcProfileMain = 77,
    SrsAvcProfileExtended = 88,
    SrsAvcProfileHigh = 100,
    SrsAvcProfileHigh10 = 110,
    SrsAvcProfileHigh10Intra = 2158,
    SrsAvcProfileHigh422 = 122,
    SrsAvcProfileHigh422Intra = 2170,
    SrsAvcProfileHigh444 = 144,
    SrsAvcProfileHigh444Predictive = 244,
    SrsAvcProfileHigh444Intra = 2192,
};
std::string srs_codec_avc_profile2str(SrsAvcProfile profile);

/**
* the level for avc/h.264.
* @see Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 207.
*/
enum SrsAvcLevel
{
    SrsAvcLevelReserved = 0,
    
    SrsAvcLevel_1 = 10,
    SrsAvcLevel_11 = 11,
    SrsAvcLevel_12 = 12,
    SrsAvcLevel_13 = 13,
    SrsAvcLevel_2 = 20,
    SrsAvcLevel_21 = 21,
    SrsAvcLevel_22 = 22,
    SrsAvcLevel_3 = 30,
    SrsAvcLevel_31 = 31,
    SrsAvcLevel_32 = 32,
    SrsAvcLevel_4 = 40,
    SrsAvcLevel_41 = 41,
    SrsAvcLevel_5 = 50,
    SrsAvcLevel_51 = 51,
};
std::string srs_codec_avc_level2str(SrsAvcLevel level);

/**
* the h264/avc and aac codec, for media stream.
*
* to demux the FLV/RTMP video/audio packet to sample,
* add each NALUs of h.264 as a sample unit to sample,
* while the entire aac raw data as a sample unit.
*
* for sequence header,
* demux it and save it in the avc_extra_data and aac_extra_data,
* 
* for the codec info, such as audio sample rate,
* decode from FLV/RTMP header, then use codec info in sequence 
* header to override it.
*/
class SrsAvcAacCodec
{
private:
    SrsStream* stream;
public:
    /**
    * metadata specified
    */
    int             duration;
    int             width;
    int             height;
    int             frame_rate;
    // @see: SrsCodecVideo
    int             video_codec_id;
    int             video_data_rate; // in bps
    // @see: SrsCod ecAudioType
    int             audio_codec_id;
    int             audio_data_rate; // in bps
public:
    /**
    * video specified
    */
    // profile_idc, H.264-AVC-ISO_IEC_14496-10.pdf, page 45.
    SrsAvcProfile   avc_profile; 
    // level_idc, H.264-AVC-ISO_IEC_14496-10.pdf, page 45.
    SrsAvcLevel     avc_level; 
    // lengthSizeMinusOne, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
    int8_t          NAL_unit_length;
    u_int16_t       sequenceParameterSetLength;
    char*           sequenceParameterSetNALUnit;
    u_int16_t       pictureParameterSetLength;
    char*           pictureParameterSetNALUnit;
private:
    // the avc payload format.
    SrsAvcPayloadFormat payload_format;
public:
    /**
    * audio specified
    * audioObjectType, in 1.6.2.1 AudioSpecificConfig, page 33,
    * 1.5.1.1 Audio object type definition, page 23,
    *           in aac-mp4a-format-ISO_IEC_14496-3+2001.pdf.
    */
    SrsAacObjectType    aac_object;
    /**
    * samplingFrequencyIndex
    */
    u_int8_t        aac_sample_rate;
    /**
    * channelConfiguration
    */
    u_int8_t        aac_channels;
public:
    /**
    * the avc extra data, the AVC sequence header,
    * without the flv codec header,
    * @see: ffmpeg, AVCodecContext::extradata
    */
    int             avc_extra_size;
    char*           avc_extra_data;
    /**
    * the aac extra data, the AAC sequence header,
    * without the flv codec header,
    * @see: ffmpeg, AVCodecContext::extradata
    */
    int             aac_extra_size;
    char*           aac_extra_data;
public:
    SrsAvcAacCodec();
    virtual ~SrsAvcAacCodec();
// the following function used for hls to build the sample and codec.
public:
    /**
    * demux the audio packet in aac codec.
    * the packet mux in FLV/RTMP format defined in flv specification.
    * demux the audio speicified data(sound_format, sound_size, ...) to sample.
    * demux the aac specified data(aac_profile, ...) to codec from sequence header.
    * demux the aac raw to sample units.
    */
    virtual int audio_aac_demux(char* data, int size, SrsCodecSample* sample);
    virtual int audio_mp3_demux(char* data, int size, SrsCodecSample* sample);
    /**
    * demux the video packet in h.264 codec.
    * the packet mux in FLV/RTMP format defined in flv specification.
    * demux the video specified data(frame_type, codec_id, ...) to sample.
    * demux the h.264 sepcified data(avc_profile, ...) to codec from sequence header.
    * demux the h.264 NALUs to sampe units.
    */
    virtual int video_avc_demux(char* data, int size, SrsCodecSample* sample);
public:
    /**
    * directly demux the sequence header, without RTMP packet header.
    */
    virtual int audio_aac_sequence_header_demux(char* data, int size);
private:
    /**
    * when avc packet type is SrsCodecVideoAVCTypeSequenceHeader,
    * decode the sps and pps.
    */
    virtual int avc_demux_sps_pps(SrsStream* stream);
    /**
     * decode the sps rbsp stream.
     */
    virtual int avc_demux_sps();
    virtual int avc_demux_sps_rbsp(char* rbsp, int nb_rbsp);
    /**
    * demux the avc NALU in "AnnexB" 
    * from H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
    */
    virtual int avc_demux_annexb_format(SrsStream* stream, SrsCodecSample* sample);
    /**
    * demux the avc NALU in "ISO Base Media File Format" 
    * from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
    */
    virtual int avc_demux_ibmf_format(SrsStream* stream, SrsCodecSample* sample);
};

#endif
// following is generated by src/kernel/srs_kernel_file.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_KERNEL_FILE_HPP
#define SRS_KERNEL_FILE_HPP

/*
//#include <srs_kernel_file.hpp>
*/
//#include <srs_core.hpp>

#include <string>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <sys/uio.h>
#endif

/**
* file writer, to write to file.
*/
class SrsFileWriter
{
private:
    std::string _file;
    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 append mode.
    */
    virtual int open_append(std::string file);
    virtual void close();
public:
    virtual bool is_open();
    virtual void lseek(int64_t offset);
    virtual int64_t tellg();
public:
    /**
    * write to file. 
    * @param pnwrite the output nb_write, NULL to ignore.
    */
    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
     */
    virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite);
};

/**
* file reader, to read from file.
*/
class SrsFileReader
{
private:
    std::string _file;
    int fd;
public:
    SrsFileReader();
    virtual ~SrsFileReader();
public:
    /**
    * open file reader, can open then close then open...
    */
    virtual int open(std::string file);
    virtual void close();
public:
    // TODO: FIXME: extract interface.
    virtual bool is_open();
    virtual int64_t tellg();
    virtual void skip(int64_t size);
    virtual int64_t lseek(int64_t offset);
    virtual int64_t filesize();
public:
    /**
    * read from file. 
    * @param pnread the output nb_read, NULL to ignore.
    */
    virtual int read(void* buf, size_t count, ssize_t* pnread);
};

#endif

// following is generated by src/kernel/srs_kernel_consts.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_KERNEL_CONSTS_HPP
#define SRS_KERNEL_CONSTS_HPP

/*
//#include <srs_kernel_consts.hpp>
*/

//#include <srs_core.hpp>

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
// RTMP consts values
///////////////////////////////////////////////////////////
// default vhost of rtmp
#define SRS_CONSTS_RTMP_DEFAULT_VHOST "__defaultVhost__"
// default port of rtmp
#define SRS_CONSTS_RTMP_DEFAULT_PORT "1935"

// the default chunk size for system.
#define SRS_CONSTS_RTMP_SRS_CHUNK_SIZE 60000
// 6. Chunking, RTMP protocol default chunk size.
#define SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE 128

/**
* 6. Chunking
* The chunk size is configurable. It can be set using a control
* message(Set Chunk Size) as described in section 7.1. The maximum
* chunk size can be 65536 bytes and minimum 128 bytes. Larger values
* reduce CPU usage, but also commit to larger writes that can delay
* other content on lower bandwidth connections. Smaller chunks are not
* good for high-bit rate streaming. Chunk size is maintained
* independently for each direction.
*/
#define SRS_CONSTS_RTMP_MIN_CHUNK_SIZE 128
#define SRS_CONSTS_RTMP_MAX_CHUNK_SIZE 65536

 
// the following is the timeout for rtmp protocol, 
// to avoid death connection.

// the timeout to send data to client,
// if timeout, close the connection.
#define SRS_CONSTS_RTMP_SEND_TIMEOUT_US (int64_t)(30*1000*1000LL)

// 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,
// generally, it's the pulse time for data seding.
// @remark, recomment to 500ms.
#define SRS_CONSTS_RTMP_PULSE_TIMEOUT_US (int64_t)(500*1000LL)

/**
* max rtmp header size:
*     1bytes basic header,
*     11bytes message header,
*     4bytes timestamp header,
* that is, 1+11+4=16bytes.
*/
#define SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE 16
/**
* max rtmp header size:
*     1bytes basic header,
*     4bytes timestamp header,
* that is, 1+4=5bytes.
*/
// always use fmt0 as cache.
#define SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE 5

/**
* for performance issue, 
* the iovs cache, @see https://github.com/simple-rtmp-server/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)
*
* @remark, SRS will realloc when the iovs not enough.
*/
#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
* 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,
* so the c0c3 cache should be (SRS_PERF_MW_MSGS * 16)
*
* @remark, SRS will try another loop when c0c3 cache dry, for we cannot realloc it.
*       so we use larger c0c3 cache, that is (SRS_PERF_MW_MSGS * 32)
*/
#define SRS_CONSTS_C0C3_HEADERS_MAX (SRS_PERF_MW_MSGS * 32)

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
// SRS consts values
///////////////////////////////////////////////////////////
#define SRS_CONSTS_NULL_FILE "/dev/null"
#define SRS_CONSTS_LOCALHOST "127.0.0.1"

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
// log consts values
///////////////////////////////////////////////////////////
// downloading speed-up, play to edge, ingest from origin
#define SRS_CONSTS_LOG_EDGE_PLAY "EIG"
// uploading speed-up, publish to edge, foward to origin
#define SRS_CONSTS_LOG_EDGE_PUBLISH "EFW"
// edge/origin forwarder.
#define SRS_CONSTS_LOG_FOWARDER "FWR"
// play stream on edge/origin.
#define SRS_CONSTS_LOG_PLAY "PLA"
// client publish to edge/origin
#define SRS_CONSTS_LOG_CLIENT_PUBLISH "CPB"
// web/flash publish to edge/origin
#define SRS_CONSTS_LOG_WEB_PUBLISH "WPB"
// ingester for edge(play)/origin
#define SRS_CONSTS_LOG_INGESTER "IGS"
// hls log id.
#define SRS_CONSTS_LOG_HLS "HLS"
// encoder log id.
#define SRS_CONSTS_LOG_ENCODER "ENC"
// http stream log id.
#define SRS_CONSTS_LOG_HTTP_STREAM "HTS"
// http stream cache log id.
#define SRS_CONSTS_LOG_HTTP_STREAM_CACHE "HTC"
// stream caster log id.
#define SRS_CONSTS_LOG_STREAM_CASTER "SCS"

///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////

///////////////////////////////////////////////////////////
// RTMP consts values
///////////////////////////////////////////////////////////
#define SRS_CONSTS_RTMP_SET_DATAFRAME            "@setDataFrame"
#define SRS_CONSTS_RTMP_ON_METADATA              "onMetaData"

///////////////////////////////////////////////////////////
// HTTP/HLS consts values
///////////////////////////////////////////////////////////
// @see hls-m3u8-draft-pantos-http-live-streaming-12.pdf, page 4
// Lines are terminated by either a single LF character or a CR
// character followed by an LF character.
// CR             = <US-ASCII CR, carriage return (13)>
#define SRS_CONSTS_CR '\r' // 0x0D
// LF             = <US-ASCII LF, linefeed (10)>
#define SRS_CONSTS_LF '\n' // 0x0A

///////////////////////////////////////////////////////////
// HTTP consts values
///////////////////////////////////////////////////////////
// linux path seprator
#define SRS_CONSTS_HTTP_PATH_SEP '/'
// query string seprator
#define SRS_CONSTS_HTTP_QUERY_SEP '?'

// the default recv timeout.
#define SRS_HTTP_RECV_TIMEOUT_US 60 * 1000 * 1000

// 6.1.1 Status Code and Reason Phrase
#define SRS_CONSTS_HTTP_Continue                       100
#define SRS_CONSTS_HTTP_SwitchingProtocols             101
#define SRS_CONSTS_HTTP_OK                             200
#define SRS_CONSTS_HTTP_Created                        201
#define SRS_CONSTS_HTTP_Accepted                       202
#define SRS_CONSTS_HTTP_NonAuthoritativeInformation    203
#define SRS_CONSTS_HTTP_NoContent                      204
#define SRS_CONSTS_HTTP_ResetContent                   205
#define SRS_CONSTS_HTTP_PartialContent                 206
#define SRS_CONSTS_HTTP_MultipleChoices                300
#define SRS_CONSTS_HTTP_MovedPermanently               301
#define SRS_CONSTS_HTTP_Found                          302
#define SRS_CONSTS_HTTP_SeeOther                       303
#define SRS_CONSTS_HTTP_NotModified                    304
#define SRS_CONSTS_HTTP_UseProxy                       305
#define SRS_CONSTS_HTTP_TemporaryRedirect              307
#define SRS_CONSTS_HTTP_BadRequest                     400
#define SRS_CONSTS_HTTP_Unauthorized                   401
#define SRS_CONSTS_HTTP_PaymentRequired                402
#define SRS_CONSTS_HTTP_Forbidden                      403
#define SRS_CONSTS_HTTP_NotFound                       404
#define SRS_CONSTS_HTTP_MethodNotAllowed               405
#define SRS_CONSTS_HTTP_NotAcceptable                  406
#define SRS_CONSTS_HTTP_ProxyAuthenticationRequired    407
#define SRS_CONSTS_HTTP_RequestTimeout                 408
#define SRS_CONSTS_HTTP_Conflict                       409
#define SRS_CONSTS_HTTP_Gone                           410
#define SRS_CONSTS_HTTP_LengthRequired                 411
#define SRS_CONSTS_HTTP_PreconditionFailed             412
#define SRS_CONSTS_HTTP_RequestEntityTooLarge          413
#define SRS_CONSTS_HTTP_RequestURITooLarge             414
#define SRS_CONSTS_HTTP_UnsupportedMediaType           415
#define SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable   416
#define SRS_CONSTS_HTTP_ExpectationFailed              417
#define SRS_CONSTS_HTTP_InternalServerError            500
#define SRS_CONSTS_HTTP_NotImplemented                 501
#define SRS_CONSTS_HTTP_BadGateway                     502
#define SRS_CONSTS_HTTP_ServiceUnavailable             503
#define SRS_CONSTS_HTTP_GatewayTimeout                 504
#define SRS_CONSTS_HTTP_HTTPVersionNotSupported        505

#define SRS_CONSTS_HTTP_Continue_str                           "Continue"
#define SRS_CONSTS_HTTP_SwitchingProtocols_str                 "Switching Protocols"
#define SRS_CONSTS_HTTP_OK_str                                 "OK"
#define SRS_CONSTS_HTTP_Created_str                            "Created"
#define SRS_CONSTS_HTTP_Accepted_str                           "Accepted"
#define SRS_CONSTS_HTTP_NonAuthoritativeInformation_str        "Non Authoritative Information"
#define SRS_CONSTS_HTTP_NoContent_str                          "No Content"
#define SRS_CONSTS_HTTP_ResetContent_str                       "Reset Content"
#define SRS_CONSTS_HTTP_PartialContent_str                     "Partial Content"
#define SRS_CONSTS_HTTP_MultipleChoices_str                    "Multiple Choices"
#define SRS_CONSTS_HTTP_MovedPermanently_str                   "Moved Permanently"
#define SRS_CONSTS_HTTP_Found_str                              "Found"
#define SRS_CONSTS_HTTP_SeeOther_str                           "See Other"
#define SRS_CONSTS_HTTP_NotModified_str                        "Not Modified"
#define SRS_CONSTS_HTTP_UseProxy_str                           "Use Proxy"
#define SRS_CONSTS_HTTP_TemporaryRedirect_str                  "Temporary Redirect"
#define SRS_CONSTS_HTTP_BadRequest_str                         "Bad Request"
#define SRS_CONSTS_HTTP_Unauthorized_str                       "Unauthorized"
#define SRS_CONSTS_HTTP_PaymentRequired_str                    "Payment Required"
#define SRS_CONSTS_HTTP_Forbidden_str                          "Forbidden"
#define SRS_CONSTS_HTTP_NotFound_str                           "Not Found"
#define SRS_CONSTS_HTTP_MethodNotAllowed_str                   "Method Not Allowed"
#define SRS_CONSTS_HTTP_NotAcceptable_str                      "Not Acceptable"
#define SRS_CONSTS_HTTP_ProxyAuthenticationRequired_str        "Proxy Authentication Required"
#define SRS_CONSTS_HTTP_RequestTimeout_str                     "Request Timeout"
#define SRS_CONSTS_HTTP_Conflict_str                           "Conflict"
#define SRS_CONSTS_HTTP_Gone_str                               "Gone"
#define SRS_CONSTS_HTTP_LengthRequired_str                     "Length Required"
#define SRS_CONSTS_HTTP_PreconditionFailed_str                 "Precondition Failed"
#define SRS_CONSTS_HTTP_RequestEntityTooLarge_str              "Request Entity Too Large"
#define SRS_CONSTS_HTTP_RequestURITooLarge_str                 "Request URI Too Large"
#define SRS_CONSTS_HTTP_UnsupportedMediaType_str               "Unsupported Media Type"
#define SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable_str       "Requested Range Not Satisfiable"
#define SRS_CONSTS_HTTP_ExpectationFailed_str                  "Expectation Failed"
#define SRS_CONSTS_HTTP_InternalServerError_str                "Internal Server Error"
#define SRS_CONSTS_HTTP_NotImplemented_str                     "Not Implemented"
#define SRS_CONSTS_HTTP_BadGateway_str                         "Bad Gateway"
#define SRS_CONSTS_HTTP_ServiceUnavailable_str                 "Service Unavailable"
#define SRS_CONSTS_HTTP_GatewayTimeout_str                     "Gateway Timeout"
#define SRS_CONSTS_HTTP_HTTPVersionNotSupported_str            "HTTP Version Not Supported"

///////////////////////////////////////////////////////////
// RTSP consts values
///////////////////////////////////////////////////////////
// 7.1.1 Status Code and Reason Phrase
#define SRS_CONSTS_RTSP_Continue                       100
#define SRS_CONSTS_RTSP_OK                             200
#define SRS_CONSTS_RTSP_Created                        201
#define SRS_CONSTS_RTSP_LowOnStorageSpace              250
#define SRS_CONSTS_RTSP_MultipleChoices                300
#define SRS_CONSTS_RTSP_MovedPermanently               301
#define SRS_CONSTS_RTSP_MovedTemporarily               302
#define SRS_CONSTS_RTSP_SeeOther                       303
#define SRS_CONSTS_RTSP_NotModified                    304
#define SRS_CONSTS_RTSP_UseProxy                       305
#define SRS_CONSTS_RTSP_BadRequest                     400
#define SRS_CONSTS_RTSP_Unauthorized                   401
#define SRS_CONSTS_RTSP_PaymentRequired                402
#define SRS_CONSTS_RTSP_Forbidden                      403
#define SRS_CONSTS_RTSP_NotFound                       404
#define SRS_CONSTS_RTSP_MethodNotAllowed               405
#define SRS_CONSTS_RTSP_NotAcceptable                  406
#define SRS_CONSTS_RTSP_ProxyAuthenticationRequired    407
#define SRS_CONSTS_RTSP_RequestTimeout                 408
#define SRS_CONSTS_RTSP_Gone                           410
#define SRS_CONSTS_RTSP_LengthRequired                 411
#define SRS_CONSTS_RTSP_PreconditionFailed             412
#define SRS_CONSTS_RTSP_RequestEntityTooLarge          413
#define SRS_CONSTS_RTSP_RequestURITooLarge             414
#define SRS_CONSTS_RTSP_UnsupportedMediaType           415
#define SRS_CONSTS_RTSP_ParameterNotUnderstood         451
#define SRS_CONSTS_RTSP_ConferenceNotFound             452
#define SRS_CONSTS_RTSP_NotEnoughBandwidth             453
#define SRS_CONSTS_RTSP_SessionNotFound                454
#define SRS_CONSTS_RTSP_MethodNotValidInThisState      455
#define SRS_CONSTS_RTSP_HeaderFieldNotValidForResource 456
#define SRS_CONSTS_RTSP_InvalidRange                   457
#define SRS_CONSTS_RTSP_ParameterIsReadOnly            458
#define SRS_CONSTS_RTSP_AggregateOperationNotAllowed   459
#define SRS_CONSTS_RTSP_OnlyAggregateOperationAllowed  460
#define SRS_CONSTS_RTSP_UnsupportedTransport           461
#define SRS_CONSTS_RTSP_DestinationUnreachable         462
#define SRS_CONSTS_RTSP_InternalServerError            500
#define SRS_CONSTS_RTSP_NotImplemented                 501
#define SRS_CONSTS_RTSP_BadGateway                     502
#define SRS_CONSTS_RTSP_ServiceUnavailable             503
#define SRS_CONSTS_RTSP_GatewayTimeout                 504
#define SRS_CONSTS_RTSP_RTSPVersionNotSupported        505
#define SRS_CONSTS_RTSP_OptionNotSupported             551

#define SRS_CONSTS_RTSP_Continue_str                            "Continue"
#define SRS_CONSTS_RTSP_OK_str                                  "OK"
#define SRS_CONSTS_RTSP_Created_str                             "Created"
#define SRS_CONSTS_RTSP_LowOnStorageSpace_str                   "Low on Storage Space"
#define SRS_CONSTS_RTSP_MultipleChoices_str                     "Multiple Choices"
#define SRS_CONSTS_RTSP_MovedPermanently_str                    "Moved Permanently"
#define SRS_CONSTS_RTSP_MovedTemporarily_str                    "Moved Temporarily"
#define SRS_CONSTS_RTSP_SeeOther_str                            "See Other"
#define SRS_CONSTS_RTSP_NotModified_str                         "Not Modified"
#define SRS_CONSTS_RTSP_UseProxy_str                            "Use Proxy"
#define SRS_CONSTS_RTSP_BadRequest_str                          "Bad Request"
#define SRS_CONSTS_RTSP_Unauthorized_str                        "Unauthorized"
#define SRS_CONSTS_RTSP_PaymentRequired_str                     "Payment Required"
#define SRS_CONSTS_RTSP_Forbidden_str                           "Forbidden"
#define SRS_CONSTS_RTSP_NotFound_str                            "Not Found"
#define SRS_CONSTS_RTSP_MethodNotAllowed_str                    "Method Not Allowed"
#define SRS_CONSTS_RTSP_NotAcceptable_str                       "Not Acceptable"
#define SRS_CONSTS_RTSP_ProxyAuthenticationRequired_str         "Proxy Authentication Required"
#define SRS_CONSTS_RTSP_RequestTimeout_str                      "Request Timeout"
#define SRS_CONSTS_RTSP_Gone_str                                "Gone"
#define SRS_CONSTS_RTSP_LengthRequired_str                      "Length Required"
#define SRS_CONSTS_RTSP_PreconditionFailed_str                  "Precondition Failed"
#define SRS_CONSTS_RTSP_RequestEntityTooLarge_str               "Request Entity Too Large"
#define SRS_CONSTS_RTSP_RequestURITooLarge_str                  "Request URI Too Large"
#define SRS_CONSTS_RTSP_UnsupportedMediaType_str                "Unsupported Media Type"
#define SRS_CONSTS_RTSP_ParameterNotUnderstood_str              "Invalid parameter"
#define SRS_CONSTS_RTSP_ConferenceNotFound_str                  "Illegal Conference Identifier"
#define SRS_CONSTS_RTSP_NotEnoughBandwidth_str                  "Not Enough Bandwidth"
#define SRS_CONSTS_RTSP_SessionNotFound_str                     "Session Not Found"
#define SRS_CONSTS_RTSP_MethodNotValidInThisState_str           "Method Not Valid In This State"
#define SRS_CONSTS_RTSP_HeaderFieldNotValidForResource_str      "Header Field Not Valid"
#define SRS_CONSTS_RTSP_InvalidRange_str                        "Invalid Range"
#define SRS_CONSTS_RTSP_ParameterIsReadOnly_str                 "Parameter Is Read-Only"
#define SRS_CONSTS_RTSP_AggregateOperationNotAllowed_str        "Aggregate Operation Not Allowed"
#define SRS_CONSTS_RTSP_OnlyAggregateOperationAllowed_str       "Only Aggregate Operation Allowed"
#define SRS_CONSTS_RTSP_UnsupportedTransport_str                "Unsupported Transport"
#define SRS_CONSTS_RTSP_DestinationUnreachable_str              "Destination Unreachable"
#define SRS_CONSTS_RTSP_InternalServerError_str                 "Internal Server Error"
#define SRS_CONSTS_RTSP_NotImplemented_str                      "Not Implemented"
#define SRS_CONSTS_RTSP_BadGateway_str                          "Bad Gateway"
#define SRS_CONSTS_RTSP_ServiceUnavailable_str                  "Service Unavailable"
#define SRS_CONSTS_RTSP_GatewayTimeout_str                      "Gateway Timeout"
#define SRS_CONSTS_RTSP_RTSPVersionNotSupported_str             "RTSP Version Not Supported"
#define SRS_CONSTS_RTSP_OptionNotSupported_str                  "Option not support"

#endif

// following is generated by src/kernel/srs_kernel_aac.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_KERNEL_AAC_HPP
#define SRS_KERNEL_AAC_HPP

/*
//#include <srs_kernel_aac.hpp>
*/
//#include <srs_core.hpp>

#include <string>

//#include <srs_kernel_codec.hpp>

class SrsStream;
class SrsFileWriter;
class SrsFileReader;

/**
* encode data to aac file.
*/
class SrsAacEncoder
{
private:
    SrsFileWriter* _fs;
private:
    SrsAacObjectType aac_object;
    int8_t aac_sample_rate;
    int8_t aac_channels;
    bool got_sequence_header;
private:
    SrsStream* tag_stream;
public:
    SrsAacEncoder();
    virtual ~SrsAacEncoder();
public:
    /**
    * initialize the underlayer file stream.
    * @remark user can initialize multiple times to encode multiple aac files.
    * @remark, user must free the fs, aac encoder never close/free it.
    */
    virtual int initialize(SrsFileWriter* fs);
public:
    /**
    * write audio/video packet.
    * @remark assert data is not NULL.
    */
    virtual int write_audio(int64_t timestamp, char* data, int size);
};

#endif

// following is generated by src/kernel/srs_kernel_mp3.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_KERNEL_MP3_HPP
#define SRS_KERNEL_MP3_HPP

/*
//#include <srs_kernel_mp3.hpp>
*/
//#include <srs_core.hpp>

#include <string>

class SrsStream;
class SrsFileWriter;
class SrsFileReader;

/**
* encode data to aac file.
*/
class SrsMp3Encoder
{
private:
    SrsFileWriter* _fs;
private:
    SrsStream* tag_stream;
public:
    SrsMp3Encoder();
    virtual ~SrsMp3Encoder();
public:
    /**
    * 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.
    */
    virtual int initialize(SrsFileWriter* fs);
public:
    /**
    * write mp3 id3 v2.3 header.
    * @see mp3.id3v2.3.0.pdf, http://id3.org/id3v2.3.0
    */
    virtual int write_header();
    /**
    * write audio/video packet.
    * @remark assert data is not NULL.
    */
    virtual int write_audio(int64_t timestamp, char* data, int size);
};

#endif

// following is generated by src/kernel/srs_kernel_ts.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_KERNEL_TS_HPP
#define SRS_KERNEL_TS_HPP

/*
//#include <srs_kernel_ts.hpp>
*/
//#include <srs_core.hpp>

#include <string>
#include <map>
#include <vector>

//#include <srs_kernel_codec.hpp>

class SrsStream;
class SrsTsCache;
class SrsTSMuxer;
class SrsFileWriter;
class SrsFileReader;
class SrsAvcAacCodec;
class SrsCodecSample;
class SrsSimpleBuffer;
class SrsTsAdaptationField;
class SrsTsPayload;
class SrsTsMessage;
class SrsTsPacket;
class SrsTsContext;

// Transport Stream packets are 188 bytes in length.
#define SRS_TS_PACKET_SIZE          188

/**
* the pid of ts packet,
* Table 2-3 - PID table, hls-mpeg-ts-iso13818-1.pdf, page 37
* NOTE - The transport packets with PID values 0x0000, 0x0001, and 0x0010-0x1FFE are allowed to carry a PCR.
*/
enum SrsTsPid
{
    // Program Association Table(see Table 2-25).
    SrsTsPidPAT             = 0x00,
    // Conditional Access Table (see Table 2-27).
    SrsTsPidCAT             = 0x01,
    // Transport Stream Description Table
    SrsTsPidTSDT            = 0x02,
    // Reserved
    SrsTsPidReservedStart   = 0x03,
    SrsTsPidReservedEnd     = 0x0f,
    // May be assigned as network_PID, Program_map_PID, elementary_PID, or for other purposes
    SrsTsPidAppStart        = 0x10,
    SrsTsPidAppEnd          = 0x1ffe,
    // null packets (see Table 2-3)
    SrsTsPidNULL    = 0x01FFF,
};

/**
* the transport_scrambling_control of ts packet,
* Table 2-4 - Scrambling control values, hls-mpeg-ts-iso13818-1.pdf, page 38
*/
enum SrsTsScrambled
{
    // Not scrambled
    SrsTsScrambledDisabled      = 0x00,
    // User-defined
    SrsTsScrambledUserDefined1  = 0x01,
    // User-defined
    SrsTsScrambledUserDefined2  = 0x02,
    // User-defined
    SrsTsScrambledUserDefined3  = 0x03,
};

/**
* the adaption_field_control of ts packet,
* Table 2-5 - Adaptation field control values, hls-mpeg-ts-iso13818-1.pdf, page 38
*/
enum SrsTsAdaptationFieldType
{
    // Reserved for future use by ISO/IEC
    SrsTsAdaptationFieldTypeReserved      = 0x00,
    // No adaptation_field, payload only
    SrsTsAdaptationFieldTypePayloadOnly   = 0x01,
    // Adaptation_field only, no payload
    SrsTsAdaptationFieldTypeAdaptionOnly  = 0x02,
    // Adaptation_field followed by payload
    SrsTsAdaptationFieldTypeBoth          = 0x03,
};

/**
* the actually parsed ts pid,
* @see SrsTsPid, some pid, for example, PMT/Video/Audio is specified by PAT or other tables.
*/
enum SrsTsPidApply
{
    SrsTsPidApplyReserved = 0, // TSPidTypeReserved, nothing parsed, used reserved.
    
    SrsTsPidApplyPAT, // Program associtate table
    SrsTsPidApplyPMT, // Program map table.
    
    SrsTsPidApplyVideo, // for video
    SrsTsPidApplyAudio, // vor audio
};

/**
* Table 2-29 - Stream type assignments
*/
enum SrsTsStream
{
    // ITU-T | ISO/IEC Reserved
    SrsTsStreamReserved         = 0x00,
    // ISO/IEC 11172 Video
    // ITU-T Rec. H.262 | ISO/IEC 13818-2 Video or ISO/IEC 11172-2 constrained parameter video stream
    // ISO/IEC 11172 Audio
    // ISO/IEC 13818-3 Audio
    SrsTsStreamAudioMp3         = 0x04,
    // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 private_sections
    // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 PES packets containing private data
    // ISO/IEC 13522 MHEG
    // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Annex A DSM-CC
    // ITU-T Rec. H.222.1
    // ISO/IEC 13818-6 type A
    // ISO/IEC 13818-6 type B
    // ISO/IEC 13818-6 type C
    // ISO/IEC 13818-6 type D
    // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 auxiliary
    // ISO/IEC 13818-7 Audio with ADTS transport syntax
    SrsTsStreamAudioAAC        = 0x0f,
    // ISO/IEC 14496-2 Visual
    SrsTsStreamVideoMpeg4      = 0x10,
    // ISO/IEC 14496-3 Audio with the LATM transport syntax as defined in ISO/IEC 14496-3 / AMD 1
    SrsTsStreamAudioMpeg4      = 0x11,
    // ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in PES packets
    // ISO/IEC 14496-1 SL-packetized stream or FlexMux stream carried in ISO/IEC14496_sections.
    // ISO/IEC 13818-6 Synchronized Download Protocol
    // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 Reserved
    // 0x15-0x7F
    SrsTsStreamVideoH264       = 0x1b,
    // User Private
    // 0x80-0xFF
    SrsTsStreamAudioAC3        = 0x81,
    SrsTsStreamAudioDTS        = 0x8a,
};
std::string srs_ts_stream2string(SrsTsStream stream);

/**
* the ts channel.
*/
struct SrsTsChannel
{
    int pid;
    SrsTsPidApply apply;
    SrsTsStream stream;
    SrsTsMessage* msg;
    SrsTsContext* context;
    // for encoder.
    u_int8_t continuity_counter;

    SrsTsChannel();
    virtual ~SrsTsChannel();
};

/**
* the stream_id of PES payload of ts packet.
* Table 2-18 - Stream_id assignments, hls-mpeg-ts-iso13818-1.pdf, page 52.
*/
enum SrsTsPESStreamId
{
    // program_stream_map
    SrsTsPESStreamIdProgramStreamMap            = 0xbc, // 0b10111100
    // private_stream_1
    SrsTsPESStreamIdPrivateStream1              = 0xbd, // 0b10111101
    // padding_stream
    SrsTsPESStreamIdPaddingStream               = 0xbe, // 0b10111110
    // private_stream_2
    SrsTsPESStreamIdPrivateStream2              = 0xbf, // 0b10111111

    // 110x xxxx
    // ISO/IEC 13818-3 or ISO/IEC 11172-3 or ISO/IEC 13818-7 or ISO/IEC
    // 14496-3 audio stream number x xxxx
    // ((sid >> 5) & 0x07) == SrsTsPESStreamIdAudio
    // @remark, use SrsTsPESStreamIdAudioCommon as actually audio, SrsTsPESStreamIdAudio to check whether audio.
    SrsTsPESStreamIdAudioChecker                = 0x06, // 0b110
        SrsTsPESStreamIdAudioCommon             = 0xc0,

    // 1110 xxxx
    // ITU-T Rec. H.262 | ISO/IEC 13818-2 or ISO/IEC 11172-2 or ISO/IEC
    // 14496-2 video stream number xxxx
    // ((stream_id >> 4) & 0x0f) == SrsTsPESStreamIdVideo
    // @remark, use SrsTsPESStreamIdVideoCommon as actually video, SrsTsPESStreamIdVideo to check whether video.
    SrsTsPESStreamIdVideoChecker                = 0x0e, // 0b1110
        SrsTsPESStreamIdVideoCommon             = 0xe0,

    // ECM_stream
    SrsTsPESStreamIdEcmStream                   = 0xf0, // 0b11110000
    // EMM_stream
    SrsTsPESStreamIdEmmStream                   = 0xf1, // 0b11110001
    // DSMCC_stream
    SrsTsPESStreamIdDsmccStream                 = 0xf2, // 0b11110010
    // 13522_stream
    SrsTsPESStreamId13522Stream                 = 0xf3, // 0b11110011
    // H_222_1_type_A
    SrsTsPESStreamIdH2221TypeA                  = 0xf4, // 0b11110100
    // H_222_1_type_B
    SrsTsPESStreamIdH2221TypeB                  = 0xf5, // 0b11110101
    // H_222_1_type_C
    SrsTsPESStreamIdH2221TypeC                  = 0xf6, // 0b11110110
    // H_222_1_type_D
    SrsTsPESStreamIdH2221TypeD                  = 0xf7, // 0b11110111
    // H_222_1_type_E
    SrsTsPESStreamIdH2221TypeE                  = 0xf8, // 0b11111000
    // ancillary_stream
    SrsTsPESStreamIdAncillaryStream             = 0xf9, // 0b11111001
    // SL_packetized_stream
    SrsTsPESStreamIdSlPacketizedStream          = 0xfa, // 0b11111010
    // FlexMux_stream
    SrsTsPESStreamIdFlexMuxStream               = 0xfb, // 0b11111011
    // reserved data stream
    // 1111 1100 ... 1111 1110
    // program_stream_directory
    SrsTsPESStreamIdProgramStreamDirectory      = 0xff, // 0b11111111
};

/**
* the media audio/video message parsed from PES packet.
*/
class SrsTsMessage
{
public:
    // decoder only,
    // the ts messgae does not use them, 
    // for user to get the channel and packet.
    SrsTsChannel* channel;
    SrsTsPacket* packet;
public:
    // the audio cache buffer start pts, to flush audio if full.
    // @remark the pts is not the adjust one, it's the orignal pts.
    int64_t start_pts;
    // whether this message with pcr info,
    // generally, the video IDR(I frame, the keyframe of h.264) carray the pcr info.
    bool write_pcr;
    // whether got discontinuity ts, for example, sequence header changed.
    bool is_discontinuity;
public:
    // the timestamp in 90khz
    int64_t dts;
    int64_t pts;
    // the id of pes stream to indicates the payload codec.
    // @remark use is_audio() and is_video() to check it, and stream_number() to finger it out.
    SrsTsPESStreamId sid;
    // the size of payload, 0 indicates the length() of payload.
    u_int16_t PES_packet_length;
    // the chunk id.
    u_int8_t continuity_counter;
    // the payload bytes.
    SrsSimpleBuffer* payload;
public:
    SrsTsMessage(SrsTsChannel* c = NULL, SrsTsPacket* p = NULL);
    virtual ~SrsTsMessage();
// decoder
public:
    /**
    * dumps all bytes in stream to ts message.
    */
    virtual int dump(SrsStream* stream, int* pnb_bytes);
    /**
    * whether ts message is completed to reap.
    * @param payload_unit_start_indicator whether new ts message start.
    *       PES_packet_length is 0, the payload_unit_start_indicator=1 to reap ts message.
    *       PES_packet_length > 0, the payload.length() == PES_packet_length to reap ts message.
    * @remark when PES_packet_length>0, the payload_unit_start_indicator should never be 1 when not completed.
    * @remark when fresh, the payload_unit_start_indicator should be 1.
    */
    virtual bool completed(int8_t payload_unit_start_indicator);
    /**
    * whether the message is fresh.
    */
    virtual bool fresh();
public:
    /**
    * whether the sid indicates the elementary stream audio.
    */
    virtual bool is_audio();
    /**
    * whether the sid indicates the elementary stream video.
    */
    virtual bool is_video();
    /**
    * when audio or video, get the stream number which specifies the format of stream.
    * @return the stream number for audio/video; otherwise, -1.
    */
    virtual int stream_number();
public:
    /**
     * detach the ts message,
     * for user maybe need to parse the message by queue.
     * @remark we always use the payload of original message.
     */
    virtual SrsTsMessage* detach();
};

/**
* the ts message handler.
*/
class ISrsTsHandler
{
public:
    ISrsTsHandler();
    virtual ~ISrsTsHandler();
public:
    /**
    * when ts context got message, use handler to process it.
    * @param msg the ts msg, user should never free it.
    * @return an int error code.
    */
    virtual int on_ts_message(SrsTsMessage* msg) = 0;
};

/**
* the context of ts, to decode the ts stream.
*/
class SrsTsContext
{
// codec
private:
    std::map<int, SrsTsChannel*> pids;
    bool pure_audio;
// encoder
private:
    // when any codec changed, write the PAT/PMT.
    SrsCodecVideo vcodec;
    SrsCodecAudio acodec;
public:
    SrsTsContext();
    virtual ~SrsTsContext();
public:
    /**
     * whether the hls stream is pure audio stream.
     */
    virtual bool is_pure_audio();
    /**
     * when PMT table parsed, we know some info about stream.
     */
    virtual void on_pmt_parsed();
    /**
     * reset the context for a new ts segment start.
     */
    virtual void reset();
// codec
public:
    /**
    * get the pid apply, the parsed pid.
    * @return the apply channel; NULL for invalid.
    */
    virtual SrsTsChannel* get(int pid);
    /**
    * set the pid apply, the parsed pid.
    */
    virtual void set(int pid, SrsTsPidApply apply_pid, SrsTsStream stream = SrsTsStreamReserved);
// decode methods
public:
    /**
    * the stream contains only one ts packet.
    * @param handler the ts message handler to process the msg.
    * @remark we will consume all bytes in stream.
    */
    virtual int decode(SrsStream* stream, ISrsTsHandler* handler);
// encode methods
public:
    /**
    * write the PES packet, the video/audio stream.
    * @param msg the video/audio msg to write to ts.
    * @param vc the video codec, write the PAT/PMT table when changed.
    * @param ac the audio codec, write the PAT/PMT table when changed.
    */
    virtual int encode(SrsFileWriter* writer, SrsTsMessage* msg, SrsCodecVideo vc, SrsCodecAudio ac);
private:
    virtual int encode_pat_pmt(SrsFileWriter* writer, int16_t vpid, SrsTsStream vs, int16_t apid, SrsTsStream as);
    virtual int encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t pid, SrsTsStream sid, bool pure_audio);
};

/**
* the packet in ts stream,
* 2.4.3.2 Transport Stream packet layer, hls-mpeg-ts-iso13818-1.pdf, page 36
* Transport Stream packets shall be 188 bytes long.
*/
class SrsTsPacket
{
public:
    // 1B
    /**
    * The sync_byte is a fixed 8-bit field whose value is '0100 0111' (0x47). Sync_byte emulation in the choice of
    * values for other regularly occurring fields, such as PID, should be avoided.
    */
    int8_t sync_byte; //8bits

    // 2B
    /**
    * The transport_error_indicator is a 1-bit flag. When set to '1' it indicates that at least
    * 1 uncorrectable bit error exists in the associated Transport Stream packet. This bit may be set to '1' by entities external to
    * the transport layer. When set to '1' this bit shall not be reset to '0' unless the bit value(s) in error have been corrected.
    */
    int8_t transport_error_indicator; //1bit
    /**
    * The payload_unit_start_indicator is a 1-bit flag which has normative meaning for
    * Transport Stream packets that carry PES packets (refer to 2.4.3.6) or PSI data (refer to 2.4.4).
    * 
    * When the payload of the Transport Stream packet contains PES packet data, the payload_unit_start_indicator has the
    * following significance: a '1' indicates that the payload of this Transport Stream packet will commence(start) with the first byte
    * of a PES packet and a '0' indicates no PES packet shall start in this Transport Stream packet. If the
    * payload_unit_start_indicator is set to '1', then one and only one PES packet starts in this Transport Stream packet. This
    * also applies to private streams of stream_type 6 (refer to Table 2-29).
    *
    * When the payload of the Transport Stream packet contains PSI data, the payload_unit_start_indicator has the following
    * significance: if the Transport Stream packet carries the first byte of a PSI section, the payload_unit_start_indicator value
    * shall be '1', indicating that the first byte of the payload of this Transport Stream packet carries the pointer_field. If the
    * Transport Stream packet does not carry the first byte of a PSI section, the payload_unit_start_indicator value shall be '0',
    * indicating that there is no pointer_field in the payload. Refer to 2.4.4.1 and 2.4.4.2. This also applies to private streams of
    * stream_type 5 (refer to Table 2-29).
    * 
    * For null packets the payload_unit_start_indicator shall be set to '0'.
    * 
    * The meaning of this bit for Transport Stream packets carrying only private data is not defined in this Specification.
    */
    int8_t payload_unit_start_indicator; //1bit
    /**
    * The transport_priority is a 1-bit indicator. When set to '1' it indicates that the associated packet is
    * of greater priority than other packets having the same PID which do not have the bit set to '1'. The transport mechanism
    * can use this to prioritize its data within an elementary stream. Depending on the application the transport_priority field
    * may be coded regardless of the PID or within one PID only. This field may be changed by channel specific encoders or
    * decoders.
    */
    int8_t transport_priority; //1bit
    /**
    * The PID is a 13-bit field, indicating the type of the data stored in the packet payload. PID value 0x0000 is
    * reserved for the Program Association Table (see Table 2-25). PID value 0x0001 is reserved for the Conditional Access
    * Table (see Table 2-27). PID values 0x0002 - 0x000F are reserved. PID value 0x1FFF is reserved for null packets (see
    * Table 2-3).
    */
    SrsTsPid pid; //13bits

    // 1B
    /**
    * This 2-bit field indicates the scrambling mode of the Transport Stream packet payload.
    * The Transport Stream packet header, and the adaptation field when present, shall not be scrambled. In the case of a null
    * packet the value of the transport_scrambling_control field shall be set to '00' (see Table 2-4).
    */
    SrsTsScrambled transport_scrambling_control; //2bits
    /**
    * This 2-bit field indicates whether this Transport Stream packet header is followed by an
    * adaptation field and/or payload (see Table 2-5).
    *
    * ITU-T Rec. H.222.0 | ISO/IEC 13818-1 decoders shall discard Transport Stream packets with the
    * adaptation_field_control field set to a value of '00'. In the case of a null packet the value of the adaptation_field_control
    * shall be set to '01'.
    */
    SrsTsAdaptationFieldType adaption_field_control; //2bits
    /**
    * The continuity_counter is a 4-bit field incrementing with each Transport Stream packet with the
    * same PID. The continuity_counter wraps around to 0 after its maximum value. The continuity_counter shall not be
    * incremented when the adaptation_field_control of the packet equals '00'(reseverd) or '10'(adaptation field only).
    * 
    * In Transport Streams, duplicate packets may be sent as two, and only two, consecutive Transport Stream packets of the
    * same PID. The duplicate packets shall have the same continuity_counter value as the original packet and the
    * adaptation_field_control field shall be equal to '01'(payload only) or '11'(both). In duplicate packets each byte of the original packet shall be
    * duplicated, with the exception that in the program clock reference fields, if present, a valid value shall be encoded.
    *
    * The continuity_counter in a particular Transport Stream packet is continuous when it differs by a positive value of one
    * from the continuity_counter value in the previous Transport Stream packet of the same PID, or when either of the nonincrementing
    * conditions (adaptation_field_control set to '00' or '10', or duplicate packets as described above) are met.
    * The continuity counter may be discontinuous when the discontinuity_indicator is set to '1' (refer to 2.4.3.4). In the case of
    * a null packet the value of the continuity_counter is undefined.
    */
    u_int8_t continuity_counter; //4bits
private:
    SrsTsAdaptationField* adaptation_field;
    SrsTsPayload* payload;
public:
    SrsTsContext* context;
public:
    SrsTsPacket(SrsTsContext* c);
    virtual ~SrsTsPacket();
public:
    virtual int decode(SrsStream* stream, SrsTsMessage** ppmsg);
public:
    virtual int size();
    virtual int encode(SrsStream* stream);
    virtual void padding(int nb_stuffings);
public:
    static SrsTsPacket* create_pat(SrsTsContext* context, 
        int16_t pmt_number, int16_t pmt_pid
    );
    static SrsTsPacket* create_pmt(SrsTsContext* context, 
        int16_t pmt_number, int16_t pmt_pid, int16_t vpid, SrsTsStream vs, 
        int16_t apid, SrsTsStream as
    );
    static SrsTsPacket* create_pes_first(SrsTsContext* context, 
        int16_t pid, SrsTsPESStreamId sid, u_int8_t continuity_counter, bool discontinuity, 
        int64_t pcr, int64_t dts, int64_t pts, int size
    );
    static SrsTsPacket* create_pes_continue(SrsTsContext* context, 
        int16_t pid, SrsTsPESStreamId sid, u_int8_t continuity_counter
    );
};

/**
* the adaption field of ts packet.
* 2.4.3.5 Semantic definition of fields in adaptation field, hls-mpeg-ts-iso13818-1.pdf, page 39
* Table 2-6 - Transport Stream adaptation field, hls-mpeg-ts-iso13818-1.pdf, page 40
*/
class SrsTsAdaptationField
{
public:
    // 1B
    /**
    * The adaptation_field_length is an 8-bit field specifying the number of bytes in the
    * adaptation_field immediately following the adaptation_field_length. The value 0 is for inserting a single stuffing byte in
    * a Transport Stream packet. When the adaptation_field_control value is '11', the value of the adaptation_field_length shall
    * be in the range 0 to 182. When the adaptation_field_control value is '10', the value of the adaptation_field_length shall
    * be 183. For Transport Stream packets carrying PES packets, stuffing is needed when there is insufficient PES packet data
    * to completely fill the Transport Stream packet payload bytes. Stuffing is accomplished by defining an adaptation field
    * longer than the sum of the lengths of the data elements in it, so that the payload bytes remaining after the adaptation field
    * exactly accommodates the available PES packet data. The extra space in the adaptation field is filled with stuffing bytes.
    *
    * This is the only method of stuffing allowed for Transport Stream packets carrying PES packets. For Transport Stream
    * packets carrying PSI, an alternative stuffing method is described in 2.4.4.
    */
    u_int8_t adaption_field_length; //8bits
    // 1B
    /**
    * This is a 1-bit field which when set to '1' indicates that the discontinuity state is true for the
    * current Transport Stream packet. When the discontinuity_indicator is set to '0' or is not present, the discontinuity state is
    * false. The discontinuity indicator is used to indicate two types of discontinuities, system time-base discontinuities and
    * continuity_counter discontinuities.
    * 
    * A system time-base discontinuity is indicated by the use of the discontinuity_indicator in Transport Stream packets of a
    * PID designated as a PCR_PID (refer to 2.4.4.9). When the discontinuity state is true for a Transport Stream packet of a
    * PID designated as a PCR_PID, the next PCR in a Transport Stream packet with that same PID represents a sample of a
    * new system time clock for the associated program. The system time-base discontinuity point is defined to be the instant
    * in time when the first byte of a packet containing a PCR of a new system time-base arrives at the input of the T-STD.
    * The discontinuity_indicator shall be set to '1' in the packet in which the system time-base discontinuity occurs. The
    * discontinuity_indicator bit may also be set to '1' in Transport Stream packets of the same PCR_PID prior to the packet
    * which contains the new system time-base PCR. In this case, once the discontinuity_indicator has been set to '1', it shall
    * continue to be set to '1' in all Transport Stream packets of the same PCR_PID up to and including the Transport Stream
    * packet which contains the first PCR of the new system time-base. After the occurrence of a system time-base
    * discontinuity, no fewer than two PCRs for the new system time-base shall be received before another system time-base
    * discontinuity can occur. Further, except when trick mode status is true, data from no more than two system time-bases
    * shall be present in the set of T-STD buffers for one program at any time.
    *
    * Prior to the occurrence of a system time-base discontinuity, the first byte of a Transport Stream packet which contains a
    * PTS or DTS which refers to the new system time-base shall not arrive at the input of the T-STD. After the occurrence of
    * a system time-base discontinuity, the first byte of a Transport Stream packet which contains a PTS or DTS which refers
    * to the previous system time-base shall not arrive at the input of the T-STD.
    *
    * A continuity_counter discontinuity is indicated by the use of the discontinuity_indicator in any Transport Stream packet.
    * When the discontinuity state is true in any Transport Stream packet of a PID not designated as a PCR_PID, the
    * continuity_counter in that packet may be discontinuous with respect to the previous Transport Stream packet of the same
    * PID. When the discontinuity state is true in a Transport Stream packet of a PID that is designated as a PCR_PID, the
    * continuity_counter may only be discontinuous in the packet in which a system time-base discontinuity occurs. A
    * continuity counter discontinuity point occurs when the discontinuity state is true in a Transport Stream packet and the
    * continuity_counter in the same packet is discontinuous with respect to the previous Transport Stream packet of the same
    * PID. A continuity counter discontinuity point shall occur at most one time from the initiation of the discontinuity state
    * until the conclusion of the discontinuity state. Furthermore, for all PIDs that are not designated as PCR_PIDs, when the
    * discontinuity_indicator is set to '1' in a packet of a specific PID, the discontinuity_indicator may be set to '1' in the next
    * Transport Stream packet of that same PID, but shall not be set to '1' in three consecutive Transport Stream packet of that
    * same PID.
    *
    * For the purpose of this clause, an elementary stream access point is defined as follows:
    *       Video - The first byte of a video sequence header.
    *       Audio - The first byte of an audio frame.
    *
    * After a continuity counter discontinuity in a Transport packet which is designated as containing elementary stream data,
    * the first byte of elementary stream data in a Transport Stream packet of the same PID shall be the first byte of an
    * elementary stream access point or in the case of video, the first byte of an elementary stream access point or a
    * sequence_end_code followed by an access point. Each Transport Stream packet which contains elementary stream data
    * with a PID not designated as a PCR_PID, and in which a continuity counter discontinuity point occurs, and in which a
    * PTS or DTS occurs, shall arrive at the input of the T-STD after the system time-base discontinuity for the associated
    * program occurs. In the case where the discontinuity state is true, if two consecutive Transport Stream packets of the same
    * PID occur which have the same continuity_counter value and have adaptation_field_control values set to '01' or '11', the
    * second packet may be discarded. A Transport Stream shall not be constructed in such a way that discarding such a packet
    * will cause the loss of PES packet payload data or PSI data.
    *
    * After the occurrence of a discontinuity_indicator set to '1' in a Transport Stream packet which contains PSI information,
    * a single discontinuity in the version_number of PSI sections may occur. At the occurrence of such a discontinuity, a
    * version of the TS_program_map_sections of the appropriate program shall be sent with section_length = = 13 and the
    * current_next_indicator = = 1, such that there are no program_descriptors and no elementary streams described. This shall
    * then be followed by a version of the TS_program_map_section for each affected program with the version_number
    * incremented by one and the current_next_indicator = = 1, containing a complete program definition. This indicates a
    * version change in PSI data.
    */
    int8_t discontinuity_indicator; //1bit
    /**
    * The random_access_indicator is a 1-bit field that indicates that the current Transport
    * Stream packet, and possibly subsequent Transport Stream packets with the same PID, contain some information to aid
    * random access at this point. Specifically, when the bit is set to '1', the next PES packet to start in the payload of Transport
    * Stream packets with the current PID shall contain the first byte of a video sequence header if the PES stream type (refer
    * to Table 2-29) is 1 or 2, or shall contain the first byte of an audio frame if the PES stream type is 3 or 4. In addition, in
    * the case of video, a presentation timestamp shall be present in the PES packet containing the first picture following the
    * sequence header. In the case of audio, the presentation timestamp shall be present in the PES packet containing the first
    * byte of the audio frame. In the PCR_PID the random_access_indicator may only be set to '1' in Transport Stream packet
    * containing the PCR fields.
    */
    int8_t random_access_indicator; //1bit
    /**
    * The elementary_stream_priority_indicator is a 1-bit field. It indicates, among
    * packets with the same PID, the priority of the elementary stream data carried within the payload of this Transport Stream
    * packet. A '1' indicates that the payload has a higher priority than the payloads of other Transport Stream packets. In the
    * case of video, this field may be set to '1' only if the payload contains one or more bytes from an intra-coded slice. A
    * value of '0' indicates that the payload has the same priority as all other packets which do not have this bit set to '1'.
    */
    int8_t elementary_stream_priority_indicator; //1bit
    /**
    * The PCR_flag is a 1-bit flag. A value of '1' indicates that the adaptation_field contains a PCR field coded in
    * two parts. A value of '0' indicates that the adaptation field does not contain any PCR field.
    */
    int8_t PCR_flag; //1bit
    /**
    * The OPCR_flag is a 1-bit flag. A value of '1' indicates that the adaptation_field contains an OPCR field
    * coded in two parts. A value of '0' indicates that the adaptation field does not contain any OPCR field.
    */
    int8_t OPCR_flag; //1bit
    /**
    * The splicing_point_flag is a 1-bit flag. When set to '1', it indicates that a splice_countdown field
    * shall be present in the associated adaptation field, specifying the occurrence of a splicing point. A value of '0' indicates
    * that a splice_countdown field is not present in the adaptation field.
    */
    int8_t splicing_point_flag; //1bit
    /**
    * The transport_private_data_flag is a 1-bit flag. A value of '1' indicates that the
    * adaptation field contains one or more private_data bytes. A value of '0' indicates the adaptation field does not contain any
    * private_data bytes.
    */
    int8_t transport_private_data_flag; //1bit
    /**
    * The adaptation_field_extension_flag is a 1-bit field which when set to '1' indicates
    * the presence of an adaptation field extension. A value of '0' indicates that an adaptation field extension is not present in
    * the adaptation field.
    */
    int8_t adaptation_field_extension_flag; //1bit
    
    // if PCR_flag, 6B
    /**
    * The program_clock_reference (PCR) is a
    * 42-bit field coded in two parts. The first part, program_clock_reference_base, is a 33-bit field whose value is given by
    * PCR_base(i), as given in equation 2-2. The second part, program_clock_reference_extension, is a 9-bit field whose value
    * is given by PCR_ext(i), as given in equation 2-3. The PCR indicates the intended time of arrival of the byte containing
    * the last bit of the program_clock_reference_base at the input of the system target decoder.
    */
    int64_t program_clock_reference_base; //33bits
    /**
    * 6bits reserved, must be '1'
    */
    int8_t const1_value0; // 6bits
    int16_t program_clock_reference_extension; //9bits
    
    // if OPCR_flag, 6B
    /**
    * The optional original
    * program reference (OPCR) is a 42-bit field coded in two parts. These two parts, the base and the extension, are coded
    * identically to the two corresponding parts of the PCR field. The presence of the OPCR is indicated by the OPCR_flag.
    * The OPCR field shall be coded only in Transport Stream packets in which the PCR field is present. OPCRs are permitted
    * in both single program and multiple program Transport Streams.
    *
    * OPCR assists in the reconstruction of a single program Transport Stream from another Transport Stream. When
    * reconstructing the original single program Transport Stream, the OPCR may be copied to the PCR field. The resulting
    * PCR value is valid only if the original single program Transport Stream is reconstructed exactly in its entirety. This
    * would include at least any PSI and private data packets which were present in the original Transport Stream and would
    * possibly require other private arrangements. It also means that the OPCR must be an identical copy of its associated PCR
    * in the original single program Transport Stream.
    */
    int64_t original_program_clock_reference_base; //33bits
    /**
    * 6bits reserved, must be '1'
    */
    int8_t const1_value2; // 6bits
    int16_t original_program_clock_reference_extension; //9bits
    
    // if splicing_point_flag, 1B
    /**
    * The splice_countdown is an 8-bit field, representing a value which may be positive or negative. A
    * positive value specifies the remaining number of Transport Stream packets, of the same PID, following the associated
    * Transport Stream packet until a splicing point is reached. Duplicate Transport Stream packets and Transport Stream
    * packets which only contain adaptation fields are excluded. The splicing point is located immediately after the last byte of
    * the Transport Stream packet in which the associated splice_countdown field reaches zero. In the Transport Stream packet
    * where the splice_countdown reaches zero, the last data byte of the Transport Stream packet payload shall be the last byte
    * of a coded audio frame or a coded picture. In the case of video, the corresponding access unit may or may not be
    * terminated by a sequence_end_code. Transport Stream packets with the same PID, which follow, may contain data from
    * a different elementary stream of the same type.
    *
    * The payload of the next Transport Stream packet of the same PID (duplicate packets and packets without payload being
    * excluded) shall commence with the first byte of a PES packet.In the case of audio, the PES packet payload shall
    * commence with an access point. In the case of video, the PES packet payload shall commence with an access point, or
    * with a sequence_end_code, followed by an access point. Thus, the previous coded audio frame or coded picture aligns
    * with the packet boundary, or is padded to make this so. Subsequent to the splicing point, the countdown field may also
    * be present. When the splice_countdown is a negative number whose value is minus n(-n), it indicates that the associated
    * Transport Stream packet is the n-th packet following the splicing point (duplicate packets and packets without payload
    * being excluded).
    * 
    * For the purposes of this subclause, an access point is defined as follows:
    *       Video - The first byte of a video_sequence_header.
    *       Audio - The first byte of an audio frame.
    */
    int8_t splice_countdown; //8bits
    
    // if transport_private_data_flag, 1+p[0] B
    /**
    * The transport_private_data_length is an 8-bit field specifying the number of
    * private_data bytes immediately following the transport private_data_length field. The number of private_data bytes shall
    * not be such that private data extends beyond the adaptation field.
    */
    u_int8_t transport_private_data_length; //8bits
    char* transport_private_data; //[transport_private_data_length]bytes
    
    // if adaptation_field_extension_flag, 2+x B
    /**
    * The adaptation_field_extension_length is an 8-bit field. It indicates the number of
    * bytes of the extended adaptation field data immediately following this field, including reserved bytes if present.
    */
    u_int8_t adaptation_field_extension_length; //8bits
    /**
    * This is a 1-bit field which when set to '1' indicates the presence of the ltw_offset
    * field.
    */
    int8_t ltw_flag; //1bit
    /**
    * This is a 1-bit field which when set to '1' indicates the presence of the piecewise_rate field.
    */
    int8_t piecewise_rate_flag; //1bit
    /**
    * This is a 1-bit flag which when set to '1' indicates that the splice_type and DTS_next_AU fields
    * are present. A value of '0' indicates that neither splice_type nor DTS_next_AU fields are present. This field shall not be
    * set to '1' in Transport Stream packets in which the splicing_point_flag is not set to '1'. Once it is set to '1' in a Transport
    * Stream packet in which the splice_countdown is positive, it shall be set to '1' in all the subsequent Transport Stream
    * packets of the same PID that have the splicing_point_flag set to '1', until the packet in which the splice_countdown
    * reaches zero (including this packet). When this flag is set, if the elementary stream carried in this PID is an audio stream,
    * the splice_type field shall be set to '0000'. If the elementary stream carried in this PID is a video stream, it shall fulfil the
    * constraints indicated by the splice_type value.
    */
    int8_t seamless_splice_flag; //1bit
    /**
    * reserved 5bits, must be '1'
    */
    int8_t const1_value1; //5bits
    // if ltw_flag, 2B
    /**
    * (legal time window_valid_flag) - This is a 1-bit field which when set to '1' indicates that the value of the
    * ltw_offset shall be valid. A value of '0' indicates that the value in the ltw_offset field is undefined.
    */
    int8_t ltw_valid_flag; //1bit
    /**
    * (legal time window offset) - This is a 15-bit field, the value of which is defined only if the ltw_valid flag has
    * a value of '1'. When defined, the legal time window offset is in units of (300/fs) seconds, where fs is the system clock
    * frequency of the program that this PID belongs to, and fulfils:
    *       offset = t1(i) - t(i)
    *       ltw_offset = offset//1
    * where i is the index of the first byte of this Transport Stream packet, offset is the value encoded in this field, t(i) is the
    * arrival time of byte i in the T-STD, and t1(i) is the upper bound in time of a time interval called the Legal Time Window
    * which is associated with this Transport Stream packet.
    */
    int16_t ltw_offset; //15bits
    // if piecewise_rate_flag, 3B
    //2bits reserved
    /**
    * The meaning of this 22-bit field is only defined when both the ltw_flag and the ltw_valid_flag are set
    * to '1'. When defined, it is a positive integer specifying a hypothetical bitrate R which is used to define the end times of
    * the Legal Time Windows of Transport Stream packets of the same PID that follow this packet but do not include the
    * legal_time_window_offset field.
    */
    int32_t piecewise_rate; //22bits
    // if seamless_splice_flag, 5B
    /**
    * This is a 4-bit field. From the first occurrence of this field onwards, it shall have the same value in all the
    * subsequent Transport Stream packets of the same PID in which it is present, until the packet in which the
    * splice_countdown reaches zero (including this packet). If the elementary stream carried in that PID is an audio stream,
    * this field shall have the value '0000'. If the elementary stream carried in that PID is a video stream, this field indicates the
    * conditions that shall be respected by this elementary stream for splicing purposes. These conditions are defined as a
    * function of profile, level and splice_type in Table 2-7 through Table 2-16.
    */
    int8_t splice_type; //4bits
    /**
    * (decoding time stamp next access unit) - This is a 33-bit field, coded in three parts. In the case of
    * continuous and periodic decoding through this splicing point it indicates the decoding time of the first access unit
    * following the splicing point. This decoding time is expressed in the time base which is valid in the Transport Stream
    * packet in which the splice_countdown reaches zero. From the first occurrence of this field onwards, it shall have the
    * same value in all the subsequent Transport Stream packets of the same PID in which it is present, until the packet in
    * which the splice_countdown reaches zero (including this packet).
    */
    int8_t DTS_next_AU0; //3bits
    int8_t marker_bit0; //1bit
    int16_t DTS_next_AU1; //15bits
    int8_t marker_bit1; //1bit
    int16_t DTS_next_AU2; //15bits
    int8_t marker_bit2; //1bit
    // left bytes.
    /**
    * This is a fixed 8-bit value equal to '1111 1111' that can be inserted by the encoder. It is discarded by the
    * decoder.
    */
    int nb_af_ext_reserved;
    
    // left bytes.
    /**
    * This is a fixed 8-bit value equal to '1111 1111' that can be inserted by the encoder. It is discarded by the
    * decoder.
    */
    int nb_af_reserved;
private:
    SrsTsPacket* packet;
public:
    SrsTsAdaptationField(SrsTsPacket* pkt);
    virtual ~SrsTsAdaptationField();
public:
    virtual int decode(SrsStream* stream);
public:
    virtual int size();
    virtual int encode(SrsStream* stream);
};

/**
* 2.4.4.4 Table_id assignments, hls-mpeg-ts-iso13818-1.pdf, page 62
* The table_id field identifies the contents of a Transport Stream PSI section as shown in Table 2-26.
*/
enum SrsTsPsiId
{
    // program_association_section
    SrsTsPsiIdPas               = 0x00,
    // conditional_access_section (CA_section)
    SrsTsPsiIdCas               = 0x01,
    // TS_program_map_section
    SrsTsPsiIdPms               = 0x02,
    // TS_description_section
    SrsTsPsiIdDs                = 0x03,
    // ISO_IEC_14496_scene_description_section
    SrsTsPsiIdSds               = 0x04,
    // ISO_IEC_14496_object_descriptor_section
    SrsTsPsiIdOds               = 0x05,
    // ITU-T Rec. H.222.0 | ISO/IEC 13818-1 reserved
    SrsTsPsiIdIso138181Start    = 0x06,
    SrsTsPsiIdIso138181End      = 0x37,
    // Defined in ISO/IEC 13818-6
    SrsTsPsiIdIso138186Start    = 0x38,
    SrsTsPsiIdIso138186End      = 0x3F,
    // User private
    SrsTsPsiIdUserStart         = 0x40,
    SrsTsPsiIdUserEnd           = 0xFE,
    // forbidden
    SrsTsPsiIdForbidden         = 0xFF,
};

/**
* the payload of ts packet, can be PES or PSI payload.
*/
class SrsTsPayload
{
protected:
    SrsTsPacket* packet;
public:
    SrsTsPayload(SrsTsPacket* p);
    virtual ~SrsTsPayload();
public:
    virtual int decode(SrsStream* stream, SrsTsMessage** ppmsg) = 0;
public:
    virtual int size() = 0;
    virtual int encode(SrsStream* stream) = 0;
};

/**
* the PES payload of ts packet.
* 2.4.3.6 PES packet, hls-mpeg-ts-iso13818-1.pdf, page 49
*/
class SrsTsPayloadPES : public SrsTsPayload
{
public:
    // 3B
    /**
    * The packet_start_code_prefix is a 24-bit code. Together with the stream_id that follows it
    * constitutes a packet start code that identifies the beginning of a packet. The packet_start_code_prefix is the bit string
    * '0000 0000 0000 0000 0000 0001' (0x000001).
    */
    int32_t packet_start_code_prefix; //24bits
    // 1B
    /**
    * In Program Streams, the stream_id specifies the type and number of the elementary stream as defined by the
    * stream_id Table 2-18. In Transport Streams, the stream_id may be set to any valid value which correctly describes the
    * elementary stream type as defined in Table 2-18. In Transport Streams, the elementary stream type is specified in the
    * Program Specific Information as specified in 2.4.4.
    */
    // @see SrsTsPESStreamId, value can be SrsTsPESStreamIdAudioCommon or SrsTsPESStreamIdVideoCommon.
    u_int8_t stream_id; //8bits
    // 2B
    /**
    * A 16-bit field specifying the number of bytes in the PES packet following the last byte of the
    * field. A value of 0 indicates that the PES packet length is neither specified nor bounded and is allowed only in
    * PES packets whose payload consists of bytes from a video elementary stream contained in Transport Stream packets.
    */
    u_int16_t PES_packet_length; //16bits

    // 1B
    /**
    * 2bits const '10'
    */
    int8_t const2bits; //2bits
    /**
    * The 2-bit PES_scrambling_control field indicates the scrambling mode of the PES packet
    * payload. When scrambling is performed at the PES level, the PES packet header, including the optional fields when
    * present, shall not be scrambled (see Table 2-19).
    */
    int8_t PES_scrambling_control; //2bits
    /**
    * This is a 1-bit field indicating the priority of the payload in this PES packet. A '1' indicates a higher
    * priority of the payload of the PES packet payload than a PES packet payload with this field set to '0'. A multiplexor can
    * use the PES_priority bit to prioritize its data within an elementary stream. This field shall not be changed by the transport
    * mechanism.
    */
    int8_t PES_priority; //1bit
    /**
    * This is a 1-bit flag. When set to a value of '1' it indicates that the PES packet header is
    * immediately followed by the video start code or audio syncword indicated in the data_stream_alignment_descriptor
    * in 2.6.10 if this descriptor is present. If set to a value of '1' and the descriptor is not present, alignment as indicated in
    * alignment_type '01' in Table 2-47 and Table 2-48 is required. When set to a value of '0' it is not defined whether any such
    * alignment occurs or not.
    */
    int8_t data_alignment_indicator; //1bit
    /**
    * This is a 1-bit field. When set to '1' it indicates that the material of the associated PES packet payload is
    * protected by copyright. When set to '0' it is not defined whether the material is protected by copyright. A copyright
    * descriptor described in 2.6.24 is associated with the elementary stream which contains this PES packet and the copyright
    * flag is set to '1' if the descriptor applies to the material contained in this PES packet
    */
    int8_t copyright; //1bit
    /**
    * This is a 1-bit field. When set to '1' the contents of the associated PES packet payload is an original.
    * When set to '0' it indicates that the contents of the associated PES packet payload is a copy.
    */
    int8_t original_or_copy; //1bit

    // 1B
    /**
    * This is a 2-bit field. When the PTS_DTS_flags field is set to '10', the PTS fields shall be present in
    * the PES packet header. When the PTS_DTS_flags field is set to '11', both the PTS fields and DTS fields shall be present
    * in the PES packet header. When the PTS_DTS_flags field is set to '00' no PTS or DTS fields shall be present in the PES
    * packet header. The value '01' is forbidden.
    */
    int8_t PTS_DTS_flags; //2bits
    /**
    * A 1-bit flag, which when set to '1' indicates that ESCR base and extension fields are present in the PES
    * packet header. When set to '0' it indicates that no ESCR fields are present.
    */
    int8_t ESCR_flag; //1bit
    /**
    * A 1-bit flag, which when set to '1' indicates that the ES_rate field is present in the PES packet header.
    * When set to '0' it indicates that no ES_rate field is present.
    */
    int8_t ES_rate_flag; //1bit
    /**
    * A 1-bit flag, which when set to '1' it indicates the presence of an 8-bit trick mode field. When
    * set to '0' it indicates that this field is not present.
    */
    int8_t DSM_trick_mode_flag; //1bit
    /**
    * A 1-bit flag, which when set to '1' indicates the presence of the additional_copy_info field.
    * When set to '0' it indicates that this field is not present.
    */
    int8_t additional_copy_info_flag; //1bit
    /**
    * A 1-bit flag, which when set to '1' indicates that a CRC field is present in the PES packet. When set to
    * '0' it indicates that this field is not present.
    */
    int8_t PES_CRC_flag; //1bit
    /**
    * A 1-bit flag, which when set to '1' indicates that an extension field exists in this PES packet
    * header. When set to '0' it indicates that this field is not present.
    */
    int8_t PES_extension_flag; //1bit

    // 1B
    /**
    * An 8-bit field specifying the total number of bytes occupied by the optional fields and any
    * stuffing bytes contained in this PES packet header. The presence of optional fields is indicated in the byte that precedes
    * the PES_header_data_length field.
    */
    u_int8_t PES_header_data_length; //8bits

    // 5B
    /**
    * Presentation times shall be related to decoding times as follows: The PTS is a 33-bit
    * number coded in three separate fields. It indicates the time of presentation, tp n (k), in the system target decoder of a
    * presentation unit k of elementary stream n. The value of PTS is specified in units of the period of the system clock
    * frequency divided by 300 (yielding 90 kHz). The presentation time is derived from the PTS according to equation 2-11
    * below. Refer to 2.7.4 for constraints on the frequency of coding presentation timestamps.
    */
    // ===========1B
    // 4bits const
    // 3bits PTS [32..30]
    // 1bit const '1'
    // ===========2B
    // 15bits PTS [29..15]
    // 1bit const '1'
    // ===========2B
    // 15bits PTS [14..0]
    // 1bit const '1'
    int64_t pts; // 33bits

    // 5B
    /**
    * The DTS is a 33-bit number coded in three separate fields. It indicates the decoding time,
    * td n (j), in the system target decoder of an access unit j of elementary stream n. The value of DTS is specified in units of
    * the period of the system clock frequency divided by 300 (yielding 90 kHz).
    */
    // ===========1B
    // 4bits const
    // 3bits DTS [32..30]
    // 1bit const '1'
    // ===========2B
    // 15bits DTS [29..15]
    // 1bit const '1'
    // ===========2B
    // 15bits DTS [14..0]
    // 1bit const '1'
    int64_t dts; // 33bits

    // 6B
    /**
    * The elementary stream clock reference is a 42-bit field coded in two parts. The first
    * part, ESCR_base, is a 33-bit field whose value is given by ESCR_base(i), as given in equation 2-14. The second part,
    * ESCR_ext, is a 9-bit field whose value is given by ESCR_ext(i), as given in equation 2-15. The ESCR field indicates the
    * intended time of arrival of the byte containing the last bit of the ESCR_base at the input of the PES-STD for PES streams
    * (refer to 2.5.2.4).
    */
    // 2bits reserved
    // 3bits ESCR_base[32..30]
    // 1bit const '1'
    // 15bits ESCR_base[29..15]
    // 1bit const '1'
    // 15bits ESCR_base[14..0]
    // 1bit const '1'
    // 9bits ESCR_extension
    // 1bit const '1'
    int64_t ESCR_base; //33bits
    int16_t ESCR_extension; //9bits

    // 3B
    /**
    * The ES_rate field is a 22-bit unsigned integer specifying the rate at which the
    * system target decoder receives bytes of the PES packet in the case of a PES stream. The ES_rate is valid in the PES
    * packet in which it is included and in subsequent PES packets of the same PES stream until a new ES_rate field is
    * encountered. The value of the ES_rate is measured in units of 50 bytes/second. The value 0 is forbidden. The value of the
    * ES_rate is used to define the time of arrival of bytes at the input of a P-STD for PES streams defined in 2.5.2.4. The
    * value encoded in the ES_rate field may vary from PES_packet to PES_packet.
    */
    // 1bit const '1'
    // 22bits ES_rate
    // 1bit const '1'
    int32_t ES_rate; //22bits

    // 1B
    /**
    * A 3-bit field that indicates which trick mode is applied to the associated video stream. In cases of
    * other types of elementary streams, the meanings of this field and those defined by the following five bits are undefined.
    * For the definition of trick_mode status, refer to the trick mode section of 2.4.2.3.
    */
    int8_t trick_mode_control; //3bits
    int8_t trick_mode_value; //5bits

    // 1B
    // 1bit const '1'
    /**
    * This 7-bit field contains private data relating to copyright information.
    */
    int8_t additional_copy_info; //7bits

    // 2B
    /**
    * The previous_PES_packet_CRC is a 16-bit field that contains the CRC value that yields
    * a zero output of the 16 registers in the decoder similar to the one defined in Annex A,
    */
    int16_t previous_PES_packet_CRC; //16bits

    // 1B
    /**
    * A 1-bit flag which when set to '1' indicates that the PES packet header contains private data.
    * When set to a value of '0' it indicates that private data is not present in the PES header.
    */
    int8_t PES_private_data_flag; //1bit
    /**
    * A 1-bit flag which when set to '1' indicates that an ISO/IEC 11172-1 pack header or a
    * Program Stream pack header is stored in this PES packet header. If this field is in a PES packet that is contained in a
    * Program Stream, then this field shall be set to '0'. In a Transport Stream, when set to the value '0' it indicates that no pack
    * header is present in the PES header.
    */
    int8_t pack_header_field_flag; //1bit
    /**
    * A 1-bit flag which when set to '1' indicates that the
    * program_packet_sequence_counter, MPEG1_MPEG2_identifier, and original_stuff_length fields are present in this
    * PES packet. When set to a value of '0' it indicates that these fields are not present in the PES header.
    */
    int8_t program_packet_sequence_counter_flag; //1bit
    /**
    * A 1-bit flag which when set to '1' indicates that the P-STD_buffer_scale and P-STD_buffer_size
    * are present in the PES packet header. When set to a value of '0' it indicates that these fields are not present in the
    * PES header.
    */
    int8_t P_STD_buffer_flag; //1bit
    /**
    * reverved value, must be '1'
    */
    int8_t const1_value0; //3bits
    /**
    * A 1-bit field which when set to '1' indicates the presence of the PES_extension_field_length
    * field and associated fields. When set to a value of '0' this indicates that the PES_extension_field_length field and any
    * associated fields are not present.
    */
    int8_t PES_extension_flag_2; //1bit

    // 16B
    /**
    * This is a 16-byte field which contains private data. This data, combined with the fields before and
    * after, shall not emulate the packet_start_code_prefix (0x000001).
    */
    char* PES_private_data; //128bits

    // (1+x)B
    /**
    * This is an 8-bit field which indicates the length, in bytes, of the pack_header_field().
    */
    u_int8_t pack_field_length; //8bits
    char* pack_field; //[pack_field_length] bytes

    // 2B
    // 1bit const '1'
    /**
    * The program_packet_sequence_counter field is a 7-bit field. It is an optional
    * counter that increments with each successive PES packet from a Program Stream or from an ISO/IEC 11172-1 Stream or
    * the PES packets associated with a single program definition in a Transport Stream, providing functionality similar to a
    * continuity counter (refer to 2.4.3.2). This allows an application to retrieve the original PES packet sequence of a Program
    * Stream or the original packet sequence of the original ISO/IEC 11172-1 stream. The counter will wrap around to 0 after
    * its maximum value. Repetition of PES packets shall not occur. Consequently, no two consecutive PES packets in the
    * program multiplex shall have identical program_packet_sequence_counter values.
    */
    int8_t program_packet_sequence_counter; //7bits
    // 1bit const '1'
    /**
    * A 1-bit flag which when set to '1' indicates that this PES packet carries information from
    * an ISO/IEC 11172-1 stream. When set to '0' it indicates that this PES packet carries information from a Program Stream.
    */
    int8_t MPEG1_MPEG2_identifier; //1bit
    /**
    * This 6-bit field specifies the number of stuffing bytes used in the original ITU-T
    * Rec. H.222.0 | ISO/IEC 13818-1 PES packet header or in the original ISO/IEC 11172-1 packet header.
    */
    int8_t original_stuff_length; //6bits

    // 2B
    // 2bits const '01'
    /**
    * The P-STD_buffer_scale is a 1-bit field, the meaning of which is only defined if this PES packet
    * is contained in a Program Stream. It indicates the scaling factor used to interpret the subsequent P-STD_buffer_size field.
    * If the preceding stream_id indicates an audio stream, P-STD_buffer_scale shall have the value '0'. If the preceding
    * stream_id indicates a video stream, P-STD_buffer_scale shall have the value '1'. For all other stream types, the value
    * may be either '1' or '0'.
    */
    int8_t P_STD_buffer_scale; //1bit
    /**
    * The P-STD_buffer_size is a 13-bit unsigned integer, the meaning of which is only defined if this
    * PES packet is contained in a Program Stream. It defines the size of the input buffer, BS n , in the P-STD. If
    * P-STD_buffer_scale has the value '0', then the P-STD_buffer_size measures the buffer size in units of 128 bytes. If
    * P-STD_buffer_scale has the value '1', then the P-STD_buffer_size measures the buffer size in units of 1024 bytes.
    */
    int16_t P_STD_buffer_size; //13bits

    // (1+x)B
    // 1bit const '1'
    /**
    * This is a 7-bit field which specifies the length, in bytes, of the data following this field in
    * the PES extension field up to and including any reserved bytes.
    */
    u_int8_t PES_extension_field_length; //7bits
    char* PES_extension_field; //[PES_extension_field_length] bytes

    // NB
    /**
    * This is a fixed 8-bit value equal to '1111 1111' that can be inserted by the encoder, for example to meet
    * the requirements of the channel. It is discarded by the decoder. No more than 32 stuffing bytes shall be present in one
    * PES packet header.
    */
    int nb_stuffings;

    // NB
    /**
    * PES_packet_data_bytes shall be contiguous bytes of data from the elementary stream
    * indicated by the packet's stream_id or PID. When the elementary stream data conforms to ITU-T
    * Rec. H.262 | ISO/IEC 13818-2 or ISO/IEC 13818-3, the PES_packet_data_bytes shall be byte aligned to the bytes of this
    * Recommendation | International Standard. The byte-order of the elementary stream shall be preserved. The number of
    * PES_packet_data_bytes, N, is specified by the PES_packet_length field. N shall be equal to the value indicated in the
    * PES_packet_length minus the number of bytes between the last byte of the PES_packet_length field and the first
    * PES_packet_data_byte.
    *
    * In the case of a private_stream_1, private_stream_2, ECM_stream, or EMM_stream, the contents of the
    * PES_packet_data_byte field are user definable and will not be specified by ITU-T | ISO/IEC in the future.
    */
    int nb_bytes;

    // NB
    /**
    * This is a fixed 8-bit value equal to '1111 1111'. It is discarded by the decoder.
    */
    int nb_paddings;
public:
    SrsTsPayloadPES(SrsTsPacket* p);
    virtual ~SrsTsPayloadPES();
public:
    virtual int decode(SrsStream* stream, SrsTsMessage** ppmsg);
public:
    virtual int size();
    virtual int encode(SrsStream* stream);
private:
    virtual int decode_33bits_dts_pts(SrsStream* stream, int64_t* pv);
    virtual int encode_33bits_dts_pts(SrsStream* stream, u_int8_t fb, int64_t v);
};

/**
* the PSI payload of ts packet.
* 2.4.4 Program specific information, hls-mpeg-ts-iso13818-1.pdf, page 59
*/
class SrsTsPayloadPSI : public SrsTsPayload
{
public:
    // 1B
    /**
    * This is an 8-bit field whose value shall be the number of bytes, immediately following the pointer_field
    * until the first byte of the first section that is present in the payload of the Transport Stream packet (so a value of 0x00 in
    * the pointer_field indicates that the section starts immediately after the pointer_field). When at least one section begins in
    * a given Transport Stream packet, then the payload_unit_start_indicator (refer to 2.4.3.2) shall be set to 1 and the first
    * byte of the payload of that Transport Stream packet shall contain the pointer. When no section begins in a given
    * Transport Stream packet, then the payload_unit_start_indicator shall be set to 0 and no pointer shall be sent in the
    * payload of that packet.
    */
    int8_t pointer_field;
public:
    // 1B
    /**
    * This is an 8-bit field, which shall be set to 0x00 as shown in Table 2-26.
    */
    SrsTsPsiId table_id; //8bits
    
    // 2B
    /**
    * The section_syntax_indicator is a 1-bit field which shall be set to '1'.
    */
    int8_t section_syntax_indicator; //1bit
    /**
    * const value, must be '0'
    */
    int8_t const0_value; //1bit
    /**
    * reverved value, must be '1'
    */
    int8_t const1_value; //2bits
    /**
    * This is a 12-bit field, the first two bits of which shall be '00'. The remaining 10 bits specify the number
    * of bytes of the section, starting immediately following the section_length field, and including the CRC. The value in this
    * field shall not exceed 1021 (0x3FD).
    */
    u_int16_t section_length; //12bits
public:
    // the specified psi info, for example, PAT fields.
public:
    // 4B
    /**
    * This is a 32-bit field that contains the CRC value that gives a zero output of the registers in the decoder
    * defined in Annex A after processing the entire section.
    * @remark crc32(bytes without pointer field, before crc32 field)
    */
    int32_t CRC_32; //32bits
public:
    SrsTsPayloadPSI(SrsTsPacket* p);
    virtual ~SrsTsPayloadPSI();
public:
    virtual int decode(SrsStream* stream, SrsTsMessage** ppmsg);
public:
    virtual int size();
    virtual int encode(SrsStream* stream);
protected:
    virtual int psi_size() = 0;
    virtual int psi_encode(SrsStream* stream) = 0;
    virtual int psi_decode(SrsStream* stream) = 0;
};

/**
* the program of PAT of PSI ts packet.
*/
class SrsTsPayloadPATProgram
{
public:
    // 4B
    /**
    * Program_number is a 16-bit field. It specifies the program to which the program_map_PID is
    * applicable. When set to 0x0000, then the following PID reference shall be the network PID. For all other cases the value
    * of this field is user defined. This field shall not take any single value more than once within one version of the Program
    * Association Table.
    */
    int16_t number; // 16bits
    /**
    * reverved value, must be '1'
    */
    int8_t const1_value; //3bits
    /**
    * program_map_PID/network_PID 13bits
    * network_PID - The network_PID is a 13-bit field, which is used only in conjunction with the value of the
    * program_number set to 0x0000, specifies the PID of the Transport Stream packets which shall contain the Network
    * Information Table. The value of the network_PID field is defined by the user, but shall only take values as specified in
    * Table 2-3. The presence of the network_PID is optional.
    */
    int16_t pid; //13bits
public:
    SrsTsPayloadPATProgram(int16_t n = 0, int16_t p = 0);
    virtual ~SrsTsPayloadPATProgram();
public:
    virtual int decode(SrsStream* stream);
public:
    virtual int size();
    virtual int encode(SrsStream* stream);
};

/**
* the PAT payload of PSI ts packet.
* 2.4.4.3 Program association Table, hls-mpeg-ts-iso13818-1.pdf, page 61
* The Program Association Table provides the correspondence between a program_number and the PID value of the
* Transport Stream packets which carry the program definition. The program_number is the numeric label associated with
* a program.
*/
class SrsTsPayloadPAT : public SrsTsPayloadPSI
{
public:
    // 2B
    /**
    * This is a 16-bit field which serves as a label to identify this Transport Stream from any other
    * multiplex within a network. Its value is defined by the user.
    */
    u_int16_t transport_stream_id; //16bits
    
    // 1B
    /**
    * reverved value, must be '1'
    */
    int8_t const3_value; //2bits
    /**
    * This 5-bit field is the version number of the whole Program Association Table. The version number
    * shall be incremented by 1 modulo 32 whenever the definition of the Program Association Table changes. When the
    * current_next_indicator is set to '1', then the version_number shall be that of the currently applicable Program Association
    * Table. When the current_next_indicator is set to '0', then the version_number shall be that of the next applicable Program
    * Association Table.
    */
    int8_t version_number; //5bits
    /**
    * A 1-bit indicator, which when set to '1' indicates that the Program Association Table sent is
    * currently applicable. When the bit is set to '0', it indicates that the table sent is not yet applicable and shall be the next
    * table to become valid.
    */
    int8_t current_next_indicator; //1bit
    
    // 1B
    /**
    * This 8-bit field gives the number of this section. The section_number of the first section in the
    * Program Association Table shall be 0x00. It shall be incremented by 1 with each additional section in the Program
    * Association Table.
    */
    u_int8_t section_number; //8bits
    
    // 1B
    /**
    * This 8-bit field specifies the number of the last section (that is, the section with the highest
    * section_number) of the complete Program Association Table.
    */
    u_int8_t last_section_number; //8bits
    
    // multiple 4B program data.
    std::vector<SrsTsPayloadPATProgram*> programs;
public:
    SrsTsPayloadPAT(SrsTsPacket* p);
    virtual ~SrsTsPayloadPAT();
protected:
    virtual int psi_decode(SrsStream* stream);
protected:
    virtual int psi_size();
    virtual int psi_encode(SrsStream* stream);
};

/**
* the esinfo for PMT program.
*/
class SrsTsPayloadPMTESInfo
{
public:
    // 1B
    /**
    * This is an 8-bit field specifying the type of program element carried within the packets with the PID
    * whose value is specified by the elementary_PID. The values of stream_type are specified in Table 2-29.
    */
    SrsTsStream stream_type; //8bits
    
    // 2B
    /**
    * reverved value, must be '1'
    */
    int8_t const1_value0; //3bits
    /**
    * This is a 13-bit field specifying the PID of the Transport Stream packets which carry the associated
    * program element.
    */
    int16_t elementary_PID; //13bits
    
    // (2+x)B
    /**
    * reverved value, must be '1'
    */
    int8_t const1_value1; //4bits
    /**
    * This is a 12-bit field, the first two bits of which shall be '00'. The remaining 10 bits specify the number
    * of bytes of the descriptors of the associated program element immediately following the ES_info_length field.
    */
    int16_t ES_info_length; //12bits
    char* ES_info; //[ES_info_length] bytes.
public:
    SrsTsPayloadPMTESInfo(SrsTsStream st = SrsTsStreamReserved, int16_t epid = 0);
    virtual ~SrsTsPayloadPMTESInfo();
public:
    virtual int decode(SrsStream* stream);
public:
    virtual int size();
    virtual int encode(SrsStream* stream);
};

/**
* the PMT payload of PSI ts packet.
* 2.4.4.8 Program Map Table, hls-mpeg-ts-iso13818-1.pdf, page 64
* The Program Map Table provides the mappings between program numbers and the program elements that comprise
* them. A single instance of such a mapping is referred to as a "program definition". The program map table is the
* complete collection of all program definitions for a Transport Stream. This table shall be transmitted in packets, the PID
* values of which are selected by the encoder. More than one PID value may be used, if desired. The table is contained in
* one or more sections with the following syntax. It may be segmented to occupy multiple sections. In each section, the
* section number field shall be set to zero. Sections are identified by the program_number field.
*/
class SrsTsPayloadPMT : public SrsTsPayloadPSI
{
public:
    // 2B
    /**
    * program_number is a 16-bit field. It specifies the program to which the program_map_PID is
    * applicable. One program definition shall be carried within only one TS_program_map_section. This implies that a
    * program definition is never longer than 1016 (0x3F8). See Informative Annex C for ways to deal with the cases when
    * that length is not sufficient. The program_number may be used as a designation for a broadcast channel, for example. By
    * describing the different program elements belonging to a program, data from different sources (e.g. sequential events)
    * can be concatenated together to form a continuous set of streams using a program_number. For examples of applications
    * refer to Annex C.
    */
    u_int16_t program_number; //16bits
    
    // 1B
    /**
    * reverved value, must be '1'
    */
    int8_t const1_value0; //2bits
    /**
    * This 5-bit field is the version number of the TS_program_map_section. The version number shall be
    * incremented by 1 modulo 32 when a change in the information carried within the section occurs. Version number refers
    * to the definition of a single program, and therefore to a single section. When the current_next_indicator is set to '1', then
    * the version_number shall be that of the currently applicable TS_program_map_section. When the current_next_indicator
    * is set to '0', then the version_number shall be that of the next applicable TS_program_map_section.
    */
    int8_t version_number; //5bits
    /**
    * A 1-bit field, which when set to '1' indicates that the TS_program_map_section sent is
    * currently applicable. When the bit is set to '0', it indicates that the TS_program_map_section sent is not yet applicable
    * and shall be the next TS_program_map_section to become valid.
    */
    int8_t current_next_indicator; //1bit
    
    // 1B
    /**
    * The value of this 8-bit field shall be 0x00.
    */
    u_int8_t section_number; //8bits
    
    // 1B
    /**
    * The value of this 8-bit field shall be 0x00.
    */
    u_int8_t last_section_number; //8bits
    
    // 2B
    /**
    * reverved value, must be '1'
    */
    int8_t const1_value1; //3bits
    /**
    * This is a 13-bit field indicating the PID of the Transport Stream packets which shall contain the PCR fields
    * valid for the program specified by program_number. If no PCR is associated with a program definition for private
    * streams, then this field shall take the value of 0x1FFF. Refer to the semantic definition of PCR in 2.4.3.5 and Table 2-3
    * for restrictions on the choice of PCR_PID value.
    */
    int16_t PCR_PID; //13bits
    
    // 2B
    int8_t const1_value2; //4bits
    /**
    * This is a 12-bit field, the first two bits of which shall be '00'. The remaining 10 bits specify the
    * number of bytes of the descriptors immediately following the program_info_length field.
    */
    u_int16_t program_info_length; //12bits
    char* program_info_desc; //[program_info_length]bytes
    
    // array of TSPMTESInfo.
    std::vector<SrsTsPayloadPMTESInfo*> infos;
public:
    SrsTsPayloadPMT(SrsTsPacket* p);
    virtual ~SrsTsPayloadPMT();
protected:
    virtual int psi_decode(SrsStream* stream);
protected:
    virtual int psi_size();
    virtual int psi_encode(SrsStream* stream);
};

/**
* write data from frame(header info) and buffer(data) to ts file.
* it's a simple object wrapper for utility from nginx-rtmp: SrsMpegtsWriter
*/
class SrsTSMuxer
{
private:
    SrsCodecVideo vcodec;
    SrsCodecAudio acodec;
private:
    SrsTsContext* context;
    SrsFileWriter* writer;
    std::string path;
public:
    SrsTSMuxer(SrsFileWriter* w, SrsTsContext* c, SrsCodecAudio ac, SrsCodecVideo vc);
    virtual ~SrsTSMuxer();
public:
    /**
    * open the writer, donot write the PSI of ts.
    */
    virtual int open(std::string _path);
    /**
    * 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
    */
    virtual int update_acodec(SrsCodecAudio ac);
    /**
    * write an audio frame to ts, 
    */
    virtual int write_audio(SrsTsMessage* audio);
    /**
    * write a video frame to ts, 
    */
    virtual int write_video(SrsTsMessage* video);
    /**
    * close the writer.
    */
    virtual void close();
};

/**
* ts stream cache, 
* use to cache ts stream.
* 
* about the flv tbn problem:
*   flv tbn is 1/1000, ts tbn is 1/90000,
*   when timestamp convert to flv tbn, it will loose precise,
*   so we must gather audio frame together, and recalc the timestamp @see SrsTsAacJitter,
*   we use a aac jitter to correct the audio pts.
*/
class SrsTsCache
{
public:
    // current ts message.
    SrsTsMessage* audio;
    SrsTsMessage* video;
public:
    SrsTsCache();
    virtual ~SrsTsCache();
public:
    /**
    * write audio to cache
    */
    virtual int cache_audio(SrsAvcAacCodec* codec, int64_t dts, SrsCodecSample* sample);
    /**
    * write video to muxer.
    */
    virtual int cache_video(SrsAvcAacCodec* codec, int64_t dts, SrsCodecSample* sample);
private:
    virtual int do_cache_mp3(SrsAvcAacCodec* codec, SrsCodecSample* sample);
    virtual int do_cache_aac(SrsAvcAacCodec* codec, SrsCodecSample* sample);
    virtual int do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample);
};

/**
* encode data to ts file.
*/
class SrsTsEncoder
{
private:
    SrsFileWriter* _fs;
private:
    SrsAvcAacCodec* codec;
    SrsCodecSample* sample;
    SrsTsCache* cache;
    SrsTSMuxer* muxer;
    SrsTsContext* context;
public:
    SrsTsEncoder();
    virtual ~SrsTsEncoder();
public:
    /**
    * initialize the underlayer file stream.
    */
    virtual int initialize(SrsFileWriter* fs);
public:
    /**
    * write audio/video packet.
    * @remark assert data is not NULL.
    */
    virtual int write_audio(int64_t timestamp, char* data, int size);
    virtual int write_video(int64_t timestamp, char* data, int size);
private:
    virtual int flush_audio();
    virtual int flush_video();
};

#endif

// following is generated by src/kernel/srs_kernel_buffer.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_KERNEL_BUFFER_HPP
#define SRS_KERNEL_BUFFER_HPP

/*
//#include <srs_kernel_buffer.hpp>
*/

//#include <srs_core.hpp>

#include <vector>

/**
* the simple buffer use vector to append bytes,
* it's for hls and http, and need to be refined in future.
*/
class SrsSimpleBuffer
{
private:
    std::vector<char> data;
public:
    SrsSimpleBuffer();
    virtual ~SrsSimpleBuffer();
public:
    /**
    * get the length of buffer. empty if zero.
    * @remark assert length() is not negative.
    */
    virtual int length();
    /**
    * get the buffer bytes.
    * @return the bytes, NULL if empty.
    */
    virtual char* bytes();
    /**
    * erase size of bytes from begin.
    * @param size to erase size of bytes. 
    *       clear if size greater than or equals to length()
    * @remark ignore size is not positive.
    */
    virtual void erase(int size);
    /**
    * append specified bytes to buffer.
    * @param size the size of bytes
    * @remark assert size is positive.
    */
    virtual void append(const char* bytes, int size);
};

#endif
// following is generated by src/protocol/srs_rtmp_amf0.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_AMF0_HPP
#define SRS_RTMP_PROTOCOL_AMF0_HPP

/*
//#include <srs_rtmp_amf0.hpp>
*/

//#include <srs_core.hpp>

#include <string>
#include <vector>

class SrsStream;
class SrsAmf0Object;
class SrsAmf0EcmaArray;
class SrsAmf0StrictArray;

// internal objects, user should never use it.
namespace _srs_internal
{
    class SrsUnSortedHashtable;
    class SrsAmf0ObjectEOF;
    class SrsAmf0Date;
}

/*
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
Usages:

1. the bytes proxy: SrsStream
    // when we got some bytes from file or network,
    // use SrsStream proxy to read/write bytes
    
    // for example, read bytes from file or network.
    char* bytes = ...; 
    
    // initialize the stream, proxy for bytes.
    SrsStream stream;
    stream.initialize(bytes);
    
    // use stream instead.

2. directly read AMF0 any instance from stream:
    SrsAmf0Any* pany = NULL;
    srs_amf0_read_any(&stream, &pany);

3. use SrsAmf0Any to discovery instance from stream:
    SrsAmf0Any* pany = NULL;
    SrsAmf0Any::discovery(&stream, &pany);

4. directly read specified AMF0 instance value from stream:
    string value;
    srs_amf0_read_string(&stream, value);
    
5. directly read specified AMF0 instance from stream:
    SrsAmf0Any* str = SrsAmf0Any::str();
    str->read(&stream);

6. get value from AMF0 instance:
    // parse or set by other user
    SrsAmf0Any* any = ...;
    
    if (any->is_string()) {
        string str = any->to_string();
    }

7. get complex object from AMF0 insance:
    // parse or set by other user
    SrsAmf0Any* any = ...;
    
    if (any->is_object()) {
        SrsAmf0Object* obj = any->to_object();
        obj->set("width", SrsAmf0Any::number(1024));
        obj->set("height", SrsAmf0Any::number(576));
    }

8. serialize AMF0 instance to bytes:
    // parse or set by other user
    SrsAmf0Any* any = ...;
    
    char* bytes = new char[any->total_size()];
    
    SrsStream stream;
    stream.initialize(bytes);
    
    any->write(&stream);

@remark: for detail usage, see interfaces of each object.
@remark: all examples ignore the error process.
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
////////////////////////////////////////////////////////////////////////
*/

/**
* any amf0 value.
* 2.1 Types Overview
* value-type = number-type | boolean-type | string-type | object-type 
*         | null-marker | undefined-marker | reference-type | ecma-array-type 
*         | strict-array-type | date-type | long-string-type | xml-document-type 
*         | typed-object-type
*/
class SrsAmf0Any
{
public:
    char marker;
public:
    SrsAmf0Any();
    virtual ~SrsAmf0Any();
// type identify, user should identify the type then convert from/to value.
public:
    /**
    * whether current instance is an AMF0 string.
    * @return true if instance is an AMF0 string; otherwise, false.
    * @remark, if true, use to_string() to get its value.
    */
    virtual bool is_string();
    /**
    * whether current instance is an AMF0 boolean.
    * @return true if instance is an AMF0 boolean; otherwise, false.
    * @remark, if true, use to_boolean() to get its value.
    */
    virtual bool is_boolean();
    /**
    * whether current instance is an AMF0 number.
    * @return true if instance is an AMF0 number; otherwise, false.
    * @remark, if true, use to_number() to get its value.
    */
    virtual bool is_number();
    /**
    * whether current instance is an AMF0 null.
    * @return true if instance is an AMF0 null; otherwise, false.
    */
    virtual bool is_null();
    /**
    * whether current instance is an AMF0 undefined.
    * @return true if instance is an AMF0 undefined; otherwise, false.
    */
    virtual bool is_undefined();
    /**
    * whether current instance is an AMF0 object.
    * @return true if instance is an AMF0 object; otherwise, false.
    * @remark, if true, use to_object() to get its value.
    */
    virtual bool is_object();
    /**
    * whether current instance is an AMF0 object-EOF.
    * @return true if instance is an AMF0 object-EOF; otherwise, false.
    */
    virtual bool is_object_eof();
    /**
    * whether current instance is an AMF0 ecma-array.
    * @return true if instance is an AMF0 ecma-array; otherwise, false.
    * @remark, if true, use to_ecma_array() to get its value.
    */
    virtual bool is_ecma_array();
    /**
    * whether current instance is an AMF0 strict-array.
    * @return true if instance is an AMF0 strict-array; otherwise, false.
    * @remark, if true, use to_strict_array() to get its value.
    */
    virtual bool is_strict_array();
    /**
    * whether current instance is an AMF0 date.
    * @return true if instance is an AMF0 date; otherwise, false.
    * @remark, if true, use to_date() to get its value.
    */
    virtual bool is_date();
    /**
    * whether current instance is an AMF0 object, object-EOF, ecma-array or strict-array.
    */
    virtual bool is_complex_object();
// get value of instance
public:
    /**
    * get a string copy of instance.
    * @remark assert is_string(), user must ensure the type then convert.
    */
    virtual std::string to_str();
    /**
    * get the raw str of instance,
    * user can directly set the content of str.
    * @remark assert is_string(), user must ensure the type then convert.
    */
    virtual const char* to_str_raw();
    /**
    * convert instance to amf0 boolean,
    * @remark assert is_boolean(), user must ensure the type then convert.
    */
    virtual bool to_boolean();
    /**
    * convert instance to amf0 number,
    * @remark assert is_number(), user must ensure the type then convert.
    */
    virtual double to_number();
    /**
    * convert instance to date,
    * @remark assert is_date(), user must ensure the type then convert.
    */
    virtual int64_t to_date();
    virtual int16_t to_date_time_zone();
    /**
    * convert instance to amf0 object,
    * @remark assert is_object(), user must ensure the type then convert.
    */
    virtual SrsAmf0Object* to_object();
    /**
    * convert instance to ecma array,
    * @remark assert is_ecma_array(), user must ensure the type then convert.
    */
    virtual SrsAmf0EcmaArray* to_ecma_array();
    /**
    * convert instance to strict array,
    * @remark assert is_strict_array(), user must ensure the type then convert.
    */
    virtual SrsAmf0StrictArray* to_strict_array();
// set value of instance
public:
    /**
    * set the number of any when is_number() indicates true.
    * user must ensure the type is a number, or assert failed.
    */
    virtual void set_number(double value);
// serialize/deseriaize instance.
public:
    /**
    * get the size of amf0 any, including the marker size.
    * the size is the bytes which instance serialized to.
    */
    virtual int total_size() = 0;
    /**
    * read AMF0 instance from stream.
    */
    virtual int read(SrsStream* stream) = 0;
    /**
    * write AMF0 instance to stream.
    */
    virtual int write(SrsStream* stream) = 0;
    /**
    * copy current AMF0 instance.
    */
    virtual SrsAmf0Any* copy() = 0;
    /**
    * human readable print 
    * @param pdata, output the heap data, NULL to ignore.
    * @return return the *pdata for print. NULL to ignore.
    * @remark user must free the data returned or output by pdata.
    */
    virtual char* human_print(char** pdata, int* psize);
// create AMF0 instance.
public:
    /**
    * create an AMF0 string instance, set string content by value.
    */
    static SrsAmf0Any* str(const char* value = NULL); 
    /**
    * create an AMF0 boolean instance, set boolean content by value.
    */
    static SrsAmf0Any* boolean(bool value = false);
    /**
    * create an AMF0 number instance, set number content by value.
    */
    static SrsAmf0Any* number(double value = 0.0);
    /**
    * create an AMF0 date instance
    */
    static SrsAmf0Any* date(int64_t value = 0);
    /**
    * create an AMF0 null instance
    */
    static SrsAmf0Any* null();
    /**
    * create an AMF0 undefined instance
    */
    static SrsAmf0Any* undefined();
    /**
    * create an AMF0 empty object instance
    */
    static SrsAmf0Object* object();
    /**
    * create an AMF0 object-EOF instance
    */
    static SrsAmf0Any* object_eof();
    /**
    * create an AMF0 empty ecma-array instance
    */
    static SrsAmf0EcmaArray* ecma_array();
    /**
    * create an AMF0 empty strict-array instance
    */
    static SrsAmf0StrictArray* strict_array();
// discovery instance from stream
public:
    /**
    * discovery AMF0 instance from stream
    * @param ppvalue, output the discoveried AMF0 instance.
    *       NULL if error.
    * @remark, instance is created without read from stream, user must
    *       use (*ppvalue)->read(stream) to get the instance.
    */
    static int discovery(SrsStream* stream, SrsAmf0Any** ppvalue);
};

/**
* 2.5 Object Type
* anonymous-object-type = object-marker *(object-property)
* object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker)
*/
class SrsAmf0Object : public SrsAmf0Any
{
private:
    _srs_internal::SrsUnSortedHashtable* properties;
    _srs_internal::SrsAmf0ObjectEOF* eof;
private:
    friend class SrsAmf0Any;
    /**
    * make amf0 object to private,
    * use should never declare it, use SrsAmf0Any::object() to create it.
    */
    SrsAmf0Object();
public:
    virtual ~SrsAmf0Object();
// serialize/deserialize to/from stream.
public:
    virtual int total_size();
    virtual int read(SrsStream* stream);
    virtual int write(SrsStream* stream);
    virtual SrsAmf0Any* copy();
// properties iteration
public:
    /**
    * clear all propergies.
    */
    virtual void clear();
    /**
    * get the count of properties(key:value).
    */
    virtual int count();
    /**
    * get the property(key:value) key at index.
    * @remark: max index is count().
    */
    virtual std::string key_at(int index);
    /**
    * get the property(key:value) key raw bytes at index.
    * user can directly set the key bytes.
    * @remark: max index is count().
    */
    virtual const char* key_raw_at(int index);
    /**
    * get the property(key:value) value at index.
    * @remark: max index is count().
    */
    virtual SrsAmf0Any* value_at(int index);
// property set/get.
public:
    /**
    * set the property(key:value) of object,
    * @param key, string property name.
    * @param value, an AMF0 instance property value.
    * @remark user should never free the value, this instance will manage it.
    */
    virtual void set(std::string key, SrsAmf0Any* value);
    /**
    * get the property(key:value) of object,
    * @param name, the property name/key
    * @return the property AMF0 value, NULL if not found.
    * @remark user should never free the returned value, copy it if needed.
    */
    virtual SrsAmf0Any* get_property(std::string name);
    /**
    * get the string property, ensure the property is_string().
    * @return the property AMF0 value, NULL if not found, or not a string.
    * @remark user should never free the returned value, copy it if needed.
    */
    virtual SrsAmf0Any* ensure_property_string(std::string name);
    /**
    * get the number property, ensure the property is_number().
    * @return the property AMF0 value, NULL if not found, or not a number.
    * @remark user should never free the returned value, copy it if needed.
    */
    virtual SrsAmf0Any* ensure_property_number(std::string name);
};

/**
* 2.10 ECMA Array Type
* ecma-array-type = associative-count *(object-property)
* associative-count = U32
* object-property = (UTF-8 value-type) | (UTF-8-empty object-end-marker)
*/
class SrsAmf0EcmaArray : public SrsAmf0Any
{
private:
    _srs_internal::SrsUnSortedHashtable* properties;
    _srs_internal::SrsAmf0ObjectEOF* eof;
    int32_t _count;
private:
    friend class SrsAmf0Any;
    /**
    * make amf0 object to private,
    * use should never declare it, use SrsAmf0Any::ecma_array() to create it.
    */
    SrsAmf0EcmaArray();
public:
    virtual ~SrsAmf0EcmaArray();
// serialize/deserialize to/from stream.
public:
    virtual int total_size();
    virtual int read(SrsStream* stream);
    virtual int write(SrsStream* stream);
    virtual SrsAmf0Any* copy();
// properties iteration
public:
    /**
    * clear all propergies.
    */
    virtual void clear();
    /**
    * get the count of properties(key:value).
    */
    virtual int count();
    /**
    * get the property(key:value) key at index.
    * @remark: max index is count().
    */
    virtual std::string key_at(int index);
    /**
    * get the property(key:value) key raw bytes at index.
    * user can directly set the key bytes.
    * @remark: max index is count().
    */
    virtual const char* key_raw_at(int index);
    /**
    * get the property(key:value) value at index.
    * @remark: max index is count().
    */
    virtual SrsAmf0Any* value_at(int index);
// property set/get.
public:
    /**
    * set the property(key:value) of array,
    * @param key, string property name.
    * @param value, an AMF0 instance property value.
    * @remark user should never free the value, this instance will manage it.
    */
    virtual void set(std::string key, SrsAmf0Any* value);
    /**
    * get the property(key:value) of array,
    * @param name, the property name/key
    * @return the property AMF0 value, NULL if not found.
    * @remark user should never free the returned value, copy it if needed.
    */
    virtual SrsAmf0Any* get_property(std::string name);
    /**
    * get the string property, ensure the property is_string().
    * @return the property AMF0 value, NULL if not found, or not a string.
    * @remark user should never free the returned value, copy it if needed.
    */
    virtual SrsAmf0Any* ensure_property_string(std::string name);
    /**
    * get the number property, ensure the property is_number().
    * @return the property AMF0 value, NULL if not found, or not a number.
    * @remark user should never free the returned value, copy it if needed.
    */
    virtual SrsAmf0Any* ensure_property_number(std::string name);
};

/**
* 2.12 Strict Array Type
* array-count = U32 
* strict-array-type = array-count *(value-type)
*/
class SrsAmf0StrictArray : public SrsAmf0Any
{
private:
    std::vector<SrsAmf0Any*> properties;
    int32_t _count;
private:
    friend class SrsAmf0Any;
    /**
    * make amf0 object to private,
    * use should never declare it, use SrsAmf0Any::strict_array() to create it.
    */
    SrsAmf0StrictArray();
public:
    virtual ~SrsAmf0StrictArray();
// serialize/deserialize to/from stream.
public:
    virtual int total_size();
    virtual int read(SrsStream* stream);
    virtual int write(SrsStream* stream);
    virtual SrsAmf0Any* copy();
// properties iteration
public:
    /**
    * clear all elements.
    */
    virtual void clear();
    /**
    * get the count of elements
    */
    virtual int count();
    /**
    * get the elements key at index.
    * @remark: max index is count().
    */
    virtual SrsAmf0Any* at(int index);
// property set/get.
public:
    /**
    * append new element to array
    * @param any, an AMF0 instance property value.
    * @remark user should never free the any, this instance will manage it.
    */
    virtual void append(SrsAmf0Any* any);
};

/**
* the class to get amf0 object size
*/
class SrsAmf0Size
{
public:
    static int utf8(std::string value);
    static int str(std::string value);
    static int number();
    static int date();
    static int null();
    static int undefined();
    static int boolean();
    static int object(SrsAmf0Object* obj);
    static int object_eof();
    static int ecma_array(SrsAmf0EcmaArray* arr);
    static int strict_array(SrsAmf0StrictArray* arr);
    static int any(SrsAmf0Any* o);
};

/**
* read anything from stream.
* @param ppvalue, the output amf0 any elem.
*         NULL if error; otherwise, never NULL and user must free it.
*/
extern int srs_amf0_read_any(SrsStream* stream, SrsAmf0Any** ppvalue);

/**
* read amf0 string from stream.
* 2.4 String Type
* string-type = string-marker UTF-8
*/
extern int srs_amf0_read_string(SrsStream* stream, std::string& value);
extern int srs_amf0_write_string(SrsStream* stream, std::string value);

/**
* read amf0 boolean from stream.
* 2.4 String Type
* boolean-type = boolean-marker U8
*         0 is false, <> 0 is true
*/
extern int srs_amf0_read_boolean(SrsStream* stream, bool& value);
extern int srs_amf0_write_boolean(SrsStream* stream, bool value);

/**
* read amf0 number from stream.
* 2.2 Number Type
* number-type = number-marker DOUBLE
*/
extern int srs_amf0_read_number(SrsStream* stream, double& value);
extern int srs_amf0_write_number(SrsStream* stream, double value);

/**
* read amf0 null from stream.
* 2.7 null Type
* null-type = null-marker
*/
extern int srs_amf0_read_null(SrsStream* stream);
extern int srs_amf0_write_null(SrsStream* stream);

/**
* read amf0 undefined from stream.
* 2.8 undefined Type
* undefined-type = undefined-marker
*/
extern int srs_amf0_read_undefined(SrsStream* stream);
extern int srs_amf0_write_undefined(SrsStream* stream);

// internal objects, user should never use it.
namespace _srs_internal
{
    /**
    * read amf0 string from stream.
    * 2.4 String Type
    * string-type = string-marker UTF-8
    * @return default value is empty string.
    * @remark: use SrsAmf0Any::str() to create it.
    */
    class SrsAmf0String : public SrsAmf0Any
    {
    public:
        std::string value;
    private:
        friend class SrsAmf0Any;
        /**
        * make amf0 string to private,
        * use should never declare it, use SrsAmf0Any::str() to create it.
        */
        SrsAmf0String(const char* _value);
    public:
        virtual ~SrsAmf0String();
    public:
        virtual int total_size();
        virtual int read(SrsStream* stream);
        virtual int write(SrsStream* stream);
        virtual SrsAmf0Any* copy();
    };
    
    /**
    * read amf0 boolean from stream.
    * 2.4 String Type
    * boolean-type = boolean-marker U8
    *         0 is false, <> 0 is true
    * @return default value is false.
    */
    class SrsAmf0Boolean : public SrsAmf0Any
    {
    public:
        bool value;
    private:
        friend class SrsAmf0Any;
        /**
        * make amf0 boolean to private,
        * use should never declare it, use SrsAmf0Any::boolean() to create it.
        */
        SrsAmf0Boolean(bool _value);
    public:
        virtual ~SrsAmf0Boolean();
    public:
        virtual int total_size();
        virtual int read(SrsStream* stream);
        virtual int write(SrsStream* stream);
        virtual SrsAmf0Any* copy();
    };
    
    /**
    * read amf0 number from stream.
    * 2.2 Number Type
    * number-type = number-marker DOUBLE
    * @return default value is 0.
    */
    class SrsAmf0Number : public SrsAmf0Any
    {
    public:
        double value;
    private:
        friend class SrsAmf0Any;
        /**
        * make amf0 number to private,
        * use should never declare it, use SrsAmf0Any::number() to create it.
        */
        SrsAmf0Number(double _value);
    public:
        virtual ~SrsAmf0Number();
    public:
        virtual int total_size();
        virtual int read(SrsStream* stream);
        virtual int write(SrsStream* stream);
        virtual SrsAmf0Any* copy();
    };
    
    /**
    * 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
    */
    class SrsAmf0Date : public SrsAmf0Any
    {
    private:
        int64_t _date_value;
        int16_t _time_zone;
    private:
        friend class SrsAmf0Any;
        /**
        * make amf0 date to private,
        * use should never declare it, use SrsAmf0Any::date() to create it.
        */
        SrsAmf0Date(int64_t value);
    public:
        virtual ~SrsAmf0Date();
    // serialize/deserialize to/from stream.
    public:
        virtual int total_size();
        virtual int read(SrsStream* stream);
        virtual int write(SrsStream* stream);
        virtual SrsAmf0Any* copy();
    public:
        /**
        * get the date value.
        */
        virtual int64_t date();
        /**
        * get the time_zone.
        */
        virtual int16_t time_zone();
    };
    
    /**
    * read amf0 null from stream.
    * 2.7 null Type
    * null-type = null-marker
    */
    class SrsAmf0Null : public SrsAmf0Any
    {
    private:
        friend class SrsAmf0Any;
        /**
        * make amf0 null to private,
        * use should never declare it, use SrsAmf0Any::null() to create it.
        */
        SrsAmf0Null();
    public:
        virtual ~SrsAmf0Null();
    public:
        virtual int total_size();
        virtual int read(SrsStream* stream);
        virtual int write(SrsStream* stream);
        virtual SrsAmf0Any* copy();
    };
    
    /**
    * read amf0 undefined from stream.
    * 2.8 undefined Type
    * undefined-type = undefined-marker
    */
    class SrsAmf0Undefined : public SrsAmf0Any
    {
    private:
        friend class SrsAmf0Any;
        /**
        * make amf0 undefined to private,
        * use should never declare it, use SrsAmf0Any::undefined() to create it.
        */
        SrsAmf0Undefined();
    public:
        virtual ~SrsAmf0Undefined();
    public:
        virtual int total_size();
        virtual int read(SrsStream* stream);
        virtual int write(SrsStream* stream);
        virtual SrsAmf0Any* copy();
    };
    
    /**
    * to ensure in inserted order.
    * for the FMLE will crash when AMF0Object is not ordered by inserted,
    * if ordered in map, the string compare order, the FMLE will creash when
    * get the response of connect app.
    */
    class SrsUnSortedHashtable
    {
    private:
        typedef std::pair<std::string, SrsAmf0Any*> SrsAmf0ObjectPropertyType;
        std::vector<SrsAmf0ObjectPropertyType> properties;
    public:
        SrsUnSortedHashtable();
        virtual ~SrsUnSortedHashtable();
    public:
        virtual int count();
        virtual void clear();
        virtual std::string key_at(int index);
        virtual const char* key_raw_at(int index);
        virtual SrsAmf0Any* value_at(int index);
        /**
        * set the value of hashtable.
        * @param value, the value to set. NULL to delete the property.
        */
        virtual void set(std::string key, SrsAmf0Any* value);
    public:
        virtual SrsAmf0Any* get_property(std::string name);
        virtual SrsAmf0Any* ensure_property_string(std::string name);
        virtual SrsAmf0Any* ensure_property_number(std::string name);
    public:
        virtual void copy(SrsUnSortedHashtable* src);
    };
    
    /**
    * 2.11 Object End Type
    * object-end-type = UTF-8-empty object-end-marker
    * 0x00 0x00 0x09
    */
    class SrsAmf0ObjectEOF : public SrsAmf0Any
    {
    public:
        SrsAmf0ObjectEOF();
        virtual ~SrsAmf0ObjectEOF();
    public:
        virtual int total_size();
        virtual int read(SrsStream* stream);
        virtual int write(SrsStream* stream);
        virtual SrsAmf0Any* copy();
    };

    /**
    * read amf0 utf8 string from stream.
    * 1.3.1 Strings and UTF-8
    * UTF-8 = U16 *(UTF8-char)
    * UTF8-char = UTF8-1 | UTF8-2 | UTF8-3 | UTF8-4
    * UTF8-1 = %x00-7F
    * @remark only support UTF8-1 char.
    */
    extern int srs_amf0_read_utf8(SrsStream* stream, std::string& value);
    extern int srs_amf0_write_utf8(SrsStream* stream, std::string value);
    
    extern bool srs_amf0_is_object_eof(SrsStream* stream);
    extern int srs_amf0_write_object_eof(SrsStream* stream, SrsAmf0ObjectEOF* value);
    
    extern int srs_amf0_write_any(SrsStream* stream, SrsAmf0Any* value);
};

#endif
// following is generated by src/protocol/srs_rtmp_io.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_IO_HPP
#define SRS_RTMP_PROTOCOL_IO_HPP

/*
//#include <srs_rtmp_io.hpp>
*/

//#include <srs_core.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <sys/uio.h>
#endif

/**
* the system io reader/writer architecture:
+---------------+     +--------------------+      +---------------+
| IBufferReader |     |    IStatistic      |      | IBufferWriter |
+---------------+     +--------------------+      +---------------+
| + read()      |     | + get_recv_bytes() |      | + write()     |
+------+--------+     | + get_recv_bytes() |      | + writev()    |
      / \             +---+--------------+-+      +-------+-------+
       |                 / \            / \              / \
       |                  |              |                |
+------+------------------+-+      +-----+----------------+--+
| IProtocolReader           |      | IProtocolWriter         |
+---------------------------+      +-------------------------+
| + readfully()             |      | + set_send_timeout()    |
| + set_recv_timeout()      |      +-------+-----------------+
+------------+--------------+             / \     
            / \                            |   
             |                             | 
          +--+-----------------------------+-+
          |       IProtocolReaderWriter      |
          +----------------------------------+
          | + is_never_timeout()             |
          +----------------------------------+
*/

/**
* the reader for the buffer to read from whatever channel.
*/
class ISrsBufferReader
{
public:
    ISrsBufferReader();
    virtual ~ISrsBufferReader();
// for protocol/amf0/msg-codec
public:
    virtual int read(void* buf, size_t size, ssize_t* nread) = 0;
};

/**
* the writer for the buffer to write to whatever channel.
*/
class ISrsBufferWriter
{
public:
    ISrsBufferWriter();
    virtual ~ISrsBufferWriter();
// for protocol
public:
    /**
    * write bytes over writer.
    * @nwrite the actual written bytes. NULL to ignore.
    */
    virtual int write(void* buf, size_t size, ssize_t* nwrite) = 0;
    /**
    * write iov over writer.
    * @nwrite the actual written bytes. NULL to ignore.
    */
    virtual int writev(const iovec *iov, int iov_size, ssize_t* nwrite) = 0;
};

/**
* get the statistic of channel.
*/
class ISrsProtocolStatistic
{
public:
    ISrsProtocolStatistic();
    virtual ~ISrsProtocolStatistic();
// for protocol
public:
    /**
    * get the total recv bytes over underlay fd.
    */
    virtual int64_t get_recv_bytes() = 0;
    /**
    * get the total send bytes over underlay fd.
    */
    virtual int64_t get_send_bytes() = 0;
};

/**
* the reader for the protocol to read from whatever channel.
*/
class ISrsProtocolReader : public virtual ISrsBufferReader, public virtual ISrsProtocolStatistic
{
public:
    ISrsProtocolReader();
    virtual ~ISrsProtocolReader();
// for protocol
public:
    /**
    * set the recv timeout in us, recv will error when timeout.
    * @remark, if not set, use ST_UTIME_NO_TIMEOUT, never timeout.
    */
    virtual void set_recv_timeout(int64_t timeout_us) = 0;
    /**
    * get the recv timeout in us.
    */
    virtual int64_t get_recv_timeout() = 0;
// for handshake.
public:
    /**
    * read specified size bytes of data
    * @param nread, the actually read size, NULL to ignore.
    */
    virtual int read_fully(void* buf, size_t size, ssize_t* nread) = 0;
};

/**
* the writer for the protocol to write to whatever channel.
*/
class ISrsProtocolWriter : public virtual ISrsBufferWriter, public virtual ISrsProtocolStatistic
{
public:
    ISrsProtocolWriter();
    virtual ~ISrsProtocolWriter();
// for protocol
public:
    /**
    * set the send timeout in us, send will error when timeout.
    * @remark, if not set, use ST_UTIME_NO_TIMEOUT, never timeout.
    */
    virtual void set_send_timeout(int64_t timeout_us) = 0;
    /**
    * get the send timeout in us.
    */
    virtual int64_t get_send_timeout() = 0;
};

/**
* the reader and writer.
*/
class ISrsProtocolReaderWriter : public virtual ISrsProtocolReader, public virtual ISrsProtocolWriter
{
public:
    ISrsProtocolReaderWriter();
    virtual ~ISrsProtocolReaderWriter();
// for protocol
public:
    /**
    * whether the specified timeout_us is never timeout.
    */
    virtual bool is_never_timeout(int64_t timeout_us) = 0;
};

#endif

// following is generated by src/protocol/srs_rtmp_stack.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_STACK_HPP
#define SRS_RTMP_PROTOCOL_STACK_HPP

/*
//#include <srs_rtmp_stack.hpp>
*/

//#include <srs_core.hpp>

#include <map>
#include <vector>
#include <string>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <sys/uio.h>
#endif

//#include <srs_kernel_log.hpp>
//#include <srs_kernel_error.hpp>
//#include <srs_kernel_consts.hpp>
//#include <srs_core_performance.hpp>
//#include <srs_kernel_flv.hpp>

class ISrsProtocolReaderWriter;
class SrsFastBuffer;
class SrsPacket;
class SrsStream;
class SrsAmf0Object;
class SrsAmf0Any;
class SrsMessageHeader;
class SrsCommonMessage;
class SrsChunkStream;
class SrsSharedPtrMessage;
class IMergeReadHandler;

/****************************************************************************
 *****************************************************************************
 ****************************************************************************/
/**
 * amf0 command message, command name macros
 */
#define RTMP_AMF0_COMMAND_CONNECT               "connect"
#define RTMP_AMF0_COMMAND_CREATE_STREAM         "createStream"
#define RTMP_AMF0_COMMAND_CLOSE_STREAM          "closeStream"
#define RTMP_AMF0_COMMAND_PLAY                  "play"
#define RTMP_AMF0_COMMAND_PAUSE                 "pause"
#define RTMP_AMF0_COMMAND_ON_BW_DONE            "onBWDone"
#define RTMP_AMF0_COMMAND_ON_STATUS             "onStatus"
#define RTMP_AMF0_COMMAND_RESULT                "_result"
#define RTMP_AMF0_COMMAND_ERROR                 "_error"
#define RTMP_AMF0_COMMAND_RELEASE_STREAM        "releaseStream"
#define RTMP_AMF0_COMMAND_FC_PUBLISH            "FCPublish"
#define RTMP_AMF0_COMMAND_UNPUBLISH             "FCUnpublish"
#define RTMP_AMF0_COMMAND_PUBLISH               "publish"
#define RTMP_AMF0_DATA_SAMPLE_ACCESS            "|RtmpSampleAccess"

/****************************************************************************
*****************************************************************************
****************************************************************************/

/**
 * the decoded message payload.
 * @remark we seperate the packet from message,
 *        for the packet focus on logic and domain data,
 *        the message bind to the protocol and focus on protocol, such as header.
 *         we can merge the message and packet, using OOAD hierachy, packet extends from message,
 *         it's better for me to use components -- the message use the packet as payload.
 */
class SrsPacket
{
public:
    SrsPacket();
    virtual ~SrsPacket();
public:
    /**
     * the subpacket can override this encode,
     * for example, video and audio will directly set the payload withou memory copy,
     * other packet which need to serialize/encode to bytes by override the
     * get_size and encode_packet.
     */
    virtual int encode(int& size, char*& payload);
    // decode functions for concrete packet to override.
public:
    /**
     * subpacket must override to decode packet from stream.
     * @remark never invoke the super.decode, it always failed.
     */
    virtual int decode(SrsStream* stream);
    // encode functions for concrete packet to override.
public:
    /**
     * the cid(chunk id) specifies the chunk to send data over.
     * generally, each message perfer some cid, for example,
     * all protocol control messages perfer RTMP_CID_ProtocolControl,
     * SrsSetWindowAckSizePacket is protocol control message.
     */
    virtual int get_prefer_cid();
    /**
     * subpacket must override to provide the right message type.
     * the message type set the RTMP message type in header.
     */
    virtual int get_message_type();
protected:
    /**
     * subpacket can override to calc the packet size.
     */
    virtual int get_size();
    /**
     * subpacket can override to encode the payload to stream.
     * @remark never invoke the super.encode_packet, it always failed.
     */
    virtual int encode_packet(SrsStream* stream);
};

/**
* the protocol provides the rtmp-message-protocol services,
* to recv RTMP message from RTMP chunk stream,
* and to send out RTMP message over RTMP chunk stream.
*/
class SrsProtocol
{
private:
    class AckWindowSize
    {
    public:
        int ack_window_size;
        int64_t acked_size;
        
        AckWindowSize();
    };
// peer in/out
private:
    /**
    * underlayer socket object, send/recv bytes.
    */
    ISrsProtocolReaderWriter* skt;
    /**
    * requests sent out, used to build the response.
    * key: transactionId
    * value: the request command name
    */
    std::map<double, std::string> requests;
// peer in
private:
    /**
    * chunk stream to decode RTMP messages.
    */
    std::map<int, SrsChunkStream*> chunk_streams;
    /**
    * cache some frequently used chunk header.
    * cs_cache, the chunk stream cache.
    * @see https://github.com/simple-rtmp-server/srs/issues/249
    */
    SrsChunkStream** cs_cache;
    /**
    * bytes buffer cache, recv from skt, provide services for stream.
    */
    SrsFastBuffer* in_buffer;
    /**
    * input chunk size, default to 128, set by peer packet.
    */
    int32_t in_chunk_size;
    /**
    * input ack size, when to send the acked packet.
    */
    AckWindowSize in_ack_size;
    /**
    * 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
    */
    bool auto_response_when_recv;
    /**
    * when not auto response message, manual flush the messages in queue.
    */
    std::vector<SrsPacket*> manual_response_queue;
// peer out
private:
    /**
    * cache for multiple messages send,
    * initialize to iovec[SRS_CONSTS_IOVS_MAX] and realloc when consumed,
    * it's ok to realloc the iovs cache, for all ptr is ok.
    */
    iovec* out_iovs;
    int nb_out_iovs;
    /**
    * output header cache.
    * used for type0, 11bytes(or 15bytes with extended timestamp) header.
    * or for type3, 1bytes(or 5bytes with extended timestamp) header.
    * the c0c3 caches must use unit SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE bytes.
    * 
    * @remark, the c0c3 cache cannot be realloc.
    */
    char out_c0c3_caches[SRS_CONSTS_C0C3_HEADERS_MAX];
    // whether warned user to increase the c0c3 header cache.
    bool warned_c0c3_cache_dry;
    /**
    * output chunk size, default to 128, set by config.
    */
    int32_t out_chunk_size;
public:
    SrsProtocol(ISrsProtocolReaderWriter* io);
    virtual ~SrsProtocol();
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);
    /**
    * flush for manual response when the auto response is disabled
    * by set_auto_response(false), we default use auto response, so donot
    * need to call this api(the protocol sdk will auto send message).
    * @see the auto_response_when_recv and manual_response_queue.
    */
    virtual int manual_response_flush();
public:
#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
public:
    /**
    * 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();
public:
    /**
    * 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:
    /**
    * 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<SrsConnectAppResPacket>(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<class T>
    int expect_message(SrsCommonMessage** pmsg, T** ppacket)
    {
        *pmsg = NULL;
        *ppacket = NULL;
        
        int ret = ERROR_SUCCESS;
        
        while (true) {
            SrsCommonMessage* msg = NULL;
            if ((ret = recv_message(&msg)) != ERROR_SUCCESS) {
                if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                    srs_error("recv message failed. ret=%d", ret);
                }
                return ret;
            }
            srs_verbose("recv message success.");
            
            SrsPacket* packet = NULL;
            if ((ret = decode_message(msg, &packet)) != ERROR_SUCCESS) {
                srs_error("decode message failed. ret=%d", ret);
                srs_freep(msg);
                srs_freep(packet);
                return ret;
            }
            
            T* pkt = dynamic_cast<T*>(packet);
            if (!pkt) {
                srs_info("drop message(type=%d, size=%d, time=%"PRId64", sid=%d).", 
                    msg->header.message_type, msg->header.payload_length,
                    msg->header.timestamp, msg->header.stream_id);
                srs_freep(msg);
                srs_freep(packet);
                continue;
            }
            
            *pmsg = msg;
            *ppacket = pkt;
            break;
        }
        
        return ret;
    }
private:
    /**
    * send out the messages, donot free it, 
    * the caller must free the param msgs.
    */
    virtual int do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs);
    /**
    * send iovs. send multiple times if exceed limits.
    */
    virtual int do_iovs_send(iovec* iovs, int size);
    /**
    * underlayer api for send and free packet.
    */
    virtual int do_send_and_free_packet(SrsPacket* packet, int stream_id);
    /**
    * use simple algorithm to send the header and bytes.
    * @remark, for do_send_and_free_packet to send.
    */
    virtual int do_simple_send(SrsMessageHeader* mh, char* payload, int size);
    /**
    * imp for decode_message
    */
    virtual int do_decode_message(SrsMessageHeader& header, SrsStream* stream, SrsPacket** ppacket);
    /**
    * recv bytes oriented RTMP message from protocol stack.
    * return error if error occur and nerver set the pmsg,
    * return success and pmsg set to NULL if no entire message got,
    * return success and pmsg set to entire message if got one.
    */
    virtual int recv_interlaced_message(SrsCommonMessage** pmsg);
    /**
    * read the chunk basic header(fmt, cid) from chunk stream.
    * user can discovery a SrsChunkStream by cid.
    */
    virtual int read_basic_header(char& fmt, int& cid);
    /**
    * read the chunk message header(timestamp, payload_length, message_type, stream_id) 
    * from chunk stream and save to SrsChunkStream.
    */
    virtual int read_message_header(SrsChunkStream* chunk, char fmt);
    /**
    * read the chunk payload, remove the used bytes in buffer,
    * if got entire message, set the pmsg.
    */
    virtual int read_message_payload(SrsChunkStream* chunk, SrsCommonMessage** pmsg);
    /**
    * when recv message, update the context.
    */
    virtual int on_recv_message(SrsCommonMessage* msg);
    /**
    * when message sentout, update the context.
    */
    virtual int on_send_packet(SrsMessageHeader* mh, SrsPacket* packet);
private:
    /**
    * auto response the ack message.
    */
    virtual int response_acknowledgement_message();
    /**
    * auto response the ping message.
    */
    virtual int response_ping_message(int32_t timestamp);
};

/**
* incoming chunk stream maybe interlaced,
* use the chunk stream to cache the input RTMP chunk streams.
*/
class SrsChunkStream
{
public:
    /**
    * represents the basic header fmt,
    * which used to identify the variant message header type.
    */
    char fmt;
    /**
    * represents the basic header cid,
    * which is the chunk stream id.
    */
    int cid;
    /**
    * cached message header
    */
    SrsMessageHeader header;
    /**
    * whether the chunk message header has extended timestamp.
    */
    bool extended_timestamp;
    /**
    * partially read message.
    */
    SrsCommonMessage* msg;
    /**
    * decoded msg count, to identify whether the chunk stream is fresh.
    */
    int64_t msg_count;
public:
    SrsChunkStream(int _cid);
    virtual ~SrsChunkStream();
};

/**
* 4.1.1. connect
* The client sends the connect command to the server to request
* connection to a server application instance.
*/
class SrsConnectAppPacket : public SrsPacket
{
public:
    /**
    * Name of the command. Set to "connect".
    */
    std::string command_name;
    /**
    * Always set to 1.
    */
    double transaction_id;
    /**
    * Command information object which has the name-value pairs.
    * @remark: alloc in packet constructor, user can directly use it, 
    *       user should never alloc it again which will cause memory leak.
    * @remark, never be NULL.
    */
    SrsAmf0Object* command_object;
    /**
    * Any optional information
    * @remark, optional, init to and maybe NULL.
    */
    SrsAmf0Object* args;
public:
    SrsConnectAppPacket();
    virtual ~SrsConnectAppPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};
/**
* response for SrsConnectAppPacket.
*/
class SrsConnectAppResPacket : public SrsPacket
{
public:
    /**
    * _result or _error; indicates whether the response is result or error.
    */
    std::string command_name;
    /**
    * Transaction ID is 1 for call connect responses
    */
    double transaction_id;
    /**
    * Name-value pairs that describe the properties(fmsver etc.) of the connection.
    * @remark, never be NULL.
    */
    SrsAmf0Object* props;
    /**
    * Name-value pairs that describe the response from|the server. 'code',
    * 'level', 'description' are names of few among such information.
    * @remark, never be NULL.
    */
    SrsAmf0Object* info;
public:
    SrsConnectAppResPacket();
    virtual ~SrsConnectAppResPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* 4.1.2. Call
* The call method of the NetConnection object runs remote procedure
* calls (RPC) at the receiving end. The called RPC name is passed as a
* parameter to the call command.
*/
class SrsCallPacket : public SrsPacket
{
public:
    /**
    * Name of the remote procedure that is called.
    */
    std::string command_name;
    /**
    * If a response is expected we give a transaction Id. Else we pass a value of 0
    */
    double transaction_id;
    /**
    * If there exists any command info this
    * is set, else this is set to null type.
    * @remark, optional, init to and maybe NULL.
    */
    SrsAmf0Any* command_object;
    /**
    * Any optional arguments to be provided
    * @remark, optional, init to and maybe NULL.
    */
    SrsAmf0Any* arguments;
public:
    SrsCallPacket();
    virtual ~SrsCallPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};
/**
* response for SrsCallPacket.
*/
class SrsCallResPacket : public SrsPacket
{
public:
    /**
    * Name of the command. 
    */
    std::string command_name;
    /**
    * ID of the command, to which the response belongs to
    */
    double transaction_id;
    /**
    * If there exists any command info this is set, else this is set to null type.
    * @remark, optional, init to and maybe NULL.
    */
    SrsAmf0Any* command_object;
    /**
    * Response from the method that was called.
    * @remark, optional, init to and maybe NULL.
    */
    SrsAmf0Any* response;
public:
    SrsCallResPacket(double _transaction_id);
    virtual ~SrsCallResPacket();
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* 4.1.3. createStream
* The client sends this command to the server to create a logical
* channel for message communication The publishing of audio, video, and
* metadata is carried out over stream channel created using the
* createStream command.
*/
class SrsCreateStreamPacket : public SrsPacket
{
public:
    /**
    * Name of the command. Set to "createStream".
    */
    std::string command_name;
    /**
    * Transaction ID of the command.
    */
    double transaction_id;
    /**
    * If there exists any command info this is set, else this is set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* command_object; // null
public:
    SrsCreateStreamPacket();
    virtual ~SrsCreateStreamPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};
/**
* response for SrsCreateStreamPacket.
*/
class SrsCreateStreamResPacket : public SrsPacket
{
public:
    /**
    * _result or _error; indicates whether the response is result or error.
    */
    std::string command_name;
    /**
    * ID of the command that response belongs to.
    */
    double transaction_id;
    /**
    * If there exists any command info this is set, else this is set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* command_object; // null
    /**
    * The return value is either a stream ID or an error information object.
    */
    double stream_id;
public:
    SrsCreateStreamResPacket(double _transaction_id, double _stream_id);
    virtual ~SrsCreateStreamResPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* client close stream packet.
*/
class SrsCloseStreamPacket : public SrsPacket
{
public:
    /**
    * Name of the command, set to "closeStream".
    */
    std::string command_name;
    /**
    * Transaction ID set to 0.
    */
    double transaction_id;
    /**
    * Command information object does not exist. Set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* command_object; // null
public:
    SrsCloseStreamPacket();
    virtual ~SrsCloseStreamPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
};

/**
* FMLE start publish: ReleaseStream/PublishStream
*/
class SrsFMLEStartPacket : public SrsPacket
{
public:
    /**
    * Name of the command
    */
    std::string command_name;
    /**
    * the transaction ID to get the response.
    */
    double transaction_id;
    /**
    * If there exists any command info this is set, else this is set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* command_object; // null
    /**
    * the stream name to start publish or release.
    */
    std::string stream_name;
public:
    SrsFMLEStartPacket();
    virtual ~SrsFMLEStartPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
// factory method to create specified FMLE packet.
public:
    static SrsFMLEStartPacket* create_release_stream(std::string stream);
    static SrsFMLEStartPacket* create_FC_publish(std::string stream);
};
/**
* response for SrsFMLEStartPacket.
*/
class SrsFMLEStartResPacket : public SrsPacket
{
public:
    /**
    * Name of the command
    */
    std::string command_name;
    /**
    * the transaction ID to get the response.
    */
    double transaction_id;
    /**
    * If there exists any command info this is set, else this is set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* command_object; // null
    /**
    * the optional args, set to undefined.
    * @remark, never be NULL, an AMF0 undefined instance.
    */
    SrsAmf0Any* args; // undefined
public:
    SrsFMLEStartResPacket(double _transaction_id);
    virtual ~SrsFMLEStartResPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* FMLE/flash publish
* 4.2.6. Publish
* The client sends the publish command to publish a named stream to the
* server. Using this name, any client can play this stream and receive
* the published audio, video, and data messages.
*/
class SrsPublishPacket : public SrsPacket
{
public:
    /**
    * Name of the command, set to "publish".
    */
    std::string command_name;
    /**
    * Transaction ID set to 0.
    */
    double transaction_id;
    /**
    * Command information object does not exist. Set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* command_object; // null
    /**
    * Name with which the stream is published.
    */
    std::string stream_name;
    /**
    * Type of publishing. Set to "live", "record", or "append".
    *   record: The stream is published and the data is recorded to a new file.The file
    *           is stored on the server in a subdirectory within the directory that
    *           contains the server application. If the file already exists, it is 
    *           overwritten.
    *   append: The stream is published and the data is appended to a file. If no file
    *           is found, it is created.
    *   live: Live data is published without recording it in a file.
    * @remark, SRS only support live.
    * @remark, optional, default to live.
    */
    std::string type;
public:
    SrsPublishPacket();
    virtual ~SrsPublishPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* 4.2.8. pause
* The client sends the pause command to tell the server to pause or
* start playing.
*/
class SrsPausePacket : public SrsPacket
{
public:
    /**
    * Name of the command, set to "pause".
    */
    std::string command_name;
    /**
    * There is no transaction ID for this command. Set to 0.
    */
    double transaction_id;
    /**
    * Command information object does not exist. Set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* command_object; // null
    /**
    * true or false, to indicate pausing or resuming play
    */
    bool is_pause;
    /**
    * Number of milliseconds at which the the stream is paused or play resumed.
    * This is the current stream time at the Client when stream was paused. When the
    * playback is resumed, the server will only send messages with timestamps
    * greater than this value.
    */
    double time_ms;
public:
    SrsPausePacket();
    virtual ~SrsPausePacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
};

/**
* 4.2.1. play
* The client sends this command to the server to play a stream.
*/
class SrsPlayPacket : public SrsPacket
{
public:
    /**
    * Name of the command. Set to "play".
    */
    std::string command_name;
    /**
    * Transaction ID set to 0.
    */
    double transaction_id;
    /**
    * Command information does not exist. Set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* command_object; // null
    /**
    * Name of the stream to play.
    * To play video (FLV) files, specify the name of the stream without a file
    *       extension (for example, "sample").
    * To play back MP3 or ID3 tags, you must precede the stream name with mp3:
    *       (for example, "mp3:sample".)
    * To play H.264/AAC files, you must precede the stream name with mp4: and specify the
    *       file extension. For example, to play the file sample.m4v, specify 
    *       "mp4:sample.m4v"
    */
    std::string stream_name;
    /**
    * An optional parameter that specifies the start time in seconds.
    * The default value is -2, which means the subscriber first tries to play the live 
    *       stream specified in the Stream Name field. If a live stream of that name is 
    *       not found, it plays the recorded stream specified in the Stream Name field.
    * If you pass -1 in the Start field, only the live stream specified in the Stream 
    *       Name field is played.
    * If you pass 0 or a positive number in the Start field, a recorded stream specified 
    *       in the Stream Name field is played beginning from the time specified in the 
    *       Start field.
    * If no recorded stream is found, the next item in the playlist is played.
    */
    double start;
    /**
    * An optional parameter that specifies the duration of playback in seconds.
    * The default value is -1. The -1 value means a live stream is played until it is no
    *       longer available or a recorded stream is played until it ends.
    * If u pass 0, it plays the single frame since the time specified in the Start field 
    *       from the beginning of a recorded stream. It is assumed that the value specified 
    *       in the Start field is equal to or greater than 0.
    * If you pass a positive number, it plays a live stream for the time period specified 
    *       in the Duration field. After that it becomes available or plays a recorded 
    *       stream for the time specified in the Duration field. (If a stream ends before the
    *       time specified in the Duration field, playback ends when the stream ends.)
    * If you pass a negative number other than -1 in the Duration field, it interprets the 
    *       value as if it were -1.
    */
    double duration;
    /**
    * An optional Boolean value or number that specifies whether to flush any
    * previous playlist.
    */
    bool reset;
public:
    SrsPlayPacket();
    virtual ~SrsPlayPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* response for SrsPlayPacket.
* @remark, user must set the stream_id in header.
*/
class SrsPlayResPacket : public SrsPacket
{
public:
    /**
    * Name of the command. If the play command is successful, the command
    * name is set to onStatus.
    */
    std::string command_name;
    /**
    * Transaction ID set to 0.
    */
    double transaction_id;
    /**
    * Command information does not exist. Set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* command_object; // null
    /**
    * If the play command is successful, the client receives OnStatus message from
    * server which is NetStream.Play.Start. If the specified stream is not found,
    * NetStream.Play.StreamNotFound is received.
    * @remark, never be NULL, an AMF0 object instance.
    */
    SrsAmf0Object* desc;
public:
    SrsPlayResPacket();
    virtual ~SrsPlayResPacket();
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* when bandwidth test done, notice client.
*/
class SrsOnBWDonePacket : public SrsPacket
{
public:
    /**
    * Name of command. Set to "onBWDone"
    */
    std::string command_name;
    /**
    * Transaction ID set to 0.
    */
    double transaction_id;
    /**
    * Command information does not exist. Set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* args; // null
public:
    SrsOnBWDonePacket();
    virtual ~SrsOnBWDonePacket();
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* onStatus command, AMF0 Call
* @remark, user must set the stream_id by SrsCommonMessage.set_packet().
*/
class SrsOnStatusCallPacket : public SrsPacket
{
public:
    /**
    * Name of command. Set to "onStatus"
    */
    std::string command_name;
    /**
    * Transaction ID set to 0.
    */
    double transaction_id;
    /**
    * Command information does not exist. Set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* args; // null
    /**
    * Name-value pairs that describe the response from the server. 
    * 'code','level', 'description' are names of few among such information.
    * @remark, never be NULL, an AMF0 object instance.
    */
    SrsAmf0Object* data;
public:
    SrsOnStatusCallPacket();
    virtual ~SrsOnStatusCallPacket();
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* the special packet for the bandwidth test.
* actually, it's a SrsOnStatusCallPacket, but
* 1. encode with data field, to send data to client.
* 2. decode ignore the data field, donot care.
*/
class SrsBandwidthPacket : public SrsPacket
{
private:
    disable_default_copy(SrsBandwidthPacket);
public:
    /**
    * Name of command. 
    */
    std::string command_name;
    /**
    * Transaction ID set to 0.
    */
    double transaction_id;
    /**
    * Command information does not exist. Set to null type.
    * @remark, never be NULL, an AMF0 null instance.
    */
    SrsAmf0Any* args; // null
    /**
    * Name-value pairs that describe the response from the server.
    * 'code','level', 'description' are names of few among such information.
    * @remark, never be NULL, an AMF0 object instance.
    */
    SrsAmf0Object* data;
public:
    SrsBandwidthPacket();
    virtual ~SrsBandwidthPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
// help function for bandwidth packet.
public:
    virtual bool is_start_play();
    virtual bool is_starting_play();
    virtual bool is_stop_play();
    virtual bool is_stopped_play();
    virtual bool is_start_publish();
    virtual bool is_starting_publish();
    virtual bool is_stop_publish();
    virtual bool is_stopped_publish();
    virtual bool is_finish();
    virtual bool is_final();
    static SrsBandwidthPacket* create_start_play();
    static SrsBandwidthPacket* create_starting_play();
    static SrsBandwidthPacket* create_playing();
    static SrsBandwidthPacket* create_stop_play();
    static SrsBandwidthPacket* create_stopped_play();
    static SrsBandwidthPacket* create_start_publish();
    static SrsBandwidthPacket* create_starting_publish();
    static SrsBandwidthPacket* create_publishing();
    static SrsBandwidthPacket* create_stop_publish();
    static SrsBandwidthPacket* create_stopped_publish();
    static SrsBandwidthPacket* create_finish();
    static SrsBandwidthPacket* create_final();
private:
    virtual SrsBandwidthPacket* set_command(std::string command);
};

/**
* onStatus data, AMF0 Data
* @remark, user must set the stream_id by SrsCommonMessage.set_packet().
*/
class SrsOnStatusDataPacket : public SrsPacket
{
public:
    /**
    * Name of command. Set to "onStatus"
    */
    std::string command_name;
    /**
    * Name-value pairs that describe the response from the server.
    * 'code', are names of few among such information.
    * @remark, never be NULL, an AMF0 object instance.
    */
    SrsAmf0Object* data;
public:
    SrsOnStatusDataPacket();
    virtual ~SrsOnStatusDataPacket();
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* AMF0Data RtmpSampleAccess
* @remark, user must set the stream_id by SrsCommonMessage.set_packet().
*/
class SrsSampleAccessPacket : public SrsPacket
{
public:
    /**
    * Name of command. Set to "|RtmpSampleAccess".
    */
    std::string command_name;
    /**
    * whether allow access the sample of video.
    * @see: https://github.com/simple-rtmp-server/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: http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/net/NetStream.html#audioSampleAccess
    */
    bool audio_sample_access;
public:
    SrsSampleAccessPacket();
    virtual ~SrsSampleAccessPacket();
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* the stream metadata.
* FMLE: @setDataFrame
* others: onMetaData
*/
class SrsOnMetaDataPacket : public SrsPacket
{
public:
    /**
    * Name of metadata. Set to "onMetaData"
    */
    std::string name;
    /**
    * Metadata of stream.
    * @remark, never be NULL, an AMF0 object instance.
    */
    SrsAmf0Object* metadata;
public:
    SrsOnMetaDataPacket();
    virtual ~SrsOnMetaDataPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* 5.5. Window Acknowledgement Size (5)
* The client or the server sends this message to inform the peer which
* window size to use when sending acknowledgment.
*/
class SrsSetWindowAckSizePacket : public SrsPacket
{
public:
    int32_t ackowledgement_window_size;
public:
    SrsSetWindowAckSizePacket();
    virtual ~SrsSetWindowAckSizePacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* 5.3. Acknowledgement (3)
* The client or the server sends the acknowledgment to the peer after
* receiving bytes equal to the window size.
*/
class SrsAcknowledgementPacket : public SrsPacket
{
public:
    int32_t sequence_number;
public:
    SrsAcknowledgementPacket();
    virtual ~SrsAcknowledgementPacket();
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

/**
* 7.1. Set Chunk Size
* Protocol control message 1, Set Chunk Size, is used to notify the
* peer about the new maximum chunk size.
*/
class SrsSetChunkSizePacket : public SrsPacket
{
public:
    /**
    * The maximum chunk size can be 65536 bytes. The chunk size is
    * maintained independently for each direction.
    */
    int32_t chunk_size;
public:
    SrsSetChunkSizePacket();
    virtual ~SrsSetChunkSizePacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

// 5.6. Set Peer Bandwidth (6)
enum SrsPeerBandwidthType
{
    // The sender can mark this message hard (0), soft (1), or dynamic (2)
    // using the Limit type field.
    SrsPeerBandwidthHard = 0,
    SrsPeerBandwidthSoft = 1,
    SrsPeerBandwidthDynamic = 2,
};

/**
* 5.6. Set Peer Bandwidth (6)
* The client or the server sends this message to update the output
* bandwidth of the peer.
*/
class SrsSetPeerBandwidthPacket : public SrsPacket
{
public:
    int32_t bandwidth;
    // @see: SrsPeerBandwidthType
    int8_t type;
public:
    SrsSetPeerBandwidthPacket();
    virtual ~SrsSetPeerBandwidthPacket();
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

// 3.7. User Control message
enum SrcPCUCEventType
{
    // generally, 4bytes event-data
    
    /**
    * The server sends this event to notify the client
    * that a stream has become functional and can be
    * used for communication. By default, this event
    * is sent on ID 0 after the application connect
    * command is successfully received from the
    * client. The event data is 4-byte and represents
    * the stream ID of the stream that became
    * functional.
    */
    SrcPCUCStreamBegin              = 0x00,

    /**
    * The server sends this event to notify the client
    * that the playback of data is over as requested
    * on this stream. No more data is sent without
    * issuing additional commands. The client discards
    * the messages received for the stream. The
    * 4 bytes of event data represent the ID of the
    * stream on which playback has ended.
    */
    SrcPCUCStreamEOF                = 0x01,

    /**
    * The server sends this event to notify the client
    * that there is no more data on the stream. If the
    * server does not detect any message for a time
    * period, it can notify the subscribed clients 
    * that the stream is dry. The 4 bytes of event 
    * data represent the stream ID of the dry stream. 
    */
    SrcPCUCStreamDry                = 0x02,

    /**
    * The client sends this event to inform the server
    * of the buffer size (in milliseconds) that is 
    * used to buffer any data coming over a stream.
    * This event is sent before the server starts  
    * processing the stream. The first 4 bytes of the
    * event data represent the stream ID and the next
    * 4 bytes represent the buffer length, in 
    * milliseconds.
    */
    SrcPCUCSetBufferLength          = 0x03, // 8bytes event-data

    /**
    * The server sends this event to notify the client
    * that the stream is a recorded stream. The
    * 4 bytes event data represent the stream ID of
    * the recorded stream.
    */
    SrcPCUCStreamIsRecorded         = 0x04,

    /**
    * The server sends this event to test whether the
    * client is reachable. Event data is a 4-byte
    * timestamp, representing the local server time
    * when the server dispatched the command. The
    * client responds with kMsgPingResponse on
    * receiving kMsgPingRequest.  
    */
    SrcPCUCPingRequest              = 0x06,

    /**
    * The client sends this event to the server in
    * response to the ping request. The event data is
    * a 4-byte timestamp, which was received with the
    * kMsgPingRequest request.
    */
    SrcPCUCPingResponse             = 0x07,
};

/**
* 5.4. User Control Message (4)
* 
* for the EventData is 4bytes.
* Stream Begin(=0)              4-bytes stream ID
* Stream EOF(=1)                4-bytes stream ID
* StreamDry(=2)                 4-bytes stream ID
* SetBufferLength(=3)           8-bytes 4bytes stream ID, 4bytes buffer length.
* StreamIsRecorded(=4)          4-bytes stream ID
* PingRequest(=6)               4-bytes timestamp local server time
* PingResponse(=7)              4-bytes timestamp received ping request.
* 
* 3.7. User Control message
* +------------------------------+-------------------------
* | Event Type ( 2- bytes ) | Event Data
* +------------------------------+-------------------------
* Figure 5 Pay load for the 'User Control Message'.
*/
class SrsUserControlPacket : public SrsPacket
{
public:
    /**
    * Event type is followed by Event data.
    * @see: SrcPCUCEventType
    */
    int16_t event_type;
    int32_t event_data;
    /**
    * 4bytes if event_type is SetBufferLength; otherwise 0.
    */
    int32_t extra_data;
public:
    SrsUserControlPacket();
    virtual ~SrsUserControlPacket();
// decode functions for concrete packet to override.
public:
    virtual int decode(SrsStream* stream);
// encode functions for concrete packet to override.
public:
    virtual int get_prefer_cid();
    virtual int get_message_type();
protected:
    virtual int get_size();
    virtual int encode_packet(SrsStream* stream);
};

#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 <srs_rtmp_sdk.hpp>
*/

//#include <srs_core.hpp>

#include <string>

//#include <srs_rtmp_stack.hpp>
//#include <srs_core_performance.hpp>

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<SrsConnectAppResPacket>(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<class T>
    int expect_message(SrsCommonMessage** pmsg, T** ppacket)
    {
        return protocol->expect_message<T>(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<SrsConnectAppResPacket>(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<class T>
    int expect_message(SrsCommonMessage** pmsg, T** ppacket)
    {
        return protocol->expect_message<T>(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)

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_HANDSHKAE_HPP
#define SRS_RTMP_PROTOCOL_HANDSHKAE_HPP

/*
//#include <srs_rtmp_handshake.hpp>
*/

//#include <srs_core.hpp>

class ISrsProtocolReaderWriter;
class SrsComplexHandshake;
class SrsHandshakeBytes;
class SrsStream;

#ifdef SRS_AUTO_SSL

// for openssl.
#include <openssl/hmac.h>

namespace _srs_internal
{
    // the digest key generate size.
    #define SRS_OpensslHashSize 512
    extern u_int8_t SrsGenuineFMSKey[];
    extern u_int8_t SrsGenuineFPKey[];
    int openssl_HMACsha256(const void* key, int key_size, const void* data, int data_size, void* digest);
    int openssl_generate_key(char* public_key, int32_t size);
    
    /**
    * the DH wrapper.
    */
    class SrsDH
    {
    private:
        DH* pdh;
    public:
        SrsDH();
        virtual ~SrsDH();
    public:
        /**
        * initialize dh, generate the public and private key.
        * @param ensure_128bytes_public_key whether ensure public key is 128bytes,
        *       sometimes openssl generate 127bytes public key.
        *       default to false to donot ensure.
        */
        virtual int initialize(bool ensure_128bytes_public_key = false);
        /**
        * copy the public key.
        * @param pkey the bytes to copy the public key.
        * @param pkey_size the max public key size, output the actual public key size.
        *       user should never ignore this size.
        * @remark, when ensure_128bytes_public_key, the size always 128.
        */
        virtual int copy_public_key(char* pkey, int32_t& pkey_size);
        /**
        * generate and copy the shared key.
        * generate the shared key with peer public key.
        * @param ppkey peer public key.
        * @param ppkey_size the size of ppkey.
        * @param skey the computed shared key.
        * @param skey_size the max shared key size, output the actual shared key size.
        *       user should never ignore this size.
        */
        virtual int copy_shared_key(const char* ppkey, int32_t ppkey_size, char* skey, int32_t& skey_size);
    private:
        virtual int do_initialize();
    };
    /**
    * the schema type.
    */
    enum srs_schema_type 
    {
        srs_schema_invalid = 2,
        
        /**
        * key-digest sequence
        */
        srs_schema0 = 0,
        
        /**
        * digest-key sequence
        * @remark, FMS requires the schema1(digest-key), or connect failed.
        */
        // 
        srs_schema1 = 1,
    };
    
    /**
    * 764bytes key structure
    *     random-data: (offset)bytes
    *     key-data: 128bytes
    *     random-data: (764-offset-128-4)bytes
    *     offset: 4bytes
    * @see also: http://blog.csdn.net/win_lin/article/details/13006803
    */
    class key_block
    {
    public:
        // (offset)bytes
        char* random0;
        int random0_size;
        
        // 128bytes
        char key[128];
        
        // (764-offset-128-4)bytes
        char* random1;
        int random1_size;
        
        // 4bytes
        int32_t offset;
    public:
        key_block();
        virtual ~key_block();
    public:
        // parse key block from c1s1.
        // if created, user must free it by srs_key_block_free
        // @stream contains c1s1_key_bytes the key start bytes
        int parse(SrsStream* stream);
    private:
        // calc the offset of key,
        // the key->offset cannot be used as the offset of key.
        int calc_valid_offset();
    };
    
    /**
    * 764bytes digest structure
    *     offset: 4bytes
    *     random-data: (offset)bytes
    *     digest-data: 32bytes
    *     random-data: (764-4-offset-32)bytes
    * @see also: http://blog.csdn.net/win_lin/article/details/13006803
    */
    class digest_block
    {
    public:
        // 4bytes
        int32_t offset;
        
        // (offset)bytes
        char* random0;
        int random0_size;
        
        // 32bytes
        char digest[32];
        
        // (764-4-offset-32)bytes
        char* random1;
        int random1_size;
    public:
        digest_block();
        virtual ~digest_block();
    public:
        // parse digest block from c1s1.
        // if created, user must free it by srs_digest_block_free
        // @stream contains c1s1_digest_bytes the digest start bytes
        int parse(SrsStream* stream);
    private:
        // calc the offset of digest,
        // the key->offset cannot be used as the offset of digest.
        int calc_valid_offset();
    };
    
    class c1s1;
    
    /**
    * the c1s1 strategy, use schema0 or schema1.
    * the template method class to defines common behaviors,
    * while the concrete class to implements in schema0 or schema1.
    */
    class c1s1_strategy
    {
    protected:
        key_block key;
        digest_block digest;
    public:
        c1s1_strategy();
        virtual ~c1s1_strategy();
    public:
        /**
        * get the scema.
        */
        virtual srs_schema_type schema() = 0;
        /**
        * get the digest.
        */
        virtual char* get_digest();
        /**
        * get the key.
        */
        virtual char* get_key();
        /**
        * copy to bytes.
        * @param size must be 1536.
        */
        virtual int dump(c1s1* owner, char* _c1s1, int size);
        /**
        * server: parse the c1s1, discovery the key and digest by schema.
        * use the c1_validate_digest() to valid the digest of c1.
        */
        virtual int parse(char* _c1s1, int size) = 0;
    public:
        /**
        * client: create and sign c1 by schema.
        * sign the c1, generate the digest.
        *         calc_c1_digest(c1, schema) {
        *            get c1s1-joined from c1 by specified schema
        *            digest-data = HMACsha256(c1s1-joined, FPKey, 30)
        *            return digest-data;
        *        }
        *        random fill 1536bytes c1 // also fill the c1-128bytes-key
        *        time = time() // c1[0-3]
        *        version = [0x80, 0x00, 0x07, 0x02] // c1[4-7]
        *        schema = choose schema0 or schema1
        *        digest-data = calc_c1_digest(c1, schema)
        *        copy digest-data to c1
        */
        virtual int c1_create(c1s1* owner);
        /**
        * server: validate the parsed c1 schema
        */
        virtual int c1_validate_digest(c1s1* owner, bool& is_valid);
        /**
        * server: create and sign the s1 from c1.
        *       // decode c1 try schema0 then schema1
        *       c1-digest-data = get-c1-digest-data(schema0)
        *       if c1-digest-data equals to calc_c1_digest(c1, schema0) {  
        *           c1-key-data = get-c1-key-data(schema0)  
        *           schema = schema0
        *       } else {  
        *           c1-digest-data = get-c1-digest-data(schema1)  
        *           if c1-digest-data not equals to calc_c1_digest(c1, schema1) {
        *               switch to simple handshake.  
        *               return  
        *           }
        *           c1-key-data = get-c1-key-data(schema1)  
        *           schema = schema1
        *       }
        * 
        *       // generate s1
        *       random fill 1536bytes s1
        *       time = time() // c1[0-3]
        *       version = [0x04, 0x05, 0x00, 0x01] // s1[4-7]
        *       s1-key-data=shared_key=DH_compute_key(peer_pub_key=c1-key-data)
        *       get c1s1-joined by specified schema
        *       s1-digest-data = HMACsha256(c1s1-joined, FMSKey, 36)
        *       copy s1-digest-data and s1-key-data to s1.
        * @param c1, to get the peer_pub_key of client.
        */
        virtual int s1_create(c1s1* owner, c1s1* c1);
        /**
        * server: validate the parsed s1 schema
        */
        virtual int s1_validate_digest(c1s1* owner, bool& is_valid);
    public:
        /**
        * calc the digest for c1
        */
        virtual int calc_c1_digest(c1s1* owner, char*& c1_digest);
        /**
        * calc the digest for s1
        */
        virtual int calc_s1_digest(c1s1* owner, char*& s1_digest);
        /**
        * copy whole c1s1 to bytes.
        * @param size must always be 1536 with digest, and 1504 without digest.
        */
        virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest) = 0;
        /**
        * copy time and version to stream.
        */
        virtual void copy_time_version(SrsStream* stream, c1s1* owner);
        /**
        * copy key to stream.
        */
        virtual void copy_key(SrsStream* stream);
        /**
        * copy digest to stream.
        */
        virtual void copy_digest(SrsStream* stream, bool with_digest);
    };
    
    /**
    * c1s1 schema0
    *     key: 764bytes
    *     digest: 764bytes
    */
    class c1s1_strategy_schema0 : public c1s1_strategy
    {
    public:
        c1s1_strategy_schema0();
        virtual ~c1s1_strategy_schema0();
    public:
        virtual srs_schema_type schema();
        virtual int parse(char* _c1s1, int size);
    public:
        virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest);
    };
    
    /**
    * c1s1 schema1
    *     digest: 764bytes
    *     key: 764bytes
    */
    class c1s1_strategy_schema1 : public c1s1_strategy
    {
    public:
        c1s1_strategy_schema1();
        virtual ~c1s1_strategy_schema1();
    public:
        virtual srs_schema_type schema();
        virtual int parse(char* _c1s1, int size);
    public:
        virtual int copy_to(c1s1* owner, char* bytes, int size, bool with_digest);
    };

    /**
    * c1s1 schema0
    *     time: 4bytes
    *     version: 4bytes
    *     key: 764bytes
    *     digest: 764bytes
    * c1s1 schema1
    *     time: 4bytes
    *     version: 4bytes
    *     digest: 764bytes
    *     key: 764bytes
    * @see also: http://blog.csdn.net/win_lin/article/details/13006803
    */
    class c1s1
    {
    public:
        // 4bytes
        int32_t time;
        // 4bytes
        int32_t version;
        // 764bytes+764bytes
        c1s1_strategy* payload;
    public:
        c1s1();
        virtual ~c1s1();
    public:
        /**
        * get the scema.
        */
        virtual srs_schema_type schema();
        /**
        * get the digest key.
        */
        virtual char* get_digest();
        /**
        * get the key.
        */
        virtual char* get_key();
    public:
        /**
        * copy to bytes.
        * @param size, must always be 1536.
        */
        virtual int dump(char* _c1s1, int size);
        /**
        * server: parse the c1s1, discovery the key and digest by schema.
        * @param size, must always be 1536.
        * use the c1_validate_digest() to valid the digest of c1.
        * use the s1_validate_digest() to valid the digest of s1.
        */
        virtual int parse(char* _c1s1, int size, srs_schema_type _schema);
    public:
        /**
        * client: create and sign c1 by schema.
        * sign the c1, generate the digest.
        *         calc_c1_digest(c1, schema) {
        *            get c1s1-joined from c1 by specified schema
        *            digest-data = HMACsha256(c1s1-joined, FPKey, 30)
        *            return digest-data;
        *        }
        *        random fill 1536bytes c1 // also fill the c1-128bytes-key
        *        time = time() // c1[0-3]
        *        version = [0x80, 0x00, 0x07, 0x02] // c1[4-7]
        *        schema = choose schema0 or schema1
        *        digest-data = calc_c1_digest(c1, schema)
        *        copy digest-data to c1
        */
        virtual int c1_create(srs_schema_type _schema);
        /**
        * server: validate the parsed c1 schema
        */
        virtual int c1_validate_digest(bool& is_valid);
    public:
        /**
        * server: create and sign the s1 from c1.
        *       // decode c1 try schema0 then schema1
        *       c1-digest-data = get-c1-digest-data(schema0)
        *       if c1-digest-data equals to calc_c1_digest(c1, schema0) {  
        *           c1-key-data = get-c1-key-data(schema0)  
        *           schema = schema0
        *       } else {  
        *           c1-digest-data = get-c1-digest-data(schema1)  
        *           if c1-digest-data not equals to calc_c1_digest(c1, schema1) {
        *               switch to simple handshake.  
        *               return  
        *           }
        *           c1-key-data = get-c1-key-data(schema1)  
        *           schema = schema1
        *       }
        * 
        *       // generate s1
        *       random fill 1536bytes s1
        *       time = time() // c1[0-3]
        *       version = [0x04, 0x05, 0x00, 0x01] // s1[4-7]
        *       s1-key-data=shared_key=DH_compute_key(peer_pub_key=c1-key-data)
        *       get c1s1-joined by specified schema
        *       s1-digest-data = HMACsha256(c1s1-joined, FMSKey, 36)
        *       copy s1-digest-data and s1-key-data to s1.
        */
        virtual int s1_create(c1s1* c1);
        /**
        * server: validate the parsed s1 schema
        */
        virtual int s1_validate_digest(bool& is_valid);
    };
    
    /**
    * the c2s2 complex handshake structure.
    * random-data: 1504bytes
    * digest-data: 32bytes
    * @see also: http://blog.csdn.net/win_lin/article/details/13006803
    */
    class c2s2
    {
    public:
        char random[1504];
        char digest[32];
    public:
        c2s2();
        virtual ~c2s2();
    public:
        /**
        * copy to bytes.
        * @param size, must always be 1536.
        */
        virtual int dump(char* _c2s2, int size);
        /**
        * parse the c2s2
        * @param size, must always be 1536.
        */
        virtual int parse(char* _c2s2, int size);
    public:
        /**
        * create c2.
        * random fill c2s2 1536 bytes
        * 
        * // client generate C2, or server valid C2
        * temp-key = HMACsha256(s1-digest, FPKey, 62)
        * c2-digest-data = HMACsha256(c2-random-data, temp-key, 32)
        */
        virtual int c2_create(c1s1* s1);
        
        /**
        * validate the c2 from client.
        */
        virtual int c2_validate(c1s1* s1, bool& is_valid);
    public:
        /**
        * create s2.
        * random fill c2s2 1536 bytes
        * 
        * // server generate S2, or client valid S2
        * temp-key = HMACsha256(c1-digest, FMSKey, 68)
        * s2-digest-data = HMACsha256(s2-random-data, temp-key, 32)
        */
        virtual int s2_create(c1s1* c1);
        
        /**
        * validate the s2 from server.
        */
        virtual int s2_validate(c1s1* c1, bool& is_valid);
    };
}

#endif

/**
* simple handshake.
* user can try complex handshake first, 
* rollback to simple handshake if error ERROR_RTMP_TRY_SIMPLE_HS
*/
class SrsSimpleHandshake
{
public:
    SrsSimpleHandshake();
    virtual ~SrsSimpleHandshake();
public:
    /**
    * simple handshake.
    */
    virtual int handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrsProtocolReaderWriter* io);
    virtual int handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrsProtocolReaderWriter* io);
};

/**
* rtmp complex handshake,
* @see also crtmp(crtmpserver) or librtmp,
* @see also: http://blog.csdn.net/win_lin/article/details/13006803
*/
class SrsComplexHandshake
{
public:
    SrsComplexHandshake();
    virtual ~SrsComplexHandshake();
public:
    /**
    * complex hanshake.
    * @return user must:
    *     continue connect app if success,
    *     try simple handshake if error is ERROR_RTMP_TRY_SIMPLE_HS,
    *     otherwise, disconnect
    */
    virtual int handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrsProtocolReaderWriter* io);
    virtual int handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrsProtocolReaderWriter* io);
};

#endif
// following is generated by src/protocol/srs_rtmp_utility.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_CONSTS_HPP
#define SRS_RTMP_PROTOCOL_CONSTS_HPP

/*
//#include <srs_rtmp_utility.hpp>
*/
//#include <srs_core.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <sys/uio.h>
#endif

#include <string>

//#include <srs_kernel_consts.hpp>

class SrsMessageHeader;
class SrsSharedPtrMessage;
class ISrsProtocolReaderWriter;

/**
* parse the tcUrl, output the schema, host, vhost, app and port.
* @param tcUrl, the input tcUrl, for example, 
*       rtmp://192.168.1.10:19350/live?vhost=vhost.ossrs.net
* @param schema, for example, rtmp
* @param host, for example, 192.168.1.10
* @param vhost, for example, vhost.ossrs.net.
*       vhost default to host, when user not set vhost in query of app.
* @param app, for example, live
* @param port, for example, 19350
*       default to 1935 if not specified.
* param param, for example, vhost=vhost.ossrs.net
*/
extern void srs_discovery_tc_url(
    std::string tcUrl, 
    std::string& schema, std::string& host, std::string& vhost, 
    std::string& app, std::string& port, std::string& param
);

/**
* resolve the vhost in query string
* @pram vhost, update the vhost if query contains the vhost.
* @param app, may contains the vhost in query string format:
*   app?vhost=request_vhost
*   app...vhost...request_vhost
* @param param, the query, for example, ?vhost=xxx
*/ 
extern void srs_vhost_resolve(
    std::string& vhost, std::string& app, std::string& param
);

/**
* generate ramdom data for handshake.
*/
extern void srs_random_generate(char* bytes, int size);

/**
* generate the tcUrl.
* @param param, the app parameters in tcUrl. for example, ?key=xxx,vhost=xxx
* @return the tcUrl generated from ip/vhost/app/port.
* @remark when vhost equals to __defaultVhost__, use ip as vhost.
* @remark ignore port if port equals to default port 1935.
*/
extern std::string srs_generate_tc_url(
    std::string ip, std::string vhost, std::string app, std::string port,
    std::string param
);

/**
* compare the memory in bytes.
* @return true if completely equal; otherwise, false.
*/
extern bool srs_bytes_equals(void* pa, void* pb, int size);

/**
* create shared ptr message from bytes.
* @param data the packet bytes. user should never free it.
* @param ppmsg output the shared ptr message. user should free it.
*/
extern int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg);

// get the stream identify, vhost/app/stream.
extern std::string srs_generate_stream_url(std::string vhost, std::string app, std::string stream);

// write large numbers of iovs.
extern int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, ssize_t* pnwrite = NULL);

#endif

// following is generated by src/protocol/srs_rtmp_msg_array.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_MSG_ARRAY_HPP
#define SRS_RTMP_PROTOCOL_MSG_ARRAY_HPP

/*
//#include <srs_rtmp_msg_array.hpp>
*/

//#include <srs_core.hpp>

class SrsSharedPtrMessage;

/**
* the class to auto free the shared ptr message array.
* when need to get some messages, for instance, from Consumer queue,
* create a message array, whose msgs can used to accept the msgs,
* then send each message and set to NULL.
*
* @remark: user must free all msgs in array, for the SRS2.0 protocol stack
*       provides an api to send messages, @see send_and_free_messages
*/
class SrsMessageArray
{
public:
    /**
    * when user already send the msg in msgs, please set to NULL,
    * for instance, msg= msgs.msgs[i], msgs.msgs[i]=NULL, send(msg),
    * where send(msg) will always send and free it.
    */
    SrsSharedPtrMessage** msgs;
    int max;
public:
    /**
    * create msg array, initialize array to NULL ptrs.
    */
    SrsMessageArray(int max_msgs);
    /**
    * free the msgs not sent out(not NULL).
    */
    virtual ~SrsMessageArray();
public:
    /**
    * free specified count of messages.
    */
    virtual void free(int count);
private:
    /**
    * zero initialize the message array.
    */
    virtual void zero(int count);
};

#endif

// following is generated by src/protocol/srs_protocol_buffer.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_PROTOCOL_BUFFER_HPP
#define SRS_PROTOCOL_BUFFER_HPP

/*
//#include <srs_protocol_buffer.hpp>
*/

//#include <srs_core.hpp>

//#include <srs_rtmp_io.hpp>
//#include <srs_core_performance.hpp>
//#include <srs_kernel_buffer.hpp>

#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.
* @see https://github.com/simple-rtmp-server/srs/issues/241
*/
class IMergeReadHandler
{
public:
    IMergeReadHandler();
    virtual ~IMergeReadHandler();
public:
    /**
    * when read from channel, notice the merge handler to sleep for
    * some small bytes.
    * @remark, it only for server-side, client srs-librtmp just ignore.
    */
    virtual void on_read(ssize_t nread) = 0;
};
#endif

/**
* the buffer provices bytes cache for protocol. generally, 
* protocol recv data from socket, put into buffer, decode to RTMP message.
* Usage:
*       ISrsBufferReader* r = ......;
*       SrsFastBuffer* fb = ......;
*       fb->grow(r, 1024);
*       char* header = fb->read_slice(100);
*       char* payload = fb->read_payload(924);
*/
// TODO: FIXME: add utest for it.
class SrsFastBuffer
{
private:
#ifdef SRS_PERF_MERGED_READ
    // the merged handler
    bool merged_read;
    IMergeReadHandler* _handler;
#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
    // ptr to the current read position.
    char* p;
    // ptr to the content end.
    char* end;
    // ptr to the buffer.
    //      buffer <= p <= end <= buffer+nb_buffer
    char* buffer;
    // the size of buffer.
    int nb_buffer;
public:
    SrsFastBuffer();
    virtual ~SrsFastBuffer();
public:
    /**
    * get the size of current bytes in buffer.
    */
    virtual int size();
    /**
    * get the current bytes in buffer.
    * @remark user should use read_slice() if possible, 
    *       the bytes() is used to test bytes, for example, to detect the bytes schema.
    */
    virtual char* bytes();
    /**
    * create buffer with specifeid size.
    * @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
    */
    virtual void set_buffer(int buffer_size);
public:
    /**
    * read 1byte from buffer, move to next bytes.
    * @remark assert buffer already grow(1).
    */
    virtual char read_1byte();
    /**
    * read a slice in size bytes, move to next bytes.
    * user can use this char* ptr directly, and should never free it.
    * @remark user can use the returned ptr util grow(size),
    *       for the ptr returned maybe invalid after grow(x).
    */
    virtual char* read_slice(int size);
    /**
    * skip some bytes in buffer.
    * @param size the bytes to skip. positive to next; negative to previous.
    * @remark assert buffer already grow(size).
    * @remark always use read_slice to consume bytes, which will reset for EOF.
    *       while skip never consume bytes.
    */
    virtual void skip(int size);
public:
    /**
    * grow buffer to the required size, loop to read from skt to fill.
    * @param reader, read more bytes from reader to fill the buffer to required size.
    * @param required_size, loop to fill to ensure buffer size to required. 
    * @return an int error code, error if required_size negative.
    * @remark, we actually maybe read more than required_size, maybe 4k for example.
    */
    virtual int grow(ISrsBufferReader* reader, int required_size);
public:
#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
    * @remark the merged read is optional, ignore if not specifies.
    */
    virtual void set_merge_read(bool v, IMergeReadHandler* handler);
#endif
};

#endif
// following is generated by src/protocol/srs_raw_avc.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_PROTOCOL_RAW_AVC_HPP
#define SRS_PROTOCOL_RAW_AVC_HPP

/*
//#include <srs_raw_avc.hpp>
*/

//#include <srs_core.hpp>

#include <string>

//#include <srs_kernel_codec.hpp>

class SrsStream;

/**
* the raw h.264 stream, in annexb.
*/
class SrsRawH264Stream
{
public:
    SrsRawH264Stream();
    virtual ~SrsRawH264Stream();
public:
    /**
    * demux the stream in annexb format.
    * @param stream the input stream bytes.
    * @param pframe the output h.264 frame in stream. user should never free it.
    * @param pnb_frame the output h.264 frame size.
    */
    virtual int annexb_demux(SrsStream* stream, char** pframe, int* pnb_frame);
    /**
    * whether the frame is sps or pps.
    */
    virtual bool is_sps(char* frame, int nb_frame);
    virtual bool is_pps(char* frame, int nb_frame);
    /**
    * demux the sps or pps to string.
    * @param sps/pps output the sps/pps.
    */
    virtual int sps_demux(char* frame, int nb_frame, std::string& sps);
    virtual int pps_demux(char* frame, int nb_frame, std::string& pps);
public:
    /**
    * h264 raw data to h264 packet, without flv payload header.
    * mux the sps/pps to flv sequence header packet.
    * @param sh output the sequence header.
    */
    virtual int mux_sequence_header(std::string sps, std::string pps, u_int32_t dts, u_int32_t pts, std::string& sh);
    /**
    * h264 raw data to h264 packet, without flv payload header.
    * mux the ibp to flv ibp packet.
    * @param ibp output the packet.
    * @param frame_type output the frame type.
    */
    virtual int mux_ipb_frame(char* frame, int nb_frame, std::string& ibp);
    /**
    * mux the avc video packet to flv video packet.
    * @param frame_type, SrsCodecVideoAVCFrameKeyFrame or SrsCodecVideoAVCFrameInterFrame.
    * @param avc_packet_type, SrsCodecVideoAVCTypeSequenceHeader or SrsCodecVideoAVCTypeNALU.
    * @param video the h.264 raw data.
    * @param flv output the muxed flv packet.
    * @param nb_flv output the muxed flv size.
    */
    virtual int mux_avc2flv(std::string video, int8_t frame_type, int8_t avc_packet_type, u_int32_t dts, u_int32_t pts, char** flv, int* nb_flv);
};

/**
* the header of adts sample.
*/
struct SrsRawAacStreamCodec
{
    int8_t protection_absent;
    SrsAacObjectType aac_object;
    int8_t sampling_frequency_index;
    int8_t channel_configuration;
    int16_t frame_length;

    char sound_format;
    char sound_rate;
    char sound_size;
    char sound_type;
    // 0 for sh; 1 for raw data.
    int8_t aac_packet_type;
};

/**
* the raw aac stream, in adts.
*/
class SrsRawAacStream
{
public:
    SrsRawAacStream();
    virtual ~SrsRawAacStream();
public:
    /**
    * demux the stream in adts format.
    * @param stream the input stream bytes.
    * @param pframe the output aac frame in stream. user should never free it.
    * @param pnb_frame the output aac frame size.
    * @param codec the output codec info.
    */
    virtual int adts_demux(SrsStream* stream, char** pframe, int* pnb_frame, SrsRawAacStreamCodec& codec);
    /**
    * aac raw data to aac packet, without flv payload header.
    * mux the aac specific config to flv sequence header packet.
    * @param sh output the sequence header.
    */
    virtual int mux_sequence_header(SrsRawAacStreamCodec* codec, std::string& sh);
    /**
    * mux the aac audio packet to flv audio packet.
    * @param frame the aac raw data.
    * @param nb_frame the count of aac frame.
    * @param codec the codec info of aac.
    * @param flv output the muxed flv packet.
    * @param nb_flv output the muxed flv size.
    */
    virtual int mux_aac2flv(char* frame, int nb_frame, SrsRawAacStreamCodec* codec, u_int32_t dts, char** flv, int* nb_flv);
};

#endif
// following is generated by src/protocol/srs_rtsp_stack.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_PROTOCOL_RTSP_STACK_HPP
#define SRS_PROTOCOL_RTSP_STACK_HPP

/*
//#include <srs_rtsp_stack.hpp>
*/

//#include <srs_core.hpp>

#include <string>
#include <sstream>

//#include <srs_kernel_consts.hpp>

#ifdef SRS_AUTO_STREAM_CASTER

class SrsStream;
class SrsSimpleBuffer;
class SrsCodecSample;
class ISrsProtocolReaderWriter;

// rtsp specification
// CR             = <US-ASCII CR, carriage return (13)>
#define SRS_RTSP_CR SRS_CONSTS_CR // 0x0D
// LF             = <US-ASCII LF, linefeed (10)>
#define SRS_RTSP_LF SRS_CONSTS_LF // 0x0A
// SP             = <US-ASCII SP, space (32)>
#define SRS_RTSP_SP ' ' // 0x20

// 4 RTSP Message, @see rtsp-rfc2326-1998.pdf, page 37
// Lines are terminated by CRLF, but
// receivers should be prepared to also interpret CR and LF by
// themselves as line terminators.
#define SRS_RTSP_CRLF "\r\n" // 0x0D0A
#define SRS_RTSP_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A

// RTSP token
#define SRS_RTSP_TOKEN_CSEQ "CSeq"
#define SRS_RTSP_TOKEN_PUBLIC "Public"
#define SRS_RTSP_TOKEN_CONTENT_TYPE "Content-Type"
#define SRS_RTSP_TOKEN_CONTENT_LENGTH "Content-Length"
#define SRS_RTSP_TOKEN_TRANSPORT "Transport"
#define SRS_RTSP_TOKEN_SESSION "Session"

// RTSP methods
#define SRS_METHOD_OPTIONS            "OPTIONS"
#define SRS_METHOD_DESCRIBE           "DESCRIBE"
#define SRS_METHOD_ANNOUNCE           "ANNOUNCE"
#define SRS_METHOD_SETUP              "SETUP"
#define SRS_METHOD_PLAY               "PLAY"
#define SRS_METHOD_PAUSE              "PAUSE"
#define SRS_METHOD_TEARDOWN           "TEARDOWN"
#define SRS_METHOD_GET_PARAMETER      "GET_PARAMETER"
#define SRS_METHOD_SET_PARAMETER      "SET_PARAMETER"
#define SRS_METHOD_REDIRECT           "REDIRECT"
#define SRS_METHOD_RECORD             "RECORD"
// Embedded (Interleaved) Binary Data

// RTSP-Version
#define SRS_RTSP_VERSION "RTSP/1.0"

/**
* the rtsp sdp parse state.
*/
enum SrsRtspSdpState
{
    /**
    * other sdp properties.
    */
    SrsRtspSdpStateOthers,
    /**
    * parse sdp audio state.
    */
    SrsRtspSdpStateAudio,
    /**
    * parse sdp video state.
    */
    SrsRtspSdpStateVideo,
};

/**
* 10 Method Definitions, @see rtsp-rfc2326-1998.pdf, page 57
* The method token indicates the method to be performed on the resource
* identified by the Request-URI. The method is case-sensitive. New
* methods may be defined in the future. Method names may not start with
* a $ character (decimal 24) and must be a token. Methods are
* summarized in Table 2.
* Notes on Table 2: PAUSE is recommended, but not required in that a
* fully functional server can be built that does not support this
* method, for example, for live feeds. If a server does not support a
* particular method, it MUST return "501 Not Implemented" and a client
* SHOULD not try this method again for this server.
*/
enum SrsRtspMethod
{
    SrsRtspMethodDescribe           = 0x0001,
    SrsRtspMethodAnnounce           = 0x0002,
    SrsRtspMethodGetParameter       = 0x0004,
    SrsRtspMethodOptions            = 0x0008,
    SrsRtspMethodPause              = 0x0010,
    SrsRtspMethodPlay               = 0x0020,
    SrsRtspMethodRecord             = 0x0040,
    SrsRtspMethodRedirect           = 0x0080,
    SrsRtspMethodSetup              = 0x0100,
    SrsRtspMethodSetParameter       = 0x0200,
    SrsRtspMethodTeardown           = 0x0400,
};

/**
* the state of rtsp token.
*/
enum SrsRtspTokenState
{
    /**
    * parse token failed, default state.
    */
    SrsRtspTokenStateError = 100,
    /**
    * when SP follow the token.
    */
    SrsRtspTokenStateNormal = 101,
    /**
    * when CRLF follow the token.
    */
    SrsRtspTokenStateEOF = 102,
};

/**
* the rtp packet.
* 5. RTP Data Transfer Protocol, @see rtp-rfc3550-2003.pdf, page 12
*/
class SrsRtpPacket
{
public:
    /**
    * version (V): 2 bits
    * This eld identies the version of RTP. The version dened by this specication is two (2).
    * (The value 1 is used by the rst draft version of RTP and the value 0 is used by the protocol
    * initially implemented in the \vat" audio tool.)
    */
    int8_t version; //2bits
    /**
    * padding (P): 1 bit
    * If the padding bit is set, the packet contains one or more additional padding octets at the
    * end which are not part of the payload. The last octet of the padding contains a count of
    * how many padding octets should be ignored, including itself. Padding may be needed by
    * some encryption algorithms with xed block sizes or for carrying several RTP packets in a
    * lower-layer protocol data unit.
    */
    int8_t padding; //1bit
    /**
    * extension (X): 1 bit
    * If the extension bit is set, the xed header must be followed by exactly one header extension,
    * with a format dened in Section 5.3.1.
    */
    int8_t extension; //1bit
    /**
    * CSRC count (CC): 4 bits
    * The CSRC count contains the number of CSRC identiers that follow the xed header.
    */
    int8_t csrc_count; //4bits
    /**
    * marker (M): 1 bit
    * The interpretation of the marker is dened by a prole. It is intended to allow signicant
    * events such as frame boundaries to be marked in the packet stream. A prole may dene
    * additional marker bits or specify that there is no marker bit by changing the number of bits
    * in the payload type eld (see Section 5.3).
    */
    int8_t marker; //1bit
    /**
    * payload type (PT): 7 bits
    * This eld identies the format of the RTP payload and determines its interpretation by the
    * application. A prole may specify a default static mapping of payload type codes to payload
    * formats. Additional payload type codes may be dened dynamically through non-RTP means
    * (see Section 3). A set of default mappings for audio and video is specied in the companion
    * RFC 3551 [1]. An RTP source may change the payload type during a session, but this eld
    * should not be used for multiplexing separate media streams (see Section 5.2).
    * A receiver must ignore packets with payload types that it does not understand.
    */
    int8_t payload_type; //7bits
    /**
    * sequence number: 16 bits
    * The sequence number increments by one for each RTP data packet sent, and may be used
    * by the receiver to detect packet loss and to restore packet sequence. The initial value of the
    * sequence number should be random (unpredictable) to make known-plaintext attacks on
    * encryption more dicult, even if the source itself does not encrypt according to the method
    * in Section 9.1, because the packets may flow through a translator that does. Techniques for
    * choosing unpredictable numbers are discussed in [17].
    */
    u_int16_t sequence_number; //16bits
    /**
    * timestamp: 32 bits
    * The timestamp reflects the sampling instant of the rst octet in the RTP data packet. The
    * sampling instant must be derived from a clock that increments monotonically and linearly
    * in time to allow synchronization and jitter calculations (see Section 6.4.1). The resolution
    * of the clock must be sucient for the desired synchronization accuracy and for measuring
    * packet arrival jitter (one tick per video frame is typically not sucient). The clock frequency
    * is dependent on the format of data carried as payload and is specied statically in the prole
    * or payload format specication that denes the format, or may be specied dynamically for
    * payload formats dened through non-RTP means. If RTP packets are generated periodically,
    * the nominal sampling instant as determined from the sampling clock is to be used, not a
    * reading of the system clock. As an example, for xed-rate audio the timestamp clock would
    * likely increment by one for each sampling period. If an audio application reads blocks covering
    * 160 sampling periods from the input device, the timestamp would be increased by 160 for
    * each such block, regardless of whether the block is transmitted in a packet or dropped as
    * silent.
    *
    * The initial value of the timestamp should be random, as for the sequence number. Several
    * consecutive RTP packets will have equal timestamps if they are (logically) generated at once,
    * e.g., belong to the same video frame. Consecutive RTP packets may contain timestamps that
    * are not monotonic if the data is not transmitted in the order it was sampled, as in the case
    * of MPEG interpolated video frames. (The sequence numbers of the packets as transmitted
    * will still be monotonic.)
    * 
    * RTP timestamps from dierent media streams may advance at dierent rates and usually
    * have independent, random osets. Therefore, although these timestamps are sucient to
    * reconstruct the timing of a single stream, directly comparing RTP timestamps from dierent
    * media is not eective for synchronization. Instead, for each medium the RTP timestamp
    * is related to the sampling instant by pairing it with a timestamp from a reference clock
    * (wallclock) that represents the time when the data corresponding to the RTP timestamp was
    * sampled. The reference clock is shared by all media to be synchronized. The timestamp
    * pairs are not transmitted in every data packet, but at a lower rate in RTCP SR packets as
    * described in Section 6.4.
    * 
    * The sampling instant is chosen as the point of reference for the RTP timestamp because it is
    * known to the transmitting endpoint and has a common denition for all media, independent
    * of encoding delays or other processing. The purpose is to allow synchronized presentation of
    * all media sampled at the same time.
    * 
    * Applications transmitting stored data rather than data sampled in real time typically use a
    * virtual presentation timeline derived from wallclock time to determine when the next frame
    * or other unit of each medium in the stored data should be presented. In this case, the RTP
    * timestamp would reflect the presentation time for each unit. That is, the RTP timestamp for
    * each unit would be related to the wallclock time at which the unit becomes current on the
    * virtual presentation timeline. Actual presentation occurs some time later as determined by
    * the receiver.
    * 
    * An example describing live audio narration of prerecorded video illustrates the signicance
    * of choosing the sampling instant as the reference point. In this scenario, the video would
    * be presented locally for the narrator to view and would be simultaneously transmitted using
    * RTP. The \sampling instant" of a video frame transmitted in RTP would be established by
    * referencing its timestamp to the wallclock time when that video frame was presented to the
    * narrator. The sampling instant for the audio RTP packets containing the narrator's speech
    * would be established by referencing the same wallclock time when the audio was sampled.
    * The audio and video may even be transmitted by dierent hosts if the reference clocks on
    * the two hosts are synchronized by some means such as NTP. A receiver can then synchronize
    * presentation of the audio and video packets by relating their RTP timestamps using the
    * timestamp pairs in RTCP SR packets.
    */
    u_int32_t timestamp; //32bits
    /**
    * SSRC: 32 bits
    * The SSRC eld identies the synchronization source. This identier should be chosen
    * randomly, with the intent that no two synchronization sources within the same RTP session
    * will have the same SSRC identier. An example algorithm for generating a random identier
    * is presented in Appendix A.6. Although the probability of multiple sources choosing the same
    * identier is low, all RTP implementations must be prepared to detect and resolve collisions.
    * Section 8 describes the probability of collision along with a mechanism for resolving collisions
    * and detecting RTP-level forwarding loops based on the uniqueness of the SSRC identier. If
    * a source changes its source transport address, it must also choose a new SSRC identier to
    * avoid being interpreted as a looped source (see Section 8.2).
    */
    u_int32_t ssrc; //32bits

    // the payload.
    SrsSimpleBuffer* payload;
    // whether transport in chunked payload.
    bool chunked;
    // whether message is completed.
    // normal message always completed.
    // while chunked completed when the last chunk arriaved.
    bool completed;

    /**
    * the audio samples, one rtp packets may contains multiple audio samples.
    */
    SrsCodecSample* audio_samples;
public:
    SrsRtpPacket();
    virtual ~SrsRtpPacket();
public:
    /**
    * copy the header from src.
    */
    virtual void copy(SrsRtpPacket* src);
    /**
    * reap the src to this packet, reap the payload.
    */
    virtual void reap(SrsRtpPacket* src);
    /**
    * decode rtp packet from stream.
    */
    virtual int decode(SrsStream* stream);
private:
    virtual int decode_97(SrsStream* stream);
    virtual int decode_96(SrsStream* stream);
};

/**
* the sdp in announce, @see rtsp-rfc2326-1998.pdf, page 159
* Appendix C: Use of SDP for RTSP Session Descriptions
* The Session Description Protocol (SDP, RFC 2327 [6]) may be used to
* describe streams or presentations in RTSP.
*/
class SrsRtspSdp
{
private:
    SrsRtspSdpState state;
public:
    /**
    * the version of sdp.
    */
    std::string version;
    /**
    * the owner/creator of sdp.
    */
    std::string owner_username;
    std::string owner_session_id;
    std::string owner_session_version;
    std::string owner_network_type;
    std::string owner_address_type;
    std::string owner_address;
    /**
    * the session name of sdp.
    */
    std::string session_name;
    /**
    * the connection info of sdp.
    */
    std::string connection_network_type;
    std::string connection_address_type;
    std::string connection_address;
    /**
    * the tool attribute of sdp.
    */
    std::string tool;
    /**
    * the video attribute of sdp.
    */
    std::string video_port;
    std::string video_protocol;
    std::string video_transport_format;
    std::string video_bandwidth_kbps;
    std::string video_codec;
    std::string video_sample_rate;
    std::string video_stream_id;
    // fmtp
    std::string video_packetization_mode;
    std::string video_sps; // sequence header: sps.
    std::string video_pps; // sequence header: pps.
    /**
    * the audio attribute of sdp.
    */
    std::string audio_port;
    std::string audio_protocol;
    std::string audio_transport_format;
    std::string audio_bandwidth_kbps;
    std::string audio_codec;
    std::string audio_sample_rate;
    std::string audio_channel;
    std::string audio_stream_id;
    // fmtp
    std::string audio_profile_level_id;
    std::string audio_mode;
    std::string audio_size_length;
    std::string audio_index_length;
    std::string audio_index_delta_length;
    std::string audio_sh; // sequence header.
public:
    SrsRtspSdp();
    virtual ~SrsRtspSdp();
public:
    /**
    * parse a line of token for sdp.
    */
    virtual int parse(std::string token);
private:
    /**
    * generally, the fmtp is the sequence header for video or audio.
    */
    virtual int parse_fmtp_attribute(std::string attr);
    /**
    * generally, the control is the stream info for video or audio.
    */
    virtual int parse_control_attribute(std::string attr);
    /**
    * decode the string by base64.
    */
    virtual std::string base64_decode(std::string value);
};

/**
* the rtsp transport.
* 12.39 Transport, @see rtsp-rfc2326-1998.pdf, page 115
* This request header indicates which transport protocol is to be used
* and configures its parameters such as destination address,
* compression, multicast time-to-live and destination port for a single
* stream. It sets those values not already determined by a presentation
* description.
*/
class SrsRtspTransport
{
public:
    // The syntax for the transport specifier is
    //      transport/profile/lower-transport
    std::string transport;
    std::string profile;
    std::string lower_transport;
    // unicast | multicast
    // mutually exclusive indication of whether unicast or multicast
    // delivery will be attempted. Default value is multicast.
    // Clients that are capable of handling both unicast and
    // multicast transmission MUST indicate such capability by
    // including two full transport-specs with separate parameters
    // for each.
    std::string cast_type;
    // The mode parameter indicates the methods to be supported for
    // this session. Valid values are PLAY and RECORD. If not
    // provided, the default is PLAY.
    std::string mode;
    // This parameter provides the unicast RTP/RTCP port pair on
    // which the client has chosen to receive media data and control
    // information. It is specified as a range, e.g.,
    //      client_port=3456-3457.
    // where client will use port in:
    //      [client_port_min, client_port_max)
    int client_port_min;
    int client_port_max;
public:
    SrsRtspTransport();
    virtual ~SrsRtspTransport();
public:
    /**
    * parse a line of token for transport.
    */
    virtual int parse(std::string attr);
};

/**
* the rtsp request message.
* 6 Request, @see rtsp-rfc2326-1998.pdf, page 39
* A request message from a client to a server or vice versa includes,
* within the first line of that message, the method to be applied to
* the resource, the identifier of the resource, and the protocol
* version in use.
* Request = Request-Line ; Section 6.1
*           *( general-header ; Section 5
*           | request-header ; Section 6.2
*           | entity-header ) ; Section 8.1
*           CRLF
*           [ message-body ] ; Section 4.3
*/
class SrsRtspRequest
{
public:
    /**
    * 6.1 Request Line
    * Request-Line = Method SP Request-URI SP RTSP-Version CRLF
    */
    std::string method;
    std::string uri;
    std::string version;
    /**
    * 12.17 CSeq
    * The CSeq field specifies the sequence number for an RTSP requestresponse
    * pair. This field MUST be present in all requests and
    * responses. For every RTSP request containing the given sequence
    * number, there will be a corresponding response having the same
    * number. Any retransmitted request must contain the same sequence
    * number as the original (i.e. the sequence number is not incremented
    * for retransmissions of the same request).
    */
    long seq;
    /**
    * 12.16 Content-Type, @see rtsp-rfc2326-1998.pdf, page 99
    * See [H14.18]. Note that the content types suitable for RTSP are
    * likely to be restricted in practice to presentation descriptions and
    * parameter-value types.
    */
    std::string content_type;
    /**
    * 12.14 Content-Length, @see rtsp-rfc2326-1998.pdf, page 99
    * This field contains the length of the content of the method (i.e.
    * after the double CRLF following the last header). Unlike HTTP, it
    * MUST be included in all messages that carry content beyond the header
    * portion of the message. If it is missing, a default value of zero is
    * assumed. It is interpreted according to [H14.14].
    */
    long content_length;
    /**
    * the session id.
    */
    std::string session;

    /**
    * the sdp in announce, NULL for no sdp.
    */
    SrsRtspSdp* sdp;
    /**
    * the transport in setup, NULL for no transport.
    */
    SrsRtspTransport* transport;
    /**
    * for setup message, parse the stream id from uri.
    */
    int stream_id;
public:
    SrsRtspRequest();
    virtual ~SrsRtspRequest();
public:
    virtual bool is_options();
    virtual bool is_announce();
    virtual bool is_setup();
    virtual bool is_record();
};

/**
* the rtsp response message.
* 7 Response, @see rtsp-rfc2326-1998.pdf, page 43
* [H6] applies except that HTTP-Version is replaced by RTSP-Version.
* Also, RTSP defines additional status codes and does not define some
* HTTP codes. The valid response codes and the methods they can be used
* with are defined in Table 1.
* After receiving and interpreting a request message, the recipient
* responds with an RTSP response message.
*       Response = Status-Line ; Section 7.1
*                   *( general-header ; Section 5
*                   | response-header ; Section 7.1.2
*                   | entity-header ) ; Section 8.1
*                   CRLF
*                   [ message-body ] ; Section 4.3
*/
class SrsRtspResponse
{
public:
    /**
    * 7.1 Status-Line
    * The first line of a Response message is the Status-Line, consisting
    * of the protocol version followed by a numeric status code, and the
    * textual phrase associated with the status code, with each element
    * separated by SP characters. No CR or LF is allowed except in the
    * final CRLF sequence.
    *       Status-Line = RTSP-Version SP Status-Code SP Reason-Phrase CRLF
    */
    // @see about the version of rtsp, see SRS_RTSP_VERSION
    // @see about the status of rtsp, see SRS_CONSTS_RTSP_OK
    int status;
    /**
    * 12.17 CSeq, @see rtsp-rfc2326-1998.pdf, page 99
    * The CSeq field specifies the sequence number for an RTSP requestresponse
    * pair. This field MUST be present in all requests and
    * responses. For every RTSP request containing the given sequence
    * number, there will be a corresponding response having the same
    * number. Any retransmitted request must contain the same sequence
    * number as the original (i.e. the sequence number is not incremented
    * for retransmissions of the same request).
    */
    long seq;
    /**
    * the session id.
    */
    std::string session;
public:
    SrsRtspResponse(int cseq);
    virtual ~SrsRtspResponse();
public:
    /**
    * encode message to string.
    */
    virtual int encode(std::stringstream& ss);
protected:
    /**
    * sub classes override this to encode the headers.
    */
    virtual int encode_header(std::stringstream& ss);
};

/**
* 10.1 OPTIONS, @see rtsp-rfc2326-1998.pdf, page 59
* The behavior is equivalent to that described in [H9.2]. An OPTIONS
* request may be issued at any time, e.g., if the client is about to
* try a nonstandard request. It does not influence server state.
*/
class SrsRtspOptionsResponse : public SrsRtspResponse
{
public:
    /**
    * join of SrsRtspMethod
    */
    SrsRtspMethod methods;
public:
    SrsRtspOptionsResponse(int cseq);
    virtual ~SrsRtspOptionsResponse();
protected:
    virtual int encode_header(std::stringstream& ss);
};

/**
* 10.4 SETUP, @see rtsp-rfc2326-1998.pdf, page 65
* The SETUP request for a URI specifies the transport mechanism to be
* used for the streamed media. A client can issue a SETUP request for a
* stream that is already playing to change transport parameters, which
* a server MAY allow. If it does not allow this, it MUST respond with
* error "455 Method Not Valid In This State". For the benefit of any
* intervening firewalls, a client must indicate the transport
* parameters even if it has no influence over these parameters, for
* example, where the server advertises a fixed multicast address.
*/
class SrsRtspSetupResponse : public SrsRtspResponse
{
public:
    // the client specified port.
    int client_port_min;
    int client_port_max;
    // client will use the port in:
    //      [local_port_min, local_port_max)
    int local_port_min;
    int local_port_max;
    // session.
    std::string session;
public:
    SrsRtspSetupResponse(int cseq);
    virtual ~SrsRtspSetupResponse();
protected:
    virtual int encode_header(std::stringstream& ss);
};

/**
* the rtsp protocol stack to parse the rtsp packets.
*/
class SrsRtspStack
{
private:
    /**
    * cached bytes buffer.
    */
    SrsSimpleBuffer* buf;
    /**
    * underlayer socket object, send/recv bytes.
    */
    ISrsProtocolReaderWriter* skt;
public:
    SrsRtspStack(ISrsProtocolReaderWriter* s);
    virtual ~SrsRtspStack();
public:
    /**
    * recv rtsp message from underlayer io.
    * @param preq the output rtsp request message, which user must free it.
    * @return an int error code. 
    *       ERROR_RTSP_REQUEST_HEADER_EOF indicates request header EOF.
    */
    virtual int recv_message(SrsRtspRequest** preq);
    /**
    * send rtsp message over underlayer io.
    * @param res the rtsp response message, which user should never free it.
    * @return an int error code.
    */
    virtual int send_message(SrsRtspResponse* res);
private:
    /**
    * recv the rtsp message.
    */
    virtual int do_recv_message(SrsRtspRequest* req);
    /**
    * read a normal token from io, error when token state is not normal.
    */
    virtual int recv_token_normal(std::string& token);
    /**
    * read a normal token from io, error when token state is not eof.
    */
    virtual int recv_token_eof(std::string& token);
    /**
    * read the token util got eof, for example, to read the response status Reason-Phrase
    * @param pconsumed, output the token parsed length. NULL to ignore.
    */
    virtual int recv_token_util_eof(std::string& token, int* pconsumed = NULL);
    /**
    * read a token from io, split by SP, endswith CRLF:
    *       token1 SP token2 SP ... tokenN CRLF
    * @param token, output the read token.
    * @param state, output the token parse state.
    * @param normal_ch, the char to indicates the normal token. 
    *       the SP use to indicates the normal token, @see SRS_RTSP_SP
    *       the 0x00 use to ignore normal token flag. @see recv_token_util_eof
    * @param pconsumed, output the token parsed length. NULL to ignore.
    */
    virtual int recv_token(std::string& token, SrsRtspTokenState& state, char normal_ch = SRS_RTSP_SP, int* pconsumed = NULL);
};

#endif

#endif

// following is generated by src/protocol/srs_http_stack.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_PROTOCOL_HTTP_HPP
#define SRS_PROTOCOL_HTTP_HPP

/*
 //#include <srs_http_stack.hpp>
 */
//#include <srs_core.hpp>

#include <map>
#include <string>
#include <vector>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <sys/uio.h>
#endif

class SrsFileReader;
class SrsHttpHeader;
class ISrsHttpMessage;
class SrsHttpMuxEntry;
class ISrsHttpResponseWriter;

// http specification
// CR             = <US-ASCII CR, carriage return (13)>
#define SRS_HTTP_CR SRS_CONSTS_CR // 0x0D
// LF             = <US-ASCII LF, linefeed (10)>
#define SRS_HTTP_LF SRS_CONSTS_LF // 0x0A
// SP             = <US-ASCII SP, space (32)>
#define SRS_HTTP_SP ' ' // 0x20
// HT             = <US-ASCII HT, horizontal-tab (9)>
#define SRS_HTTP_HT '\x09' // 0x09

// HTTP/1.1 defines the sequence CR LF as the end-of-line marker for all
// protocol elements except the entity-body (see appendix 19.3 for
// tolerant applications).
#define SRS_HTTP_CRLF "\r\n" // 0x0D0A
#define SRS_HTTP_CRLFCRLF "\r\n\r\n" // 0x0D0A0D0A

// @see ISrsHttpMessage._http_ts_send_buffer
#define SRS_HTTP_TS_SEND_BUFFER_SIZE 4096

// for ead all of http body, read each time.
#define SRS_HTTP_READ_CACHE_BYTES 4096

// default http listen port.
#define SRS_DEFAULT_HTTP_PORT 80

// for http parser macros
#define SRS_CONSTS_HTTP_OPTIONS HTTP_OPTIONS
#define SRS_CONSTS_HTTP_GET HTTP_GET
#define SRS_CONSTS_HTTP_POST HTTP_POST
#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);

// get the status text of code.
extern std::string srs_generate_http_status_text(int status);

// bodyAllowedForStatus reports whether a given response status code
// permits a body.  See RFC2616, section 4.4.
extern bool srs_go_http_body_allowd(int status);

// DetectContentType implements the algorithm described
// at http://mimesniff.spec.whatwg.org/ to determine the
// Content-Type of the given data.  It considers at most the
// first 512 bytes of data.  DetectContentType always returns
// a valid MIME type: if it cannot determine a more specific one, it
// returns "application/octet-stream".
extern std::string srs_go_http_detect(char* data, int size);

// state of message
enum SrsHttpParseState {
    SrsHttpParseStateInit = 0,
    SrsHttpParseStateStart,
    SrsHttpParseStateHeaderComplete,
    SrsHttpParseStateMessageComplete
};

// A Header represents the key-value pairs in an HTTP header.
class SrsHttpHeader
{
private:
    std::map<std::string, std::string> headers;
public:
    SrsHttpHeader();
    virtual ~SrsHttpHeader();
public:
    // Add adds the key, value pair to the header.
    // It appends to any existing values associated with key.
    virtual void set(std::string key, std::string value);
    // Get gets the first value associated with the given key.
    // If there are no values associated with the key, Get returns "".
    // To access multiple values of a key, access the map directly
    // with CanonicalHeaderKey.
    virtual std::string get(std::string key);
public:
    /**
     * get the content length. -1 if not set.
     */
    virtual int64_t content_length();
    /**
     * set the content length by header "Content-Length"
     */
    virtual void set_content_length(int64_t size);
public:
    /**
     * get the content type. empty string if not set.
     */
    virtual std::string content_type();
    /**
     * set the content type by header "Content-Type"
     */
    virtual void set_content_type(std::string ct);
public:
    /**
     * write all headers to string stream.
     */
    virtual void write(std::stringstream& ss);
};

// A ResponseWriter interface is used by an HTTP handler to
// construct an HTTP response.
// Usage 1, response with specified length content:
//      ISrsHttpResponseWriter* w; // create or get response.
//      std::string msg = "Hello, HTTP!";
//      w->header()->set_content_type("text/plain; charset=utf-8");
//      w->header()->set_content_length(msg.length());
//      w->write_header(SRS_CONSTS_HTTP_OK);
//      w->write((char*)msg.data(), (int)msg.length());
//      w->final_request(); // optional flush.
// Usage 2, response with HTTP code only, zero content length.
//      ISrsHttpResponseWriter* w; // create or get response.
//      w->header()->set_content_length(0);
//      w->write_header(SRS_CONSTS_HTTP_OK);
//      w->final_request();
// Usage 3, response in chunked encoding.
//      ISrsHttpResponseWriter* w; // create or get response.
//      std::string msg = "Hello, HTTP!";
//      w->header()->set_content_type("application/octet-stream");
//      w->write_header(SRS_CONSTS_HTTP_OK);
//      w->write((char*)msg.data(), (int)msg.length());
//      w->write((char*)msg.data(), (int)msg.length());
//      w->write((char*)msg.data(), (int)msg.length());
//      w->write((char*)msg.data(), (int)msg.length());
//      w->final_request(); // required to end the chunked and flush.
class ISrsHttpResponseWriter
{
public:
    ISrsHttpResponseWriter();
    virtual ~ISrsHttpResponseWriter();
public:
    // when chunked mode,
    // final the request to complete the chunked encoding.
    // for no-chunked mode,
    // final to send request, for example, content-length is 0.
    virtual int final_request() = 0;
    
    // Header returns the header map that will be sent by WriteHeader.
    // Changing the header after a call to WriteHeader (or Write) has
    // no effect.
    virtual SrsHttpHeader* header() = 0;
    
    // Write writes the data to the connection as part of an HTTP reply.
    // If WriteHeader has not yet been called, Write calls WriteHeader(http.StatusOK)
    // before writing the data.  If the Header does not contain a
    // Content-Type line, Write adds a Content-Type set to the result of passing
    // the initial 512 bytes of written data to DetectContentType.
    // @param data, the data to send. NULL to flush header only.
    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
     */
    virtual int writev(iovec* iov, int iovcnt, ssize_t* pnwrite) = 0;
    
    // WriteHeader sends an HTTP response header with status code.
    // If WriteHeader is not called explicitly, the first call to Write
    // will trigger an implicit WriteHeader(http.StatusOK).
    // Thus explicit calls to WriteHeader are mainly used to
    // send error codes.
    // @remark, user must set header then write or write_header.
    virtual void write_header(int code) = 0;
};

/**
 * the reader interface for http response.
 */
class ISrsHttpResponseReader
{
public:
    ISrsHttpResponseReader();
    virtual ~ISrsHttpResponseReader();
public:
    /**
     * whether response read EOF.
     */
    virtual bool eof() = 0;
    /**
     * read from the response body.
     * @param data, the buffer to read data buffer to.
     * @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.
     */
    virtual int read(char* data, int nb_data, int* nb_read) = 0;
};

// Objects implementing the Handler interface can be
// registered to serve a particular path or subtree
// in the HTTP server.
//
// ServeHTTP should write reply headers and data to the ResponseWriter
// and then return.  Returning signals that the request is finished
// and that the HTTP server can move on to the next request on
// the connection.
class ISrsHttpHandler
{
public:
    SrsHttpMuxEntry* entry;
public:
    ISrsHttpHandler();
    virtual ~ISrsHttpHandler();
public:
    virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r) = 0;
};

// Redirect to a fixed URL
class SrsHttpRedirectHandler : public ISrsHttpHandler
{
private:
    std::string url;
    int code;
public:
    SrsHttpRedirectHandler(std::string u, int c);
    virtual ~SrsHttpRedirectHandler();
public:
    virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
};

// NotFound replies to the request with an HTTP 404 not found error.
class SrsHttpNotFoundHandler : public ISrsHttpHandler
{
public:
    SrsHttpNotFoundHandler();
    virtual ~SrsHttpNotFoundHandler();
public:
    virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
};

// FileServer returns a handler that serves HTTP requests
// with the contents of the file system rooted at root.
//
// To use the operating system's file system implementation,
// use http.Dir:
//
//     http.Handle("/", SrsHttpFileServer("/tmp"))
//     http.Handle("/", SrsHttpFileServer("static-dir"))
class SrsHttpFileServer : public ISrsHttpHandler
{
protected:
    std::string dir;
public:
    SrsHttpFileServer(std::string root_dir);
    virtual ~SrsHttpFileServer();
public:
    virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
private:
    /**
     * serve the file by specified path
     */
    virtual int serve_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
    virtual int serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
    virtual int serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath);
protected:
    /**
     * when access flv file with x.flv?start=xxx
     */
    virtual int serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int offset);
    /**
     * when access mp4 file with x.mp4?range=start-end
     * @param start the start offset in bytes.
     * @param end the end offset in bytes. -1 to end of file.
     * @remark response data in [start, end].
     */
    virtual int serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, std::string fullpath, int start, int end);
protected:
    /**
     * copy the fs to response writer in size bytes.
     */
    virtual int copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, ISrsHttpMessage* r, int size);
};

// the mux entry for server mux.
// the matcher info, for example, the pattern and handler.
class SrsHttpMuxEntry
{
public:
    bool explicit_match;
    ISrsHttpHandler* handler;
    std::string pattern;
    bool enabled;
public:
    SrsHttpMuxEntry();
    virtual ~SrsHttpMuxEntry();
};

/**
 * the hijacker for http pattern match.
 */
class ISrsHttpMatchHijacker
{
public:
    ISrsHttpMatchHijacker();
    virtual ~ISrsHttpMatchHijacker();
public:
    /**
     * when match the request failed, no handler to process request.
     * @param request the http request message to match the handler.
     * @param ph the already matched handler, hijack can rewrite it.
     */
    virtual int hijack(ISrsHttpMessage* request, ISrsHttpHandler** ph) = 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
// most closely matches the URL.
//
// Patterns name fixed, rooted paths, like "/favicon.ico",
// or rooted subtrees, like "/images/" (note the trailing slash).
// Longer patterns take precedence over shorter ones, so that
// if there are handlers registered for both "/images/"
// and "/images/thumbnails/", the latter handler will be
// called for paths beginning "/images/thumbnails/" and the
// former will receive requests for any other paths in the
// "/images/" subtree.
//
// Note that since a pattern ending in a slash names a rooted subtree,
// the pattern "/" matches all paths not matched by other registered
// patterns, not just the URL with Path == "/".
//
// Patterns may optionally begin with a host name, restricting matches to
// URLs on that host only.  Host-specific patterns take precedence over
// general patterns, so that a handler might register for the two patterns
// "/codesearch" and "codesearch.google.com/" without also taking over
// requests for "http://www.google.com/".
//
// ServeMux also takes care of sanitizing the URL request path,
// redirecting any request containing . or .. elements to an
// equivalent .- and ..-free URL.
class SrsHttpServeMux
{
private:
    // the pattern handler, to handle the http request.
    std::map<std::string, SrsHttpMuxEntry*> entries;
    // the vhost handler.
    // when find the handler to process the request,
    // append the matched vhost when pattern not starts with /,
    // for example, for pattern /live/livestream.flv of vhost ossrs.net,
    // the path will rewrite to ossrs.net/live/livestream.flv
    std::map<std::string, ISrsHttpHandler*> vhosts;
    // all hijackers for http match.
    // for example, the hstrs(http stream trigger rtmp source)
    // can hijack and install handler when request incoming and no handler.
    std::vector<ISrsHttpMatchHijacker*> hijackers;
public:
    SrsHttpServeMux();
    virtual ~SrsHttpServeMux();
public:
    /**
     * initialize the http serve mux.
     */
    virtual int initialize();
    /**
     * hijack the http match.
     */
    virtual void hijack(ISrsHttpMatchHijacker* h);
    virtual void unhijack(ISrsHttpMatchHijacker* h);
public:
    // 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
public:
    virtual int serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r);
private:
    virtual int find_handler(ISrsHttpMessage* r, ISrsHttpHandler** ph);
    virtual int match(ISrsHttpMessage* r, ISrsHttpHandler** ph);
    virtual bool path_match(std::string pattern, std::string path);
};

// for http header.
typedef std::pair<std::string, std::string> SrsHttpHeaderField;

// A Request represents an HTTP request received by a server
// or to be sent by a client.
//
// 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.
 */
class ISrsHttpMessage
{
private:
    /**
     * use a buffer to read and send ts file.
     */
    // TODO: FIXME: remove it.
    char* _http_ts_send_buffer;
public:
    ISrsHttpMessage();
    virtual ~ISrsHttpMessage();
public:
    /**
     * the http request level cache.
     */
    virtual char* http_ts_send_buffer();
public:
    virtual u_int8_t method() = 0;
    virtual u_int16_t status_code() = 0;
    /**
     * method helpers.
     */
    virtual std::string method_str() = 0;
    virtual bool is_http_get() = 0;
    virtual bool is_http_put() = 0;
    virtual bool is_http_post() = 0;
    virtual bool is_http_delete() = 0;
    virtual bool is_http_options() = 0;
public:
    /**
     * whether should keep the connection alive.
     */
    virtual bool is_keep_alive() = 0;
    /**
     * the uri contains the host and path.
     */
    virtual std::string uri() = 0;
    /**
     * the url maybe the path.
     */
    virtual std::string url() = 0;
    virtual std::string host() = 0;
    virtual std::string path() = 0;
    virtual std::string ext() = 0;
public:
    /**
     * read body to string.
     * @remark for small http body.
     */
    virtual int body_read_all(std::string& body) = 0;
    /**
     * get the body reader, to read one by one.
     * @remark when body is very large, or chunked, use this.
     */
    virtual ISrsHttpResponseReader* body_reader() = 0;
    /**
     * the content length, -1 for chunked or not set.
     */
    virtual int64_t content_length() = 0;
public:
    /**
     * get the param in query string,
     * for instance, query is "start=100&end=200",
     * then query_get("start") is "100", and query_get("end") is "200"
     */
    virtual std::string query_get(std::string key) = 0;
    /**
     * get the headers.
     */
    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;
};

#endif
// following is generated by src/protocol/srs_protocol_kbps.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_PROTOCOL_KBPS_HPP
#define SRS_PROTOCOL_KBPS_HPP

/*
//#include <srs_protocol_kbps.hpp>
*/

//#include <srs_core.hpp>

//#include <srs_rtmp_io.hpp>

/**
* a kbps sample, for example, 1minute kbps, 
* 10minute kbps sample.
*/
class SrsKbpsSample
{
public:
    int64_t bytes;
    int64_t time;
    int kbps;
public:
    SrsKbpsSample();
};

/**
* a slice of kbps statistic, for input or output.
* a slice contains a set of sessions, which has a base offset of bytes,
* where a slice is:
*       starttime(oldest session startup time)
*               bytes(total bytes of previous sessions)
*               io_bytes_base(bytes offset of current session)
*                       last_bytes(bytes of current session)
* so, the total send bytes now is:
*       send_bytes = bytes + last_bytes - io_bytes_base
* so, the bytes sent duration current session is:
*       send_bytes = last_bytes - io_bytes_base
* @remark use set_io to start new session.
* @remakr the slice is a data collection object driven by SrsKbps.
*/
class SrsKbpsSlice
{
private:
    union slice_io {
        ISrsProtocolStatistic* in;
        ISrsProtocolStatistic* out;
    };
public:
    // the slice io used for SrsKbps to invoke,
    // the SrsKbpsSlice itself never use it.
    slice_io io;
    // session startup bytes
    // @remark, use total_bytes() to get the total bytes of slice.
    int64_t bytes;
    // slice starttime, the first time to record bytes.
    int64_t starttime;
    // session startup bytes number for io when set it,
    // the base offset of bytes for io.
    int64_t io_bytes_base;
    // last updated bytes number,
    // cache for io maybe freed.
    int64_t last_bytes;
    // samples
    SrsKbpsSample sample_30s;
    SrsKbpsSample sample_1m;
    SrsKbpsSample sample_5m;
    SrsKbpsSample sample_60m;
public:
    // for the delta bytes.
    int64_t delta_bytes;
public:
    SrsKbpsSlice();
    virtual ~SrsKbpsSlice();
public:
    /**
    * get current total bytes.
    */
    virtual int64_t get_total_bytes();
    /**
    * resample all samples.
    */
    virtual void sample();
};

/**
* the interface which provices delta of bytes.
* for a delta, for example, a live stream connection, we can got the delta by:
*       IKbpsDelta* delta = ...;
*       delta->resample();
*       kbps->add_delta(delta);
*       delta->cleanup();
*/
class IKbpsDelta
{
public:
    IKbpsDelta();
    virtual ~IKbpsDelta();
public:
    /**
    * resample to generate the value of delta bytes.
    */
    virtual void resample() = 0;
    /**
    * get the send or recv bytes delta.
    */
    virtual int64_t get_send_bytes_delta() = 0;
    virtual int64_t get_recv_bytes_delta() = 0;
    /**
    * cleanup the value of delta bytes.
    */
    virtual void cleanup() = 0;
};

/**
 * to statistic the kbps of io.
 * itself can be a statistic source, for example, used for SRS bytes stat.
 * there are some usage scenarios:
 * 1. connections to calc kbps by sample():
 *       SrsKbps* kbps = ...;
 *       kbps->set_io(in, out)
 *       kbps->sample()
 *       kbps->get_xxx_kbps().
 *   the connections know how many bytes already send/recv.
 * 2. server to calc kbps by add_delta():
 *       SrsKbps* kbps = ...;
 *       kbps->set_io(NULL, NULL)
 *       for each connection in connections:
 *           IKbpsDelta* delta = connection; // where connection implements IKbpsDelta
 *           delta->resample()
 *           kbps->add_delta(delta)
 *           delta->cleanup()
 *       kbps->sample()
 *       kbps->get_xxx_kbps().
 * 3. kbps used as IKbpsDelta, to provides delta bytes:
 *      SrsKbps* kbps = ...;
 *      kbps->set_io(in, out);
 *      IKbpsDelta* delta = (IKbpsDelta*)kbps;
 *      delta->resample();
 *      printf("delta is %d/%d", delta->get_send_bytes_delta(), delta->get_recv_bytes_delta());
 *      delta->cleanup();
 *   the server never know how many bytes already send/recv, for the connection maybe closed.
 */
class SrsKbps : public virtual ISrsProtocolStatistic, public virtual IKbpsDelta
{
private:
    SrsKbpsSlice is;
    SrsKbpsSlice os;
public:
    SrsKbps();
    virtual ~SrsKbps();
public:
    /**
    * set io to start new session.
    * set the underlayer reader/writer,
    * if the io destroied, for instance, the forwarder reconnect,
    * user must set the io of SrsKbps to NULL to continue to use the kbps object.
    * @param in the input stream statistic. can be NULL.
    * @param out the output stream statistic. can be NULL.
    * @remark if in/out is NULL, use the cached data for kbps.
    */
    virtual void set_io(ISrsProtocolStatistic* in, ISrsProtocolStatistic* out);
public:
    /**
    * get total kbps, duration is from the startup of io.
    * @remark, use sample() to update data.
    */
    virtual int get_send_kbps();
    virtual int get_recv_kbps();
    // 30s
    virtual int get_send_kbps_30s();
    virtual int get_recv_kbps_30s();
    // 5m
    virtual int get_send_kbps_5m();
    virtual int get_recv_kbps_5m();
public:
    /**
    * get the total send/recv bytes, from the startup of the oldest io.
    * @remark, use sample() to update data.
    */
    virtual int64_t get_send_bytes();
    virtual int64_t get_recv_bytes();
public:
    /**
    * resample to get the delta.
    */
    virtual void resample();
    /**
    * get the delta of send/recv bytes.
    */
    virtual int64_t get_send_bytes_delta();
    virtual int64_t get_recv_bytes_delta();
    /**
    * cleanup the delta.
    */
    virtual void cleanup();
public:
    /**
    * add delta to kbps clac mechenism.
    * we donot know the total bytes, but know the delta, for instance, 
    * for rtmp server to calc total bytes and kbps.
    * @remark user must invoke sample() to calc result after invoke this method.
    * @param delta, assert should never be NULL.
    */
    virtual void add_delta(IKbpsDelta* delta);
    /**
    * resample all samples, ignore if in/out is NULL.
    * used for user to calc the kbps, to sample new kbps value.
    * @remark if user, for instance, the rtmp server to calc the total bytes,
    *       use the add_delta() is better solutions.
    */
    virtual void sample();
};

#endif
// following is generated by src/libs/srs_librtmp.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_LIB_RTMP_HPP
#define SRS_LIB_RTMP_HPP

/*
//#include <srs_librtmp.h>
*/

/**
* srs-librtmp is a librtmp like library,
* used to play/publish rtmp stream from/to rtmp server.
* socket: use sync and block socket to connect/recv/send data with server.
* depends: no need other libraries; depends on ssl if use srs_complex_handshake.
* thread-safe: no
*/

/*************************************************************
**************************************************************
* Windows SRS-LIBRTMP pre-declare
**************************************************************
*************************************************************/
// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifdef _WIN32
    // include windows first.
    #include <windows.h>
    // the type used by this header for windows.
    typedef unsigned long long u_int64_t;
    typedef long long int64_t;
    typedef unsigned int u_int32_t;
    typedef int int32_t;
    typedef unsigned char u_int8_t;
    typedef char int8_t;
    typedef unsigned short u_int16_t;
    typedef short int16_t;
    typedef int64_t ssize_t;
    struct iovec {
        void  *iov_base;    /* Starting address */
        size_t iov_len;     /* Number of bytes to transfer */
    };
#endif

#include <sys/types.h>

#ifdef __cplusplus
extern "C"{
#endif

// typedefs
typedef int srs_bool;

/*************************************************************
**************************************************************
* srs-librtmp version
**************************************************************
*************************************************************/
extern int srs_version_major();
extern int srs_version_minor();
extern int srs_version_revision();

/*************************************************************
**************************************************************
* RTMP protocol context
**************************************************************
*************************************************************/
// the RTMP handler.
typedef void* srs_rtmp_t;
typedef void* srs_amf0_t;

/**
* create/destroy a rtmp protocol stack.
* @url rtmp url, for example: 
*         rtmp://localhost/live/livestream
*
* @return a rtmp handler, or NULL if error occured.
*/
extern srs_rtmp_t srs_rtmp_create(const char* url);
/**
* create rtmp with url, used for connection specified application.
* @param url the tcUrl, for exmple:
*         rtmp://localhost/live
* @remark this is used to create application connection-oriented,
*       for example, the bandwidth client used this, no stream specified.
*
* @return a rtmp handler, or NULL if error occured.
*/
extern srs_rtmp_t srs_rtmp_create2(const char* url);
/**
* close and destroy the rtmp stack.
* @remark, user should never use the rtmp again.
*/
extern void srs_rtmp_destroy(srs_rtmp_t rtmp);

/*************************************************************
**************************************************************
* RTMP protocol stack
**************************************************************
*************************************************************/
/**
* connect and handshake with server
* category: publish/play
* previous: rtmp-create
* next: connect-app
*
* @return 0, success; otherswise, failed.
*/
/**
* simple handshake specifies in rtmp 1.0,
* not depends on ssl.
*/
/**
* srs_rtmp_handshake equals to invoke:
*       srs_rtmp_dns_resolve()
*       srs_rtmp_connect_server()
*       srs_rtmp_do_simple_handshake()
* user can use these functions if needed.
*/
extern int srs_rtmp_handshake(srs_rtmp_t rtmp);
// parse uri, create socket, resolve host
extern int srs_rtmp_dns_resolve(srs_rtmp_t rtmp);
// connect socket to server
extern int srs_rtmp_connect_server(srs_rtmp_t rtmp);
// do simple handshake over socket.
extern int srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp);
// do complex handshake over socket.
extern int srs_rtmp_do_complex_handshake(srs_rtmp_t rtmp);

/**
* set the args of connect packet for rtmp.
* @param args, the extra amf0 object args.
* @remark, all params can be NULL to ignore.
* @remark, user should never free the args for we directly use it.
*/
extern int srs_rtmp_set_connect_args(srs_rtmp_t rtmp, 
    const char* tcUrl, const char* swfUrl, const char* pageUrl, srs_amf0_t args
);

/**
* connect to rtmp vhost/app
* category: publish/play
* previous: handshake
* next: publish or play
*
* @return 0, success; otherswise, failed.
*/
extern int srs_rtmp_connect_app(srs_rtmp_t rtmp);

/**
* connect to server, get the debug srs info.
* 
* SRS debug info:
* @param srs_server_ip, 128bytes, debug info, server ip client connected at.
* @param srs_server, 128bytes, server info.
* @param srs_primary, 128bytes, primary authors.
* @param srs_authors, 128bytes, authors.
* @param srs_version, 32bytes, server version.
* @param srs_id, int, debug info, client id in server log.
* @param srs_pid, int, debug info, server pid in log.
*
* @return 0, success; otherswise, failed.
*/
extern int srs_rtmp_connect_app2(srs_rtmp_t rtmp,
    char srs_server_ip[128], char srs_server[128], 
    char srs_primary[128], char srs_authors[128], 
    char srs_version[32], int* srs_id, int* srs_pid
);

/**
* play a live/vod stream.
* category: play
* previous: connect-app
* next: destroy
* @return 0, success; otherwise, failed.
*/
extern int srs_rtmp_play_stream(srs_rtmp_t rtmp);

/**
* publish a live stream.
* category: publish
* previous: connect-app
* next: destroy
* @return 0, success; otherwise, failed.
*/
extern int srs_rtmp_publish_stream(srs_rtmp_t rtmp);

/**
* do bandwidth check with srs server.
* 
* bandwidth info:
* @param start_time, output the start time, in ms.
* @param end_time, output the end time, in ms.
* @param play_kbps, output the play/download kbps.
* @param publish_kbps, output the publish/upload kbps.
* @param play_bytes, output the play/download bytes.
* @param publish_bytes, output the publish/upload bytes.
* @param play_duration, output the play/download test duration, in ms.
* @param publish_duration, output the publish/upload test duration, in ms.
*
* @return 0, success; otherswise, failed.
*/
extern int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, 
    int64_t* start_time, int64_t* end_time, 
    int* play_kbps, int* publish_kbps,
    int* play_bytes, int* publish_bytes,
    int* play_duration, int* publish_duration
);

/**
* E.4.1 FLV Tag, page 75
*/
// 8 = audio
#define SRS_RTMP_TYPE_AUDIO 8
// 9 = video
#define SRS_RTMP_TYPE_VIDEO 9
// 18 = script data
#define SRS_RTMP_TYPE_SCRIPT 18
/**
* read a audio/video/script-data packet from rtmp stream.
* @param type, output the packet type, macros:
*            SRS_RTMP_TYPE_AUDIO, FlvTagAudio
*            SRS_RTMP_TYPE_VIDEO, FlvTagVideo
*            SRS_RTMP_TYPE_SCRIPT, FlvTagScript
*            otherswise, invalid type.
* @param timestamp, in ms, overflow in 50days
* @param data, the packet data, according to type:
*             FlvTagAudio, @see "E.4.2.1 AUDIODATA"
*            FlvTagVideo, @see "E.4.3.1 VIDEODATA"
*            FlvTagScript, @see "E.4.4.1 SCRIPTDATA"
* @param size, size of packet.
* @return the error code. 0 for success; otherwise, error.
*
* @remark: for read, user must free the data.
* @remark: for write, user should never free the data, even if error.
* @example /trunk/research/librtmp/srs_play.c
* @example /trunk/research/librtmp/srs_publish.c
*
* @return 0, success; otherswise, failed.
*/
extern int srs_rtmp_read_packet(srs_rtmp_t rtmp, 
    char* type, u_int32_t* timestamp, char** data, int* size
);
extern int srs_rtmp_write_packet(srs_rtmp_t rtmp, 
    char type, u_int32_t timestamp, char* data, int size
);

/**
* whether type is script data and the data is onMetaData.
*/
extern srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size);

/*************************************************************
**************************************************************
* audio raw codec
**************************************************************
*************************************************************/
/**
* write an audio raw frame to srs.
* not similar to h.264 video, the audio never aggregated, always
* encoded one frame by one, so this api is used to write a frame.
*
* @param sound_format Format of SoundData. The following values are defined:
*               0 = Linear PCM, platform endian
*               1 = ADPCM
*               2 = MP3
*               3 = Linear PCM, little endian
*               4 = Nellymoser 16 kHz mono
*               5 = Nellymoser 8 kHz mono
*               6 = Nellymoser
*               7 = G.711 A-law logarithmic PCM
*               8 = G.711 mu-law logarithmic PCM
*               9 = reserved
*               10 = AAC
*               11 = Speex
*               14 = MP3 8 kHz
*               15 = Device-specific sound
*               Formats 7, 8, 14, and 15 are reserved.
*               AAC is supported in Flash Player 9,0,115,0 and higher.
*               Speex is supported in Flash Player 10 and higher.
* @param sound_rate Sampling rate. The following values are defined:
*               0 = 5.5 kHz
*               1 = 11 kHz
*               2 = 22 kHz
*               3 = 44 kHz
* @param sound_size Size of each audio sample. This parameter only pertains to
*               uncompressed formats. Compressed formats always decode
*               to 16 bits internally.
*               0 = 8-bit samples
*               1 = 16-bit samples
* @param sound_type Mono or stereo sound
*               0 = Mono sound
*               1 = Stereo sound
* @param timestamp The timestamp of audio.
*
* @example /trunk/research/librtmp/srs_aac_raw_publish.c
* @example /trunk/research/librtmp/srs_audio_raw_publish.c
*
* @remark for aac, the frame must be in ADTS format. 
*       @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, 1.A.2.2 ADTS
* @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 E.4.2.1 AUDIODATA of video_file_format_spec_v10_1.pdf
* 
* @return 0, success; otherswise, failed.
*/
extern int srs_audio_write_raw_frame(srs_rtmp_t rtmp, 
    char sound_format, char sound_rate, char sound_size, char sound_type,
    char* frame, int frame_size, u_int32_t timestamp
);

/**
* whether aac raw data is in adts format,
* which bytes sequence matches '1111 1111 1111'B, that is 0xFFF.
* @param aac_raw_data the input aac raw data, a encoded aac frame data.
* @param ac_raw_size the size of aac raw data.
*
* @reamrk used to check whether current frame is in adts format.
*       @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 75, 1.A.2.2 ADTS
* @example /trunk/research/librtmp/srs_aac_raw_publish.c
*
* @return 0 false; otherwise, true.
*/
extern srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size);

/**
* parse the adts header to get the frame size,
* which bytes sequence matches '1111 1111 1111'B, that is 0xFFF.
* @param aac_raw_data the input aac raw data, a encoded aac frame data.
* @param ac_raw_size the size of aac raw data.
*
* @return failed when <=0 failed; otherwise, ok.
*/
extern int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size);

/*************************************************************
**************************************************************
* h264 raw codec
**************************************************************
*************************************************************/
/**
* write h.264 raw frame over RTMP to rtmp server.
* @param frames the input h264 raw data, encoded h.264 I/P/B frames data.
*       frames can be one or more than one frame,
*       each frame prefixed h.264 annexb header, by N[00] 00 00 01, where N>=0, 
*       for instance, frame = header(00 00 00 01) + payload(67 42 80 29 95 A0 14 01 6E 40)
*       about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
* @param frames_size the size of h264 raw data. 
*       assert frames_size > 0, at least has 1 bytes header.
* @param dts the dts of h.264 raw data.
* @param pts the pts of h.264 raw data.
* 
* @remark, user should free the frames.
* @remark, the tbn of dts/pts is 1/1000 for RTMP, that is, in ms.
* @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
* 
* @return 0, success; otherswise, failed.
*       for dvbsp error, @see srs_h264_is_dvbsp_error().
*       for duplictated sps error, @see srs_h264_is_duplicated_sps_error().
*       for duplictated pps error, @see srs_h264_is_duplicated_pps_error().
*/
/**
For the example file: 
    http://winlinvip.github.io/srs.release/3rdparty/720p.h264.raw
The data sequence is:
    // SPS
    000000016742802995A014016E40
    // PPS
    0000000168CE3880
    // IFrame
    0000000165B8041014C038008B0D0D3A071.....
    // PFrame
    0000000141E02041F8CDDC562BBDEFAD2F.....
User can send the SPS+PPS, then each frame:
    // SPS+PPS
    srs_h264_write_raw_frames('000000016742802995A014016E400000000168CE3880', size, dts, pts)
    // IFrame
    srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts)
    // PFrame
    srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts)
User also can send one by one:
    // SPS
    srs_h264_write_raw_frames('000000016742802995A014016E4', size, dts, pts)
    // PPS
    srs_h264_write_raw_frames('00000000168CE3880', size, dts, pts)
    // IFrame
    srs_h264_write_raw_frames('0000000165B8041014C038008B0D0D3A071......', size, dts, pts)
    // PFrame
    srs_h264_write_raw_frames('0000000141E02041F8CDDC562BBDEFAD2F......', size, dts, pts) 
*/
extern int srs_h264_write_raw_frames(srs_rtmp_t rtmp, 
    char* frames, int frames_size, u_int32_t dts, u_int32_t pts
);
/**
* whether error_code is dvbsp(drop video before sps/pps/sequence-header) error.
*
* @see https://github.com/simple-rtmp-server/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,
*       so, when error and reconnect the rtmp, the first video is not sps/pps(sequence header),
*       this will cause SRS server to disable HLS.
*/
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
* @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
* @example /trunk/research/librtmp/srs_h264_raw_publish.c
*/
extern srs_bool srs_h264_is_duplicated_pps_error(int error_code);
/**
* whether h264 raw data starts with the annexb,
* which bytes sequence matches N[00] 00 00 01, where N>=0.
* @param h264_raw_data the input h264 raw data, a encoded h.264 I/P/B frame data.
* @paam h264_raw_size the size of h264 raw data.
* @param pnb_start_code output the size of start code, must >=3. 
*       NULL to ignore.
*
* @reamrk used to check whether current frame is in annexb format.
* @example /trunk/research/librtmp/srs_h264_raw_publish.c
*
* @return 0 false; otherwise, true.
*/
extern srs_bool srs_h264_startswith_annexb(
    char* h264_raw_data, int h264_raw_size, 
    int* pnb_start_code
);

/*************************************************************
**************************************************************
* flv codec
* @example /trunk/research/librtmp/srs_flv_injecter.c
* @example /trunk/research/librtmp/srs_flv_parser.c
* @example /trunk/research/librtmp/srs_ingest_flv.c
* @example /trunk/research/librtmp/srs_ingest_rtmp.c
**************************************************************
*************************************************************/
typedef void* srs_flv_t;
/* open flv file for both read/write. */
extern srs_flv_t srs_flv_open_read(const char* file);
extern srs_flv_t srs_flv_open_write(const char* file);
extern void srs_flv_close(srs_flv_t flv);
/**
* read the flv header. 9bytes header. 
* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc.
*   3bytes, signature, "FLV",
*   1bytes, version, 0x01,
*   1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
*   4bytes, dataoffset, 0x09, The length of this header in bytes
*
* @return 0, success; otherswise, failed.
* @remark, drop the 4bytes zero previous tag size.
*/
extern int srs_flv_read_header(srs_flv_t flv, char header[9]);
/**
* read the flv tag header, 1bytes tag, 3bytes data_size, 
* 4bytes time, 3bytes stream id. 
* @param ptype, output the type of tag, macros:
*            SRS_RTMP_TYPE_AUDIO, FlvTagAudio
*            SRS_RTMP_TYPE_VIDEO, FlvTagVideo
*            SRS_RTMP_TYPE_SCRIPT, FlvTagScript
* @param pdata_size, output the size of tag data.
* @param ptime, output the time of tag, the dts in ms.
*
* @return 0, success; otherswise, failed.
* @remark, user must ensure the next is a tag, srs never check it.
*/
extern int srs_flv_read_tag_header(srs_flv_t flv, 
    char* ptype, int32_t* pdata_size, u_int32_t* ptime
);
/**
* read the tag data. drop the 4bytes previous tag size 
* @param data, the data to read, user alloc and free it.
* @param size, the size of data to read, get by srs_flv_read_tag_header().
* @remark, srs will ignore and drop the 4bytes previous tag size.
*/
extern int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size);
/**
* write the flv header. 9bytes header. 
* @param header, @see E.2 The FLV header, flv_v10_1.pdf in SRS doc.
*   3bytes, signature, "FLV",
*   1bytes, version, 0x01,
*   1bytes, flags, UB[5] 0, UB[1] audio present, UB[1] 0, UB[1] video present.
*   4bytes, dataoffset, 0x09, The length of this header in bytes
*
* @return 0, success; otherswise, failed.
* @remark, auto write the 4bytes zero previous tag size.
*/
extern int srs_flv_write_header(srs_flv_t flv, char header[9]);
/**
* write the flv tag to file.
*
* @return 0, success; otherswise, failed.
* @remark, auto write the 4bytes zero previous tag size.
*/
/* write flv tag to file, auto write the 4bytes previous tag size */
extern int srs_flv_write_tag(srs_flv_t flv, 
    char type, int32_t time, char* data, int size
);
/**
* get the tag size, for flv injecter to adjust offset, 
*       size = tag_header(11B) + data_size + previous_tag(4B)
* @return the size of tag.
*/
extern int srs_flv_size_tag(int data_size);
/* file stream */
/* file stream tellg to get offset */
extern int64_t srs_flv_tellg(srs_flv_t flv);
/* seek file stream, offset is form the start of file */
extern void srs_flv_lseek(srs_flv_t flv, int64_t offset);
/* error code */
/* whether the error code indicates EOF */
extern srs_bool srs_flv_is_eof(int error_code);
/* media codec */
/**
* whether the video body is sequence header 
* @param data, the data of tag, read by srs_flv_read_tag_data().
* @param size, the size of tag, read by srs_flv_read_tag_data().
*/
extern srs_bool srs_flv_is_sequence_header(char* data, int32_t size);
/**
* whether the video body is keyframe 
* @param data, the data of tag, read by srs_flv_read_tag_data().
* @param size, the size of tag, read by srs_flv_read_tag_data().
*/
extern srs_bool srs_flv_is_keyframe(char* data, int32_t size);

/*************************************************************
**************************************************************
* amf0 codec
* @example /trunk/research/librtmp/srs_ingest_flv.c
* @example /trunk/research/librtmp/srs_ingest_rtmp.c
**************************************************************
*************************************************************/
/* the output handler. */
typedef double srs_amf0_number;
/**
* parse amf0 from data.
* @param nparsed, the parsed size, NULL to ignore.
* @return the parsed amf0 object. NULL for error.
* @remark user must free the parsed or created object by srs_amf0_free.
*/
extern srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed);
extern srs_amf0_t srs_amf0_create_string(const char* value);
extern srs_amf0_t srs_amf0_create_number(srs_amf0_number value);
extern srs_amf0_t srs_amf0_create_ecma_array();
extern srs_amf0_t srs_amf0_create_strict_array();
extern srs_amf0_t srs_amf0_create_object();
extern srs_amf0_t srs_amf0_ecma_array_to_object(srs_amf0_t ecma_arr);
extern void srs_amf0_free(srs_amf0_t amf0);
/* size and to bytes */
extern int srs_amf0_size(srs_amf0_t amf0);
extern int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size);
/* type detecter */
extern srs_bool srs_amf0_is_string(srs_amf0_t amf0);
extern srs_bool srs_amf0_is_boolean(srs_amf0_t amf0);
extern srs_bool srs_amf0_is_number(srs_amf0_t amf0);
extern srs_bool srs_amf0_is_null(srs_amf0_t amf0);
extern srs_bool srs_amf0_is_object(srs_amf0_t amf0);
extern srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0);
extern srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0);
/* value converter */
extern const char* srs_amf0_to_string(srs_amf0_t amf0);
extern srs_bool srs_amf0_to_boolean(srs_amf0_t amf0);
extern srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0);
/* value setter */
extern void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value);
/* object value converter */
extern int srs_amf0_object_property_count(srs_amf0_t amf0);
extern const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index);
extern srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index);
extern srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name);
extern void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value);
extern void srs_amf0_object_clear(srs_amf0_t amf0);
/* ecma array value converter */
extern int srs_amf0_ecma_array_property_count(srs_amf0_t amf0);
extern const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index);
extern srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index);
extern srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name);
extern void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value);
/* strict array value converter */
extern int srs_amf0_strict_array_property_count(srs_amf0_t amf0);
extern srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index);
extern void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value);

/*************************************************************
**************************************************************
* utilities
**************************************************************
*************************************************************/
/**
* get the current system time in ms.
* use gettimeofday() to get system time.
*/
extern int64_t srs_utils_time_ms();

/**
* get the send bytes.
*/
extern int64_t srs_utils_send_bytes(srs_rtmp_t rtmp);

/**
* get the recv bytes.
*/
extern int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp);

/**
* parse the dts and pts by time in header and data in tag,
* or to parse the RTMP packet by srs_rtmp_read_packet().
*
* @param time, the timestamp of tag, read by srs_flv_read_tag_header().
* @param type, the type of tag, read by srs_flv_read_tag_header().
* @param data, the data of tag, read by srs_flv_read_tag_data().
* @param size, the size of tag, read by srs_flv_read_tag_header().
* @param ppts, output the pts in ms,
*
* @return 0, success; otherswise, failed.
* @remark, the dts always equals to @param time.
* @remark, the pts=dts for audio or data.
* @remark, video only support h.264.
*/
extern int srs_utils_parse_timestamp(
    u_int32_t time, char type, char* data, int size,
    u_int32_t* ppts
);
    
/**
 * whether the flv tag specified by param type is ok.
 * @return true when tag is video/audio/script-data; otherwise, false.
 */
extern srs_bool srs_utils_flv_tag_is_ok(char type);
extern srs_bool srs_utils_flv_tag_is_audio(char type);
extern srs_bool srs_utils_flv_tag_is_video(char type);
extern srs_bool srs_utils_flv_tag_is_av(char type);

/**
* get the CodecID of video tag.
* Codec Identifier. The following values are defined:
*           2 = Sorenson H.263
*           3 = Screen video
*           4 = On2 VP6
*           5 = On2 VP6 with alpha channel
*           6 = Screen video version 2
*           7 = AVC
* @return the code id. 0 for error.
*/
extern char srs_utils_flv_video_codec_id(char* data, int size);

/**
* get the AVCPacketType of video tag.
* The following values are defined:
*           0 = AVC sequence header
*           1 = AVC NALU
*           2 = AVC end of sequence (lower level NALU sequence ender is
*               not required or supported)
* @return the avc packet type. -1(0xff) for error.
*/
extern char srs_utils_flv_video_avc_packet_type(char* data, int size);

/**
* get the FrameType of video tag.
* Type of video frame. The following values are defined:
*           1 = key frame (for AVC, a seekable frame)
*           2 = inter frame (for AVC, a non-seekable frame)
*           3 = disposable inter frame (H.263 only)
*           4 = generated key frame (reserved for server use only)
*           5 = video info/command frame
* @return the frame type. 0 for error.
*/
extern char srs_utils_flv_video_frame_type(char* data, int size);

/**
* get the SoundFormat of audio tag.
* Format of SoundData. The following values are defined:
*               0 = Linear PCM, platform endian
*               1 = ADPCM
*               2 = MP3
*               3 = Linear PCM, little endian
*               4 = Nellymoser 16 kHz mono
*               5 = Nellymoser 8 kHz mono
*               6 = Nellymoser
*               7 = G.711 A-law logarithmic PCM
*               8 = G.711 mu-law logarithmic PCM
*               9 = reserved
*               10 = AAC
*               11 = Speex
*               14 = MP3 8 kHz
*               15 = Device-specific sound
*               Formats 7, 8, 14, and 15 are reserved.
*               AAC is supported in Flash Player 9,0,115,0 and higher.
*               Speex is supported in Flash Player 10 and higher.
* @return the sound format. -1(0xff) for error.
*/
extern char srs_utils_flv_audio_sound_format(char* data, int size);

/**
* get the SoundRate of audio tag.
* Sampling rate. The following values are defined:
*               0 = 5.5 kHz
*               1 = 11 kHz
*               2 = 22 kHz
*               3 = 44 kHz
* @return the sound rate. -1(0xff) for error.
*/
extern char srs_utils_flv_audio_sound_rate(char* data, int size);

/**
* get the SoundSize of audio tag.
* Size of each audio sample. This parameter only pertains to
* uncompressed formats. Compressed formats always decode
* to 16 bits internally.
*               0 = 8-bit samples
*               1 = 16-bit samples
* @return the sound size. -1(0xff) for error.
*/
extern char srs_utils_flv_audio_sound_size(char* data, int size);

/**
* get the SoundType of audio tag.
* Mono or stereo sound
*               0 = Mono sound
*               1 = Stereo sound
* @return the sound type. -1(0xff) for error.
*/
extern char srs_utils_flv_audio_sound_type(char* data, int size);

/**
* get the AACPacketType of audio tag.
* The following values are defined:
*               0 = AAC sequence header
*               1 = AAC raw
* @return the aac packet type. -1(0xff) for error.
*/
extern char srs_utils_flv_audio_aac_packet_type(char* data, int size);

/*************************************************************
**************************************************************
* human readable print.
**************************************************************
*************************************************************/
/**
* human readable print 
* @param pdata, output the heap data, NULL to ignore.
*       user must use srs_amf0_free_bytes to free it.
* @return return the *pdata for print. NULL to ignore.
*/
extern char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize);
/**
* convert the flv tag type to string.
*     SRS_RTMP_TYPE_AUDIO to "Audio"
*     SRS_RTMP_TYPE_VIDEO to "Video"
*     SRS_RTMP_TYPE_SCRIPT to "Data"
*     otherwise, "Unknown"
* @remark user never free the return char*, 
*   it's static shared const string.
*/
extern const char* srs_human_flv_tag_type2string(char type);

/**
* get the codec id string.
*           H.263 = Sorenson H.263
*           Screen = Screen video
*           VP6 = On2 VP6
*           VP6Alpha = On2 VP6 with alpha channel
*           Screen2 = Screen video version 2
*           H.264 = AVC
*           otherwise, "Unknown"
* @remark user never free the return char*, 
*   it's static shared const string.
*/
extern const char* srs_human_flv_video_codec_id2string(char codec_id);

/**
* get the avc packet type string.
*           SH = AVC sequence header
*           Nalu = AVC NALU
*           SpsPpsEnd = AVC end of sequence
*           otherwise, "Unknown"
* @remark user never free the return char*, 
*   it's static shared const string.
*/
extern const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type);

/**
* get the frame type string.
*           I = key frame (for AVC, a seekable frame)
*           P/B = inter frame (for AVC, a non-seekable frame)
*           DI = disposable inter frame (H.263 only)
*           GI = generated key frame (reserved for server use only)
*           VI = video info/command frame
*           otherwise, "Unknown"
* @remark user never free the return char*, 
*   it's static shared const string.
*/
extern const char* srs_human_flv_video_frame_type2string(char frame_type);

/**
* get the SoundFormat string.
* Format of SoundData. The following values are defined:
*               LinearPCM = Linear PCM, platform endian
*               ADPCM = ADPCM
*               MP3 = MP3
*               LinearPCMLe = Linear PCM, little endian
*               NellymoserKHz16 = Nellymoser 16 kHz mono
*               NellymoserKHz8 = Nellymoser 8 kHz mono
*               Nellymoser = Nellymoser
*               G711APCM = G.711 A-law logarithmic PCM
*               G711MuPCM = G.711 mu-law logarithmic PCM
*               Reserved = reserved
*               AAC = AAC
*               Speex = Speex
*               MP3KHz8 = MP3 8 kHz
*               DeviceSpecific = Device-specific sound
*               otherwise, "Unknown"
* @remark user never free the return char*, 
*   it's static shared const string.
*/
extern const char* srs_human_flv_audio_sound_format2string(char sound_format);

/**
* get the SoundRate of audio tag.
* Sampling rate. The following values are defined:
*               5.5KHz = 5.5 kHz
*               11KHz = 11 kHz
*               22KHz = 22 kHz
*               44KHz = 44 kHz
*               otherwise, "Unknown"
* @remark user never free the return char*, 
*   it's static shared const string.
*/
extern const char* srs_human_flv_audio_sound_rate2string(char sound_rate);

/**
* get the SoundSize of audio tag.
* Size of each audio sample. This parameter only pertains to
* uncompressed formats. Compressed formats always decode
* to 16 bits internally.
*               8bit = 8-bit samples
*               16bit = 16-bit samples
*               otherwise, "Unknown"
* @remark user never free the return char*, 
*   it's static shared const string.
*/
extern const char* srs_human_flv_audio_sound_size2string(char sound_size);

/**
* get the SoundType of audio tag.
* Mono or stereo sound
*               Mono = Mono sound
*               Stereo = Stereo sound
*               otherwise, "Unknown"
* @remark user never free the return char*, 
*   it's static shared const string.
*/
extern const char* srs_human_flv_audio_sound_type2string(char sound_type);

/**
* get the AACPacketType of audio tag.
* The following values are defined:
*               SH = AAC sequence header
*               Raw = AAC raw
*               otherwise, "Unknown"
* @remark user never free the return char*, 
*   it's static shared const string.
*/
extern const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type);

/**
* print the rtmp packet, use srs_human_trace/srs_human_verbose for packet,
* and use srs_human_raw for script data body.
* @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);
extern int srs_human_print_rtmp_packet2(char type, u_int32_t timestamp, char* data, int size, u_int32_t pre_timestamp);

// log to console, for use srs-librtmp application.
extern const char* srs_human_format_time();

// when disabled log, donot compile it.
#ifdef SRS_DISABLE_LOG
    #define srs_human_trace(msg, ...) (void)0
    #define srs_human_verbose(msg, ...) (void)0
    #define srs_human_raw(msg, ...) (void)0
#else
    #define srs_human_trace(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n")
    #define srs_human_verbose(msg, ...) printf("[%s] ", srs_human_format_time());printf(msg, ##__VA_ARGS__);printf("\n")
    #define srs_human_raw(msg, ...) printf(msg, ##__VA_ARGS__)
#endif

/*************************************************************
**************************************************************
* IO hijack, use your specified io functions.
**************************************************************
*************************************************************/
// the void* will convert to your handler for io hijack.
typedef void* srs_hijack_io_t;
#ifdef SRS_HIJACK_IO
    #ifndef _WIN32
        // for iovec.
        #include <sys/uio.h>
    #endif
    /**
    * get the hijack io object in rtmp protocol sdk.
    * @remark, user should never provides this method, srs-librtmp provides it.
    */
    extern srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp);
#endif
// define the following macro and functions in your module to hijack the io.
// the example @see https://github.com/winlinvip/st-load
// which use librtmp but use its own io(use st also).
#ifdef SRS_HIJACK_IO
    /**
    * create hijack.
    * @return NULL for error; otherwise, ok.
    */
    extern srs_hijack_io_t srs_hijack_io_create();
    /**
    * destroy the context, user must close the socket.
    */
    extern void srs_hijack_io_destroy(srs_hijack_io_t ctx);
    /**
    * create socket, not connect yet.
    * @return 0, success; otherswise, failed.
    */
    extern int srs_hijack_io_create_socket(srs_hijack_io_t ctx);
    /**
    * connect socket at server_ip:port.
    * @return 0, success; otherswise, failed.
    */
    extern int srs_hijack_io_connect(srs_hijack_io_t ctx, const char* server_ip, int port);
    /**
    * read from socket.
    * @return 0, success; otherswise, failed.
    */
    extern int srs_hijack_io_read(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread);
    /**
    * set the socket recv timeout.
    * @return 0, success; otherswise, failed.
    */
    extern void srs_hijack_io_set_recv_timeout(srs_hijack_io_t ctx, int64_t timeout_us);
    /**
    * get the socket recv timeout.
    * @return 0, success; otherswise, failed.
    */
    extern int64_t srs_hijack_io_get_recv_timeout(srs_hijack_io_t ctx);
    /**
    * get the socket recv bytes.
    * @return 0, success; otherswise, failed.
    */
    extern int64_t srs_hijack_io_get_recv_bytes(srs_hijack_io_t ctx);
    /**
    * set the socket send timeout.
    * @return 0, success; otherswise, failed.
    */
    extern void srs_hijack_io_set_send_timeout(srs_hijack_io_t ctx, int64_t timeout_us);
    /**
    * get the socket send timeout.
    * @return 0, success; otherswise, failed.
    */
    extern int64_t srs_hijack_io_get_send_timeout(srs_hijack_io_t ctx);
    /**
    * get the socket send bytes.
    * @return 0, success; otherswise, failed.
    */
    extern int64_t srs_hijack_io_get_send_bytes(srs_hijack_io_t ctx);
    /**
    * writev of socket.
    * @return 0, success; otherswise, failed.
    */
    extern int srs_hijack_io_writev(srs_hijack_io_t ctx, const iovec *iov, int iov_size, ssize_t* nwrite);
    /**
    * whether the timeout is never timeout.
    * @return 0, success; otherswise, failed.
    */
    extern bool srs_hijack_io_is_never_timeout(srs_hijack_io_t ctx, int64_t timeout_us);
    /**
    * read fully, fill the buf exactly size bytes.
    * @return 0, success; otherswise, failed.
    */
    extern int srs_hijack_io_read_fully(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread);
    /**
    * write bytes to socket.
    * @return 0, success; otherswise, failed.
    */
    extern int srs_hijack_io_write(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nwrite);
#endif

/*************************************************************
**************************************************************
* Windows SRS-LIBRTMP solution
**************************************************************
*************************************************************/
// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifdef _WIN32
    #define _CRT_SECURE_NO_WARNINGS
    #include <time.h>
    int gettimeofday(struct timeval* tv, struct timezone* tz);
    #define PRId64 "lld"
    
    typedef int socklen_t;
    const char *inet_ntop(int af, const void *src, char *dst, socklen_t size);
    typedef int mode_t;
    #define S_IRUSR 0
    #define S_IWUSR 0
    #define S_IRGRP 0
    #define S_IWGRP 0
    #define S_IROTH 0
    
    #include <io.h>
    #include <fcntl.h>
    #define open _open
    #define close _close
    #define lseek _lseek
    #define write _write
    #define read _read
    
    typedef int pid_t;
    pid_t getpid(void);
    #define snprintf _snprintf
    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();
#endif

#ifdef __cplusplus
}
#endif

#endif

// following is generated by src/libs/srs_lib_simple_socket.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_LIB_SIMPLE_SOCKET_HPP
#define SRS_LIB_SIMPLE_SOCKET_HPP

/*
//#include <srs_lib_simple_socket.hpp>
*/

//#include <srs_core.hpp>

//#include <srs_rtmp_io.hpp>
//#include <srs_librtmp.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
    #define SOCKET int
#endif

/**
* simple socket stream,
* use tcp socket, sync block mode, for client like srs-librtmp.
*/
class SimpleSocketStream : public ISrsProtocolReaderWriter
{
private:
    srs_hijack_io_t io;
public:
    SimpleSocketStream();
    virtual ~SimpleSocketStream();
public:
    virtual srs_hijack_io_t hijack_io();
    virtual int create_socket();
    virtual int connect(const char* server, int port);
// ISrsBufferReader
public:
    virtual int read(void* buf, size_t size, ssize_t* nread);
// ISrsProtocolReader
public:
    virtual void set_recv_timeout(int64_t timeout_us);
    virtual int64_t get_recv_timeout();
    virtual int64_t get_recv_bytes();
// ISrsProtocolWriter
public:
    virtual void set_send_timeout(int64_t timeout_us);
    virtual int64_t get_send_timeout();
    virtual int64_t get_send_bytes();
    virtual int writev(const iovec *iov, int iov_size, ssize_t* nwrite);
// ISrsProtocolReaderWriter
public:
    virtual bool is_never_timeout(int64_t timeout_us);
    virtual int read_fully(void* buf, size_t size, ssize_t* nread);
    virtual int write(void* buf, size_t size, ssize_t* nwrite);
};

#endif

// following is generated by src/libs/srs_lib_bandwidth.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_LIB_BANDWIDTH_HPP
#define SRS_LIB_BANDWIDTH_HPP

/*
//#include <srs_lib_bandwidth.hpp>
*/

//#include <srs_core.hpp>

class SrsRtmpClient;
class SrsBandwidthPacket;

/**
* bandwith client library for srs-librtmp.
*/
class SrsBandwidthClient
{
private:
    SrsRtmpClient* _rtmp;
public:
    SrsBandwidthClient();
    virtual ~SrsBandwidthClient();
public:
    /**
    * initialize the bandwidth check client.
    */
    virtual int initialize(SrsRtmpClient* rtmp);
    /**
    * do bandwidth check.
    * 
    * bandwidth info:
    * @param start_time, output the start time, in ms.
    * @param end_time, output the end time, in ms.
    * @param play_kbps, output the play/download kbps.
    * @param publish_kbps, output the publish/upload kbps.
    * @param play_bytes, output the play/download bytes.
    * @param publish_bytes, output the publish/upload bytes.
    * @param play_duration, output the play/download test duration, in ms.
    * @param publish_duration, output the publish/upload test duration, in ms.
    */
    virtual int bandwidth_check(
        int64_t* start_time, int64_t* end_time, 
        int* play_kbps, int* publish_kbps,
        int* play_bytes, int* publish_bytes,
        int* play_duration, int* publish_duration
    );
private:
    /**
    * play check/test, downloading bandwidth kbps.
    */
    virtual int play_start();
    virtual int play_checking();
    virtual int play_stop();
    /**
    * publish check/test, publishing bandwidth kbps.
    */
    virtual int publish_start(int& duration_ms, int& play_kbps);
    virtual int publish_checking(int duration_ms, int play_kbps);
    virtual int publish_stop();
    /**
    * report and final packet
    */
    virtual int final(SrsBandwidthPacket** ppkt);
};

#endif

// following is generated by src/core/srs_core.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 <srs_core.hpp>


// following is generated by src/core/srs_core_autofree.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 <srs_core_autofree.hpp>

// following is generated by src/core/srs_core_performance.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 <srs_core_performance.hpp>

// following is generated by src/core/srs_core_mem_watch.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 <srs_core_mem_watch.hpp>

#ifdef SRS_MEM_WATCH

#include <map>
#include <stdio.h>
using namespace std;

struct SrsMemoryObject
{
    void* ptr;
    std::string category;
    int size;
};

std::map<void*, SrsMemoryObject*> _srs_ptrs;

void srs_memory_watch(void* ptr, string category, int size)
{
    SrsMemoryObject* obj = NULL;
    
    std::map<void*, SrsMemoryObject*>::iterator it;
    if ((it = _srs_ptrs.find(ptr)) != _srs_ptrs.end()) {
        obj = it->second;
    } else {
        obj = new SrsMemoryObject();
        _srs_ptrs[ptr] = obj;
    }
    
    obj->ptr = ptr;
    obj->category = category;
    obj->size = size;
}

void srs_memory_unwatch(void* ptr)
{
    std::map<void*, SrsMemoryObject*>::iterator it;
    if ((it = _srs_ptrs.find(ptr)) != _srs_ptrs.end()) {
        SrsMemoryObject* obj = it->second;
        srs_freep(obj);
        
        _srs_ptrs.erase(it);
    }
}

void srs_memory_report()
{
    printf("srs memory leak report:\n");
    
    int total = 0;
    std::map<void*, SrsMemoryObject*>::iterator it;
    for (it = _srs_ptrs.begin(); it != _srs_ptrs.end(); ++it) {
        SrsMemoryObject* obj = it->second;
        printf("    %s: %#"PRIx64", %dB\n", obj->category.c_str(), (int64_t)obj->ptr, obj->size);
        total += obj->size;
    }
    
    printf("%d objects leak %dKB.\n", (int)_srs_ptrs.size(), total / 1024);
}

#endif

// following is generated by src/kernel/srs_kernel_error.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 <srs_kernel_error.hpp>

bool srs_is_system_control_error(int error_code)
{
    return error_code == ERROR_CONTROL_RTMP_CLOSE
        || error_code == ERROR_CONTROL_REPUBLISH;
}

bool srs_is_client_gracefully_close(int error_code)
{
    return error_code == ERROR_SOCKET_READ
        || error_code == ERROR_SOCKET_READ_FULLY
        || error_code == ERROR_SOCKET_WRITE
        || error_code == ERROR_SOCKET_TIMEOUT;
}

// following is generated by src/kernel/srs_kernel_log.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 <srs_kernel_log.hpp>

//#include <srs_kernel_error.hpp>

ISrsLog::ISrsLog()
{
}

ISrsLog::~ISrsLog() 
{
}

int ISrsLog::initialize()
{
    return ERROR_SUCCESS;
}

void ISrsLog::verbose(const char* /*tag*/, int /*context_id*/, const char* /*fmt*/, ...)
{
}

void ISrsLog::info(const char* /*tag*/, int /*context_id*/, const char* /*fmt*/, ...)
{
}

void ISrsLog::trace(const char* /*tag*/, int /*context_id*/, const char* /*fmt*/, ...)
{
}

void ISrsLog::warn(const char* /*tag*/, int /*context_id*/, const char* /*fmt*/, ...)
{
}

void ISrsLog::error(const char* /*tag*/, int /*context_id*/, const char* /*fmt*/, ...)
{
}

ISrsThreadContext::ISrsThreadContext()
{
}

ISrsThreadContext::~ISrsThreadContext()
{
}

int ISrsThreadContext::generate_id()
{
    return 0;
}

int ISrsThreadContext::get_id()
{
    return 0;
}


// following is generated by src/kernel/srs_kernel_stream.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 <srs_kernel_stream.hpp>

using namespace std;

//#include <srs_kernel_log.hpp>
//#include <srs_kernel_error.hpp>
//#include <srs_kernel_utility.hpp>

SrsStream::SrsStream()
{
    p = _bytes = NULL;
    _size = 0;
    
    // TODO: support both little and big endian.
    srs_assert(srs_is_little_endian());
}

SrsStream::~SrsStream()
{
}

int SrsStream::initialize(char* bytes, int size)
{
    int ret = ERROR_SUCCESS;
    
    if (!bytes) {
        ret = ERROR_KERNEL_STREAM_INIT;
        srs_error("stream param bytes must not be NULL. ret=%d", ret);
        return ret;
    }
    
    if (size <= 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);

    return ret;
}

char* SrsStream::data()
{
    return _bytes;
}

int SrsStream::size()
{
    return _size;
}

int SrsStream::pos()
{
    return p - _bytes;
}

bool SrsStream::empty()
{
    return !_bytes || (p >= _bytes + _size);
}

bool SrsStream::require(int required_size)
{
    srs_assert(required_size > 0);
    
    return required_size <= _size - (p - _bytes);
}

void SrsStream::skip(int size)
{
    srs_assert(p);
    
    p += size;
}

int8_t SrsStream::read_1bytes()
{
    srs_assert(require(1));
    
    return (int8_t)*p++;
}

int16_t SrsStream::read_2bytes()
{
    srs_assert(require(2));
    
    int16_t value;
    pp = (char*)&value;
    pp[1] = *p++;
    pp[0] = *p++;
    
    return value;
}

int32_t SrsStream::read_3bytes()
{
    srs_assert(require(3));
    
    int32_t value = 0x00;
    pp = (char*)&value;
    pp[2] = *p++;
    pp[1] = *p++;
    pp[0] = *p++;
    
    return value;
}

int32_t SrsStream::read_4bytes()
{
    srs_assert(require(4));
    
    int32_t value;
    pp = (char*)&value;
    pp[3] = *p++;
    pp[2] = *p++;
    pp[1] = *p++;
    pp[0] = *p++;
    
    return value;
}

int64_t SrsStream::read_8bytes()
{
    srs_assert(require(8));
    
    int64_t value;
    pp = (char*)&value;
    pp[7] = *p++;
    pp[6] = *p++;
    pp[5] = *p++;
    pp[4] = *p++;
    pp[3] = *p++;
    pp[2] = *p++;
    pp[1] = *p++;
    pp[0] = *p++;
    
    return value;
}

string SrsStream::read_string(int len)
{
    srs_assert(require(len));
    
    std::string value;
    value.append(p, len);
    
    p += len;
    
    return value;
}

void SrsStream::read_bytes(char* data, int size)
{
    srs_assert(require(size));
    
    memcpy(data, p, size);
    
    p += size;
}

void SrsStream::write_1bytes(int8_t value)
{
    srs_assert(require(1));
    
    *p++ = value;
}

void SrsStream::write_2bytes(int16_t value)
{
    srs_assert(require(2));
    
    pp = (char*)&value;
    *p++ = pp[1];
    *p++ = pp[0];
}

void SrsStream::write_4bytes(int32_t value)
{
    srs_assert(require(4));
    
    pp = (char*)&value;
    *p++ = pp[3];
    *p++ = pp[2];
    *p++ = pp[1];
    *p++ = pp[0];
}

void SrsStream::write_3bytes(int32_t value)
{
    srs_assert(require(3));
    
    pp = (char*)&value;
    *p++ = pp[2];
    *p++ = pp[1];
    *p++ = pp[0];
}

void SrsStream::write_8bytes(int64_t value)
{
    srs_assert(require(8));
    
    pp = (char*)&value;
    *p++ = pp[7];
    *p++ = pp[6];
    *p++ = pp[5];
    *p++ = pp[4];
    *p++ = pp[3];
    *p++ = pp[2];
    *p++ = pp[1];
    *p++ = pp[0];
}

void SrsStream::write_string(string value)
{
    srs_assert(require(value.length()));
    
    memcpy(p, value.data(), value.length());
    p += value.length();
}

void SrsStream::write_bytes(char* data, int size)
{
    srs_assert(require(size));
    
    memcpy(p, data, size);
    p += size;
}

SrsBitStream::SrsBitStream()
{
    cb = 0;
    cb_left = 0;
    stream = NULL;
}

SrsBitStream::~SrsBitStream()
{
}

int SrsBitStream::initialize(SrsStream* s) {
    stream = s;
    return ERROR_SUCCESS;
}

bool SrsBitStream::empty() {
    if (cb_left) {
        return false;
    }
    return stream->empty();
}

int8_t SrsBitStream::read_bit() {
    if (!cb_left) {
        srs_assert(!stream->empty());
        cb = stream->read_1bytes();
        cb_left = 8;
    }
    
    int8_t v = (cb >> (cb_left - 1)) & 0x01;
    cb_left--;
    return v;
}

// following is generated by src/kernel/srs_kernel_utility.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 <srs_kernel_utility.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <sys/time.h>
#endif

#include <string.h>
#include <sys/stat.h>
#include <fcntl.h>

using namespace std;

//#include <srs_kernel_log.hpp>
//#include <srs_kernel_error.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_kernel_flv.hpp>

// this value must:
// equals to (SRS_SYS_CYCLE_INTERVAL*SRS_SYS_TIME_RESOLUTION_MS_TIMES)*1000
// @see SRS_SYS_TIME_RESOLUTION_MS_TIMES
#define SYS_TIME_RESOLUTION_US 300*1000

int srs_avc_nalu_read_uev(SrsBitStream* stream, int32_t& v)
{
    int ret = ERROR_SUCCESS;
    
    if (stream->empty()) {
        return ERROR_AVC_NALU_UEV;
    }
    
    // ue(v) in 9.1 Parsing process for Exp-Golomb codes
    // H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 227.
    // Syntax elements coded as ue(v), me(v), or se(v) are Exp-Golomb-coded.
    //      leadingZeroBits = -1;
    //      for( b = 0; !b; leadingZeroBits++ )
    //          b = read_bits( 1 )
    // The variable codeNum is then assigned as follows:
    //      codeNum = (2<<leadingZeroBits) - 1 + read_bits( leadingZeroBits )
    int leadingZeroBits = -1;
    for (int8_t b = 0; !b && !stream->empty(); leadingZeroBits++) {
        b = stream->read_bit();
    }
    
    if (leadingZeroBits >= 31) {
        return ERROR_AVC_NALU_UEV;
    }
    
    v = (1 << leadingZeroBits) - 1;
    for (int i = 0; i < leadingZeroBits; i++) {
        int32_t b = stream->read_bit();
        v += b << (leadingZeroBits - 1);
    }
    
    return ret;
}

int srs_avc_nalu_read_bit(SrsBitStream* stream, int8_t& v)
{
    int ret = ERROR_SUCCESS;
    
    if (stream->empty()) {
        return ERROR_AVC_NALU_UEV;
    }
    
    v = stream->read_bit();
    
    return ret;
}

static int64_t _srs_system_time_us_cache = 0;
static int64_t _srs_system_time_startup_time = 0;

int64_t srs_get_system_time_ms()
{
    if (_srs_system_time_us_cache <= 0) {
        srs_update_system_time_ms();
    }
    
    return _srs_system_time_us_cache / 1000;
}
int64_t srs_get_system_startup_time_ms()
{
    if (_srs_system_time_startup_time <= 0) {
        srs_update_system_time_ms();
    }
    
    return _srs_system_time_startup_time / 1000;
}
int64_t srs_update_system_time_ms()
{
    timeval now;
    
    if (gettimeofday(&now, NULL) < 0) {
        srs_warn("gettimeofday failed, ignore");
        return -1;
    }

    // @see: https://github.com/simple-rtmp-server/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;
    
    // for some ARM os, the starttime maybe invalid,
    // for example, on the cubieboard2, the srs_startup_time is 1262304014640,
    // while now is 1403842979210 in ms, diff is 141538964570 ms, 1638 days
    // it's impossible, and maybe the problem of startup time is invalid.
    // use date +%s to get system time is 1403844851.
    // so we use relative time.
    if (_srs_system_time_us_cache <= 0) {
        _srs_system_time_us_cache = now_us;
        _srs_system_time_startup_time = now_us;
        return _srs_system_time_us_cache / 1000;
    }
    
    // use relative time.
    int64_t diff = now_us - _srs_system_time_us_cache;
    diff = srs_max(0, diff);
    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
        _srs_system_time_startup_time += diff;
    }
    
    _srs_system_time_us_cache = now_us;
    srs_info("system time updated, startup=%"PRId64"us, now=%"PRId64"us", 
        _srs_system_time_startup_time, _srs_system_time_us_cache);
    
    return _srs_system_time_us_cache / 1000;
}

string srs_dns_resolve(string host)
{
    if (inet_addr(host.c_str()) != INADDR_NONE) {
        return host;
    }
    
    hostent* answer = gethostbyname(host.c_str());
    if (answer == NULL) {
        return "";
    }
    
    char ipv4[16];
    memset(ipv4, 0, sizeof(ipv4));
    for (int i = 0; i < answer->h_length; i++) {
        inet_ntop(AF_INET, answer->h_addr_list[i], ipv4, sizeof(ipv4));
        break;
    }
    
    return ipv4;
}

bool srs_is_little_endian()
{
    // convert to network(big-endian) order, if not equals, 
    // the system is little-endian, so need to convert the int64
    static int little_endian_check = -1;
    
    if(little_endian_check == -1) {
        union {
            int32_t i;
            int8_t c;
        } little_check_union;
        
        little_check_union.i = 0x01;
        little_endian_check = little_check_union.c;
    }
    
    return (little_endian_check == 1);
}

string srs_string_replace(string str, string old_str, string new_str)
{
    std::string ret = str;
    
    if (old_str == new_str) {
        return ret;
    }
    
    size_t pos = 0;
    while ((pos = ret.find(old_str, pos)) != std::string::npos) {
        ret = ret.replace(pos, old_str.length(), new_str);
    }
    
    return ret;
}

string srs_string_trim_end(string str, string trim_chars)
{
    std::string ret = str;
    
    for (int i = 0; i < (int)trim_chars.length(); i++) {
        char ch = trim_chars.at(i);
        
        while (!ret.empty() && ret.at(ret.length() - 1) == ch) {
            ret.erase(ret.end() - 1);
            
            // ok, matched, should reset the search
            i = 0;
        }
    }
    
    return ret;
}

string srs_string_trim_start(string str, string trim_chars)
{
    std::string ret = str;
    
    for (int i = 0; i < (int)trim_chars.length(); i++) {
        char ch = trim_chars.at(i);
        
        while (!ret.empty() && ret.at(0) == ch) {
            ret.erase(ret.begin());
            
            // ok, matched, should reset the search
            i = 0;
        }
    }
    
    return ret;
}

string srs_string_remove(string str, string remove_chars)
{
    std::string ret = str;
    
    for (int i = 0; i < (int)remove_chars.length(); i++) {
        char ch = remove_chars.at(i);
        
        for (std::string::iterator it = ret.begin(); it != ret.end();) {
            if (ch == *it) {
                it = ret.erase(it);
                
                // ok, matched, should reset the search
                i = 0;
            } else {
                ++it;
            }
        }
    }
    
    return ret;
}

bool srs_string_ends_with(string str, string flag)
{
    return str.rfind(flag) == str.length() - flag.length();
}

bool srs_string_starts_with(string str, string flag)
{
    return str.find(flag) == 0;
}

bool srs_string_contains(string str, string flag)
{
    return str.find(flag) != string::npos;
}

int srs_do_create_dir_recursively(string dir)
{
    int ret = ERROR_SUCCESS;
    
    // stat current dir, if exists, return error.
    if (srs_path_exists(dir)) {
        return ERROR_SYSTEM_DIR_EXISTS;
    }
    
    // create parent first.
    size_t pos;
    if ((pos = dir.rfind("/")) != std::string::npos) {
        std::string parent = dir.substr(0, pos);
        ret = srs_do_create_dir_recursively(parent);
        // return for error.
        if (ret != ERROR_SUCCESS && ret != ERROR_SYSTEM_DIR_EXISTS) {
            return ret;
        }
        // parent exists, set to ok.
        ret = ERROR_SUCCESS;
    }
    
    // create curren dir.
    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) {
        if (errno == EEXIST) {
            return ERROR_SYSTEM_DIR_EXISTS;
        }
        
        ret = ERROR_SYSTEM_CREATE_DIR;
        srs_error("create dir %s failed. ret=%d", dir.c_str(), ret);
        return ret;
    }
    srs_info("create dir %s success.", dir.c_str());
    
    return ret;
}

int srs_create_dir_recursively(string dir)
{
    int ret = ERROR_SUCCESS;
    
    ret = srs_do_create_dir_recursively(dir);
    
    if (ret == ERROR_SYSTEM_DIR_EXISTS) {
        return ERROR_SUCCESS;
    }
    
    return ret;
}

bool srs_path_exists(std::string path)
{
    struct stat st;
    
    // stat current dir, if exists, return error.
    if (stat(path.c_str(), &st) == 0) {
        return true;
    }

    return false;
}

string srs_path_dirname(string path)
{
    std::string dirname = path;
    size_t pos = string::npos;
    
    if ((pos = dirname.rfind("/")) != string::npos) {
        if (pos == 0) {
            return "/";
        }
        dirname = dirname.substr(0, pos);
    }
    
    return dirname;
}

string srs_path_basename(string path)
{
    std::string dirname = path;
    size_t pos = string::npos;
    
    if ((pos = dirname.rfind("/")) != string::npos) {
        // the basename("/") is "/"
        if (dirname.length() == 1) {
            return dirname;
        }
        dirname = dirname.substr(pos + 1);
    }
    
    return dirname;
}

bool srs_avc_startswith_annexb(SrsStream* stream, int* pnb_start_code)
{
    char* bytes = stream->data() + stream->pos();
    char* p = bytes;
    
    for (;;) {
        if (!stream->require(p - bytes + 3)) {
            return false;
        }
        
        // not match
        if (p[0] != (char)0x00 || p[1] != (char)0x00) {
            return false;
        }
        
        // match N[00] 00 00 01, where N>=0
        if (p[2] == (char)0x01) {
            if (pnb_start_code) {
                *pnb_start_code = (int)(p - bytes) + 3;
            }
            return true;
        }
        
        p++;
    }
    
    return false;
}

bool srs_aac_startswith_adts(SrsStream* stream)
{
    char* bytes = stream->data() + stream->pos();
    char* p = bytes;
    
    if (!stream->require((int)(p - bytes) + 2)) {
        return false;
    }
    
    // matched 12bits 0xFFF,
    // @remark, we must cast the 0xff to char to compare.
    if (p[0] != (char)0xff || (char)(p[1] & 0xf0) != (char)0xf0) {
        return false;
    }
    
    return true;
}

/*
 * MPEG2 transport stream (aka DVB) mux
 * Copyright (c) 2003 Fabrice Bellard.
 *
 * This library 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 2 of the License, or (at your option) any later version.
 *
 * This library 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 this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 */
static const u_int32_t crc_table[256] = {
	0x00000000, 0x04c11db7, 0x09823b6e, 0x0d4326d9, 0x130476dc, 0x17c56b6b,
	0x1a864db2, 0x1e475005, 0x2608edb8, 0x22c9f00f, 0x2f8ad6d6, 0x2b4bcb61,
	0x350c9b64, 0x31cd86d3, 0x3c8ea00a, 0x384fbdbd, 0x4c11db70, 0x48d0c6c7,
	0x4593e01e, 0x4152fda9, 0x5f15adac, 0x5bd4b01b, 0x569796c2, 0x52568b75,
	0x6a1936c8, 0x6ed82b7f, 0x639b0da6, 0x675a1011, 0x791d4014, 0x7ddc5da3,
	0x709f7b7a, 0x745e66cd, 0x9823b6e0, 0x9ce2ab57, 0x91a18d8e, 0x95609039,
	0x8b27c03c, 0x8fe6dd8b, 0x82a5fb52, 0x8664e6e5, 0xbe2b5b58, 0xbaea46ef,
	0xb7a96036, 0xb3687d81, 0xad2f2d84, 0xa9ee3033, 0xa4ad16ea, 0xa06c0b5d,
	0xd4326d90, 0xd0f37027, 0xddb056fe, 0xd9714b49, 0xc7361b4c, 0xc3f706fb,
	0xceb42022, 0xca753d95, 0xf23a8028, 0xf6fb9d9f, 0xfbb8bb46, 0xff79a6f1,
	0xe13ef6f4, 0xe5ffeb43, 0xe8bccd9a, 0xec7dd02d, 0x34867077, 0x30476dc0,
	0x3d044b19, 0x39c556ae, 0x278206ab, 0x23431b1c, 0x2e003dc5, 0x2ac12072,
	0x128e9dcf, 0x164f8078, 0x1b0ca6a1, 0x1fcdbb16, 0x018aeb13, 0x054bf6a4,
	0x0808d07d, 0x0cc9cdca, 0x7897ab07, 0x7c56b6b0, 0x71159069, 0x75d48dde,
	0x6b93dddb, 0x6f52c06c, 0x6211e6b5, 0x66d0fb02, 0x5e9f46bf, 0x5a5e5b08,
	0x571d7dd1, 0x53dc6066, 0x4d9b3063, 0x495a2dd4, 0x44190b0d, 0x40d816ba,
	0xaca5c697, 0xa864db20, 0xa527fdf9, 0xa1e6e04e, 0xbfa1b04b, 0xbb60adfc,
	0xb6238b25, 0xb2e29692, 0x8aad2b2f, 0x8e6c3698, 0x832f1041, 0x87ee0df6,
	0x99a95df3, 0x9d684044, 0x902b669d, 0x94ea7b2a, 0xe0b41de7, 0xe4750050,
	0xe9362689, 0xedf73b3e, 0xf3b06b3b, 0xf771768c, 0xfa325055, 0xfef34de2,
	0xc6bcf05f, 0xc27dede8, 0xcf3ecb31, 0xcbffd686, 0xd5b88683, 0xd1799b34,
	0xdc3abded, 0xd8fba05a, 0x690ce0ee, 0x6dcdfd59, 0x608edb80, 0x644fc637,
	0x7a089632, 0x7ec98b85, 0x738aad5c, 0x774bb0eb, 0x4f040d56, 0x4bc510e1,
	0x46863638, 0x42472b8f, 0x5c007b8a, 0x58c1663d, 0x558240e4, 0x51435d53,
	0x251d3b9e, 0x21dc2629, 0x2c9f00f0, 0x285e1d47, 0x36194d42, 0x32d850f5,
	0x3f9b762c, 0x3b5a6b9b, 0x0315d626, 0x07d4cb91, 0x0a97ed48, 0x0e56f0ff,
	0x1011a0fa, 0x14d0bd4d, 0x19939b94, 0x1d528623, 0xf12f560e, 0xf5ee4bb9,
	0xf8ad6d60, 0xfc6c70d7, 0xe22b20d2, 0xe6ea3d65, 0xeba91bbc, 0xef68060b,
	0xd727bbb6, 0xd3e6a601, 0xdea580d8, 0xda649d6f, 0xc423cd6a, 0xc0e2d0dd,
	0xcda1f604, 0xc960ebb3, 0xbd3e8d7e, 0xb9ff90c9, 0xb4bcb610, 0xb07daba7,
	0xae3afba2, 0xaafbe615, 0xa7b8c0cc, 0xa379dd7b, 0x9b3660c6, 0x9ff77d71,
	0x92b45ba8, 0x9675461f, 0x8832161a, 0x8cf30bad, 0x81b02d74, 0x857130c3,
	0x5d8a9099, 0x594b8d2e, 0x5408abf7, 0x50c9b640, 0x4e8ee645, 0x4a4ffbf2,
	0x470cdd2b, 0x43cdc09c, 0x7b827d21, 0x7f436096, 0x7200464f, 0x76c15bf8,
	0x68860bfd, 0x6c47164a, 0x61043093, 0x65c52d24, 0x119b4be9, 0x155a565e,
	0x18197087, 0x1cd86d30, 0x029f3d35, 0x065e2082, 0x0b1d065b, 0x0fdc1bec,
	0x3793a651, 0x3352bbe6, 0x3e119d3f, 0x3ad08088, 0x2497d08d, 0x2056cd3a,
	0x2d15ebe3, 0x29d4f654, 0xc5a92679, 0xc1683bce, 0xcc2b1d17, 0xc8ea00a0,
	0xd6ad50a5, 0xd26c4d12, 0xdf2f6bcb, 0xdbee767c, 0xe3a1cbc1, 0xe760d676,
	0xea23f0af, 0xeee2ed18, 0xf0a5bd1d, 0xf464a0aa, 0xf9278673, 0xfde69bc4,
	0x89b8fd09, 0x8d79e0be, 0x803ac667, 0x84fbdbd0, 0x9abc8bd5, 0x9e7d9662,
	0x933eb0bb, 0x97ffad0c, 0xafb010b1, 0xab710d06, 0xa6322bdf, 0xa2f33668,
	0xbcb4666d, 0xb8757bda, 0xb5365d03, 0xb1f740b4
};

// @see http://www.stmc.edu.hk/~vincent/ffmpeg_0.4.9-pre1/libavformat/mpegtsenc.c
unsigned int mpegts_crc32(const u_int8_t *data, int len)
{
    register int i;
    unsigned int crc = 0xffffffff;
    
    for (i=0; i<len; i++)
        crc = (crc << 8) ^ crc_table[((crc >> 24) ^ *data++) & 0xff];
    
    return crc;
}

u_int32_t srs_crc32(const void* buf, int size)
{
    return mpegts_crc32((const u_int8_t*)buf, size);
}

/*
 * Copyright (c) 2006 Ryan Martell. (rdm4@martellventures.com)
 *
 * This file is part of FFmpeg.
 *
 * FFmpeg 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 2.1 of the License, or (at your option) any later version.
 *
 * FFmpeg 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 FFmpeg; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 */

#ifndef UINT_MAX
#define UINT_MAX 0xffffffff
#endif

#ifndef AV_RB32
#   define AV_RB32(x)                                \
    (((uint32_t)((const u_int8_t*)(x))[0] << 24) |    \
               (((const u_int8_t*)(x))[1] << 16) |    \
               (((const u_int8_t*)(x))[2] <<  8) |    \
                ((const u_int8_t*)(x))[3])
#endif

#ifndef AV_WL32
#   define AV_WL32(p, darg) do {                \
        unsigned d = (darg);                    \
        ((u_int8_t*)(p))[0] = (d);               \
        ((u_int8_t*)(p))[1] = (d)>>8;            \
        ((u_int8_t*)(p))[2] = (d)>>16;           \
        ((u_int8_t*)(p))[3] = (d)>>24;           \
    } while(0)
#endif

#   define AV_WN(s, p, v) AV_WL##s(p, v)

#   if    defined(AV_WN32) && !defined(AV_WL32)
#       define AV_WL32(p, v) AV_WN32(p, v)
#   elif !defined(AV_WN32) &&  defined(AV_WL32)
#       define AV_WN32(p, v) AV_WL32(p, v)
#   endif

#ifndef AV_WN32
#   define AV_WN32(p, v) AV_WN(32, p, v)
#endif

#define AV_BSWAP16C(x) (((x) << 8 & 0xff00)  | ((x) >> 8 & 0x00ff))
#define AV_BSWAP32C(x) (AV_BSWAP16C(x) << 16 | AV_BSWAP16C((x) >> 16))

#ifndef av_bswap32
static const u_int32_t av_bswap32(u_int32_t x)
{
    return AV_BSWAP32C(x);
}
#endif

#define av_be2ne32(x) av_bswap32(x)

/**
 * @file
 * @brief Base64 encode/decode
 * @author Ryan Martell <rdm4@martellventures.com> (with lots of Michael)
 */

/* ---------------- private code */
static const u_int8_t map2[256] =
{
    0xfe, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff,

    0x3e, 0xff, 0xff, 0xff, 0x3f, 0x34, 0x35, 0x36,
    0x37, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0xff,
    0xff, 0xff, 0xfe, 0xff, 0xff, 0xff, 0x00, 0x01,
    0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09,
    0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11,
    0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0x1a, 0x1b,
    0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23,
    0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2a, 0x2b,
    0x2c, 0x2d, 0x2e, 0x2f, 0x30, 0x31, 0x32, 0x33,

                      0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
    0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};

#define BASE64_DEC_STEP(i) do { \
    bits = map2[in[i]]; \
    if (bits & 0x80) \
        goto out ## i; \
    v = i ? (v << 6) + bits : bits; \
} while(0)

int srs_av_base64_decode(u_int8_t* out, const char* in_str, int out_size)
{
    u_int8_t *dst = out;
    u_int8_t *end = out + out_size;
    // no sign extension
    const u_int8_t *in = (const u_int8_t*)in_str;
    unsigned bits = 0xff;
    unsigned v;

    while (end - dst > 3) {
        BASE64_DEC_STEP(0);
        BASE64_DEC_STEP(1);
        BASE64_DEC_STEP(2);
        BASE64_DEC_STEP(3);
        // Using AV_WB32 directly confuses compiler
        v = av_be2ne32(v << 8);
        AV_WN32(dst, v);
        dst += 3;
        in += 4;
    }
    if (end - dst) {
        BASE64_DEC_STEP(0);
        BASE64_DEC_STEP(1);
        BASE64_DEC_STEP(2);
        BASE64_DEC_STEP(3);
        *dst++ = v >> 16;
        if (end - dst)
            *dst++ = v >> 8;
        if (end - dst)
            *dst++ = v;
        in += 4;
    }
    while (1) {
        BASE64_DEC_STEP(0);
        in++;
        BASE64_DEC_STEP(0);
        in++;
        BASE64_DEC_STEP(0);
        in++;
        BASE64_DEC_STEP(0);
        in++;
    }

out3:
    *dst++ = v >> 10;
    v <<= 2;
out2:
    *dst++ = v >> 4;
out1:
out0:
    return bits & 1 ? -1 : dst - out;
}

/*****************************************************************************
* b64_encode: Stolen from VLC's http.c.
* Simplified by Michael.
* Fixed edge cases and made it work from data (vs. strings) by Ryan.
*****************************************************************************/

char* srs_av_base64_encode(char* out, int out_size, const u_int8_t* in, int in_size)
{
    static const char b64[] =
        "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
    char *ret, *dst;
    unsigned i_bits = 0;
    int i_shift = 0;
    int bytes_remaining = in_size;

    if (in_size >= (int)(UINT_MAX / 4) ||
        out_size < SRS_AV_BASE64_SIZE(in_size))
        return NULL;
    ret = dst = out;
    while (bytes_remaining > 3) {
        i_bits = AV_RB32(in);
        in += 3; bytes_remaining -= 3;
        *dst++ = b64[ i_bits>>26        ];
        *dst++ = b64[(i_bits>>20) & 0x3F];
        *dst++ = b64[(i_bits>>14) & 0x3F];
        *dst++ = b64[(i_bits>>8 ) & 0x3F];
    }
    i_bits = 0;
    while (bytes_remaining) {
        i_bits = (i_bits << 8) + *in++;
        bytes_remaining--;
        i_shift += 8;
    }
    while (i_shift > 0) {
        *dst++ = b64[(i_bits << 6 >> i_shift) & 0x3f];
        i_shift -= 6;
    }
    while ((dst - ret) & 3)
        *dst++ = '=';
    *dst = '\0';

    return ret;
}

#define SPACE_CHARS " \t\r\n"

int av_toupper(int c)
{
    if (c >= 'a' && c <= 'z') {
        c ^= 0x20;
    }
    return c;
}

int ff_hex_to_data(u_int8_t* data, const char* p)
{
    int c, len, v;

    len = 0;
    v = 1;
    for (;;) {
        p += strspn(p, SPACE_CHARS);
        if (*p == '\0')
            break;
        c = av_toupper((unsigned char) *p++);
        if (c >= '0' && c <= '9')
            c = c - '0';
        else if (c >= 'A' && c <= 'F')
            c = c - 'A' + 10;
        else
            break;
        v = (v << 4) | c;
        if (v & 0x100) {
            if (data)
                data[len] = v;
            len++;
            v = 1;
        }
    }
    return len;
}

int srs_chunk_header_c0(
    int perfer_cid, u_int32_t timestamp, int32_t payload_length,
    int8_t message_type, int32_t stream_id,
    char* cache, int nb_cache
) {
    // to directly set the field.
    char* pp = NULL;
    
    // generate the header.
    char* p = cache;
    
    // no header.
    if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE) {
        return 0;
    }
    
    // write new chunk stream header, fmt is 0
    *p++ = 0x00 | (perfer_cid & 0x3F);
    
    // chunk message header, 11 bytes
    // timestamp, 3bytes, big-endian
    if (timestamp < RTMP_EXTENDED_TIMESTAMP) {
        pp = (char*)&timestamp;
        *p++ = pp[2];
        *p++ = pp[1];
        *p++ = pp[0];
    } else {
        *p++ = 0xFF;
        *p++ = 0xFF;
        *p++ = 0xFF;
    }
    
    // message_length, 3bytes, big-endian
    pp = (char*)&payload_length;
    *p++ = pp[2];
    *p++ = pp[1];
    *p++ = pp[0];
    
    // message_type, 1bytes
    *p++ = message_type;
    
    // stream_id, 4bytes, little-endian
    pp = (char*)&stream_id;
    *p++ = pp[0];
    *p++ = pp[1];
    *p++ = pp[2];
    *p++ = pp[3];
    
    // for c0
    // chunk extended timestamp header, 0 or 4 bytes, big-endian
    //
    // for c3:
    // chunk extended timestamp header, 0 or 4 bytes, big-endian
    // 6.1.3. Extended Timestamp
    // This field is transmitted only when the normal time stamp in the
    // chunk message header is set to 0x00ffffff. If normal time stamp is
    // set to any value less than 0x00ffffff, this field MUST NOT be
    // present. This field MUST NOT be present if the timestamp field is not
    // present. Type 3 chunks MUST NOT have this field.
    // adobe changed for Type3 chunk:
    //        FMLE always sendout the extended-timestamp,
    //        must send the extended-timestamp to FMS,
    //        must send the extended-timestamp to flash-player.
    // @see: ngx_rtmp_prepare_message
    // @see: http://blog.csdn.net/win_lin/article/details/13363699
    // TODO: FIXME: extract to outer.
    if (timestamp >= RTMP_EXTENDED_TIMESTAMP) {
        pp = (char*)&timestamp;
        *p++ = pp[3];
        *p++ = pp[2];
        *p++ = pp[1];
        *p++ = pp[0];
    }
    
    // always has header
    return p - cache;
}

int srs_chunk_header_c3(
    int perfer_cid, u_int32_t timestamp,
    char* cache, int nb_cache
) {
    // to directly set the field.
    char* pp = NULL;
    
    // generate the header.
    char* p = cache;
    
    // no header.
    if (nb_cache < SRS_CONSTS_RTMP_MAX_FMT3_HEADER_SIZE) {
        return 0;
    }
    
    // write no message header chunk stream, fmt is 3
    // @remark, if perfer_cid > 0x3F, that is, use 2B/3B chunk header,
    // SRS will rollback to 1B chunk header.
    *p++ = 0xC0 | (perfer_cid & 0x3F);
    
    // for c0
    // chunk extended timestamp header, 0 or 4 bytes, big-endian
    //
    // for c3:
    // chunk extended timestamp header, 0 or 4 bytes, big-endian
    // 6.1.3. Extended Timestamp
    // This field is transmitted only when the normal time stamp in the
    // chunk message header is set to 0x00ffffff. If normal time stamp is
    // set to any value less than 0x00ffffff, this field MUST NOT be
    // present. This field MUST NOT be present if the timestamp field is not
    // present. Type 3 chunks MUST NOT have this field.
    // adobe changed for Type3 chunk:
    //        FMLE always sendout the extended-timestamp,
    //        must send the extended-timestamp to FMS,
    //        must send the extended-timestamp to flash-player.
    // @see: ngx_rtmp_prepare_message
    // @see: http://blog.csdn.net/win_lin/article/details/13363699
    // TODO: FIXME: extract to outer.
    if (timestamp >= RTMP_EXTENDED_TIMESTAMP) {
        pp = (char*)&timestamp;
        *p++ = pp[3];
        *p++ = pp[2];
        *p++ = pp[1];
        *p++ = pp[0];
    }
    
    // always has header
    return p - cache;
}

// following is generated by src/kernel/srs_kernel_flv.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 <srs_kernel_flv.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#endif

#include <fcntl.h>
#include <sstream>
using namespace std;

//#include <srs_kernel_log.hpp>
//#include <srs_kernel_error.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_kernel_file.hpp>
//#include <srs_kernel_codec.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_core_mem_watch.hpp>

SrsMessageHeader::SrsMessageHeader()
{
    message_type = 0;
    payload_length = 0;
    timestamp_delta = 0;
    stream_id = 0;
    
    timestamp = 0;
    // we always use the connection chunk-id
    perfer_cid = RTMP_CID_OverConnection;
}

SrsMessageHeader::~SrsMessageHeader()
{
}

bool SrsMessageHeader::is_audio()
{
    return message_type == RTMP_MSG_AudioMessage;
}

bool SrsMessageHeader::is_video()
{
    return message_type == RTMP_MSG_VideoMessage;
}

bool SrsMessageHeader::is_amf0_command()
{
    return message_type == RTMP_MSG_AMF0CommandMessage;
}

bool SrsMessageHeader::is_amf0_data()
{
    return message_type == RTMP_MSG_AMF0DataMessage;
}

bool SrsMessageHeader::is_amf3_command()
{
    return message_type == RTMP_MSG_AMF3CommandMessage;
}

bool SrsMessageHeader::is_amf3_data()
{
    return message_type == RTMP_MSG_AMF3DataMessage;
}

bool SrsMessageHeader::is_window_ackledgement_size()
{
    return message_type == RTMP_MSG_WindowAcknowledgementSize;
}

bool SrsMessageHeader::is_ackledgement()
{
    return message_type == RTMP_MSG_Acknowledgement;
}

bool SrsMessageHeader::is_set_chunk_size()
{
    return message_type == RTMP_MSG_SetChunkSize;
}

bool SrsMessageHeader::is_user_control_message()
{
    return message_type == RTMP_MSG_UserControlMessage;
}

bool SrsMessageHeader::is_set_peer_bandwidth()
{
    return message_type == RTMP_MSG_SetPeerBandwidth;
}

bool SrsMessageHeader::is_aggregate()
{
    return message_type == RTMP_MSG_AggregateMessage;
}

void SrsMessageHeader::initialize_amf0_script(int size, int stream)
{
    message_type = RTMP_MSG_AMF0DataMessage;
    payload_length = (int32_t)size;
    timestamp_delta = (int32_t)0;
    timestamp = (int64_t)0;
    stream_id = (int32_t)stream;
    
    // amf0 script use connection2 chunk-id
    perfer_cid = RTMP_CID_OverConnection2;
}

void SrsMessageHeader::initialize_audio(int size, u_int32_t time, int stream)
{
    message_type = RTMP_MSG_AudioMessage;
    payload_length = (int32_t)size;
    timestamp_delta = (int32_t)time;
    timestamp = (int64_t)time;
    stream_id = (int32_t)stream;
    
    // audio chunk-id
    perfer_cid = RTMP_CID_Audio;
}

void SrsMessageHeader::initialize_video(int size, u_int32_t time, int stream)
{
    message_type = RTMP_MSG_VideoMessage;
    payload_length = (int32_t)size;
    timestamp_delta = (int32_t)time;
    timestamp = (int64_t)time;
    stream_id = (int32_t)stream;
    
    // video chunk-id
    perfer_cid = RTMP_CID_Video;
}

SrsCommonMessage::SrsCommonMessage()
{
    payload = NULL;
    size = 0;
}

SrsCommonMessage::~SrsCommonMessage()
{
#ifdef SRS_MEM_WATCH
    srs_memory_unwatch(payload);
#endif
    srs_freep(payload);
}

void SrsCommonMessage::create_payload(int size)
{
    srs_freep(payload);
    
    payload = new char[size];
    srs_verbose("create payload for RTMP message. size=%d", size);
    
#ifdef SRS_MEM_WATCH
    srs_memory_watch(payload, "RTMP.msg.payload", size);
#endif
}

SrsSharedPtrMessage::SrsSharedPtrPayload::SrsSharedPtrPayload()
{
    payload = NULL;
    size = 0;
    shared_count = 0;
}

SrsSharedPtrMessage::SrsSharedPtrPayload::~SrsSharedPtrPayload()
{
#ifdef SRS_MEM_WATCH
    srs_memory_unwatch(payload);
#endif
    srs_freep(payload);
}

SrsSharedPtrMessage::SrsSharedPtrMessage()
{
    ptr = NULL;
}

SrsSharedPtrMessage::~SrsSharedPtrMessage()
{
    if (ptr) {
        if (ptr->shared_count == 0) {
            srs_freep(ptr);
        } else {
            ptr->shared_count--;
        }
    }
}

int SrsSharedPtrMessage::create(SrsCommonMessage* msg)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = create(&msg->header, msg->payload, msg->size)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // to prevent double free of payload:
    // initialize already attach the payload of msg,
    // detach the payload to transfer the owner to shared ptr.
    msg->payload = NULL;
    msg->size = 0;
    
    return ret;
}

int SrsSharedPtrMessage::create(SrsMessageHeader* pheader, char* payload, int size)
{
    int ret = ERROR_SUCCESS;
    
    if (ptr) {
        ret = ERROR_SYSTEM_ASSERT_FAILED;
        srs_error("should not set the payload twice. ret=%d", ret);
        srs_assert(false);
        
        return ret;
    }
    
    ptr = new SrsSharedPtrPayload();
    
    // direct attach the data.
    if (pheader) {
        ptr->header.message_type = pheader->message_type;
        ptr->header.payload_length = size;
        ptr->header.perfer_cid = pheader->perfer_cid;
        this->timestamp = pheader->timestamp;
        this->stream_id = pheader->stream_id;
    }
    ptr->payload = payload;
    ptr->size = size;
    
    // message can access it.
    this->payload = ptr->payload;
    this->size = ptr->size;
    
    return ret;
}

int SrsSharedPtrMessage::count()
{
    srs_assert(ptr);
    return ptr->shared_count;
}

bool SrsSharedPtrMessage::check(int stream_id)
{
    // we donot use the complex basic header,
    // ensure the basic header is 1bytes.
    if (ptr->header.perfer_cid < 2) {
        srs_info("change the chunk_id=%d to default=%d",
            ptr->header.perfer_cid, RTMP_CID_ProtocolControl);
        ptr->header.perfer_cid = RTMP_CID_ProtocolControl;
    }
    
    // we assume that the stream_id in a group must be the same.
    if (this->stream_id == stream_id) {
        return true;
    }
    this->stream_id = stream_id;
    
    return false;
}

bool SrsSharedPtrMessage::is_av()
{
    return ptr->header.message_type == RTMP_MSG_AudioMessage
        || ptr->header.message_type == RTMP_MSG_VideoMessage;
}

bool SrsSharedPtrMessage::is_audio()
{
    return ptr->header.message_type == RTMP_MSG_AudioMessage;
}

bool SrsSharedPtrMessage::is_video()
{
    return ptr->header.message_type == RTMP_MSG_VideoMessage;
}

int SrsSharedPtrMessage::chunk_header(char* cache, int nb_cache, bool c0)
{
    if (c0) {
        return srs_chunk_header_c0(
            ptr->header.perfer_cid, timestamp, ptr->header.payload_length,
            ptr->header.message_type, stream_id,
            cache, nb_cache);
    } else {
        return srs_chunk_header_c3(
            ptr->header.perfer_cid, timestamp,
            cache, nb_cache);
    }
}

SrsSharedPtrMessage* SrsSharedPtrMessage::copy()
{
    srs_assert(ptr);
    
    SrsSharedPtrMessage* copy = new SrsSharedPtrMessage();
    
    copy->ptr = ptr;
    ptr->shared_count++;
    
    copy->timestamp = timestamp;
    copy->stream_id = stream_id;
    copy->payload = ptr->payload;
    copy->size = ptr->size;
    
    return copy;
}

SrsFlvEncoder::SrsFlvEncoder()
{
    _fs = NULL;
    tag_stream = new SrsStream();
    
#ifdef SRS_PERF_FAST_FLV_ENCODER
    nb_tag_headers = 0;
    tag_headers = NULL;
    nb_iovss_cache = 0;
    iovss_cache = NULL;
    nb_ppts = 0;
    ppts = NULL;
#endif
}

SrsFlvEncoder::~SrsFlvEncoder()
{
    srs_freep(tag_stream);
    
#ifdef SRS_PERF_FAST_FLV_ENCODER
    srs_freep(tag_headers);
    srs_freep(iovss_cache);
    srs_freep(ppts);
#endif
}

int SrsFlvEncoder::initialize(SrsFileWriter* fs)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(fs);
    
    if (!fs->is_open()) {
        ret = ERROR_KERNEL_FLV_STREAM_CLOSED;
        srs_warn("stream is not open for encoder. ret=%d", ret);
        return ret;
    }
    
    _fs = fs;
    
    return ret;
}

int SrsFlvEncoder::write_header()
{
    int ret = ERROR_SUCCESS;
    
    // 9bytes header and 4bytes first previous-tag-size
    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)0x00, (char)0x00, (char)0x00, (char)0x09 // DataOffset UI32 The length of this header in bytes
    };
    
    // flv specification should set the audio and video flag,
    // actually in practise, application generally ignore this flag,
    // so we generally set the audio/video to 0.
    
    // write 9bytes header.
    if ((ret = write_header(flv_header)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int SrsFlvEncoder::write_header(char flv_header[9])
{
    int ret = ERROR_SUCCESS;
    
    // write data.
    if ((ret = _fs->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) {
        return ret;
    }
    
    return ret;
}

int SrsFlvEncoder::write_metadata(char type, char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(data);
    
    if ((ret = write_metadata_to_cache(type, data, size, tag_header)) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("write flv data tag failed. ret=%d", ret);
        }
        return ret;
    }
    
    return ret;
}

int SrsFlvEncoder::write_audio(int64_t timestamp, char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(data);
    
    if ((ret = write_audio_to_cache(timestamp, data, size, tag_header)) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("write flv audio tag failed. ret=%d", ret);
        }
        return ret;
    }
    
    return ret;
}

int SrsFlvEncoder::write_video(int64_t timestamp, char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(data);
    
    if ((ret = write_video_to_cache(timestamp, data, size, tag_header)) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = write_tag(tag_header, sizeof(tag_header), data, size)) != ERROR_SUCCESS) {
        srs_error("write flv video tag failed. ret=%d", ret);
        return ret;
    }
    
    return ret;
}

int SrsFlvEncoder::size_tag(int data_size)
{
    srs_assert(data_size >= 0);
    return SRS_FLV_TAG_HEADER_SIZE + data_size + SRS_FLV_PREVIOUS_TAG_SIZE;
}

#ifdef SRS_PERF_FAST_FLV_ENCODER
int SrsFlvEncoder::write_tags(SrsSharedPtrMessage** msgs, int count)
{
    int ret = ERROR_SUCCESS;
    
    // realloc the iovss.
    int nb_iovss = 3 * count;
    iovec* iovss = iovss_cache;
    if (nb_iovss_cache < nb_iovss) {
        srs_freep(iovss_cache);
        
        nb_iovss_cache = nb_iovss;
        iovss = iovss_cache = new iovec[nb_iovss];
    }
    
    // realloc the tag headers.
    char* cache = tag_headers;
    if (nb_tag_headers < count) {
        srs_freep(tag_headers);
        
        nb_tag_headers = count;
        cache = tag_headers = new char[SRS_FLV_TAG_HEADER_SIZE * count];
    }
    
    // realloc the pts.
    char* pts = ppts;
    if (nb_ppts < count) {
        srs_freep(ppts);
        
        nb_ppts = count;
        pts = ppts = new char[SRS_FLV_PREVIOUS_TAG_SIZE * count];
    }
    
    // the cache is ok, write each messages.
    iovec* iovs = iovss;
    for (int i = 0; i < count; i++) {
        SrsSharedPtrMessage* msg = msgs[i];
        
        // cache all flv header.
        if (msg->is_audio()) {
            if ((ret = write_audio_to_cache(msg->timestamp, msg->payload, msg->size, cache)) != ERROR_SUCCESS) {
                return ret;
            }
        } else if (msg->is_video()) {
            if ((ret = write_video_to_cache(msg->timestamp, msg->payload, msg->size, cache)) != ERROR_SUCCESS) {
                return ret;
            }
        } else {
            if ((ret = write_metadata_to_cache(SrsCodecFlvTagScript, msg->payload, msg->size, cache)) != ERROR_SUCCESS) {
                return ret;
            }
        }
        
        // cache all pts.
        if ((ret = write_pts_to_cache(SRS_FLV_TAG_HEADER_SIZE + msg->size, pts)) != ERROR_SUCCESS) {
            return ret;
        }
        
        // all ioves.
        iovs[0].iov_base = cache;
        iovs[0].iov_len = SRS_FLV_TAG_HEADER_SIZE;
        iovs[1].iov_base = msg->payload;
        iovs[1].iov_len = msg->size;
        iovs[2].iov_base = pts;
        iovs[2].iov_len = SRS_FLV_PREVIOUS_TAG_SIZE;
        
        // move next.
        cache += SRS_FLV_TAG_HEADER_SIZE;
        pts += SRS_FLV_PREVIOUS_TAG_SIZE;
        iovs += 3;
    }
    
    if ((ret = _fs->writev(iovss, nb_iovss, NULL)) != ERROR_SUCCESS) {
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("write flv tags failed. ret=%d", ret);
        }
        return ret;
    }
    
    return ret;
}
#endif

int SrsFlvEncoder::write_metadata_to_cache(char type, char* data, int size, char* cache)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(data);
    
    // 11 bytes tag header
    /*char tag_header[] = {
     (char)type, // TagType UB [5], 18 = script data
     (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
     (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
     (char)0x00, // TimestampExtended UI8
     (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
     };*/
    
    // write data size.
    if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) {
        return ret;
    }
    tag_stream->write_1bytes(type);
    tag_stream->write_3bytes(size);
    tag_stream->write_3bytes(0x00);
    tag_stream->write_1bytes(0x00);
    tag_stream->write_3bytes(0x00);
    
    return ret;
}

int SrsFlvEncoder::write_audio_to_cache(int64_t timestamp, char* data, int size, char* cache)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(data);
    
    timestamp &= 0x7fffffff;
    
    // 11bytes tag header
    /*char tag_header[] = {
     (char)SrsCodecFlvTagAudio, // TagType UB [5], 8 = audio
     (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
     (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
     (char)0x00, // TimestampExtended UI8
     (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
     };*/
    
    // write data size.
    if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) {
        return ret;
    }
    tag_stream->write_1bytes(SrsCodecFlvTagAudio);
    tag_stream->write_3bytes(size);
    tag_stream->write_3bytes((int32_t)timestamp);
    // default to little-endian
    tag_stream->write_1bytes((timestamp >> 24) & 0xFF);
    tag_stream->write_3bytes(0x00);
    
    return ret;
}

int SrsFlvEncoder::write_video_to_cache(int64_t timestamp, char* data, int size, char* cache)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(data);
    
    timestamp &= 0x7fffffff;
    
    // 11bytes tag header
    /*char tag_header[] = {
     (char)SrsCodecFlvTagVideo, // TagType UB [5], 9 = video
     (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
     (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
     (char)0x00, // TimestampExtended UI8
     (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
     };*/
    
    // write data size.
    if ((ret = tag_stream->initialize(cache, 11)) != ERROR_SUCCESS) {
        return ret;
    }
    tag_stream->write_1bytes(SrsCodecFlvTagVideo);
    tag_stream->write_3bytes(size);
    tag_stream->write_3bytes((int32_t)timestamp);
    // default to little-endian
    tag_stream->write_1bytes((timestamp >> 24) & 0xFF);
    tag_stream->write_3bytes(0x00);
    
    return ret;
}

int SrsFlvEncoder::write_pts_to_cache(int size, char* cache)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = tag_stream->initialize(cache, SRS_FLV_PREVIOUS_TAG_SIZE)) != ERROR_SUCCESS) {
        return ret;
    }
    tag_stream->write_4bytes(size);
    
    return ret;
}

int SrsFlvEncoder::write_tag(char* header, int header_size, char* tag, int tag_size)
{
    int ret = ERROR_SUCCESS;
    
    // PreviousTagSizeN UI32 Size of last tag, including its header, in bytes.
    char pre_size[SRS_FLV_PREVIOUS_TAG_SIZE];
    if ((ret = write_pts_to_cache(tag_size + header_size, pre_size)) != ERROR_SUCCESS) {
        return ret;
    }
    
    iovec iovs[3];
    iovs[0].iov_base = header;
    iovs[0].iov_len = header_size;
    iovs[1].iov_base = tag;
    iovs[1].iov_len = tag_size;
    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 (!srs_is_client_gracefully_close(ret)) {
            srs_error("write flv tag failed. ret=%d", ret);
        }
        return ret;
    }
    
    return ret;
}

SrsFlvDecoder::SrsFlvDecoder()
{
    _fs = NULL;
    tag_stream = new SrsStream();
}

SrsFlvDecoder::~SrsFlvDecoder()
{
    srs_freep(tag_stream);
}

int SrsFlvDecoder::initialize(SrsFileReader* fs)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(fs);
    
    if (!fs->is_open()) {
        ret = ERROR_KERNEL_FLV_STREAM_CLOSED;
        srs_warn("stream is not open for decoder. ret=%d", ret);
        return ret;
    }
    
    _fs = fs;
    
    return ret;
}

int SrsFlvDecoder::read_header(char header[9])
{
    int ret = ERROR_SUCCESS;

    srs_assert(header);
    
    if ((ret = _fs->read(header, 9, NULL)) != ERROR_SUCCESS) {
        return ret;
    }
    
    char* h = header;
    if (h[0] != 'F' || h[1] != 'L' || h[2] != 'V') {
        ret = ERROR_KERNEL_FLV_HEADER;
        srs_warn("flv header must start with FLV. ret=%d", ret);
        return ret;
    }
    
    return ret;
}

int SrsFlvDecoder::read_tag_header(char* ptype, int32_t* pdata_size, u_int32_t* ptime)
{
    int ret = ERROR_SUCCESS;

    srs_assert(ptype);
    srs_assert(pdata_size);
    srs_assert(ptime);

    char th[11]; // tag header
    
    // read tag header
    if ((ret = _fs->read(th, 11, NULL)) != ERROR_SUCCESS) {
        if (ret != ERROR_SYSTEM_FILE_EOF) {
            srs_error("read flv tag header failed. ret=%d", ret);
        }
        return ret;
    }
    
    // Reserved UB [2]
    // Filter UB [1]
    // TagType UB [5]
    *ptype = (th[0] & 0x1F);
    
    // DataSize UI24
    char* pp = (char*)pdata_size;
    pp[3] = 0;
    pp[2] = th[1];
    pp[1] = th[2];
    pp[0] = th[3];
    
    // Timestamp UI24
    pp = (char*)ptime;
    pp[2] = th[4];
    pp[1] = th[5];
    pp[0] = th[6];
    
    // TimestampExtended UI8
    pp[3] = th[7];

    return ret;
}

int SrsFlvDecoder::read_tag_data(char* data, int32_t size)
{
    int ret = ERROR_SUCCESS;

    srs_assert(data);
    
    if ((ret = _fs->read(data, size, NULL)) != ERROR_SUCCESS) {
        if (ret != ERROR_SYSTEM_FILE_EOF) {
            srs_error("read flv tag header failed. ret=%d", ret);
        }
        return ret;
    }
    
    return ret;

}

int SrsFlvDecoder::read_previous_tag_size(char previous_tag_size[4])
{
    int ret = ERROR_SUCCESS;

    srs_assert(previous_tag_size);
    
    // ignore 4bytes tag size.
    if ((ret = _fs->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);
        }
        return ret;
    }
    
    return ret;
}

SrsFlvVodStreamDecoder::SrsFlvVodStreamDecoder()
{
    _fs = NULL;
    tag_stream = new SrsStream();
}

SrsFlvVodStreamDecoder::~SrsFlvVodStreamDecoder()
{
    srs_freep(tag_stream);
}

int SrsFlvVodStreamDecoder::initialize(SrsFileReader* fs)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(fs);
    
    if (!fs->is_open()) {
        ret = ERROR_KERNEL_FLV_STREAM_CLOSED;
        srs_warn("stream is not open for decoder. ret=%d", ret);
        return ret;
    }
    
    _fs = fs;
    
    return ret;
}

int SrsFlvVodStreamDecoder::read_header_ext(char header[13])
{
    int ret = ERROR_SUCCESS;

    srs_assert(header);
    
    // @remark, always false, for sizeof(char[13]) equals to sizeof(char*)
    //srs_assert(13 == sizeof(header));
    
    // 9bytes header and 4bytes first previous-tag-size
    int size = 13;
    
    if ((ret = _fs->read(header, size, NULL)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int SrsFlvVodStreamDecoder::read_sequence_header_summary(int64_t* pstart, int* psize)
{
    int ret = ERROR_SUCCESS;

    srs_assert(pstart);
    srs_assert(psize);
    
    // simply, the first video/audio must be the sequence header.
    // and must be a sequence video and audio.
    
    // 11bytes tag header
    char tag_header[] = {
        (char)0x00, // TagType UB [5], 9 = video, 8 = audio, 18 = script data
        (char)0x00, (char)0x00, (char)0x00, // DataSize UI24 Length of the message.
        (char)0x00, (char)0x00, (char)0x00, // Timestamp UI24 Time in milliseconds at which the data in this tag applies.
        (char)0x00, // TimestampExtended UI8
        (char)0x00, (char)0x00, (char)0x00, // StreamID UI24 Always 0.
    };
    
    // discovery the sequence header video and audio.
    // @remark, maybe no video or no audio.
    bool got_video = false;
    bool got_audio = false;
    // audio/video sequence and data offset.
    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) {
            return ret;
        }
        
        if ((ret = tag_stream->initialize(tag_header, SRS_FLV_TAG_HEADER_SIZE)) != ERROR_SUCCESS) {
            return ret;
        }
        
        int8_t tag_type = tag_stream->read_1bytes();
        int32_t data_size = tag_stream->read_3bytes();
        
        bool is_video = tag_type == 0x09;
        bool is_audio = tag_type == 0x08;
        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);
            continue;
        }
        
        // if video duplicated, no audio
        if (is_video && got_video) {
            break;
        }
        // if audio duplicated, no video
        if (is_audio && got_audio) {
            break;
        }
        
        // video
        if (is_video) {
            srs_assert(!got_video);
            got_video = true;
            
            if (av_sequence_offset_start < 0) {
                av_sequence_offset_start = _fs->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);
        }
        
        // audio
        if (is_audio) {
            srs_assert(!got_audio);
            got_audio = true;
            
            if (av_sequence_offset_start < 0) {
                av_sequence_offset_start = _fs->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);
        }
    }
    
    // seek to the sequence header start offset.
    if (av_sequence_offset_start > 0) {
        _fs->lseek(av_sequence_offset_start);
        *pstart = av_sequence_offset_start;
        *psize = (int)(av_sequence_offset_end - av_sequence_offset_start);
    }
    
    return ret;
}

int SrsFlvVodStreamDecoder::lseek(int64_t offset)
{
    int ret = ERROR_SUCCESS;
    
    if (offset >= _fs->filesize()) {
        ret = ERROR_SYSTEM_FILE_EOF;
        srs_warn("flv fast decoder seek overflow file, "
            "size=%"PRId64", offset=%"PRId64", ret=%d", 
            _fs->filesize(), offset, ret);
        return ret;
    }
    
    if (_fs->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);
        return ret;
    }
    
    return ret;
}


// following is generated by src/kernel/srs_kernel_codec.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 <srs_kernel_codec.hpp>

#include <string.h>
#include <stdlib.h>
using namespace std;

//#include <srs_kernel_error.hpp>
//#include <srs_kernel_log.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_core_autofree.hpp>

string srs_codec_video2str(SrsCodecVideo codec)
{
    switch (codec) {
        case SrsCodecVideoAVC: 
            return "H264";
        case SrsCodecVideoOn2VP6:
        case SrsCodecVideoOn2VP6WithAlphaChannel:
            return "VP6";
        case SrsCodecVideoReserved:
        case SrsCodecVideoReserved1:
        case SrsCodecVideoReserved2:
        case SrsCodecVideoDisabled:
        case SrsCodecVideoSorensonH263:
        case SrsCodecVideoScreenVideo:
        case SrsCodecVideoScreenVideoVersion2:
        default:
            return "Other";
    }
}

string srs_codec_audio2str(SrsCodecAudio codec)
{
    switch (codec) {
        case SrsCodecAudioAAC:
            return "AAC";
        case SrsCodecAudioMP3:
            return "MP3";
        case SrsCodecAudioReserved1:
        case SrsCodecAudioLinearPCMPlatformEndian:
        case SrsCodecAudioADPCM:
        case SrsCodecAudioLinearPCMLittleEndian:
        case SrsCodecAudioNellymoser16kHzMono:
        case SrsCodecAudioNellymoser8kHzMono:
        case SrsCodecAudioNellymoser:
        case SrsCodecAudioReservedG711AlawLogarithmicPCM:
        case SrsCodecAudioReservedG711MuLawLogarithmicPCM:
        case SrsCodecAudioReserved:
        case SrsCodecAudioSpeex:
        case SrsCodecAudioReservedMP3_8kHz:
        case SrsCodecAudioReservedDeviceSpecificSound:
        default:
            return "Other";
    }
}

string srs_codec_aac_profile2str(SrsAacProfile aac_profile)
{
    switch (aac_profile) {
        case SrsAacProfileMain: return "Main";
        case SrsAacProfileLC: return "LC";
        case SrsAacProfileSSR: return "SSR";
        default: return "Other";
    }
}

string srs_codec_aac_object2str(SrsAacObjectType aac_object)
{
    switch (aac_object) {
        case SrsAacObjectTypeAacMain: return "Main";
        case SrsAacObjectTypeAacHE: return "HE";
        case SrsAacObjectTypeAacHEV2: return "HEv2";
        case SrsAacObjectTypeAacLC: return "LC";
        case SrsAacObjectTypeAacSSR: return "SSR";
        default: return "Other";
    }
}

SrsAacObjectType srs_codec_aac_ts2rtmp(SrsAacProfile profile)
{
    switch (profile) {
        case SrsAacProfileMain: return SrsAacObjectTypeAacMain;
        case SrsAacProfileLC: return SrsAacObjectTypeAacLC;
        case SrsAacProfileSSR: return SrsAacObjectTypeAacSSR;
        default: return SrsAacObjectTypeReserved;
    }
}

SrsAacProfile srs_codec_aac_rtmp2ts(SrsAacObjectType object_type)
{
    switch (object_type) {
        case SrsAacObjectTypeAacMain: return SrsAacProfileMain;
        case SrsAacObjectTypeAacHE:
        case SrsAacObjectTypeAacHEV2:
        case SrsAacObjectTypeAacLC: return SrsAacProfileLC;
        case SrsAacObjectTypeAacSSR: return SrsAacProfileSSR;
        default: return SrsAacProfileReserved;
    }
}

string srs_codec_avc_profile2str(SrsAvcProfile profile)
{
    switch (profile) {
        case SrsAvcProfileBaseline: return "Baseline";
        case SrsAvcProfileConstrainedBaseline: return "Baseline(Constrained)";
        case SrsAvcProfileMain: return "Main";
        case SrsAvcProfileExtended: return "Extended";
        case SrsAvcProfileHigh: return "High";
        case SrsAvcProfileHigh10: return "High(10)";
        case SrsAvcProfileHigh10Intra: return "High(10+Intra)";
        case SrsAvcProfileHigh422: return "High(422)";
        case SrsAvcProfileHigh422Intra: return "High(422+Intra)";
        case SrsAvcProfileHigh444: return "High(444)";
        case SrsAvcProfileHigh444Predictive: return "High(444+Predictive)";
        case SrsAvcProfileHigh444Intra: return "High(444+Intra)";
        default: return "Other";
    }
}

string srs_codec_avc_level2str(SrsAvcLevel level)
{
    switch (level) {
        case SrsAvcLevel_1: return "1";
        case SrsAvcLevel_11: return "1.1";
        case SrsAvcLevel_12: return "1.2";
        case SrsAvcLevel_13: return "1.3";
        case SrsAvcLevel_2: return "2";
        case SrsAvcLevel_21: return "2.1";
        case SrsAvcLevel_22: return "2.2";
        case SrsAvcLevel_3: return "3";
        case SrsAvcLevel_31: return "3.1";
        case SrsAvcLevel_32: return "3.2";
        case SrsAvcLevel_4: return "4";
        case SrsAvcLevel_41: return "4.1";
        case SrsAvcLevel_5: return "5";
        case SrsAvcLevel_51: return "5.1";
        default: return "Other";
    }
}

/**
* the public data, event HLS disable, others can use it.
*/
// 0 = 5.5 kHz = 5512 Hz
// 1 = 11 kHz = 11025 Hz
// 2 = 22 kHz = 22050 Hz
// 3 = 44 kHz = 44100 Hz
int flv_sample_rates[] = {5512, 11025, 22050, 44100};

// the sample rates in the codec,
// in the sequence header.
int aac_sample_rates[] = 
{
    96000, 88200, 64000, 48000,
    44100, 32000, 24000, 22050,
    16000, 12000, 11025,  8000,
    7350,     0,     0,    0
};

SrsFlvCodec::SrsFlvCodec()
{
}

SrsFlvCodec::~SrsFlvCodec()
{
}

bool SrsFlvCodec::video_is_keyframe(char* data, int size)
{
    // 2bytes required.
    if (size < 1) {
        return false;
    }

    char frame_type = data[0];
    frame_type = (frame_type >> 4) & 0x0F;
    
    return frame_type == SrsCodecVideoAVCFrameKeyFrame;
}

bool SrsFlvCodec::video_is_sequence_header(char* data, int size)
{
    // sequence header only for h264
    if (!video_is_h264(data, size)) {
        return false;
    }
    
    // 2bytes required.
    if (size < 2) {
        return false;
    }

    char frame_type = data[0];
    frame_type = (frame_type >> 4) & 0x0F;

    char avc_packet_type = data[1];
    
    return frame_type == SrsCodecVideoAVCFrameKeyFrame 
        && avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader;
}

bool SrsFlvCodec::audio_is_sequence_header(char* data, int size)
{
    // sequence header only for aac
    if (!audio_is_aac(data, size)) {
        return false;
    }
    
    // 2bytes required.
    if (size < 2) {
        return false;
    }
    
    char aac_packet_type = data[1];
    
    return aac_packet_type == SrsCodecAudioTypeSequenceHeader;
}

bool SrsFlvCodec::video_is_h264(char* data, int size)
{
    // 1bytes required.
    if (size < 1) {
        return false;
    }

    char codec_id = data[0];
    codec_id = codec_id & 0x0F;
    
    return codec_id == SrsCodecVideoAVC;
}

bool SrsFlvCodec::audio_is_aac(char* data, int size)
{
    // 1bytes required.
    if (size < 1) {
        return false;
    }
    
    char sound_format = data[0];
    sound_format = (sound_format >> 4) & 0x0F;
    
    return sound_format == SrsCodecAudioAAC;
}

bool SrsFlvCodec::video_is_acceptable(char* data, int size)
{
    // 1bytes required.
    if (size < 1) {
        return false;
    }
    
    char frame_type = data[0];
    char codec_id = frame_type & 0x0f;
    frame_type = (frame_type >> 4) & 0x0f;
    
    if (frame_type < 1 || frame_type > 5) {
        return false;
    }
    
    if (codec_id < 2 || codec_id > 7) {
        return false;
    }
    
    return true;
}

string srs_codec_avc_nalu2str(SrsAvcNaluType nalu_type)
{
    switch (nalu_type) {
        case SrsAvcNaluTypeNonIDR: return "NonIDR";
        case SrsAvcNaluTypeDataPartitionA: return "DataPartitionA";
        case SrsAvcNaluTypeDataPartitionB: return "DataPartitionB";
        case SrsAvcNaluTypeDataPartitionC: return "DataPartitionC";
        case SrsAvcNaluTypeIDR: return "IDR";
        case SrsAvcNaluTypeSEI: return "SEI";
        case SrsAvcNaluTypeSPS: return "SPS";
        case SrsAvcNaluTypePPS: return "PPS";
        case SrsAvcNaluTypeAccessUnitDelimiter: return "AccessUnitDelimiter";
        case SrsAvcNaluTypeEOSequence: return "EOSequence";
        case SrsAvcNaluTypeEOStream: return "EOStream";
        case SrsAvcNaluTypeFilterData: return "FilterData";
        case SrsAvcNaluTypeSPSExt: return "SPSExt";
        case SrsAvcNaluTypePrefixNALU: return "PrefixNALU";
        case SrsAvcNaluTypeSubsetSPS: return "SubsetSPS";
        case SrsAvcNaluTypeLayerWithoutPartition: return "LayerWithoutPartition";
        case SrsAvcNaluTypeCodedSliceExt: return "CodedSliceExt";
        case SrsAvcNaluTypeReserved: default: return "Other";
    }
}

SrsCodecSampleUnit::SrsCodecSampleUnit()
{
    size = 0;
    bytes = NULL;
}

SrsCodecSampleUnit::~SrsCodecSampleUnit()
{
}

SrsCodecSample::SrsCodecSample()
{
    clear();
}

SrsCodecSample::~SrsCodecSample()
{
}

void SrsCodecSample::clear()
{
    is_video = false;
    nb_sample_units = 0;

    cts = 0;
    frame_type = SrsCodecVideoAVCFrameReserved;
    avc_packet_type = SrsCodecVideoAVCTypeReserved;
    has_idr = false;
    first_nalu_type = SrsAvcNaluTypeReserved;
    
    acodec = SrsCodecAudioReserved1;
    sound_rate = SrsCodecAudioSampleRateReserved;
    sound_size = SrsCodecAudioSampleSizeReserved;
    sound_type = SrsCodecAudioSoundTypeReserved;
    aac_packet_type = SrsCodecAudioTypeReserved;
}

int SrsCodecSample::add_sample_unit(char* bytes, int size)
{
    int ret = ERROR_SUCCESS;
    
    if (nb_sample_units >= SRS_SRS_MAX_CODEC_SAMPLE) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("hls decode samples error, "
            "exceed the max count: %d, ret=%d", SRS_SRS_MAX_CODEC_SAMPLE, ret);
        return ret;
    }
    
    SrsCodecSampleUnit* sample_unit = &sample_units[nb_sample_units++];
    sample_unit->bytes = bytes;
    sample_unit->size = size;
    
    // for video, parse the nalu type, set the IDR flag.
    if (is_video) {
        SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(bytes[0] & 0x1f);
        
        if (nal_unit_type == SrsAvcNaluTypeIDR) {
            has_idr = true;
        }
    
        if (first_nalu_type == SrsAvcNaluTypeReserved) {
            first_nalu_type = nal_unit_type;
        }
    }
    
    return ret;
}

SrsAvcAacCodec::SrsAvcAacCodec()
{
    width                       = 0;
    height                      = 0;
    duration                    = 0;
    NAL_unit_length             = 0;
    frame_rate                  = 0;

    video_data_rate             = 0;
    video_codec_id              = 0;

    audio_data_rate             = 0;
    audio_codec_id              = 0;

    avc_profile                 = SrsAvcProfileReserved;
    avc_level                   = SrsAvcLevelReserved;
    aac_object                  = SrsAacObjectTypeReserved;
    aac_sample_rate             = SRS_AAC_SAMPLE_RATE_UNSET; // sample rate ignored
    aac_channels                = 0;
    avc_extra_size              = 0;
    avc_extra_data              = NULL;
    aac_extra_size              = 0;
    aac_extra_data              = NULL;

    sequenceParameterSetLength  = 0;
    sequenceParameterSetNALUnit = NULL;
    pictureParameterSetLength   = 0;
    pictureParameterSetNALUnit  = NULL;

    payload_format = SrsAvcPayloadFormatGuess;
    stream = new SrsStream();
}

SrsAvcAacCodec::~SrsAvcAacCodec()
{
    srs_freep(avc_extra_data);
    srs_freep(aac_extra_data);

    srs_freep(stream);
    srs_freep(sequenceParameterSetNALUnit);
    srs_freep(pictureParameterSetNALUnit);
}

int SrsAvcAacCodec::audio_aac_demux(char* data, int size, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
    
    sample->is_video = false;
    
    if (!data || size <= 0) {
        srs_trace("no audio present, ignore it.");
        return ret;
    }
    
    if ((ret = stream->initialize(data, size)) != ERROR_SUCCESS) {
        return ret;
    }

    // audio decode
    if (!stream->require(1)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("audio codec decode sound_format failed. ret=%d", ret);
        return ret;
    }
    
    // @see: E.4.2 Audio Tags, video_file_format_spec_v10_1.pdf, page 76
    int8_t sound_format = stream->read_1bytes();
    
    int8_t sound_type = sound_format & 0x01;
    int8_t sound_size = (sound_format >> 1) & 0x01;
    int8_t sound_rate = (sound_format >> 2) & 0x03;
    sound_format = (sound_format >> 4) & 0x0f;
    
    audio_codec_id = sound_format;
    sample->acodec = (SrsCodecAudio)audio_codec_id;

    sample->sound_type = (SrsCodecAudioSoundType)sound_type;
    sample->sound_rate = (SrsCodecAudioSampleRate)sound_rate;
    sample->sound_size = (SrsCodecAudioSampleSize)sound_size;

    // we support h.264+mp3 for hls.
    if (audio_codec_id == SrsCodecAudioMP3) {
        return ERROR_HLS_TRY_MP3;
    }
    
    // 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);
        return ret;
    }

    if (!stream->require(1)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("audio codec decode aac_packet_type failed. ret=%d", ret);
        return ret;
    }
    
    int8_t aac_packet_type = stream->read_1bytes();
    sample->aac_packet_type = (SrsCodecAudioType)aac_packet_type;
    
    if (aac_packet_type == SrsCodecAudioTypeSequenceHeader) {
        // AudioSpecificConfig
        // 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);
            aac_extra_data = new char[aac_extra_size];
            memcpy(aac_extra_data, stream->data() + stream->pos(), aac_extra_size);

            // demux the sequence header.
            if ((ret = audio_aac_sequence_header_demux(aac_extra_data, aac_extra_size)) != ERROR_SUCCESS) {
                return ret;
            }
        }
    } 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);
            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);
            return ret;
        }
    } else {
        // ignored.
    }
    
    // reset the sample rate by sequence header
    if (aac_sample_rate != SRS_AAC_SAMPLE_RATE_UNSET) {
        static int aac_sample_rates[] = {
            96000, 88200, 64000, 48000,
            44100, 32000, 24000, 22050,
            16000, 12000, 11025,  8000,
            7350,     0,     0,    0
        };
        switch (aac_sample_rates[aac_sample_rate]) {
            case 11025:
                sample->sound_rate = SrsCodecAudioSampleRate11025;
                break;
            case 22050:
                sample->sound_rate = SrsCodecAudioSampleRate22050;
                break;
            case 44100:
                sample->sound_rate = SrsCodecAudioSampleRate44100;
                break;
            default:
                break;
        };
    }
    
    srs_info("audio 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;
}

int SrsAvcAacCodec::audio_mp3_demux(char* data, int size, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;

    // we always decode aac then mp3.
    srs_assert(sample->acodec == SrsCodecAudioMP3);
    
    // @see: E.4.2 Audio Tags, video_file_format_spec_v10_1.pdf, page 76
    if (!data || size <= 1) {
        srs_trace("no mp3 audio present, ignore it.");
        return ret;
    }

    // mp3 payload.
    if ((ret = sample->add_sample_unit(data + 1, size - 1)) != ERROR_SUCCESS) {
        srs_error("audio codec add mp3 sample failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("audio decoded, type=%d, codec=%d, asize=%d, rate=%d, format=%d, size=%d", 
        sample->sound_type, audio_codec_id, sample->sound_size, sample->sound_rate, sample->acodec, size);
    
    return ret;
}

int SrsAvcAacCodec::audio_aac_sequence_header_demux(char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = stream->initialize(data, size)) != ERROR_SUCCESS) {
        return ret;
    }
        
    // only need to decode the first 2bytes:
    //      audioObjectType, aac_profile, 5bits.
    //      samplingFrequencyIndex, aac_sample_rate, 4bits.
    //      channelConfiguration, aac_channels, 4bits
    if (!stream->require(2)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("audio codec decode aac sequence header failed. ret=%d", ret);
        return ret;
    }
    u_int8_t profile_ObjectType = stream->read_1bytes();
    u_int8_t samplingFrequencyIndex = stream->read_1bytes();
        
    aac_channels = (samplingFrequencyIndex >> 3) & 0x0f;
    samplingFrequencyIndex = ((profile_ObjectType << 1) & 0x0e) | ((samplingFrequencyIndex >> 7) & 0x01);
    profile_ObjectType = (profile_ObjectType >> 3) & 0x1f;

    // set the aac sample rate.
    aac_sample_rate = samplingFrequencyIndex;

    // convert the object type in sequence header to aac profile of ADTS.
    aac_object = (SrsAacObjectType)profile_ObjectType;
    if (aac_object == SrsAacObjectTypeReserved) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("audio codec decode aac sequence header failed, "
            "adts object=%d invalid. ret=%d", profile_ObjectType, ret);
        return ret;
    }
        
    // 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
    // the source will print the sequence header info.
    //if (aac_profile > 3) {
        // Mark all extended profiles as LC
        // to make Android as happy as possible.
        // @see: ngx_rtmp_hls_parse_aac_header
        //aac_profile = 1;
    //}

    return ret;
}

int SrsAvcAacCodec::video_avc_demux(char* data, int size, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
    
    sample->is_video = true;
    
    if (!data || size <= 0) {
        srs_trace("no video present, ignore it.");
        return ret;
    }
    
    if ((ret = stream->initialize(data, size)) != ERROR_SUCCESS) {
        return ret;
    }

    // video decode
    if (!stream->require(1)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("video codec decode frame_type failed. ret=%d", ret);
        return ret;
    }
    
    // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
    int8_t frame_type = stream->read_1bytes();
    int8_t codec_id = frame_type & 0x0f;
    frame_type = (frame_type >> 4) & 0x0f;
    
    sample->frame_type = (SrsCodecVideoAVCFrame)frame_type;
    
    // ignore info frame without error,
    // @see https://github.com/simple-rtmp-server/srs/issues/288#issuecomment-69863909
    if (sample->frame_type == SrsCodecVideoAVCFrameVideoInfoFrame) {
        srs_warn("video codec 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);
        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);
        return ret;
    }
    int8_t avc_packet_type = stream->read_1bytes();
    int32_t composition_time = stream->read_3bytes();
    
    // pts = dts + cts.
    sample->cts = composition_time;
    sample->avc_packet_type = (SrsCodecVideoAVCType)avc_packet_type;
    
    if (avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) {
        if ((ret = avc_demux_sps_pps(stream)) != ERROR_SUCCESS) {
            return ret;
        }
    } 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);
            return ret;
        }

        // guess for the first time.
        if (payload_format == SrsAvcPayloadFormatGuess) {
            // One or more NALUs (Full frames are required)
            // try  "AnnexB" from H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
            if ((ret = avc_demux_annexb_format(stream, sample)) != ERROR_SUCCESS) {
                // stop try when system error.
                if (ret != ERROR_HLS_AVC_TRY_OTHERS) {
                    srs_error("avc demux for annexb failed. ret=%d", ret);
                    return ret;
                }
                
                // try "ISO Base Media File Format" from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
                if ((ret = avc_demux_ibmf_format(stream, sample)) != ERROR_SUCCESS) {
                    return ret;
                } else {
                    payload_format = SrsAvcPayloadFormatIbmf;
                    srs_info("hls guess avc payload is ibmf format.");
                }
            } else {
                payload_format = SrsAvcPayloadFormatAnnexb;
                srs_info("hls guess avc payload is annexb format.");
            }
        } else if (payload_format == SrsAvcPayloadFormatIbmf) {
            // try "ISO Base Media File Format" from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
            if ((ret = avc_demux_ibmf_format(stream, sample)) != ERROR_SUCCESS) {
                return ret;
            }
            srs_info("hls decode avc payload in ibmf format.");
        } else {
            // One or more NALUs (Full frames are required)
            // try  "AnnexB" from H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
            if ((ret = avc_demux_annexb_format(stream, sample)) != ERROR_SUCCESS) {
                // ok, we guess out the payload is annexb, but maybe changed to ibmf.
                if (ret != ERROR_HLS_AVC_TRY_OTHERS) {
                    srs_error("avc demux for annexb failed. ret=%d", ret);
                    return ret;
                }
                
                // try "ISO Base Media File Format" from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
                if ((ret = avc_demux_ibmf_format(stream, sample)) != ERROR_SUCCESS) {
                    return ret;
                } else {
                    payload_format = SrsAvcPayloadFormatIbmf;
                    srs_warn("hls avc payload change from annexb to ibmf format.");
                }
            }
            srs_info("hls decode avc payload in annexb format.");
        }
    } else {
        // ignored.
    }
    
    srs_info("video decoded, type=%d, codec=%d, avc=%d, cts=%d, size=%d", 
        frame_type, video_codec_id, avc_packet_type, composition_time, size);
    
    return ret;
}

int SrsAvcAacCodec::avc_demux_sps_pps(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // AVCDecoderConfigurationRecord
    // 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);
        avc_extra_data = new char[avc_extra_size];
        memcpy(avc_extra_data, stream->data() + stream->pos(), avc_extra_size);
    }
    
    if (!stream->require(6)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("avc decode sequenc header failed. ret=%d", ret);
        return ret;
    }
    //int8_t configurationVersion = stream->read_1bytes();
    stream->read_1bytes();
    //int8_t AVCProfileIndication = stream->read_1bytes();
    avc_profile = (SrsAvcProfile)stream->read_1bytes();
    //int8_t profile_compatibility = stream->read_1bytes();
    stream->read_1bytes();
    //int8_t AVCLevelIndication = stream->read_1bytes();
    avc_level = (SrsAvcLevel)stream->read_1bytes();
    
    // parse the NALU size.
    int8_t lengthSizeMinusOne = stream->read_1bytes();
    lengthSizeMinusOne &= 0x03;
    NAL_unit_length = lengthSizeMinusOne;
    
    // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
    // 5.2.4.1 AVC decoder configuration record
    // 5.2.4.1.2 Semantics
    // The value of this field shall be one of 0, 1, or 3 corresponding to a
    // length encoded with 1, 2, or 4 bytes, respectively.
    if (NAL_unit_length == 2) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("sps lengthSizeMinusOne should never be 2. ret=%d", ret);
        return ret;
    }
    
    // 1 sps, 7.3.2.1 Sequence parameter set RBSP syntax
    // H.264-AVC-ISO_IEC_14496-10.pdf, page 45.
    if (!stream->require(1)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("avc decode sequenc header sps failed. ret=%d", ret);
        return ret;
    }
    int8_t numOfSequenceParameterSets = stream->read_1bytes();
    numOfSequenceParameterSets &= 0x1f;
    if (numOfSequenceParameterSets != 1) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("avc decode sequenc header sps failed. ret=%d", ret);
        return ret;
    }
    if (!stream->require(2)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("avc decode sequenc header sps size failed. ret=%d", ret);
        return ret;
    }
    sequenceParameterSetLength = stream->read_2bytes();
    if (!stream->require(sequenceParameterSetLength)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("avc decode sequenc header sps data failed. ret=%d", ret);
        return ret;
    }
    if (sequenceParameterSetLength > 0) {
        srs_freep(sequenceParameterSetNALUnit);
        sequenceParameterSetNALUnit = new char[sequenceParameterSetLength];
        stream->read_bytes(sequenceParameterSetNALUnit, sequenceParameterSetLength);
    }
    // 1 pps
    if (!stream->require(1)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("avc decode sequenc header pps failed. ret=%d", ret);
        return ret;
    }
    int8_t numOfPictureParameterSets = stream->read_1bytes();
    numOfPictureParameterSets &= 0x1f;
    if (numOfPictureParameterSets != 1) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("avc decode sequenc header pps failed. ret=%d", ret);
        return ret;
    }
    if (!stream->require(2)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("avc decode sequenc header pps size failed. ret=%d", ret);
        return ret;
    }
    pictureParameterSetLength = stream->read_2bytes();
    if (!stream->require(pictureParameterSetLength)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("avc decode sequenc header pps data failed. ret=%d", ret);
        return ret;
    }
    if (pictureParameterSetLength > 0) {
        srs_freep(pictureParameterSetNALUnit);
        pictureParameterSetNALUnit = new char[pictureParameterSetLength];
        stream->read_bytes(pictureParameterSetNALUnit, pictureParameterSetLength);
    }
    
    return avc_demux_sps();
}

int SrsAvcAacCodec::avc_demux_sps()
{
    int ret = ERROR_SUCCESS;
    
    if (!sequenceParameterSetLength) {
        return ret;
    }
    
    SrsStream stream;
    if ((ret = stream.initialize(sequenceParameterSetNALUnit, sequenceParameterSetLength)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // for NALU, 7.3.1 NAL unit syntax
    // H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 61.
    if (!stream.require(1)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("avc decode sps failed. ret=%d", ret);
        return ret;
    }
    int8_t nutv = stream.read_1bytes();
    
    // forbidden_zero_bit shall be equal to 0.
    int8_t forbidden_zero_bit = (nutv >> 7) & 0x01;
    if (forbidden_zero_bit) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("forbidden_zero_bit shall be equal to 0. ret=%d", ret);
        return ret;
    }
    
    // nal_ref_idc not equal to 0 specifies that the content of the NAL unit contains a sequence parameter set or a picture
    // parameter set or a slice of a reference picture or a slice data partition of a reference picture.
    int8_t nal_ref_idc = (nutv >> 5) & 0x03;
    if (!nal_ref_idc) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("for sps, nal_ref_idc shall be not be equal to 0. ret=%d", ret);
        return ret;
    }
    
    // 7.4.1 NAL unit semantics
    // H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 61.
    // nal_unit_type specifies the type of RBSP data structure contained in the NAL unit as specified in Table 7-1.
    SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(nutv & 0x1f);
    if (nal_unit_type != 7) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("for sps, nal_unit_type shall be equal to 7. ret=%d", ret);
        return ret;
    }
    
    // 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);
    
    int nb_rbsp = 0;
    while (!stream.empty()) {
        rbsp[nb_rbsp] = stream.read_1bytes();
        
        // XX 00 00 03 XX, the 03 byte should be drop.
        if (nb_rbsp > 2 && rbsp[nb_rbsp - 2] == 0 && rbsp[nb_rbsp - 1] == 0 && rbsp[nb_rbsp] == 3) {
            // read 1byte more.
            if (stream.empty()) {
                break;
            }
            rbsp[nb_rbsp] = stream.read_1bytes();
            nb_rbsp++;
            
            continue;
        }
        
        nb_rbsp++;
    }
    
    return avc_demux_sps_rbsp((char*)rbsp, nb_rbsp);
}


int SrsAvcAacCodec::avc_demux_sps_rbsp(char* rbsp, int nb_rbsp)
{
    int ret = ERROR_SUCCESS;
    
    // reparse the rbsp.
    SrsStream stream;
    if ((ret = stream.initialize(rbsp, nb_rbsp)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // for SPS, 7.3.2.1.1 Sequence parameter set data syntax
    // H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62.
    if (!stream.require(3)) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("sps shall atleast 3bytes. ret=%d", ret);
        return ret;
    }
    u_int8_t profile_idc = stream.read_1bytes();
    if (!profile_idc) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("sps the profile_idc invalid. ret=%d", ret);
        return ret;
    }
    
    int8_t flags = stream.read_1bytes();
    if (flags & 0x03) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("sps the flags invalid. ret=%d", ret);
        return ret;
    }
    
    u_int8_t level_idc = stream.read_1bytes();
    if (!level_idc) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("sps the level_idc invalid. ret=%d", ret);
        return ret;
    }
    
    SrsBitStream bs;
    if ((ret = bs.initialize(&stream)) != ERROR_SUCCESS) {
        return ret;
    }
    
    int32_t seq_parameter_set_id = -1;
    if ((ret = srs_avc_nalu_read_uev(&bs, seq_parameter_set_id)) != ERROR_SUCCESS) {
        return ret;
    }
    if (seq_parameter_set_id < 0) {
        ret = ERROR_HLS_DECODE_ERROR;
        srs_error("sps the seq_parameter_set_id invalid. ret=%d", ret);
        return ret;
    }
    srs_info("sps parse profile=%d, level=%d, sps_id=%d", profile_idc, level_idc, seq_parameter_set_id);
    
    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;
        }
        if (chroma_format_idc == 3) {
            int8_t separate_colour_plane_flag = -1;
            if ((ret = srs_avc_nalu_read_bit(&bs, separate_colour_plane_flag)) != ERROR_SUCCESS) {
                return ret;
            }
        }
        
        int32_t bit_depth_luma_minus8 = -1;
        if ((ret = srs_avc_nalu_read_uev(&bs, bit_depth_luma_minus8)) != ERROR_SUCCESS) {
            return ret;
        }
        
        int32_t bit_depth_chroma_minus8 = -1;
        if ((ret = srs_avc_nalu_read_uev(&bs, bit_depth_chroma_minus8)) != ERROR_SUCCESS) {
            return ret;
        }
        
        int8_t qpprime_y_zero_transform_bypass_flag = -1;
        if ((ret = srs_avc_nalu_read_bit(&bs, qpprime_y_zero_transform_bypass_flag)) != ERROR_SUCCESS) {
            return ret;
        }
        
        int8_t seq_scaling_matrix_present_flag = -1;
        if ((ret = srs_avc_nalu_read_bit(&bs, seq_scaling_matrix_present_flag)) != ERROR_SUCCESS) {
            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;
        }
    }
    
    int32_t log2_max_frame_num_minus4 = -1;
    if ((ret = srs_avc_nalu_read_uev(&bs, log2_max_frame_num_minus4)) != ERROR_SUCCESS) {
        return ret;
    }
    
    int32_t pic_order_cnt_type = -1;
    if ((ret = srs_avc_nalu_read_uev(&bs, pic_order_cnt_type)) != ERROR_SUCCESS) {
        return ret;
    }
    
    if (pic_order_cnt_type == 0) {
        int32_t log2_max_pic_order_cnt_lsb_minus4 = -1;
        if ((ret = srs_avc_nalu_read_uev(&bs, log2_max_pic_order_cnt_lsb_minus4)) != ERROR_SUCCESS) {
            return ret;
        }
    } else if (pic_order_cnt_type == 1) {
        int8_t delta_pic_order_always_zero_flag = -1;
        if ((ret = srs_avc_nalu_read_bit(&bs, delta_pic_order_always_zero_flag)) != ERROR_SUCCESS) {
            return ret;
        }
        
        int32_t offset_for_non_ref_pic = -1;
        if ((ret = srs_avc_nalu_read_uev(&bs, offset_for_non_ref_pic)) != ERROR_SUCCESS) {
            return ret;
        }
        
        int32_t offset_for_top_to_bottom_field = -1;
        if ((ret = srs_avc_nalu_read_uev(&bs, offset_for_top_to_bottom_field)) != ERROR_SUCCESS) {
            return ret;
        }
        
        int32_t num_ref_frames_in_pic_order_cnt_cycle = -1;
        if ((ret = srs_avc_nalu_read_uev(&bs, num_ref_frames_in_pic_order_cnt_cycle)) != ERROR_SUCCESS) {
            return ret;
        }
        if (num_ref_frames_in_pic_order_cnt_cycle) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("sps the num_ref_frames_in_pic_order_cnt_cycle invalid. ret=%d", ret);
            return ret;
        }
    }
    
    int32_t max_num_ref_frames = -1;
    if ((ret = srs_avc_nalu_read_uev(&bs, max_num_ref_frames)) != ERROR_SUCCESS) {
        return ret;
    }
    
    int8_t gaps_in_frame_num_value_allowed_flag = -1;
    if ((ret = srs_avc_nalu_read_bit(&bs, gaps_in_frame_num_value_allowed_flag)) != ERROR_SUCCESS) {
        return ret;
    }
    
    int32_t pic_width_in_mbs_minus1 = -1;
    if ((ret = srs_avc_nalu_read_uev(&bs, pic_width_in_mbs_minus1)) != ERROR_SUCCESS) {
        return ret;
    }
    
    int32_t pic_height_in_map_units_minus1 = -1;
    if ((ret = srs_avc_nalu_read_uev(&bs, pic_height_in_map_units_minus1)) != ERROR_SUCCESS) {
        return ret;
    }
    
    width = (int)(pic_width_in_mbs_minus1 + 1) * 16;
    height = (int)(pic_height_in_map_units_minus1 + 1) * 16;
    
    return ret;
}

int SrsAvcAacCodec::avc_demux_annexb_format(SrsStream* stream, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
    
    // not annexb, try others
    if (!srs_avc_startswith_annexb(stream, NULL)) {
        return ERROR_HLS_AVC_TRY_OTHERS;
    }
    
    // AnnexB
    // B.1.1 Byte stream NAL unit syntax,
    // H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
    while (!stream->empty()) {
        // find start code
        int nb_start_code = 0;
        if (!srs_avc_startswith_annexb(stream, &nb_start_code)) {
            return ret;
        }

        // skip the start code.
        if (nb_start_code > 0) {
            stream->skip(nb_start_code);
        }
        
        // the NALU start bytes.
        char* p = stream->data() + stream->pos();
        
        // get the last matched NALU
        while (!stream->empty()) {
            if (srs_avc_startswith_annexb(stream, NULL)) {
                break;
            }
            
            stream->skip(1);
        }
        
        char* pp = stream->data() + stream->pos();
        
        // skip the empty.
        if (pp - p <= 0) {
            continue;
        }
        
        // got the NALU.
        if ((ret = sample->add_sample_unit(p, pp - p)) != ERROR_SUCCESS) {
            srs_error("annexb add video sample failed. ret=%d", ret);
            return ret;
        }
    }
    
    return ret;
}

int SrsAvcAacCodec::avc_demux_ibmf_format(SrsStream* stream, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
    
    int PictureLength = stream->size() - stream->pos();
    
    // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
    // 5.2.4.1 AVC decoder configuration record
    // 5.2.4.1.2 Semantics
    // The value of this field shall be one of 0, 1, or 3 corresponding to a
    // length encoded with 1, 2, or 4 bytes, respectively.
    srs_assert(NAL_unit_length != 2);
    
    // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 20
    for (int i = 0; i < PictureLength;) {
        // unsigned int((NAL_unit_length+1)*8) NALUnitLength;
        if (!stream->require(NAL_unit_length + 1)) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("avc decode NALU size failed. ret=%d", ret);
            return ret;
        }
        int32_t NALUnitLength = 0;
        if (NAL_unit_length == 3) {
            NALUnitLength = stream->read_4bytes();
        } else if (NAL_unit_length == 1) {
            NALUnitLength = stream->read_2bytes();
        } else {
            NALUnitLength = stream->read_1bytes();
        }
        
        // maybe stream is invalid format.
        // see: https://github.com/simple-rtmp-server/srs/issues/183
        if (NALUnitLength < 0) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("maybe stream is AnnexB format. ret=%d", ret);
            return ret;
        }
        
        // NALUnit
        if (!stream->require(NALUnitLength)) {
            ret = ERROR_HLS_DECODE_ERROR;
            srs_error("avc decode NALU data failed. ret=%d", ret);
            return ret;
        }
        // 7.3.1 NAL unit syntax, H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
        if ((ret = sample->add_sample_unit(stream->data() + stream->pos(), NALUnitLength)) != ERROR_SUCCESS) {
            srs_error("avc add video sample failed. ret=%d", ret);
            return ret;
        }
        stream->skip(NALUnitLength);
        
        i += NAL_unit_length + 1 + NALUnitLength;
    }
    
    return ret;
}

// following is generated by src/kernel/srs_kernel_file.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 <srs_kernel_file.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#endif

#include <fcntl.h>
#include <sstream>
#include <sys/uio.h>
using namespace std;

//#include <srs_kernel_log.hpp>
//#include <srs_kernel_error.hpp>

SrsFileWriter::SrsFileWriter()
{
    fd = -1;
}

SrsFileWriter::~SrsFileWriter()
{
    close();
}

int SrsFileWriter::open(string file)
{
    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);
        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) {
        ret = ERROR_SYSTEM_FILE_OPENE;
        srs_error("open file %s failed. ret=%d", file.c_str(), ret);
        return ret;
    }
    
    _file = file;
    
    return ret;
}

int SrsFileWriter::open_append(string file)
{
    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);
        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) {
        ret = ERROR_SYSTEM_FILE_OPENE;
        srs_error("open file %s failed. ret=%d", file.c_str(), ret);
        return ret;
    }
    
    _file = file;
    
    return ret;
}

void SrsFileWriter::close()
{
    int ret = ERROR_SUCCESS;
    
    if (fd < 0) {
        return;
    }
    
    if (::close(fd) < 0) {
        ret = ERROR_SYSTEM_FILE_CLOSE;
        srs_error("close file %s failed. ret=%d", _file.c_str(), ret);
        return;
    }
    fd = -1;
    
    return;
}

bool SrsFileWriter::is_open()
{
    return fd > 0;
}

void SrsFileWriter::lseek(int64_t offset)
{
    ::lseek(fd, (off_t)offset, SEEK_SET);
}

int64_t SrsFileWriter::tellg()
{
    return (int64_t)::lseek(fd, 0, SEEK_CUR);
}

int SrsFileWriter::write(void* buf, size_t count, ssize_t* pnwrite)
{
    int ret = ERROR_SUCCESS;
    
    ssize_t nwrite;
    // 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);
        return ret;
    }
    
    if (pnwrite != NULL) {
        *pnwrite = nwrite;
    }
    
    return ret;
}

int SrsFileWriter::writev(iovec* iov, int iovcnt, ssize_t* pnwrite)
{
    int ret = ERROR_SUCCESS;
    
    ssize_t nwrite = 0;
    for (int i = 0; i < iovcnt; i++) {
        iovec* piov = iov + i;
        ssize_t this_nwrite = 0;
        if ((ret = write(piov->iov_base, piov->iov_len, &this_nwrite)) != ERROR_SUCCESS) {
            return ret;
        }
        nwrite += this_nwrite;
    }
    
    if (pnwrite) {
        *pnwrite = nwrite;
    }
    
    return ret;
}

SrsFileReader::SrsFileReader()
{
    fd = -1;
}

SrsFileReader::~SrsFileReader()
{
    close();
}

int SrsFileReader::open(string file)
{
    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);
        return ret;
    }

    if ((fd = ::open(file.c_str(), O_RDONLY)) < 0) {
        ret = ERROR_SYSTEM_FILE_OPENE;
        srs_error("open file %s failed. ret=%d", file.c_str(), ret);
        return ret;
    }
    
    _file = file;
    
    return ret;
}

void SrsFileReader::close()
{
    int ret = ERROR_SUCCESS;
    
    if (fd < 0) {
        return;
    }
    
    if (::close(fd) < 0) {
        ret = ERROR_SYSTEM_FILE_CLOSE;
        srs_error("close file %s failed. ret=%d", _file.c_str(), ret);
        return;
    }
    fd = -1;
    
    return;
}

bool SrsFileReader::is_open()
{
    return fd > 0;
}

int64_t SrsFileReader::tellg()
{
    return (int64_t)::lseek(fd, 0, SEEK_CUR);
}

void SrsFileReader::skip(int64_t size)
{
    ::lseek(fd, (off_t)size, SEEK_CUR);
}

int64_t SrsFileReader::lseek(int64_t offset)
{
    return (int64_t)::lseek(fd, (off_t)offset, SEEK_SET);
}

int64_t SrsFileReader::filesize()
{
    int64_t cur = tellg();
    int64_t size = (int64_t)::lseek(fd, 0, SEEK_END);
    ::lseek(fd, (off_t)cur, SEEK_SET);
    return size;
}

int SrsFileReader::read(void* buf, size_t count, ssize_t* pnread)
{
    int ret = ERROR_SUCCESS;
    
    ssize_t nread;
    // 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);
        return ret;
    }
    
    if (nread == 0) {
        ret = ERROR_SYSTEM_FILE_EOF;
        return ret;
    }
    
    if (pnread != NULL) {
        *pnread = nread;
    }
    
    return ret;
}

// following is generated by src/kernel/srs_kernel_consts.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 <srs_kernel_consts.hpp>


// following is generated by src/kernel/srs_kernel_aac.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 <srs_kernel_aac.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#endif

#include <fcntl.h>
#include <sstream>
using namespace std;

//#include <srs_kernel_log.hpp>
//#include <srs_kernel_error.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_kernel_file.hpp>
//#include <srs_kernel_codec.hpp>

SrsAacEncoder::SrsAacEncoder()
{
    _fs = NULL;
    got_sequence_header = false;
    tag_stream = new SrsStream();
    aac_object = SrsAacObjectTypeReserved;
}

SrsAacEncoder::~SrsAacEncoder()
{
    srs_freep(tag_stream);
}

int SrsAacEncoder::initialize(SrsFileWriter* fs)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(fs);
    
    if (!fs->is_open()) {
        ret = ERROR_KERNEL_AAC_STREAM_CLOSED;
        srs_warn("stream is not open for encoder. ret=%d", ret);
        return ret;
    }
    
    _fs = fs;
    
    return ret;
}

int SrsAacEncoder::write_audio(int64_t timestamp, char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(data);
    
    timestamp &= 0x7fffffff;
    
    SrsStream* stream = tag_stream;
    if ((ret = stream->initialize(data, size)) != ERROR_SUCCESS) {
        return ret;
    }

    // audio decode
    if (!stream->require(1)) {
        ret = ERROR_AAC_DECODE_ERROR;
        srs_error("aac decode audio sound_format failed. ret=%d", ret);
        return ret;
    }
    
    // @see: E.4.2 Audio Tags, video_file_format_spec_v10_1.pdf, page 76
    int8_t sound_format = stream->read_1bytes();
    
    // @see: SrsAvcAacCodec::audio_aac_demux
    //int8_t sound_type = sound_format & 0x01;
    //int8_t sound_size = (sound_format >> 1) & 0x01;
    //int8_t sound_rate = (sound_format >> 2) & 0x03;
    sound_format = (sound_format >> 4) & 0x0f;
    
    if ((SrsCodecAudio)sound_format != SrsCodecAudioAAC) {
        ret = ERROR_AAC_DECODE_ERROR;
        srs_error("aac required, format=%d. ret=%d", sound_format, ret);
        return ret;
    }

    if (!stream->require(1)) {
        ret = ERROR_AAC_DECODE_ERROR;
        srs_error("aac decode aac_packet_type failed. ret=%d", ret);
        return ret;
    }
    
    SrsCodecAudioType aac_packet_type = (SrsCodecAudioType)stream->read_1bytes();
    if (aac_packet_type == SrsCodecAudioTypeSequenceHeader) {
        // AudioSpecificConfig
        // 1.6.2.1 AudioSpecificConfig, in aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 33.
        //
        // only need to decode the first 2bytes:
        // audioObjectType, 5bits.
        // samplingFrequencyIndex, aac_sample_rate, 4bits.
        // channelConfiguration, aac_channels, 4bits
        if (!stream->require(2)) {
            ret = ERROR_AAC_DECODE_ERROR;
            srs_error("aac decode sequence header failed. ret=%d", ret);
            return ret;
        }
        
        int8_t audioObjectType = stream->read_1bytes();
        aac_sample_rate = stream->read_1bytes();
        
        aac_channels = (aac_sample_rate >> 3) & 0x0f;
        aac_sample_rate = ((audioObjectType << 1) & 0x0e) | ((aac_sample_rate >> 7) & 0x01);
        
        audioObjectType = (audioObjectType >> 3) & 0x1f;
        aac_object = (SrsAacObjectType)audioObjectType;
        
        got_sequence_header = true;
        
        return ret;
    }
    
    if (!got_sequence_header) {
        ret = ERROR_AAC_DECODE_ERROR;
        srs_error("aac no sequence header. ret=%d", ret);
        return ret;
    }
    
    // the left is the aac raw frame data.
    int16_t aac_raw_length = stream->size() - stream->pos();
    
    // 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
    // byte_alignment()
    
    // adts_fixed_header:
    //      12bits syncword,
    //      16bits left.
    // adts_variable_header:
    //      28bits
    //      12+16+28=56bits
    // adts_error_check:
    //      16bits if protection_absent
    //      56+16=72bits
    // if protection_absent:
    //      require(7bytes)=56bits
    // else
    //      require(9bytes)=72bits
    char aac_fixed_header[7];
    if(true) {
        char* pp = aac_fixed_header;
        int16_t aac_frame_length = aac_raw_length + 7;
        
        // Syncword 12 bslbf
        *pp++ = 0xff;
        // 4bits left.
        // adts_fixed_header(), 1.A.2.2.1 Fixed Header of ADTS
        // ID 1 bslbf
        // Layer 2 uimsbf
        // protection_absent 1 bslbf
        *pp++ = 0xf1;
        
        // profile 2 uimsbf
        // sampling_frequency_index 4 uimsbf
        // private_bit 1 bslbf
        // channel_configuration 3 uimsbf
        // original/copy 1 bslbf
        // home 1 bslbf
        SrsAacProfile aac_profile = srs_codec_aac_rtmp2ts(aac_object);
        *pp++ = ((aac_profile << 6) & 0xc0) | ((aac_sample_rate << 2) & 0x3c) | ((aac_channels >> 2) & 0x01);
        // 4bits left.
        // adts_variable_header(), 1.A.2.2.2 Variable Header of ADTS
        // copyright_identification_bit 1 bslbf
        // copyright_identification_start 1 bslbf
        *pp++ = ((aac_channels << 6) & 0xc0) | ((aac_frame_length >> 11) & 0x03);
        
        // aac_frame_length 13 bslbf: Length of the frame including headers and error_check in bytes.
        // use the left 2bits as the 13 and 12 bit,
        // the aac_frame_length is 13bits, so we move 13-2=11.
        *pp++ = aac_frame_length >> 3;
        // adts_buffer_fullness 11 bslbf
        *pp++ = (aac_frame_length << 5) & 0xe0;
        
        // no_raw_data_blocks_in_frame 2 uimsbf
        *pp++ = 0xfc;
    }
    
    // write 7bytes fixed header.
    if ((ret = _fs->write(aac_fixed_header, 7, NULL)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // write aac frame body.
    if ((ret = _fs->write(data + stream->pos(), aac_raw_length, NULL)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

// following is generated by src/kernel/srs_kernel_mp3.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 <srs_kernel_mp3.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#endif

#include <fcntl.h>
#include <sstream>
using namespace std;

//#include <srs_kernel_log.hpp>
//#include <srs_kernel_error.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_kernel_file.hpp>
//#include <srs_kernel_codec.hpp>

SrsMp3Encoder::SrsMp3Encoder()
{
    _fs = NULL;
    tag_stream = new SrsStream();
}

SrsMp3Encoder::~SrsMp3Encoder()
{
    srs_freep(tag_stream);
}

int SrsMp3Encoder::initialize(SrsFileWriter* fs)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(fs);
    
    if (!fs->is_open()) {
        ret = ERROR_KERNEL_MP3_STREAM_CLOSED;
        srs_warn("stream is not open for encoder. ret=%d", ret);
        return ret;
    }
    
    _fs = fs;
    
    return ret;
}

int SrsMp3Encoder::write_header()
{
    char id3[] = {
        (char)0x49, (char)0x44, (char)0x33, // ID3
        (char)0x03, (char)0x00, // version
        (char)0x00, // flags
        (char)0x00, (char)0x00, (char)0x00, (char)0x0a, // size
        
        (char)0x00, (char)0x00, (char)0x00, (char)0x00, // FrameID
        (char)0x00, (char)0x00, (char)0x00, (char)0x00, // FrameSize
        (char)0x00, (char)0x00 // Flags
    };
    return _fs->write(id3, sizeof(id3), NULL);
}

int SrsMp3Encoder::write_audio(int64_t timestamp, char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(data);
    
    timestamp &= 0x7fffffff;
    
    SrsStream* stream = tag_stream;
    if ((ret = stream->initialize(data, size)) != ERROR_SUCCESS) {
        return ret;
    }

    // audio decode
    if (!stream->require(1)) {
        ret = ERROR_MP3_DECODE_ERROR;
        srs_error("mp3 decode audio sound_format failed. ret=%d", ret);
        return ret;
    }
    
    // @see: E.4.2 Audio Tags, video_file_format_spec_v10_1.pdf, page 76
    int8_t sound_format = stream->read_1bytes();
    
    // @see: SrsAvcAacCodec::audio_aac_demux
    //int8_t sound_type = sound_format & 0x01;
    //int8_t sound_size = (sound_format >> 1) & 0x01;
    //int8_t sound_rate = (sound_format >> 2) & 0x03;
    sound_format = (sound_format >> 4) & 0x0f;
    
    if ((SrsCodecAudio)sound_format != SrsCodecAudioMP3) {
        ret = ERROR_MP3_DECODE_ERROR;
        srs_error("mp3 required, format=%d. ret=%d", sound_format, ret);
        return ret;
    }

    if (!stream->require(1)) {
        ret = ERROR_MP3_DECODE_ERROR;
        srs_error("mp3 decode aac_packet_type failed. ret=%d", ret);
        return ret;
    }
    
    return _fs->write(data + stream->pos(), size - stream->pos(), NULL);
}

// following is generated by src/kernel/srs_kernel_ts.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 <srs_kernel_ts.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#endif

#include <fcntl.h>
#include <sstream>
using namespace std;

//#include <srs_kernel_log.hpp>
//#include <srs_kernel_error.hpp>
//#include <srs_kernel_file.hpp>
//#include <srs_kernel_codec.hpp>
//#include <srs_kernel_buffer.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_core_autofree.hpp>

// in ms, for HLS aac sync time.
#define SRS_CONF_DEFAULT_AAC_SYNC 100

// @see: ngx_rtmp_hls_audio
/* We assume here AAC frame size is 1024
 * Need to handle AAC frames with frame size of 960 */
#define _SRS_AAC_SAMPLE_SIZE 1024

// the mpegts header specifed the video/audio pid.
#define TS_PMT_NUMBER 1
#define TS_PMT_PID 0x1001
#define TS_VIDEO_AVC_PID 0x100
#define TS_AUDIO_AAC_PID 0x101
#define TS_AUDIO_MP3_PID 0x102

string srs_ts_stream2string(SrsTsStream stream)
{
    switch (stream) {
        case SrsTsStreamReserved: return "Reserved";
        case SrsTsStreamAudioMp3: return "MP3";
        case SrsTsStreamAudioAAC: return "AAC";
        case SrsTsStreamAudioAC3: return "AC3";
        case SrsTsStreamAudioDTS: return "AudioDTS";
        case SrsTsStreamVideoH264: return "H.264";
        case SrsTsStreamVideoMpeg4: return "MP4";
        case SrsTsStreamAudioMpeg4: return "MP4A";
        default: return "Other";
    }
}

SrsTsChannel::SrsTsChannel()
{
    pid = 0;
    apply = SrsTsPidApplyReserved;
    stream = SrsTsStreamReserved;
    msg = NULL;
    continuity_counter = 0;
    context = NULL;
}

SrsTsChannel::~SrsTsChannel()
{
    srs_freep(msg);
}

SrsTsMessage::SrsTsMessage(SrsTsChannel* c, SrsTsPacket* p)
{
    channel = c;
    packet = p;

    dts = pts = 0;
    sid = (SrsTsPESStreamId)0x00;
    continuity_counter = 0;
    PES_packet_length = 0;
    payload = new SrsSimpleBuffer();
    is_discontinuity = false;

    start_pts = 0;
    write_pcr = false;
}

SrsTsMessage::~SrsTsMessage()
{
    srs_freep(payload);
}

int SrsTsMessage::dump(SrsStream* stream, int* pnb_bytes)
{
    int ret = ERROR_SUCCESS;

    if (stream->empty()) {
        return ret;
    }

    // xB
    int nb_bytes = stream->size() - stream->pos();
    if (PES_packet_length > 0) {
        nb_bytes = srs_min(nb_bytes, PES_packet_length - payload->length());
    }

    if (nb_bytes > 0) {
        if (!stream->require(nb_bytes)) {
            ret = ERROR_STREAM_CASTER_TS_PSE;
            srs_error("ts: dump PSE bytes failed, requires=%dB. ret=%d", nb_bytes, ret);
            return ret;
        }

        payload->append(stream->data() + stream->pos(), nb_bytes);
        stream->skip(nb_bytes);
    }

    *pnb_bytes = nb_bytes;

    return ret;
}

bool SrsTsMessage::completed(int8_t payload_unit_start_indicator)
{
    if (PES_packet_length == 0) {
        return payload_unit_start_indicator;
    }
    return payload->length() >= PES_packet_length;
}

bool SrsTsMessage::fresh()
{
    return payload->length() == 0;
}

bool SrsTsMessage::is_audio()
{
    return ((sid >> 5) & 0x07) == SrsTsPESStreamIdAudioChecker;
}

bool SrsTsMessage::is_video()
{
    return ((sid >> 4) & 0x0f) == SrsTsPESStreamIdVideoChecker;
}

int SrsTsMessage::stream_number()
{
    if (is_audio()) {
        return sid & 0x1f;
    } else if (is_video()) {
        return sid & 0x0f;
    }
    return -1;
}

SrsTsMessage* SrsTsMessage::detach()
{
    // @remark the packet cannot be used, but channel is ok.
    SrsTsMessage* cp = new SrsTsMessage(channel, NULL);
    cp->start_pts = start_pts;
    cp->write_pcr = write_pcr;
    cp->is_discontinuity = is_discontinuity;
    cp->dts = dts;
    cp->pts = pts;
    cp->sid = sid;
    cp->PES_packet_length = PES_packet_length;
    cp->continuity_counter = continuity_counter;
    cp->payload = payload;
    payload = NULL;
    return cp;
}

ISrsTsHandler::ISrsTsHandler()
{
}

ISrsTsHandler::~ISrsTsHandler()
{
}

SrsTsContext::SrsTsContext()
{
    pure_audio = false;
    vcodec = SrsCodecVideoReserved;
    acodec = SrsCodecAudioReserved1;
}

SrsTsContext::~SrsTsContext()
{
    std::map<int, SrsTsChannel*>::iterator it;
    for (it = pids.begin(); it != pids.end(); ++it) {
        SrsTsChannel* channel = it->second;
        srs_freep(channel);
    }
    pids.clear();
}

bool SrsTsContext::is_pure_audio()
{
    return pure_audio;
}

void SrsTsContext::on_pmt_parsed()
{
    pure_audio = true;
    
    std::map<int, SrsTsChannel*>::iterator it;
    for (it = pids.begin(); it != pids.end(); ++it) {
        SrsTsChannel* channel = it->second;
        if (channel->apply == SrsTsPidApplyVideo) {
            pure_audio = false;
        }
    }
}

void SrsTsContext::reset()
{
    vcodec = SrsCodecVideoReserved;
    acodec = SrsCodecAudioReserved1;
}

SrsTsChannel* SrsTsContext::get(int pid)
{
    if (pids.find(pid) == pids.end()) {
        return NULL;
    }
    return pids[pid];
}

void SrsTsContext::set(int pid, SrsTsPidApply apply_pid, SrsTsStream stream)
{
    SrsTsChannel* channel = NULL;

    if (pids.find(pid) == pids.end()) {
        channel = new SrsTsChannel();
        channel->context = this;
        pids[pid] = channel;
    } else {
        channel = pids[pid];
    }

    channel->pid = pid;
    channel->apply = apply_pid;
    channel->stream = stream;
}

int SrsTsContext::decode(SrsStream* stream, ISrsTsHandler* handler)
{
    int ret = ERROR_SUCCESS;

    // parse util EOF of stream.
    // for example, parse multiple times for the PES_packet_length(0) packet.
    while (!stream->empty()) {
        SrsTsPacket* packet = new SrsTsPacket(this);
        SrsAutoFree(SrsTsPacket, packet);

        SrsTsMessage* msg = NULL;
        if ((ret = packet->decode(stream, &msg)) != ERROR_SUCCESS) {
            srs_error("mpegts: decode ts packet failed. ret=%d", ret);
            return ret;
        }

        if (!msg) {
            continue;
        }
        SrsAutoFree(SrsTsMessage, msg);

        if ((ret = handler->on_ts_message(msg)) != ERROR_SUCCESS) {
            srs_error("mpegts: handler ts message failed. ret=%d", ret);
            return ret;
        }
    }

    return ret;
}

int SrsTsContext::encode(SrsFileWriter* writer, SrsTsMessage* msg, SrsCodecVideo vc, SrsCodecAudio ac)
{
    int ret = ERROR_SUCCESS;

    SrsTsStream vs, as;
    int16_t video_pid = 0, audio_pid = 0;
    switch (vc) {
        case SrsCodecVideoAVC: 
            vs = SrsTsStreamVideoH264; 
            video_pid = TS_VIDEO_AVC_PID;
            break;
        case SrsCodecVideoDisabled:
            vs = SrsTsStreamReserved;
            break;
        case SrsCodecVideoReserved:
        case SrsCodecVideoReserved1:
        case SrsCodecVideoReserved2:
        case SrsCodecVideoSorensonH263:
        case SrsCodecVideoScreenVideo:
        case SrsCodecVideoOn2VP6:
        case SrsCodecVideoOn2VP6WithAlphaChannel:
        case SrsCodecVideoScreenVideoVersion2:
            vs = SrsTsStreamReserved;
            break;
    }
    switch (ac) {
        case SrsCodecAudioAAC:
            as = SrsTsStreamAudioAAC; 
            audio_pid = TS_AUDIO_AAC_PID;
            break;
        case SrsCodecAudioMP3:
            as = SrsTsStreamAudioMp3; 
            audio_pid = TS_AUDIO_MP3_PID;
            break;
        case SrsCodecAudioDisabled:
            as = SrsTsStreamReserved;
            break;
        case SrsCodecAudioReserved1:
        case SrsCodecAudioLinearPCMPlatformEndian:
        case SrsCodecAudioADPCM:
        case SrsCodecAudioLinearPCMLittleEndian:
        case SrsCodecAudioNellymoser16kHzMono:
        case SrsCodecAudioNellymoser8kHzMono:
        case SrsCodecAudioNellymoser:
        case SrsCodecAudioReservedG711AlawLogarithmicPCM:
        case SrsCodecAudioReservedG711MuLawLogarithmicPCM:
        case SrsCodecAudioReserved:
        case SrsCodecAudioSpeex:
        case SrsCodecAudioReservedMP3_8kHz:
        case SrsCodecAudioReservedDeviceSpecificSound:
            as = SrsTsStreamReserved;
            break;
    }
    
    if (as == SrsTsStreamReserved && vs == SrsTsStreamReserved) {
        ret = ERROR_HLS_NO_STREAM;
        srs_error("hls: no video or audio stream, vcodec=%d, acodec=%d. ret=%d", vc, ac, ret);
        return ret;
    }
    
    // when any codec changed, write PAT/PMT table.
    if (vcodec != vc || acodec != ac) {
        vcodec = vc;
        acodec = ac;
        if ((ret = encode_pat_pmt(writer, video_pid, vs, audio_pid, as)) != ERROR_SUCCESS) {
            return ret;
        }
    }

    // encode the media frame to PES packets over TS.
    if (msg->is_audio()) {
        return encode_pes(writer, msg, audio_pid, as, vs == SrsTsStreamReserved);
    } else {
        return encode_pes(writer, msg, video_pid, vs, vs == SrsTsStreamReserved);
    }
}

int SrsTsContext::encode_pat_pmt(SrsFileWriter* writer, int16_t vpid, SrsTsStream vs, int16_t apid, SrsTsStream as)
{
    int ret = ERROR_SUCCESS;
    
    if (vs != SrsTsStreamVideoH264 && as != SrsTsStreamAudioAAC && as != SrsTsStreamAudioMp3) {
        ret = ERROR_HLS_NO_STREAM;
        srs_error("hls: no pmt pcr pid, vs=%d, as=%d. ret=%d", vs, as, ret);
        return ret;
    }

    int16_t pmt_number = TS_PMT_NUMBER;
    int16_t pmt_pid = TS_PMT_PID;
    if (true) {
        SrsTsPacket* pkt = SrsTsPacket::create_pat(this, pmt_number, pmt_pid);
        SrsAutoFree(SrsTsPacket, pkt);

        char* buf = new char[SRS_TS_PACKET_SIZE];
        SrsAutoFree(char, buf);

        // set the left bytes with 0xFF.
        int nb_buf = pkt->size();
        srs_assert(nb_buf < SRS_TS_PACKET_SIZE);
        memset(buf + nb_buf, 0xFF, SRS_TS_PACKET_SIZE - nb_buf);

        SrsStream stream;
        if ((ret = stream.initialize(buf, nb_buf)) != ERROR_SUCCESS) {
            return ret;
        }
        if ((ret = pkt->encode(&stream)) != ERROR_SUCCESS) {
            srs_error("ts encode ts packet failed. ret=%d", ret);
            return ret;
        }
        if ((ret = writer->write(buf, SRS_TS_PACKET_SIZE, NULL)) != ERROR_SUCCESS) {
            srs_error("ts write ts packet failed. ret=%d", ret);
            return ret;
        }
    }
    if (true) {
        SrsTsPacket* pkt = SrsTsPacket::create_pmt(this, pmt_number, pmt_pid, vpid, vs, apid, as);
        SrsAutoFree(SrsTsPacket, pkt);

        char* buf = new char[SRS_TS_PACKET_SIZE];
        SrsAutoFree(char, buf);

        // set the left bytes with 0xFF.
        int nb_buf = pkt->size();
        srs_assert(nb_buf < SRS_TS_PACKET_SIZE);
        memset(buf + nb_buf, 0xFF, SRS_TS_PACKET_SIZE - nb_buf);

        SrsStream stream;
        if ((ret = stream.initialize(buf, nb_buf)) != ERROR_SUCCESS) {
            return ret;
        }
        if ((ret = pkt->encode(&stream)) != ERROR_SUCCESS) {
            srs_error("ts encode ts packet failed. ret=%d", ret);
            return ret;
        }
        if ((ret = writer->write(buf, SRS_TS_PACKET_SIZE, NULL)) != ERROR_SUCCESS) {
            srs_error("ts write ts packet failed. ret=%d", ret);
            return ret;
        }
    }

    return ret;
}

int SrsTsContext::encode_pes(SrsFileWriter* writer, SrsTsMessage* msg, int16_t pid, SrsTsStream sid, bool pure_audio)
{
    int ret = ERROR_SUCCESS;

    if (msg->payload->length() == 0) {
        return ret;
    }

    if (sid != SrsTsStreamVideoH264 && sid != SrsTsStreamAudioMp3 && sid != SrsTsStreamAudioAAC) {
        srs_info("ts: ignore the unknown stream, sid=%d", sid);
        return ret;
    }

    SrsTsChannel* channel = get(pid);
    srs_assert(channel);

    char* start = msg->payload->bytes();
    char* end = start + msg->payload->length();
    char* p = start;

    while (p < end) {
        SrsTsPacket* pkt = NULL;
        if (p == start) {
            // for pure audio stream, always write pcr.
            bool write_pcr = msg->write_pcr;
            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
            int64_t pcr = write_pcr? msg->dts : -1;
            
            // TODO: FIXME: finger it why use discontinuity of msg.
            pkt = SrsTsPacket::create_pes_first(this, 
                pid, msg->sid, channel->continuity_counter++, msg->is_discontinuity,
                pcr, msg->dts, msg->pts, msg->payload->length()
            );
        } else {
            pkt = SrsTsPacket::create_pes_continue(this, 
                pid, msg->sid, channel->continuity_counter++
            );
        }
        SrsAutoFree(SrsTsPacket, pkt);

        char* buf = new char[SRS_TS_PACKET_SIZE];
        SrsAutoFree(char, buf);

        // set the left bytes with 0xFF.
        int nb_buf = pkt->size();
        srs_assert(nb_buf < SRS_TS_PACKET_SIZE);

        int left = (int)srs_min(end - p, SRS_TS_PACKET_SIZE - nb_buf);
        int nb_stuffings = SRS_TS_PACKET_SIZE - nb_buf - left;
        if (nb_stuffings > 0) {
            // set all bytes to stuffings.
            memset(buf, 0xFF, SRS_TS_PACKET_SIZE);

            // padding with stuffings.
            pkt->padding(nb_stuffings);

            // size changed, recalc it.
            nb_buf = pkt->size();
            srs_assert(nb_buf < SRS_TS_PACKET_SIZE);

            left = (int)srs_min(end - p, SRS_TS_PACKET_SIZE - nb_buf);
            nb_stuffings = SRS_TS_PACKET_SIZE - nb_buf - left;
            srs_assert(nb_stuffings == 0);
        }
        memcpy(buf + nb_buf, p, left);
        p += left;

        SrsStream stream;
        if ((ret = stream.initialize(buf, nb_buf)) != ERROR_SUCCESS) {
            return ret;
        }
        if ((ret = pkt->encode(&stream)) != ERROR_SUCCESS) {
            srs_error("ts encode ts packet failed. ret=%d", ret);
            return ret;
        }
        if ((ret = writer->write(buf, SRS_TS_PACKET_SIZE, NULL)) != ERROR_SUCCESS) {
            srs_error("ts write ts packet failed. ret=%d", ret);
            return ret;
        }
    }

    return ret;
}

SrsTsPacket::SrsTsPacket(SrsTsContext* c)
{
    context = c;

    sync_byte = 0;
    transport_error_indicator = 0;
    payload_unit_start_indicator = 0;
    transport_priority = 0;
    pid = SrsTsPidPAT;
    transport_scrambling_control = SrsTsScrambledDisabled;
    adaption_field_control = SrsTsAdaptationFieldTypeReserved;
    continuity_counter = 0;
    adaptation_field = NULL;
    payload = NULL;
}

SrsTsPacket::~SrsTsPacket()
{
    srs_freep(adaptation_field);
    srs_freep(payload);
}

int SrsTsPacket::decode(SrsStream* stream, SrsTsMessage** ppmsg)
{
    int ret = ERROR_SUCCESS;

    int pos = stream->pos();

    // 4B ts packet header.
    if (!stream->require(4)) {
        ret = ERROR_STREAM_CASTER_TS_HEADER;
        srs_error("ts: demux header failed. ret=%d", ret);
        return ret;
    }

    sync_byte = stream->read_1bytes();
    if (sync_byte != 0x47) {
        ret = ERROR_STREAM_CASTER_TS_SYNC_BYTE;
        srs_error("ts: sync_bytes must be 0x47, actual=%#x. ret=%d", sync_byte, ret);
        return ret;
    }
    
    int16_t pidv = stream->read_2bytes();
    transport_error_indicator = (pidv >> 15) & 0x01;
    payload_unit_start_indicator = (pidv >> 14) & 0x01;
    transport_priority = (pidv >> 13) & 0x01;
    pid = (SrsTsPid)(pidv & 0x1FFF);

    int8_t ccv = stream->read_1bytes();
    transport_scrambling_control = (SrsTsScrambled)((ccv >> 6) & 0x03);
    adaption_field_control = (SrsTsAdaptationFieldType)((ccv >> 4) & 0x03);
    continuity_counter = ccv & 0x0F;

    // TODO: FIXME: create pids map when got new pid.
    
    srs_info("ts: header sync=%#x error=%d unit_start=%d priotiry=%d pid=%d scrambling=%d adaption=%d counter=%d",
        sync_byte, transport_error_indicator, payload_unit_start_indicator, transport_priority, pid,
        transport_scrambling_control, adaption_field_control, continuity_counter);

    // optional: adaptation field
    if (adaption_field_control == SrsTsAdaptationFieldTypeAdaptionOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth) {
        srs_freep(adaptation_field);
        adaptation_field = new SrsTsAdaptationField(this);

        if ((ret = adaptation_field->decode(stream)) != ERROR_SUCCESS) {
            srs_error("ts: demux af faield. ret=%d", ret);
            return ret;
        }
        srs_verbose("ts: demux af ok.");
    }

    // calc the user defined data size for payload.
    int nb_payload = SRS_TS_PACKET_SIZE - (stream->pos() - pos);

    // optional: payload.
    if (adaption_field_control == SrsTsAdaptationFieldTypePayloadOnly || adaption_field_control == SrsTsAdaptationFieldTypeBoth) {
        if (pid == SrsTsPidPAT) {
            // 2.4.4.3 Program association Table
            srs_freep(payload);
            payload = new SrsTsPayloadPAT(this);
        } else {
            SrsTsChannel* channel = context->get(pid);
            if (channel && channel->apply == SrsTsPidApplyPMT) {
                // 2.4.4.8 Program Map Table
                srs_freep(payload);
                payload = new SrsTsPayloadPMT(this);
            } else if (channel && (channel->apply == SrsTsPidApplyVideo || channel->apply == SrsTsPidApplyAudio)) {
                // 2.4.3.6 PES packet
                srs_freep(payload);
                payload = new SrsTsPayloadPES(this);
            } else {
                // left bytes as reserved.
                stream->skip(nb_payload);
            }
        }

        if (payload && (ret = payload->decode(stream, ppmsg)) != ERROR_SUCCESS) {
            srs_error("ts: demux payload failed. ret=%d", ret);
            return ret;
        }
    }

    return ret;
}

int SrsTsPacket::size()
{
    int sz = 4;
    
    sz += adaptation_field? adaptation_field->size() : 0;
    sz += payload? payload->size() : 0;

    return sz;
}

int SrsTsPacket::encode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // 4B ts packet header.
    if (!stream->require(4)) {
        ret = ERROR_STREAM_CASTER_TS_HEADER;
        srs_error("ts: mux header failed. ret=%d", ret);
        return ret;
    }

    stream->write_1bytes(sync_byte);

    int16_t pidv = pid & 0x1FFF;
    pidv |= (transport_priority << 13) & 0x2000;
    pidv |= (transport_error_indicator << 15) & 0x8000;
    pidv |= (payload_unit_start_indicator << 14) & 0x4000;
    stream->write_2bytes(pidv);

    int8_t ccv = continuity_counter & 0x0F;
    ccv |= (transport_scrambling_control << 6) & 0xC0;
    ccv |= (adaption_field_control << 4) & 0x30;
    stream->write_1bytes(ccv);
    
    srs_info("ts: header sync=%#x error=%d unit_start=%d priotiry=%d pid=%d scrambling=%d adaption=%d counter=%d",
        sync_byte, transport_error_indicator, payload_unit_start_indicator, transport_priority, pid,
        transport_scrambling_control, adaption_field_control, continuity_counter);

    // optional: adaptation field
    if (adaptation_field) {
        if ((ret = adaptation_field->encode(stream)) != ERROR_SUCCESS) {
            srs_error("ts: mux af faield. ret=%d", ret);
            return ret;
        }
        srs_verbose("ts: mux af ok.");
    }

    // optional: payload.
    if (payload) {
        if ((ret = payload->encode(stream)) != ERROR_SUCCESS) {
            srs_error("ts: mux payload failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("ts: mux payload ok.");
    }

    return ret;
}

void SrsTsPacket::padding(int nb_stuffings)
{
    if (!adaptation_field) {
        SrsTsAdaptationField* af = new SrsTsAdaptationField(this);
        adaptation_field = af;

        af->adaption_field_length = 0; // calc in size.
        af->discontinuity_indicator = 0;
        af->random_access_indicator = 0;
        af->elementary_stream_priority_indicator = 0;
        af->PCR_flag = 0;
        af->OPCR_flag = 0;
        af->splicing_point_flag = 0;
        af->transport_private_data_flag = 0;
        af->adaptation_field_extension_flag = 0;

        // consume the af size if possible.
        nb_stuffings = srs_max(0, nb_stuffings - af->size());
    }

    adaptation_field->nb_af_reserved = nb_stuffings;

    // set payload with af.
    if (adaption_field_control == SrsTsAdaptationFieldTypePayloadOnly) {
        adaption_field_control = SrsTsAdaptationFieldTypeBoth;
    }
}

SrsTsPacket* SrsTsPacket::create_pat(SrsTsContext* context, int16_t pmt_number, int16_t pmt_pid)
{
    SrsTsPacket* pkt = new SrsTsPacket(context);
    pkt->sync_byte = 0x47;
    pkt->transport_error_indicator = 0;
    pkt->payload_unit_start_indicator = 1;
    pkt->transport_priority = 0;
    pkt->pid = SrsTsPidPAT;
    pkt->transport_scrambling_control = SrsTsScrambledDisabled;
    pkt->adaption_field_control = SrsTsAdaptationFieldTypePayloadOnly;
    pkt->continuity_counter = 0;
    pkt->adaptation_field = NULL;
    SrsTsPayloadPAT* pat = new SrsTsPayloadPAT(pkt);
    pkt->payload = pat;

    pat->pointer_field = 0;
    pat->table_id = SrsTsPsiIdPas;
    pat->section_syntax_indicator = 1;
    pat->section_length = 0; // calc in size.
    pat->transport_stream_id = 1;
    pat->version_number = 0;
    pat->current_next_indicator = 1;
    pat->section_number = 0;
    pat->last_section_number = 0;
    pat->programs.push_back(new SrsTsPayloadPATProgram(pmt_number, pmt_pid));
    pat->CRC_32 = 0; // calc in encode.
    return pkt;
}

SrsTsPacket* SrsTsPacket::create_pmt(SrsTsContext* context, int16_t pmt_number, int16_t pmt_pid, int16_t vpid, SrsTsStream vs, int16_t apid, SrsTsStream as)
{
    SrsTsPacket* pkt = new SrsTsPacket(context);
    pkt->sync_byte = 0x47;
    pkt->transport_error_indicator = 0;
    pkt->payload_unit_start_indicator = 1;
    pkt->transport_priority = 0;
    pkt->pid = (SrsTsPid)pmt_pid;
    pkt->transport_scrambling_control = SrsTsScrambledDisabled;
    pkt->adaption_field_control = SrsTsAdaptationFieldTypePayloadOnly;
    // TODO: FIXME: maybe should continuous in channel.
    pkt->continuity_counter = 0;
    pkt->adaptation_field = NULL;
    SrsTsPayloadPMT* pmt = new SrsTsPayloadPMT(pkt);
    pkt->payload = pmt;

    pmt->pointer_field = 0;
    pmt->table_id = SrsTsPsiIdPms;
    pmt->section_syntax_indicator = 1;
    pmt->section_length = 0; // calc in size.
    pmt->program_number = pmt_number;
    pmt->version_number = 0;
    pmt->current_next_indicator = 1;
    pmt->section_number = 0;
    pmt->last_section_number = 0;
    pmt->program_info_length = 0;
    
    // must got one valid codec.
    srs_assert(vs == SrsTsStreamVideoH264 || as == SrsTsStreamAudioAAC || as == SrsTsStreamAudioMp3);
    
    // if mp3 or aac specified, use audio to carry pcr.
    if (as == SrsTsStreamAudioAAC || as == SrsTsStreamAudioMp3) {
        // use audio to carray pcr by default.
        // for hls, there must be atleast one audio channel.
        pmt->PCR_PID = apid;
        pmt->infos.push_back(new SrsTsPayloadPMTESInfo(as, apid));
    }
    
    // if h.264 specified, use video to carry pcr.
    if (vs == SrsTsStreamVideoH264) {
        pmt->PCR_PID = vpid;
        pmt->infos.push_back(new SrsTsPayloadPMTESInfo(vs, vpid));
    }
    
    pmt->CRC_32 = 0; // calc in encode.
    return pkt;
}

SrsTsPacket* SrsTsPacket::create_pes_first(SrsTsContext* context, 
    int16_t pid, SrsTsPESStreamId sid, u_int8_t continuity_counter, bool discontinuity, 
    int64_t pcr, int64_t dts, int64_t pts, int size
) {
    SrsTsPacket* pkt = new SrsTsPacket(context);
    pkt->sync_byte = 0x47;
    pkt->transport_error_indicator = 0;
    pkt->payload_unit_start_indicator = 1;
    pkt->transport_priority = 0;
    pkt->pid = (SrsTsPid)pid;
    pkt->transport_scrambling_control = SrsTsScrambledDisabled;
    pkt->adaption_field_control = SrsTsAdaptationFieldTypePayloadOnly;
    pkt->continuity_counter = continuity_counter;
    pkt->adaptation_field = NULL;
    SrsTsPayloadPES* pes = new SrsTsPayloadPES(pkt);
    pkt->payload = pes;

    if (pcr >= 0) {
        SrsTsAdaptationField* af = new SrsTsAdaptationField(pkt);
        pkt->adaptation_field = af;
        pkt->adaption_field_control = SrsTsAdaptationFieldTypeBoth;

        af->adaption_field_length = 0; // calc in size.
        af->discontinuity_indicator = discontinuity;
        af->random_access_indicator = 0;
        af->elementary_stream_priority_indicator = 0;
        af->PCR_flag = 1;
        af->OPCR_flag = 0;
        af->splicing_point_flag = 0;
        af->transport_private_data_flag = 0;
        af->adaptation_field_extension_flag = 0;
        af->program_clock_reference_base = pcr;
        af->program_clock_reference_extension = 0;
    }

    pes->packet_start_code_prefix = 0x01;
    pes->stream_id = (u_int8_t)sid;
    pes->PES_packet_length = (size > 0xFFFF)? 0:size;
    pes->PES_scrambling_control = 0;
    pes->PES_priority = 0;
    pes->data_alignment_indicator = 0;
    pes->copyright = 0;
    pes->original_or_copy = 0;
    pes->PTS_DTS_flags = (dts == pts)? 0x02:0x03;
    pes->ESCR_flag = 0;
    pes->ES_rate_flag = 0;
    pes->DSM_trick_mode_flag = 0;
    pes->additional_copy_info_flag = 0;
    pes->PES_CRC_flag = 0;
    pes->PES_extension_flag = 0;
    pes->PES_header_data_length = 0; // calc in size.
    pes->pts = pts;
    pes->dts = dts;
    return pkt;
}

SrsTsPacket* SrsTsPacket::create_pes_continue(SrsTsContext* context, 
    int16_t pid, SrsTsPESStreamId sid, u_int8_t continuity_counter
) {
    SrsTsPacket* pkt = new SrsTsPacket(context);
    pkt->sync_byte = 0x47;
    pkt->transport_error_indicator = 0;
    pkt->payload_unit_start_indicator = 0;
    pkt->transport_priority = 0;
    pkt->pid = (SrsTsPid)pid;
    pkt->transport_scrambling_control = SrsTsScrambledDisabled;
    pkt->adaption_field_control = SrsTsAdaptationFieldTypePayloadOnly;
    pkt->continuity_counter = continuity_counter;
    pkt->adaptation_field = NULL;
    pkt->payload = NULL;

    return pkt;
}

SrsTsAdaptationField::SrsTsAdaptationField(SrsTsPacket* pkt)
{
    packet = pkt;

    adaption_field_length = 0;
    discontinuity_indicator = 0;
    random_access_indicator = 0;
    elementary_stream_priority_indicator = 0;
    PCR_flag = 0;
    OPCR_flag = 0;
    splicing_point_flag = 0;
    transport_private_data_flag = 0;
    adaptation_field_extension_flag = 0;
    program_clock_reference_base = 0;
    program_clock_reference_extension = 0;
    original_program_clock_reference_base = 0;
    original_program_clock_reference_extension = 0;
    splice_countdown = 0;
    transport_private_data_length = 0;
    transport_private_data = NULL;
    adaptation_field_extension_length = 0;
    ltw_flag = 0;
    piecewise_rate_flag = 0;
    seamless_splice_flag = 0;
    ltw_valid_flag = 0;
    ltw_offset = 0;
    piecewise_rate = 0;
    splice_type = 0;
    DTS_next_AU0 = 0;
    marker_bit0 = 0;
    DTS_next_AU1 = 0;
    marker_bit1 = 0;
    DTS_next_AU2 = 0;
    marker_bit2 = 0;
    nb_af_ext_reserved = 0;
    nb_af_reserved = 0;

    const1_value0 = 0x3F;
    const1_value1 = 0x1F;
    const1_value2 = 0x3F;
}

SrsTsAdaptationField::~SrsTsAdaptationField()
{
    srs_freep(transport_private_data);
}

int SrsTsAdaptationField::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    if (!stream->require(2)) {
        ret = ERROR_STREAM_CASTER_TS_AF;
        srs_error("ts: demux af failed. ret=%d", ret);
        return ret;
    }
    adaption_field_length = stream->read_1bytes();

    // When the adaptation_field_control value is '11', the value of the adaptation_field_length shall
    // be in the range 0 to 182. 
    if (packet->adaption_field_control == SrsTsAdaptationFieldTypeBoth && adaption_field_length > 182) {
        ret = ERROR_STREAM_CASTER_TS_AF;
        srs_error("ts: demux af length failed, must in [0, 182], actual=%d. ret=%d", adaption_field_length, ret);
        return ret;
    }
    // When the adaptation_field_control value is '10', the value of the adaptation_field_length shall
    // be 183.
    if (packet->adaption_field_control == SrsTsAdaptationFieldTypeAdaptionOnly && adaption_field_length != 183) {
        ret = ERROR_STREAM_CASTER_TS_AF;
        srs_error("ts: demux af length failed, must be 183, actual=%d. ret=%d", adaption_field_length, ret);
        return ret;
    }
    
    // no adaptation field.
    if (adaption_field_length == 0) {
        srs_info("ts: demux af empty.");
        return ret;
    }

    // the adaptation field start at here.
    int pos_af = stream->pos();
    int8_t tmpv = stream->read_1bytes();
    
    discontinuity_indicator              =   (tmpv >> 7) & 0x01;
    random_access_indicator              =   (tmpv >> 6) & 0x01;
    elementary_stream_priority_indicator =   (tmpv >> 5) & 0x01;
    PCR_flag                             =   (tmpv >> 4) & 0x01;
    OPCR_flag                            =   (tmpv >> 3) & 0x01;
    splicing_point_flag                  =   (tmpv >> 2) & 0x01;
    transport_private_data_flag          =   (tmpv >> 1) & 0x01;
    adaptation_field_extension_flag      =   tmpv & 0x01;
    
    if (PCR_flag) {
        if (!stream->require(6)) {
            ret = ERROR_STREAM_CASTER_TS_AF;
            srs_error("ts: demux af PCR_flag failed. ret=%d", ret);
            return ret;
        }

        char* pp = NULL;
        char* p = stream->data() + stream->pos();
        stream->skip(6);

        int64_t pcrv = 0;
        pp = (char*)&pcrv;
        pp[5] = *p++;
        pp[4] = *p++;
        pp[3] = *p++;
        pp[2] = *p++;
        pp[1] = *p++;
        pp[0] = *p++;
        
        // @remark, use pcr base and ignore the extension
        // @see https://github.com/simple-rtmp-server/srs/issues/250#issuecomment-71349370
        program_clock_reference_extension = pcrv & 0x1ff;
        const1_value0 = (pcrv >> 9) & 0x3F;
        program_clock_reference_base = (pcrv >> 15) & 0x1ffffffffLL;
    }

    if (OPCR_flag) {
        if (!stream->require(6)) {
            ret = ERROR_STREAM_CASTER_TS_AF;
            srs_error("ts: demux af OPCR_flag failed. ret=%d", ret);
            return ret;
        }

        char* pp = NULL;
        char* p = stream->data() + stream->pos();
        stream->skip(6);
        
        int64_t opcrv = 0;
        pp = (char*)&opcrv;
        pp[5] = *p++;
        pp[4] = *p++;
        pp[3] = *p++;
        pp[2] = *p++;
        pp[1] = *p++;
        pp[0] = *p++;
        
        // @remark, use pcr base and ignore the extension
        // @see https://github.com/simple-rtmp-server/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;
    }

    if (splicing_point_flag) {
        if (!stream->require(1)) {
            ret = ERROR_STREAM_CASTER_TS_AF;
            srs_error("ts: demux af splicing_point_flag failed. ret=%d", ret);
            return ret;
        }
        splice_countdown = stream->read_1bytes();
    }
    
    if (transport_private_data_flag) {
        if (!stream->require(1)) {
            ret = ERROR_STREAM_CASTER_TS_AF;
            srs_error("ts: demux af transport_private_data_flag failed. ret=%d", ret);
            return ret;
        }
        transport_private_data_length = (u_int8_t)stream->read_1bytes();

        if (transport_private_data_length> 0) {
            if (!stream->require(transport_private_data_length)) {
                ret = ERROR_STREAM_CASTER_TS_AF;
                srs_error("ts: demux af transport_private_data_flag failed. ret=%d", ret);
                return ret;
            }
            srs_freep(transport_private_data);
            transport_private_data = new char[transport_private_data_length];
            stream->read_bytes(transport_private_data, transport_private_data_length);
        }
    }
    
    if (adaptation_field_extension_flag) {
        int pos_af_ext = stream->pos();

        if (!stream->require(2)) {
            ret = ERROR_STREAM_CASTER_TS_AF;
            srs_error("ts: demux af adaptation_field_extension_flag failed. ret=%d", ret);
            return ret;
        }
        adaptation_field_extension_length = (u_int8_t)stream->read_1bytes();
        int8_t ltwfv = stream->read_1bytes();
        
        piecewise_rate_flag = (ltwfv >> 6) & 0x01;
        seamless_splice_flag = (ltwfv >> 5) & 0x01;
        ltw_flag = (ltwfv >> 7) & 0x01;
        const1_value1 = ltwfv & 0x1F;

        if (ltw_flag) {
            if (!stream->require(2)) {
                ret = ERROR_STREAM_CASTER_TS_AF;
                srs_error("ts: demux af ltw_flag failed. ret=%d", ret);
                return ret;
            }
            ltw_offset = stream->read_2bytes();
            
            ltw_valid_flag = (ltw_offset >> 15) &0x01;
            ltw_offset &= 0x7FFF;
        }

        if (piecewise_rate_flag) {
            if (!stream->require(3)) {
                ret = ERROR_STREAM_CASTER_TS_AF;
                srs_error("ts: demux af piecewise_rate_flag failed. ret=%d", ret);
                return ret;
            }
            piecewise_rate = stream->read_3bytes();

            piecewise_rate &= 0x3FFFFF;
        }

        if (seamless_splice_flag) {
            if (!stream->require(5)) {
                ret = ERROR_STREAM_CASTER_TS_AF;
                srs_error("ts: demux af seamless_splice_flag failed. ret=%d", ret);
                return ret;
            }
            marker_bit0 = stream->read_1bytes();
            DTS_next_AU1 = stream->read_2bytes();
            DTS_next_AU2 = stream->read_2bytes();
            
            splice_type = (marker_bit0 >> 4) & 0x0F;
            DTS_next_AU0 = (marker_bit0 >> 1) & 0x07;
            marker_bit0 &= 0x01;
            
            marker_bit1 = DTS_next_AU1 & 0x01;
            DTS_next_AU1 = (DTS_next_AU1 >> 1) & 0x7FFF;
            
            marker_bit2 = DTS_next_AU2 & 0x01;
            DTS_next_AU2 = (DTS_next_AU2 >> 1) & 0x7FFF;
        }

        nb_af_ext_reserved = adaptation_field_extension_length - (stream->pos() - pos_af_ext);
        stream->skip(nb_af_ext_reserved);
    }

    nb_af_reserved = adaption_field_length - (stream->pos() - pos_af);
    stream->skip(nb_af_reserved);
    
    srs_info("ts: af parsed, discontinuity=%d random=%d priority=%d PCR=%d OPCR=%d slicing=%d private=%d extension=%d/%d pcr=%"PRId64"/%d opcr=%"PRId64"/%d",
        discontinuity_indicator, random_access_indicator, elementary_stream_priority_indicator, PCR_flag, OPCR_flag, splicing_point_flag,
        transport_private_data_flag, adaptation_field_extension_flag, adaptation_field_extension_length, program_clock_reference_base, 
        program_clock_reference_extension, original_program_clock_reference_base, original_program_clock_reference_extension);

    return ret;
}

int SrsTsAdaptationField::size()
{
    int sz = 2;

    sz += PCR_flag? 6 : 0;
    sz += OPCR_flag? 6 : 0;
    sz += splicing_point_flag? 1 : 0;
    sz += transport_private_data_flag? 1 + transport_private_data_length : 0;
    sz += adaptation_field_extension_flag? 2 + adaptation_field_extension_length : 0;
    sz += nb_af_ext_reserved;
    sz += nb_af_reserved;

    adaption_field_length = sz - 1;

    return sz;
}

int SrsTsAdaptationField::encode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    if (!stream->require(2)) {
        ret = ERROR_STREAM_CASTER_TS_AF;
        srs_error("ts: mux af failed. ret=%d", ret);
        return ret;
    }
    stream->write_1bytes(adaption_field_length);

    // When the adaptation_field_control value is '11', the value of the adaptation_field_length shall
    // be in the range 0 to 182. 
    if (packet->adaption_field_control == SrsTsAdaptationFieldTypeBoth && adaption_field_length > 182) {
        ret = ERROR_STREAM_CASTER_TS_AF;
        srs_error("ts: mux af length failed, must in [0, 182], actual=%d. ret=%d", adaption_field_length, ret);
        return ret;
    }
    // When the adaptation_field_control value is '10', the value of the adaptation_field_length shall
    // be 183.
    if (packet->adaption_field_control == SrsTsAdaptationFieldTypeAdaptionOnly && adaption_field_length != 183) {
        ret = ERROR_STREAM_CASTER_TS_AF;
        srs_error("ts: mux af length failed, must be 183, actual=%d. ret=%d", adaption_field_length, ret);
        return ret;
    }
    
    // no adaptation field.
    if (adaption_field_length == 0) {
        srs_info("ts: mux af empty.");
        return ret;
    }
    int8_t tmpv = adaptation_field_extension_flag & 0x01;
    tmpv |= (discontinuity_indicator << 7) & 0x80;
    tmpv |= (random_access_indicator << 6) & 0x40;
    tmpv |= (elementary_stream_priority_indicator << 5) & 0x20;
    tmpv |= (PCR_flag << 4) & 0x10;
    tmpv |= (OPCR_flag << 3) & 0x08;
    tmpv |= (splicing_point_flag << 2) & 0x04;
    tmpv |= (transport_private_data_flag << 1) & 0x02;
    stream->write_1bytes(tmpv);
    
    if (PCR_flag) {
        if (!stream->require(6)) {
            ret = ERROR_STREAM_CASTER_TS_AF;
            srs_error("ts: mux af PCR_flag failed. ret=%d", ret);
            return ret;
        }

        char* pp = NULL;
        char* p = stream->data() + stream->pos();
        stream->skip(6);
        
        // @remark, use pcr base and ignore the extension
        // @see https://github.com/simple-rtmp-server/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;

        pp = (char*)&pcrv;
        *p++ = pp[5];
        *p++ = pp[4];
        *p++ = pp[3];
        *p++ = pp[2];
        *p++ = pp[1];
        *p++ = pp[0];
    }

    if (OPCR_flag) {
        if (!stream->require(6)) {
            ret = ERROR_STREAM_CASTER_TS_AF;
            srs_error("ts: demux af OPCR_flag failed. ret=%d", ret);
            return ret;
        }
        stream->skip(6);
        srs_warn("ts: mux af ignore OPCR");
    }

    if (splicing_point_flag) {
        if (!stream->require(1)) {
            ret = ERROR_STREAM_CASTER_TS_AF;
            srs_error("ts: mux af splicing_point_flag failed. ret=%d", ret);
            return ret;
        }
        stream->write_1bytes(splice_countdown);
    }
    
    if (transport_private_data_flag) {
        if (!stream->require(1)) {
            ret = ERROR_STREAM_CASTER_TS_AF;
            srs_error("ts: mux af transport_private_data_flag failed. ret=%d", ret);
            return ret;
        }
        stream->write_1bytes(transport_private_data_length);

        if (transport_private_data_length> 0) {
            if (!stream->require(transport_private_data_length)) {
                ret = ERROR_STREAM_CASTER_TS_AF;
                srs_error("ts: mux af transport_private_data_flag failed. ret=%d", ret);
                return ret;
            }
            stream->write_bytes(transport_private_data, transport_private_data_length);
        }
    }
    
    if (adaptation_field_extension_flag) {
        if (!stream->require(2)) {
            ret = ERROR_STREAM_CASTER_TS_AF;
            srs_error("ts: mux af adaptation_field_extension_flag failed. ret=%d", ret);
            return ret;
        }
        stream->write_1bytes(adaptation_field_extension_length);
        int8_t ltwfv = const1_value1 & 0x1F;
        ltwfv |= (ltw_flag << 7) & 0x80;
        ltwfv |= (piecewise_rate_flag << 6) & 0x40;
        ltwfv |= (seamless_splice_flag << 5) & 0x20;
        stream->write_1bytes(ltwfv);

        if (ltw_flag) {
            if (!stream->require(2)) {
                ret = ERROR_STREAM_CASTER_TS_AF;
                srs_error("ts: mux af ltw_flag failed. ret=%d", ret);
                return ret;
            }
            stream->skip(2);
            srs_warn("ts: mux af ignore ltw");
        }

        if (piecewise_rate_flag) {
            if (!stream->require(3)) {
                ret = ERROR_STREAM_CASTER_TS_AF;
                srs_error("ts: mux af piecewise_rate_flag failed. ret=%d", ret);
                return ret;
            }
            stream->skip(3);
            srs_warn("ts: mux af ignore piecewise_rate");
        }

        if (seamless_splice_flag) {
            if (!stream->require(5)) {
                ret = ERROR_STREAM_CASTER_TS_AF;
                srs_error("ts: mux af seamless_splice_flag failed. ret=%d", ret);
                return ret;
            }
            stream->skip(5);
            srs_warn("ts: mux af ignore seamless_splice");
        }

        if (nb_af_ext_reserved) {
            stream->skip(nb_af_ext_reserved);
        }
    }

    if (nb_af_reserved) {
        stream->skip(nb_af_reserved);
    }
    
    srs_info("ts: af parsed, discontinuity=%d random=%d priority=%d PCR=%d OPCR=%d slicing=%d private=%d extension=%d/%d pcr=%"PRId64"/%d opcr=%"PRId64"/%d",
        discontinuity_indicator, random_access_indicator, elementary_stream_priority_indicator, PCR_flag, OPCR_flag, splicing_point_flag,
        transport_private_data_flag, adaptation_field_extension_flag, adaptation_field_extension_length, program_clock_reference_base, 
        program_clock_reference_extension, original_program_clock_reference_base, original_program_clock_reference_extension);

    return ret;
}

SrsTsPayload::SrsTsPayload(SrsTsPacket* p)
{
    packet = p;
}

SrsTsPayload::~SrsTsPayload()
{
}

SrsTsPayloadPES::SrsTsPayloadPES(SrsTsPacket* p) : SrsTsPayload(p)
{
    PES_private_data = NULL;
    pack_field = NULL;
    PES_extension_field = NULL;
    nb_stuffings = 0;
    nb_bytes = 0;
    nb_paddings = 0;
    const2bits = 0x02;
    const1_value0 = 0x07;
}

SrsTsPayloadPES::~SrsTsPayloadPES()
{
    srs_freep(PES_private_data);
    srs_freep(pack_field);
    srs_freep(PES_extension_field);
}

int SrsTsPayloadPES::decode(SrsStream* stream, SrsTsMessage** ppmsg)
{
    int ret = ERROR_SUCCESS;

    // find the channel from chunk.
    SrsTsChannel* channel = packet->context->get(packet->pid);
    if (!channel) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: demux PES no channel for pid=%#x. ret=%d", packet->pid, ret);
        return ret;
    }

    // init msg.
    SrsTsMessage* msg = channel->msg;
    if (!msg) {
        msg = new SrsTsMessage(channel, packet);
        channel->msg = msg;
    }
    
    // we must cache the fresh state of msg,
    // for the PES_packet_length is 0, the first payload_unit_start_indicator always 1,
    // so should check for the fresh and not completed it.
    bool is_fresh_msg = msg->fresh();

    // check when fresh, the payload_unit_start_indicator
    // should be 1 for the fresh msg.
    if (is_fresh_msg && !packet->payload_unit_start_indicator) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: PES fresh packet length=%d, us=%d, cc=%d. ret=%d",
            msg->PES_packet_length, packet->payload_unit_start_indicator, packet->continuity_counter,
            ret);
        return ret;
    }

    // check when not fresh and PES_packet_length>0,
    // the payload_unit_start_indicator should never be 1 when not completed.
    if (!is_fresh_msg && msg->PES_packet_length > 0
        && !msg->completed(packet->payload_unit_start_indicator)
        && packet->payload_unit_start_indicator
    ) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: PES packet length=%d, payload=%d, us=%d, cc=%d. ret=%d",
            msg->PES_packet_length, msg->payload->length(), packet->payload_unit_start_indicator, 
            packet->continuity_counter, ret);

        // reparse current msg.
        stream->skip(stream->pos() * -1);
        srs_freep(msg);
        channel->msg = NULL;
        return ERROR_SUCCESS;
    }

    // check the continuity counter
    if (!is_fresh_msg) {
        // late-incoming or duplicated continuity, drop message.
        // @remark check overflow, the counter plus 1 should greater when invalid.
        if (msg->continuity_counter >= packet->continuity_counter
            && ((msg->continuity_counter + 1) & 0x0f) > packet->continuity_counter
        ) {
            srs_warn("ts: drop PES %dB for duplicated cc=%#x", msg->continuity_counter);
            stream->skip(stream->size() - stream->pos());
            return ret;
        }

        // when got partially message, the continous count must be continuous, or drop it.
        if (((msg->continuity_counter + 1) & 0x0f) != packet->continuity_counter) {
            ret = ERROR_STREAM_CASTER_TS_PSE;
            srs_error("ts: continuity must be continous, msg=%#x, packet=%#x. ret=%d",
                msg->continuity_counter, packet->continuity_counter, ret);

            // reparse current msg.
            stream->skip(stream->pos() * -1);
            srs_freep(msg);
            channel->msg = NULL;
            return ERROR_SUCCESS;
        }
    }
    msg->continuity_counter = packet->continuity_counter;

    // for the PES_packet_length(0), reap when completed.
    if (!is_fresh_msg && msg->completed(packet->payload_unit_start_indicator)) {
        // reap previous PES packet.
        *ppmsg = msg;
        channel->msg = NULL;

        // reparse current msg.
        stream->skip(stream->pos() * -1);
        return ret;
    }

    // contious packet, append bytes for unit start is 0
    if (!packet->payload_unit_start_indicator) {
        if ((ret = msg->dump(stream, &nb_bytes)) != ERROR_SUCCESS) {
            return ret;
        }
    }

    // when unit start, parse the fresh msg.
    if (packet->payload_unit_start_indicator) {
        // 6B fixed header.
        if (!stream->require(6)) {
            ret = ERROR_STREAM_CASTER_TS_PSE;
            srs_error("ts: demux PSE failed. ret=%d", ret);
            return ret;
        }
        // 3B
        packet_start_code_prefix = stream->read_3bytes();
        // 1B
        stream_id = stream->read_1bytes();
        // 2B
        PES_packet_length = stream->read_2bytes();

        // check the packet start prefix.
        packet_start_code_prefix &= 0xFFFFFF;
        if (packet_start_code_prefix != 0x01) {
            ret = ERROR_STREAM_CASTER_TS_PSE;
            srs_error("ts: demux PES start code failed, expect=0x01, actual=%#x. ret=%d", packet_start_code_prefix, ret);
            return ret;
        }
        int pos_packet = stream->pos();

        // @remark the sid indicates the elementary stream format.
        //      the SrsTsPESStreamIdAudio and SrsTsPESStreamIdVideo is start by 0b110 or 0b1110
        SrsTsPESStreamId sid = (SrsTsPESStreamId)stream_id;
        msg->sid = sid;

        if (sid != SrsTsPESStreamIdProgramStreamMap
            && sid != SrsTsPESStreamIdPaddingStream
            && sid != SrsTsPESStreamIdPrivateStream2
            && sid != SrsTsPESStreamIdEcmStream
            && sid != SrsTsPESStreamIdEmmStream
            && sid != SrsTsPESStreamIdProgramStreamDirectory
            && sid != SrsTsPESStreamIdDsmccStream
            && sid != SrsTsPESStreamIdH2221TypeE
        ) {
            // 3B flags.
            if (!stream->require(3)) {
                ret = ERROR_STREAM_CASTER_TS_PSE;
                srs_error("ts: demux PES flags failed. ret=%d", ret);
                return ret;
            }
            // 1B
            int8_t oocv = stream->read_1bytes();
            // 1B
            int8_t pefv = stream->read_1bytes();
            // 1B
            PES_header_data_length = stream->read_1bytes();
            // position of header start.
            int pos_header = stream->pos();

            const2bits = (oocv >> 6) & 0x03;
            PES_scrambling_control = (oocv >> 4) & 0x03;
            PES_priority = (oocv >> 3) & 0x01;
            data_alignment_indicator = (oocv >> 2) & 0x01;
            copyright = (oocv >> 1) & 0x01;
            original_or_copy = oocv & 0x01;

            PTS_DTS_flags = (pefv >> 6) & 0x03;
            ESCR_flag = (pefv >> 5) & 0x01;
            ES_rate_flag = (pefv >> 4) & 0x01;
            DSM_trick_mode_flag = (pefv >> 3) & 0x01;
            additional_copy_info_flag = (pefv >> 2) & 0x01;
            PES_CRC_flag = (pefv >> 1) & 0x01;
            PES_extension_flag = pefv & 0x01;

            // check required together.
            int nb_required = 0;
            nb_required += (PTS_DTS_flags == 0x2)? 5:0;
            nb_required += (PTS_DTS_flags == 0x3)? 10:0;
            nb_required += ESCR_flag? 6:0;
            nb_required += ES_rate_flag? 3:0;
            nb_required += DSM_trick_mode_flag? 1:0;
            nb_required += additional_copy_info_flag? 1:0;
            nb_required += PES_CRC_flag? 2:0;
            nb_required += PES_extension_flag? 1:0;
            if (!stream->require(nb_required)) {
                ret = ERROR_STREAM_CASTER_TS_PSE;
                srs_error("ts: demux PES payload failed. ret=%d", ret);
                return ret;
            }

            // 5B
            if (PTS_DTS_flags == 0x2) {
                if ((ret = decode_33bits_dts_pts(stream, &pts)) != ERROR_SUCCESS) {
                    return ret;
                }
                dts = pts;

                // update the dts and pts of message.
                msg->dts = dts;
                msg->pts = pts;
            }

            // 10B
            if (PTS_DTS_flags == 0x3) {
                if ((ret = decode_33bits_dts_pts(stream, &pts)) != ERROR_SUCCESS) {
                    return ret;
                }
                if ((ret = decode_33bits_dts_pts(stream, &dts)) != ERROR_SUCCESS) {
                    return ret;
                }

                // check sync, the diff of dts and pts should never greater than 1s.
                if (dts - pts > 90000 || pts - dts > 90000) {
                    srs_warn("ts: sync dts=%"PRId64", pts=%"PRId64, dts, pts);
                }

                // update the dts and pts of message.
                msg->dts = dts;
                msg->pts = pts;
            }

            // 6B
            if (ESCR_flag) {
                ESCR_extension = 0;
                ESCR_base = 0;

                stream->skip(6);
                srs_warn("ts: demux PES, ignore the escr.");
            }

            // 3B
            if (ES_rate_flag) {
                ES_rate = stream->read_3bytes();

                ES_rate = ES_rate >> 1;
                ES_rate &= 0x3FFFFF;
            }

            // 1B
            if (DSM_trick_mode_flag) {
                trick_mode_control = stream->read_1bytes();

                trick_mode_value = trick_mode_control & 0x1f;
                trick_mode_control = (trick_mode_control >> 5) & 0x03;
            }

            // 1B
            if (additional_copy_info_flag) {
                additional_copy_info = stream->read_1bytes();

                additional_copy_info &= 0x7f;
            }

            // 2B
            if (PES_CRC_flag) {
                previous_PES_packet_CRC = stream->read_2bytes();
            }

            // 1B
            if (PES_extension_flag) {
                int8_t efv = stream->read_1bytes();

                PES_private_data_flag = (efv >> 7) & 0x01;
                pack_header_field_flag = (efv >> 6) & 0x01;
                program_packet_sequence_counter_flag = (efv >> 5) & 0x01;
                P_STD_buffer_flag = (efv >> 4) & 0x01;
                const1_value0 = (efv >> 1) & 0x07;
                PES_extension_flag_2 = efv & 0x01;

                nb_required = 0;
                nb_required += PES_private_data_flag? 16:0;
                nb_required += pack_header_field_flag? 1:0; // 1+x bytes.
                nb_required += program_packet_sequence_counter_flag? 2:0;
                nb_required += P_STD_buffer_flag? 2:0;
                nb_required += PES_extension_flag_2? 1:0; // 1+x bytes.
                if (!stream->require(nb_required)) {
                    ret = ERROR_STREAM_CASTER_TS_PSE;
                    srs_error("ts: demux PSE ext payload failed. ret=%d", ret);
                    return ret;
                }

                // 16B
                if (PES_private_data_flag) {
                    srs_freep(PES_private_data);
                    PES_private_data = new char[16];
                    stream->read_bytes(PES_private_data, 16);
                }

                // (1+x)B
                if (pack_header_field_flag) {
                    pack_field_length = stream->read_1bytes();
                    if (pack_field_length > 0) {
                        // the adjust required bytes.
                        nb_required = nb_required - 16 - 1 + pack_field_length;
                        if (!stream->require(nb_required)) {
                            ret = ERROR_STREAM_CASTER_TS_PSE;
                            srs_error("ts: demux PSE ext pack failed. ret=%d", ret);
                            return ret;
                        }
                        srs_freep(pack_field);
                        pack_field = new char[pack_field_length];
                        stream->read_bytes(pack_field, pack_field_length);
                    }
                }

                // 2B
                if (program_packet_sequence_counter_flag) {
                    program_packet_sequence_counter = stream->read_1bytes();
                    program_packet_sequence_counter &= 0x7f;

                    original_stuff_length = stream->read_1bytes();
                    MPEG1_MPEG2_identifier = (original_stuff_length >> 6) & 0x01;
                    original_stuff_length &= 0x3f;
                }

                // 2B
                if (P_STD_buffer_flag) {
                    P_STD_buffer_size = stream->read_2bytes();

                    // '01'
                    //int8_t const2bits = (P_STD_buffer_scale >>14) & 0x03;

                    P_STD_buffer_scale = (P_STD_buffer_scale >>13) & 0x01;
                    P_STD_buffer_size &= 0x1FFF;
                }

                // (1+x)B
                if (PES_extension_flag_2) {
                    PES_extension_field_length = stream->read_1bytes();
                    PES_extension_field_length &= 0x07;

                    if (PES_extension_field_length > 0) {
                        if (!stream->require(PES_extension_field_length)) {
                            ret = ERROR_STREAM_CASTER_TS_PSE;
                            srs_error("ts: demux PSE ext field failed. ret=%d", ret);
                            return ret;
                        }
                        srs_freep(PES_extension_field);
                        PES_extension_field = new char[PES_extension_field_length];
                        stream->read_bytes(PES_extension_field, PES_extension_field_length);
                    }
                }
            }

            // stuffing_byte
            nb_stuffings = PES_header_data_length - (stream->pos() - pos_header);
            if (nb_stuffings > 0) {
                if (!stream->require(nb_stuffings)) {
                    ret = ERROR_STREAM_CASTER_TS_PSE;
                    srs_error("ts: demux PSE stuffings failed. ret=%d", ret);
                    return ret;
                }
                stream->skip(nb_stuffings);
            }

            // PES_packet_data_byte, page58.
            // the packet size contains the header size.
            // The number of PES_packet_data_bytes, N, is specified by the
            // PES_packet_length field. N shall be equal to the value
            // indicated in the PES_packet_length minus the number of bytes
            // between the last byte of the PES_packet_length field and the
            // first PES_packet_data_byte.
            /**
            * when actual packet length > 0xffff(65535),
            * which exceed the max u_int16_t packet length,
            * use 0 packet length, the next unit start indicates the end of packet.
            */
            if (PES_packet_length > 0) {
                int nb_packet = PES_packet_length - (stream->pos() - pos_packet);
                msg->PES_packet_length = srs_max(0, nb_packet);
            }

            // xB
            if ((ret = msg->dump(stream, &nb_bytes)) != ERROR_SUCCESS) {
                return ret;
            }
        } else if (sid == SrsTsPESStreamIdProgramStreamMap
            || sid == SrsTsPESStreamIdPrivateStream2
            || sid == SrsTsPESStreamIdEcmStream
            || sid == SrsTsPESStreamIdEmmStream
            || sid == SrsTsPESStreamIdProgramStreamDirectory
            || sid == SrsTsPESStreamIdDsmccStream
            || sid == SrsTsPESStreamIdH2221TypeE
        ) {
            // for (i = 0; i < PES_packet_length; i++) {
            //         PES_packet_data_byte
            // }

            // xB
            if ((ret = msg->dump(stream, &nb_bytes)) != ERROR_SUCCESS) {
                return ret;
            }
        } else if (sid == SrsTsPESStreamIdPaddingStream) {
            // for (i = 0; i < PES_packet_length; i++) {
            //         padding_byte
            // }
            nb_paddings = stream->size() - stream->pos();
            stream->skip(nb_paddings);
            srs_info("ts: drop %dB padding bytes", nb_paddings);
        } else {
            int nb_drop = stream->size() - stream->pos();
            stream->skip(nb_drop);
            srs_warn("ts: drop the pes packet %dB for stream_id=%#x", nb_drop, stream_id);
        }
    }
    
    // when fresh and the PES_packet_length is 0,
    // the payload_unit_start_indicator always be 1,
    // the message should never EOF for the first packet.
    if (is_fresh_msg && msg->PES_packet_length == 0) {
        return ret;
    }

    // check msg, reap when completed.
    if (msg->completed(packet->payload_unit_start_indicator)) {
        *ppmsg = msg;
        channel->msg = NULL;
        srs_info("ts: reap msg for completed.");
    }

    return ret;
}

int SrsTsPayloadPES::size()
{
    int sz = 0;
    
    PES_header_data_length = 0;
    SrsTsPESStreamId sid = (SrsTsPESStreamId)stream_id;

    if (sid != SrsTsPESStreamIdProgramStreamMap
        && sid != SrsTsPESStreamIdPaddingStream
        && sid != SrsTsPESStreamIdPrivateStream2
        && sid != SrsTsPESStreamIdEcmStream
        && sid != SrsTsPESStreamIdEmmStream
        && sid != SrsTsPESStreamIdProgramStreamDirectory
        && sid != SrsTsPESStreamIdDsmccStream
        && sid != SrsTsPESStreamIdH2221TypeE
    ) {
        sz += 6;
        sz += 3;
        PES_header_data_length = sz;

        sz += (PTS_DTS_flags == 0x2)? 5:0;
        sz += (PTS_DTS_flags == 0x3)? 10:0;
        sz += ESCR_flag? 6:0;
        sz += ES_rate_flag? 3:0;
        sz += DSM_trick_mode_flag? 1:0;
        sz += additional_copy_info_flag? 1:0;
        sz += PES_CRC_flag? 2:0;
        sz += PES_extension_flag? 1:0;

        if (PES_extension_flag) {
            sz += PES_private_data_flag? 16:0;
            sz += pack_header_field_flag? 1 + pack_field_length:0; // 1+x bytes.
            sz += program_packet_sequence_counter_flag? 2:0;
            sz += P_STD_buffer_flag? 2:0;
            sz += PES_extension_flag_2? 1 + PES_extension_field_length:0; // 1+x bytes.
        }
        PES_header_data_length = sz - PES_header_data_length;

        sz += nb_stuffings;

        // packet bytes
    } else if (sid == SrsTsPESStreamIdProgramStreamMap
        || sid == SrsTsPESStreamIdPrivateStream2
        || sid == SrsTsPESStreamIdEcmStream
        || sid == SrsTsPESStreamIdEmmStream
        || sid == SrsTsPESStreamIdProgramStreamDirectory
        || sid == SrsTsPESStreamIdDsmccStream
        || sid == SrsTsPESStreamIdH2221TypeE
    ) {
        // packet bytes
    } else {
        // nb_drop
    }

    return sz;
}

int SrsTsPayloadPES::encode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // 6B fixed header.
    if (!stream->require(6)) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: mux PSE failed. ret=%d", ret);
        return ret;
    }

    // 3B
    stream->write_3bytes(packet_start_code_prefix);
    // 1B
    stream->write_1bytes(stream_id);
    // 2B
    // the PES_packet_length is the actual bytes size, the pplv write to ts
    // is the actual bytes plus the header size.
    int32_t pplv = 0;
    if (PES_packet_length > 0) {
        pplv = PES_packet_length + 3 + PES_header_data_length;
        pplv = (pplv > 0xFFFF)? 0 : pplv;
    }
    stream->write_2bytes(pplv);

    // check the packet start prefix.
    packet_start_code_prefix &= 0xFFFFFF;
    if (packet_start_code_prefix != 0x01) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: mux PSE start code failed, expect=0x01, actual=%#x. ret=%d", packet_start_code_prefix, ret);
        return ret;
    }

    // 3B flags.
    if (!stream->require(3)) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: mux PSE flags failed. ret=%d", ret);
        return ret;
    }
    // 1B
    int8_t oocv = original_or_copy & 0x01;
    oocv |= (const2bits << 6) & 0xC0;
    oocv |= (PES_scrambling_control << 4) & 0x30;
    oocv |= (PES_priority << 3) & 0x08;
    oocv |= (data_alignment_indicator << 2) & 0x04;
    oocv |= (copyright << 1) & 0x02;
    stream->write_1bytes(oocv);
    // 1B
    int8_t pefv = PES_extension_flag & 0x01;
    pefv |= (PTS_DTS_flags << 6) & 0xC0;
    pefv |= (ESCR_flag << 5) & 0x20;
    pefv |= (ES_rate_flag << 4) & 0x10;
    pefv |= (DSM_trick_mode_flag << 3) & 0x08;
    pefv |= (additional_copy_info_flag << 2) & 0x04;
    pefv |= (PES_CRC_flag << 1) & 0x02;
    stream->write_1bytes(pefv);
    // 1B
    stream->write_1bytes(PES_header_data_length);

    // check required together.
    int nb_required = 0;
    nb_required += (PTS_DTS_flags == 0x2)? 5:0;
    nb_required += (PTS_DTS_flags == 0x3)? 10:0;
    nb_required += ESCR_flag? 6:0;
    nb_required += ES_rate_flag? 3:0;
    nb_required += DSM_trick_mode_flag? 1:0;
    nb_required += additional_copy_info_flag? 1:0;
    nb_required += PES_CRC_flag? 2:0;
    nb_required += PES_extension_flag? 1:0;
    if (!stream->require(nb_required)) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: mux PSE payload failed. ret=%d", ret);
        return ret;
    }

    // 5B
    if (PTS_DTS_flags == 0x2) {
        if ((ret = encode_33bits_dts_pts(stream, 0x02, pts)) != ERROR_SUCCESS) {
            return ret;
        }
    }

    // 10B
    if (PTS_DTS_flags == 0x3) {
        if ((ret = encode_33bits_dts_pts(stream, 0x03, pts)) != ERROR_SUCCESS) {
            return ret;
        }
        if ((ret = encode_33bits_dts_pts(stream, 0x01, dts)) != ERROR_SUCCESS) {
            return ret;
        }

        // check sync, the diff of dts and pts should never greater than 1s.
        if (dts - pts > 90000 || pts - dts > 90000) {
            srs_warn("ts: sync dts=%"PRId64", pts=%"PRId64, dts, pts);
        }
    }

    // 6B
    if (ESCR_flag) {
        stream->skip(6);
        srs_warn("ts: demux PES, ignore the escr.");
    }

    // 3B
    if (ES_rate_flag) {
        stream->skip(3);
        srs_warn("ts: demux PES, ignore the ES_rate.");
    }

    // 1B
    if (DSM_trick_mode_flag) {
        stream->skip(1);
        srs_warn("ts: demux PES, ignore the DSM_trick_mode.");
    }

    // 1B
    if (additional_copy_info_flag) {
        stream->skip(1);
        srs_warn("ts: demux PES, ignore the additional_copy_info.");
    }

    // 2B
    if (PES_CRC_flag) {
        stream->skip(2);
        srs_warn("ts: demux PES, ignore the PES_CRC.");
    }

    // 1B
    if (PES_extension_flag) {
        int8_t efv = PES_extension_flag_2 & 0x01;
        efv |= (PES_private_data_flag << 7) & 0x80;
        efv |= (pack_header_field_flag << 6) & 0x40;
        efv |= (program_packet_sequence_counter_flag << 5) & 0x20;
        efv |= (P_STD_buffer_flag << 4) & 0x10;
        efv |= (const1_value0 << 1) & 0xE0;
        stream->write_1bytes(efv);

        nb_required = 0;
        nb_required += PES_private_data_flag? 16:0;
        nb_required += pack_header_field_flag? 1+pack_field_length:0; // 1+x bytes.
        nb_required += program_packet_sequence_counter_flag? 2:0;
        nb_required += P_STD_buffer_flag? 2:0;
        nb_required += PES_extension_flag_2? 1+PES_extension_field_length:0; // 1+x bytes.
        if (!stream->require(nb_required)) {
            ret = ERROR_STREAM_CASTER_TS_PSE;
            srs_error("ts: mux PSE ext payload failed. ret=%d", ret);
            return ret;
        }
        stream->skip(nb_required);
        srs_warn("ts: demux PES, ignore the PES_extension.");
    }

    // stuffing_byte
    if (nb_stuffings) {
        stream->skip(nb_stuffings);
        srs_warn("ts: demux PES, ignore the stuffings.");
    }

    return ret;
}

int SrsTsPayloadPES::decode_33bits_dts_pts(SrsStream* stream, int64_t* pv)
{
    int ret = ERROR_SUCCESS;

    if (!stream->require(5)) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: demux PSE dts/pts failed. ret=%d", ret);
        return ret;
    }

    // decode the 33bits schema.
    // ===========1B
    // 4bits const maybe '0001', '0010' or '0011'.
    // 3bits DTS/PTS [32..30]
    // 1bit const '1'
    int64_t dts_pts_30_32 = stream->read_1bytes();
    if ((dts_pts_30_32 & 0x01) != 0x01) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: demux PSE dts/pts 30-32 failed. ret=%d", ret);
        return ret;
    }
    // @remark, we donot check the high 4bits, maybe '0001', '0010' or '0011'.
    //      so we just ensure the high 4bits is not 0x00.
    if (((dts_pts_30_32 >> 4) & 0x0f) == 0x00) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: demux PSE dts/pts 30-32 failed. ret=%d", ret);
        return ret;
    }
    dts_pts_30_32 = (dts_pts_30_32 >> 1) & 0x07;

    // ===========2B
    // 15bits DTS/PTS [29..15]
    // 1bit const '1'
    int64_t dts_pts_15_29 = stream->read_2bytes();
    if ((dts_pts_15_29 & 0x01) != 0x01) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: demux PSE dts/pts 15-29 failed. ret=%d", ret);
        return ret;
    }
    dts_pts_15_29 = (dts_pts_15_29 >> 1) & 0x7fff;

    // ===========2B
    // 15bits DTS/PTS [14..0]
    // 1bit const '1'
    int64_t dts_pts_0_14 = stream->read_2bytes();
    if ((dts_pts_0_14 & 0x01) != 0x01) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: demux PSE dts/pts 0-14 failed. ret=%d", ret);
        return ret;
    }
    dts_pts_0_14 = (dts_pts_0_14 >> 1) & 0x7fff;

    int64_t v = 0x00;
    v |= (dts_pts_30_32 << 30) & 0x1c0000000LL;
    v |= (dts_pts_15_29 << 15) & 0x3fff8000LL;
    v |= dts_pts_0_14 & 0x7fff;
    *pv = v;

    return ret;
}

int SrsTsPayloadPES::encode_33bits_dts_pts(SrsStream* stream, u_int8_t fb, int64_t v)
{
    int ret = ERROR_SUCCESS;

    if (!stream->require(5)) {
        ret = ERROR_STREAM_CASTER_TS_PSE;
        srs_error("ts: mux PSE dts/pts failed. ret=%d", ret);
        return ret;
    }

    char* p = stream->data() + stream->pos();
    stream->skip(5);

    int32_t val = 0;
    
    val = fb << 4 | (((v >> 30) & 0x07) << 1) | 1;
    *p++ = val;
    
    val = (((v >> 15) & 0x7fff) << 1) | 1;
    *p++ = (val >> 8);
    *p++ = val;
    
    val = (((v) & 0x7fff) << 1) | 1;
    *p++ = (val >> 8);
    *p++ = val;

    return ret;
}

SrsTsPayloadPSI::SrsTsPayloadPSI(SrsTsPacket* p) : SrsTsPayload(p)
{
    pointer_field = 0;
    const0_value = 0;
    const1_value = 3;
    CRC_32 = 0;
}

SrsTsPayloadPSI::~SrsTsPayloadPSI()
{
}

int SrsTsPayloadPSI::decode(SrsStream* stream, SrsTsMessage** /*ppmsg*/)
{
    int ret = ERROR_SUCCESS;

    /**
    * When the payload of the Transport Stream packet contains PSI data, the payload_unit_start_indicator has the following
    * significance: if the Transport Stream packet carries the first byte of a PSI section, the payload_unit_start_indicator value
    * shall be '1', indicating that the first byte of the payload of this Transport Stream packet carries the pointer_field. If the
    * Transport Stream packet does not carry the first byte of a PSI section, the payload_unit_start_indicator value shall be '0',
    * indicating that there is no pointer_field in the payload. Refer to 2.4.4.1 and 2.4.4.2. This also applies to private streams of
    * stream_type 5 (refer to Table 2-29).
    */
    if (packet->payload_unit_start_indicator) {
        if (!stream->require(1)) {
            ret = ERROR_STREAM_CASTER_TS_PSI;
            srs_error("ts: demux PSI failed. ret=%d", ret);
            return ret;
        }
        pointer_field = stream->read_1bytes();
    }

    // to calc the crc32
    char* ppat = stream->data() + stream->pos();
    int pat_pos = stream->pos();

    // atleast 3B for all psi.
    if (!stream->require(3)) {
        ret = ERROR_STREAM_CASTER_TS_PSI;
        srs_error("ts: demux PSI failed. ret=%d", ret);
        return ret;
    }
    // 1B
    table_id = (SrsTsPsiId)stream->read_1bytes();
    
    // 2B
    int16_t slv = stream->read_2bytes();
    
    section_syntax_indicator = (slv >> 15) & 0x01;
    const0_value = (slv >> 14) & 0x01;
    const1_value = (slv >> 12) & 0x03;
    section_length = slv & 0x0FFF;

    // no section, ignore.
    if (section_length == 0) {
        srs_warn("ts: demux PAT ignore empty section");
        return ret;
    }

    if (!stream->require(section_length)) {
        ret = ERROR_STREAM_CASTER_TS_PSI;
        srs_error("ts: demux PAT section failed. ret=%d", ret);
        return ret;
    }

    // call the virtual method of actual PSI.
    if ((ret = psi_decode(stream)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // 4B
    if (!stream->require(4)) {
        ret = ERROR_STREAM_CASTER_TS_PSI;
        srs_error("ts: demux PSI crc32 failed. ret=%d", ret);
        return ret;
    }
    CRC_32 = stream->read_4bytes();

    // verify crc32.
    int32_t crc32 = srs_crc32(ppat, stream->pos() - pat_pos - 4);
    if (crc32 != CRC_32) {
        ret = ERROR_STREAM_CASTER_TS_CRC32;
        srs_error("ts: verify PSI crc32 failed. ret=%d", ret);
        return ret;
    }

    // consume left stuffings
    if (!stream->empty()) {
        int nb_stuffings = stream->size() - stream->pos();
        char* stuffing = stream->data() + stream->pos();

        // all stuffing must be 0xff.
        // TODO: FIXME: maybe need to remove the following.
        for (int i = 0; i < nb_stuffings; i++) {
            if ((u_int8_t)stuffing[i] != 0xff) {
                srs_warn("ts: stuff is not 0xff, actual=%#x", stuffing[i]);
                break;
            }
        }

        stream->skip(nb_stuffings);
    }

    return ret;
}

int SrsTsPayloadPSI::size()
{
    int sz = 0;

    // section size is the sl plus the crc32
    section_length = psi_size() + 4;

     sz += packet->payload_unit_start_indicator? 1:0;
     sz += 3;
     sz += section_length;

    return sz;
}

int SrsTsPayloadPSI::encode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    if (packet->payload_unit_start_indicator) {
        if (!stream->require(1)) {
            ret = ERROR_STREAM_CASTER_TS_PSI;
            srs_error("ts: mux PSI failed. ret=%d", ret);
            return ret;
        }
        stream->write_1bytes(pointer_field);
    }

    // to calc the crc32
    char* ppat = stream->data() + stream->pos();
    int pat_pos = stream->pos();

    // atleast 3B for all psi.
    if (!stream->require(3)) {
        ret = ERROR_STREAM_CASTER_TS_PSI;
        srs_error("ts: mux PSI failed. ret=%d", ret);
        return ret;
    }
    // 1B
    stream->write_1bytes(table_id);
    
    // 2B
    int16_t slv = section_length & 0x0FFF;
    slv |= (section_syntax_indicator << 15) & 0x8000;
    slv |= (const0_value << 14) & 0x4000;
    slv |= (const1_value << 12) & 0x3000;
    stream->write_2bytes(slv);

    // no section, ignore.
    if (section_length == 0) {
        srs_warn("ts: mux PAT ignore empty section");
        return ret;
    }

    if (!stream->require(section_length)) {
        ret = ERROR_STREAM_CASTER_TS_PSI;
        srs_error("ts: mux PAT section failed. ret=%d", ret);
        return ret;
    }

    // call the virtual method of actual PSI.
    if ((ret = psi_encode(stream)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // 4B
    if (!stream->require(4)) {
        ret = ERROR_STREAM_CASTER_TS_PSI;
        srs_error("ts: mux PSI crc32 failed. ret=%d", ret);
        return ret;
    }
    CRC_32 = srs_crc32(ppat, stream->pos() - pat_pos);
    stream->write_4bytes(CRC_32);

    return ret;
}

SrsTsPayloadPATProgram::SrsTsPayloadPATProgram(int16_t n, int16_t p)
{
    number = n;
    pid = p;
    const1_value = 0x07;
}

SrsTsPayloadPATProgram::~SrsTsPayloadPATProgram()
{
}

int SrsTsPayloadPATProgram::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // atleast 4B for PAT program specified
    if (!stream->require(4)) {
        ret = ERROR_STREAM_CASTER_TS_PAT;
        srs_error("ts: demux PAT failed. ret=%d", ret);
        return ret;
    }

    int tmpv = stream->read_4bytes();
    number = (int16_t)((tmpv >> 16) & 0xFFFF);
    const1_value = (int16_t)((tmpv >> 13) & 0x07);
    pid = (int16_t)(tmpv & 0x1FFF);

    return ret;
}

int SrsTsPayloadPATProgram::size()
{
    return 4;
}

int SrsTsPayloadPATProgram::encode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // atleast 4B for PAT program specified
    if (!stream->require(4)) {
        ret = ERROR_STREAM_CASTER_TS_PAT;
        srs_error("ts: mux PAT failed. ret=%d", ret);
        return ret;
    }

    int tmpv = pid & 0x1FFF;
    tmpv |= (number << 16) & 0xFFFF0000;
    tmpv |= (const1_value << 13) & 0xE000;
    stream->write_4bytes(tmpv);

    return ret;
}

SrsTsPayloadPAT::SrsTsPayloadPAT(SrsTsPacket* p) : SrsTsPayloadPSI(p)
{
    const3_value = 3;
}

SrsTsPayloadPAT::~SrsTsPayloadPAT()
{
    std::vector<SrsTsPayloadPATProgram*>::iterator it;
    for (it = programs.begin(); it != programs.end(); ++it) {
        SrsTsPayloadPATProgram* program = *it;
        srs_freep(program);
    }
    programs.clear();
}

int SrsTsPayloadPAT::psi_decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // atleast 5B for PAT specified
    if (!stream->require(5)) {
        ret = ERROR_STREAM_CASTER_TS_PAT;
        srs_error("ts: demux PAT failed. ret=%d", ret);
        return ret;
    }

    int pos = stream->pos();

    // 2B
    transport_stream_id = stream->read_2bytes();
    
    // 1B
    int8_t cniv = stream->read_1bytes();
    
    const3_value = (cniv >> 6) & 0x03;
    version_number = (cniv >> 1) & 0x1F;
    current_next_indicator = cniv & 0x01;

    // TODO: FIXME: check the indicator.
    
    // 1B
    section_number = stream->read_1bytes();
    // 1B
    last_section_number = stream->read_1bytes();

    // multiple 4B program data.
    int program_bytes = section_length - 4 - (stream->pos() - pos);
    for (int i = 0; i < program_bytes; i += 4) {
        SrsTsPayloadPATProgram* program = new SrsTsPayloadPATProgram();

        if ((ret = program->decode(stream)) != ERROR_SUCCESS) {
            return ret;
        }

        // update the apply pid table.
        packet->context->set(program->pid, SrsTsPidApplyPMT);

        programs.push_back(program);
    }

    // update the apply pid table.
    packet->context->set(packet->pid, SrsTsPidApplyPAT);
    packet->context->on_pmt_parsed();

    return ret;
}

int SrsTsPayloadPAT::psi_size()
{
    int sz = 5;
    for (int i = 0; i < (int)programs.size(); i ++) {
        SrsTsPayloadPATProgram* program = programs.at(i);
        sz += program->size();
    }
    return sz;
}

int SrsTsPayloadPAT::psi_encode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // atleast 5B for PAT specified
    if (!stream->require(5)) {
        ret = ERROR_STREAM_CASTER_TS_PAT;
        srs_error("ts: mux PAT failed. ret=%d", ret);
        return ret;
    }

    // 2B
    stream->write_2bytes(transport_stream_id);
    
    // 1B
    int8_t cniv = current_next_indicator & 0x01;
    cniv |= (version_number << 1) & 0x3E;
    cniv |= (const1_value << 6) & 0xC0;
    stream->write_1bytes(cniv);
    
    // 1B
    stream->write_1bytes(section_number);
    // 1B
    stream->write_1bytes(last_section_number);

    // multiple 4B program data.
    for (int i = 0; i < (int)programs.size(); i ++) {
        SrsTsPayloadPATProgram* program = programs.at(i);
        if ((ret = program->encode(stream)) != ERROR_SUCCESS) {
            return ret;
        }

        // update the apply pid table.
        packet->context->set(program->pid, SrsTsPidApplyPMT);
    }

    // update the apply pid table.
    packet->context->set(packet->pid, SrsTsPidApplyPAT);

    return ret;
}

SrsTsPayloadPMTESInfo::SrsTsPayloadPMTESInfo(SrsTsStream st, int16_t epid)
{
    stream_type = st;
    elementary_PID = epid;

    const1_value0 = 7;
    const1_value1 = 0x0f;
    ES_info_length = 0;
    ES_info = NULL;
}

SrsTsPayloadPMTESInfo::~SrsTsPayloadPMTESInfo()
{
    srs_freep(ES_info);
}

int SrsTsPayloadPMTESInfo::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // 5B
    if (!stream->require(5)) {
        ret = ERROR_STREAM_CASTER_TS_PMT;
        srs_error("ts: demux PMT es info failed. ret=%d", ret);
        return ret;
    }

    stream_type = (SrsTsStream)stream->read_1bytes();

    int16_t epv = stream->read_2bytes();
    const1_value0 = (epv >> 13) & 0x07;
    elementary_PID = epv & 0x1FFF;
    
    int16_t eilv = stream->read_2bytes();
    const1_value1 = (epv >> 12) & 0x0f;
    ES_info_length = eilv & 0x0FFF;

    if (ES_info_length > 0) {
        if (!stream->require(ES_info_length)) {
            ret = ERROR_STREAM_CASTER_TS_PMT;
            srs_error("ts: demux PMT es info data failed. ret=%d", ret);
            return ret;
        }
        srs_freep(ES_info);
        ES_info = new char[ES_info_length];
        stream->read_bytes(ES_info, ES_info_length);
    }

    return ret;
}

int SrsTsPayloadPMTESInfo::size()
{
    return 5 + ES_info_length;
}

int SrsTsPayloadPMTESInfo::encode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // 5B
    if (!stream->require(5)) {
        ret = ERROR_STREAM_CASTER_TS_PMT;
        srs_error("ts: mux PMT es info failed. ret=%d", ret);
        return ret;
    }

    stream->write_1bytes(stream_type);

    int16_t epv = elementary_PID & 0x1FFF;
    epv |= (const1_value0 << 13) & 0xE000;
    stream->write_2bytes(epv);
    
    int16_t eilv = ES_info_length & 0x0FFF;
    eilv |= (const1_value1 << 12) & 0xF000;
    stream->write_2bytes(eilv);

    if (ES_info_length > 0) {
        if (!stream->require(ES_info_length)) {
            ret = ERROR_STREAM_CASTER_TS_PMT;
            srs_error("ts: mux PMT es info data failed. ret=%d", ret);
            return ret;
        }
        stream->write_bytes(ES_info, ES_info_length);
    }

    return ret;
}

SrsTsPayloadPMT::SrsTsPayloadPMT(SrsTsPacket* p) : SrsTsPayloadPSI(p)
{
    const1_value0 = 3;
    const1_value1 = 7;
    const1_value2 = 0x0f;
    program_info_length = 0;
    program_info_desc = NULL;
}

SrsTsPayloadPMT::~SrsTsPayloadPMT()
{
    srs_freep(program_info_desc);

    std::vector<SrsTsPayloadPMTESInfo*>::iterator it;
    for (it = infos.begin(); it != infos.end(); ++it) {
        SrsTsPayloadPMTESInfo* info = *it;
        srs_freep(info);
    }
    infos.clear();
}

int SrsTsPayloadPMT::psi_decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // atleast 9B for PMT specified
    if (!stream->require(9)) {
        ret = ERROR_STREAM_CASTER_TS_PMT;
        srs_error("ts: demux PMT failed. ret=%d", ret);
        return ret;
    }

    // 2B
    program_number = stream->read_2bytes();
    
    // 1B
    int8_t cniv = stream->read_1bytes();
    
    const1_value0 = (cniv >> 6) & 0x03;
    version_number = (cniv >> 1) & 0x1F;
    current_next_indicator = cniv & 0x01;
    
    // 1B
    section_number = stream->read_1bytes();
    
    // 1B
    last_section_number = stream->read_1bytes();

    // 2B
    int16_t ppv = stream->read_2bytes();
    const1_value1 = (ppv >> 13) & 0x07;
    PCR_PID = ppv & 0x1FFF;
    
    // 2B
    int16_t pilv = stream->read_2bytes();
    const1_value2 = (pilv >> 12) & 0x0F;
    program_info_length = pilv & 0xFFF;

    if (program_info_length > 0) {
        if (!stream->require(program_info_length)) {
            ret = ERROR_STREAM_CASTER_TS_PMT;
            srs_error("ts: demux PMT program info failed. ret=%d", ret);
            return ret;
        }

        srs_freep(program_info_desc);
        program_info_desc = new char[program_info_length];
        stream->read_bytes(program_info_desc, program_info_length);
    }

    // [section_length] - 4(CRC) - 9B - [program_info_length]
    int ES_EOF_pos = stream->pos() + section_length - 4 - 9 - program_info_length;
    while (stream->pos() < ES_EOF_pos) {
        SrsTsPayloadPMTESInfo* info = new SrsTsPayloadPMTESInfo();
        infos.push_back(info);

        if ((ret = info->decode(stream)) != ERROR_SUCCESS) {
            return ret;
        }

        // update the apply pid table
        switch (info->stream_type) {
            case SrsTsStreamVideoH264:
            case SrsTsStreamVideoMpeg4:
                packet->context->set(info->elementary_PID, SrsTsPidApplyVideo, info->stream_type);
                break;
            case SrsTsStreamAudioAAC:
            case SrsTsStreamAudioAC3:
            case SrsTsStreamAudioDTS:
            case SrsTsStreamAudioMp3:
                packet->context->set(info->elementary_PID, SrsTsPidApplyAudio, info->stream_type);
                break;
            default:
                srs_warn("ts: drop pid=%#x, stream=%#x", info->elementary_PID, info->stream_type);
                break;
        }
    }

    // update the apply pid table.
    packet->context->set(packet->pid, SrsTsPidApplyPMT);

    return ret;
}

int SrsTsPayloadPMT::psi_size()
{
    int sz = 9;
    sz += program_info_length;
    for (int i = 0; i < (int)infos.size(); i ++) {
        SrsTsPayloadPMTESInfo* info = infos.at(i);
        sz += info->size();
    }
    return sz;
}

int SrsTsPayloadPMT::psi_encode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // atleast 9B for PMT specified
    if (!stream->require(9)) {
        ret = ERROR_STREAM_CASTER_TS_PMT;
        srs_error("ts: mux PMT failed. ret=%d", ret);
        return ret;
    }

    // 2B
    stream->write_2bytes(program_number);
    
    // 1B
    int8_t cniv = current_next_indicator & 0x01;
    cniv |= (const1_value0 << 6) & 0xC0;
    cniv |= (version_number << 1) & 0xFE;
    stream->write_1bytes(cniv);
    
    // 1B
    stream->write_1bytes(section_number);
    
    // 1B
    stream->write_1bytes(last_section_number);

    // 2B
    int16_t ppv = PCR_PID & 0x1FFF;
    ppv |= (const1_value1 << 13) & 0xE000;
    stream->write_2bytes(ppv);
    
    // 2B
    int16_t pilv = program_info_length & 0xFFF;
    pilv |= (const1_value2 << 12) & 0xF000;
    stream->write_2bytes(pilv);

    if (program_info_length > 0) {
        if (!stream->require(program_info_length)) {
            ret = ERROR_STREAM_CASTER_TS_PMT;
            srs_error("ts: mux PMT program info failed. ret=%d", ret);
            return ret;
        }

        stream->write_bytes(program_info_desc, program_info_length);
    }

    for (int i = 0; i < (int)infos.size(); i ++) {
        SrsTsPayloadPMTESInfo* info = infos.at(i);
        if ((ret = info->encode(stream)) != ERROR_SUCCESS) {
            return ret;
        }

        // update the apply pid table
        switch (info->stream_type) {
            case SrsTsStreamVideoH264:
            case SrsTsStreamVideoMpeg4:
                packet->context->set(info->elementary_PID, SrsTsPidApplyVideo, info->stream_type);
                break;
            case SrsTsStreamAudioAAC:
            case SrsTsStreamAudioAC3:
            case SrsTsStreamAudioDTS:
            case SrsTsStreamAudioMp3:
                packet->context->set(info->elementary_PID, SrsTsPidApplyAudio, info->stream_type);
                break;
            default:
                srs_warn("ts: drop pid=%#x, stream=%#x", info->elementary_PID, info->stream_type);
                break;
        }
    }

    // update the apply pid table.
    packet->context->set(packet->pid, SrsTsPidApplyPMT);

    return ret;
}

SrsTSMuxer::SrsTSMuxer(SrsFileWriter* w, SrsTsContext* c, SrsCodecAudio ac, SrsCodecVideo vc)
{
    writer = w;
    context = c;

    acodec = ac;
    vcodec = vc;
}

SrsTSMuxer::~SrsTSMuxer()
{
    close();
}

int SrsTSMuxer::open(string _path)
{
    int ret = ERROR_SUCCESS;
    
    path = _path;
    
    close();
    
    // reset the context for a new ts start.
    context->reset();
    
    if ((ret = writer->open(path)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int SrsTSMuxer::update_acodec(SrsCodecAudio ac)
{
    acodec = ac;
    return ERROR_SUCCESS;
}

int SrsTSMuxer::write_audio(SrsTsMessage* audio)
{
    int ret = ERROR_SUCCESS;

    srs_info("hls: write audio pts=%"PRId64", dts=%"PRId64", size=%d", 
        audio->pts, audio->dts, audio->PES_packet_length);
    
    if ((ret = context->encode(writer, audio, vcodec, acodec)) != ERROR_SUCCESS) {
        srs_error("hls encode audio failed. ret=%d", ret);
        return ret;
    }
    srs_info("hls encode audio ok");
    
    return ret;
}

int SrsTSMuxer::write_video(SrsTsMessage* video)
{
    int ret = ERROR_SUCCESS;

    srs_info("hls: write video pts=%"PRId64", dts=%"PRId64", size=%d", 
        video->pts, video->dts, video->PES_packet_length);
    
    if ((ret = context->encode(writer, video, vcodec, acodec)) != ERROR_SUCCESS) {
        srs_error("hls encode video failed. ret=%d", ret);
        return ret;
    }
    srs_info("hls encode video ok");
    
    return ret;
}

void SrsTSMuxer::close()
{
    writer->close();
}

SrsTsCache::SrsTsCache()
{
    audio = NULL;
    video = NULL;
}

SrsTsCache::~SrsTsCache()
{
    srs_freep(audio);
    srs_freep(video);
}
    
int SrsTsCache::cache_audio(SrsAvcAacCodec* codec, int64_t dts, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;

    // create the ts audio message.
    if (!audio) {
        audio = new SrsTsMessage();
        audio->write_pcr = false;
        audio->start_pts = dts;
    }

    audio->dts = dts;
    audio->pts = audio->dts;
    audio->sid = SrsTsPESStreamIdAudioCommon;
    
    // must be aac or mp3
    SrsCodecAudio acodec = (SrsCodecAudio)codec->audio_codec_id;
    srs_assert(acodec == SrsCodecAudioAAC || acodec == SrsCodecAudioMP3);
    
    // write video to cache.
    if (codec->audio_codec_id == SrsCodecAudioAAC) {
        if ((ret = do_cache_aac(codec, sample)) != ERROR_SUCCESS) {
            return ret;
        }
    } else {
        if ((ret = do_cache_mp3(codec, sample)) != ERROR_SUCCESS) {
            return ret;
        }
    }
    
    return ret;
}
    
int SrsTsCache::cache_video(SrsAvcAacCodec* codec, int64_t dts, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
    
    // create the ts video message.
    if (!video) {
        video = new SrsTsMessage();
        video->write_pcr = sample->frame_type == SrsCodecVideoAVCFrameKeyFrame;
        video->start_pts = dts;
    }

    video->dts = dts;
    video->pts = video->dts + sample->cts * 90;
    video->sid = SrsTsPESStreamIdVideoCommon;
    
    // write video to cache.
    if ((ret = do_cache_avc(codec, sample)) != ERROR_SUCCESS) {
        return ret;
    }

    return ret;
}

int SrsTsCache::do_cache_mp3(SrsAvcAacCodec* codec, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
        
    // for mp3, directly write to cache.
    // TODO: FIXME: implements the ts jitter.
    for (int i = 0; i < sample->nb_sample_units; i++) {
        SrsCodecSampleUnit* sample_unit = &sample->sample_units[i];
        audio->payload->append(sample_unit->bytes, sample_unit->size);
    }
    
    return ret;
}

int SrsTsCache::do_cache_aac(SrsAvcAacCodec* codec, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
    
    for (int i = 0; i < sample->nb_sample_units; i++) {
        SrsCodecSampleUnit* sample_unit = &sample->sample_units[i];
        int32_t size = sample_unit->size;
        
        if (!sample_unit->bytes || size <= 0 || size > 0x1fff) {
            ret = ERROR_HLS_AAC_FRAME_LENGTH;
            srs_error("invalid aac frame length=%d, ret=%d", size, ret);
            return ret;
        }
        
        // the frame length is the AAC raw data plus the adts header size.
        int32_t frame_length = size + 7;
        
        // AAC-ADTS
        // 6.2 Audio Data Transport Stream, ADTS
        // in aac-iso-13818-7.pdf, page 26.
        // fixed 7bytes header
        u_int8_t adts_header[7] = {0xff, 0xf9, 0x00, 0x00, 0x00, 0x0f, 0xfc};
        /*
        // adts_fixed_header
        // 2B, 16bits
        int16_t syncword; //12bits, '1111 1111 1111'
        int8_t ID; //1bit, '1'
        int8_t layer; //2bits, '00'
        int8_t protection_absent; //1bit, can be '1'
        // 12bits
        int8_t profile; //2bit, 7.1 Profiles, page 40
        TSAacSampleFrequency sampling_frequency_index; //4bits, Table 35, page 46
        int8_t private_bit; //1bit, can be '0'
        int8_t channel_configuration; //3bits, Table 8
        int8_t original_or_copy; //1bit, can be '0'
        int8_t home; //1bit, can be '0'
        
        // adts_variable_header
        // 28bits
        int8_t copyright_identification_bit; //1bit, can be '0'
        int8_t copyright_identification_start; //1bit, can be '0'
        int16_t frame_length; //13bits
        int16_t adts_buffer_fullness; //11bits, 7FF signals that the bitstream is a variable rate bitstream.
        int8_t number_of_raw_data_blocks_in_frame; //2bits, 0 indicating 1 raw_data_block()
        */
        // profile, 2bits
        SrsAacProfile aac_profile = srs_codec_aac_rtmp2ts(codec->aac_object);
        adts_header[2] = (aac_profile << 6) & 0xc0;
        // sampling_frequency_index 4bits
        adts_header[2] |= (codec->aac_sample_rate << 2) & 0x3c;
        // channel_configuration 3bits
        adts_header[2] |= (codec->aac_channels >> 2) & 0x01;
        adts_header[3] = (codec->aac_channels << 6) & 0xc0;
        // frame_length 13bits
        adts_header[3] |= (frame_length >> 11) & 0x03;
        adts_header[4] = (frame_length >> 3) & 0xff;
        adts_header[5] = ((frame_length << 5) & 0xe0);
        // adts_buffer_fullness; //11bits
        adts_header[5] |= 0x1f;

        // copy to audio buffer
        audio->payload->append((const char*)adts_header, sizeof(adts_header));
        audio->payload->append(sample_unit->bytes, sample_unit->size);
    }
    
    return ret;
}

int SrsTsCache::do_cache_avc(SrsAvcAacCodec* codec, SrsCodecSample* sample)
{
    int ret = ERROR_SUCCESS;
    
    // mux the samples in annexb format,
    // H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 324.
    /**
     * 00 00 00 01 // header
     *       xxxxxxx // data bytes
     * 00 00 01 // continue header
     *       xxxxxxx // data bytes.
     *
     * nal_unit_type specifies the type of RBSP data structure contained in the NAL unit as specified in Table 7-1.
     * Table 7-1 - NAL unit type codes, syntax element categories, and NAL unit type classes
     * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
     *      1, Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp( )
     *      2, Coded slice data partition A slice_data_partition_a_layer_rbsp( )
     *      3, Coded slice data partition B slice_data_partition_b_layer_rbsp( )
     *      4, Coded slice data partition C slice_data_partition_c_layer_rbsp( )
     *      5, Coded slice of an IDR picture slice_layer_without_partitioning_rbsp( )
     *      6, Supplemental enhancement information (SEI) sei_rbsp( )
     *      7, Sequence parameter set seq_parameter_set_rbsp( )
     *      8, Picture parameter set pic_parameter_set_rbsp( )
     *      9, Access unit delimiter access_unit_delimiter_rbsp( )
     *      10, End of sequence end_of_seq_rbsp( )
     *      11, End of stream end_of_stream_rbsp( )
     *      12, Filler data filler_data_rbsp( )
     *      13, Sequence parameter set extension seq_parameter_set_extension_rbsp( )
     *      14, Prefix NAL unit prefix_nal_unit_rbsp( )
     *      15, Subset sequence parameter set subset_seq_parameter_set_rbsp( )
     *      19, Coded slice of an auxiliary coded picture without partitioning slice_layer_without_partitioning_rbsp( )
     *      20, Coded slice extension slice_layer_extension_rbsp( )
     * the first ts message of apple sample:
     *      annexb 4B header, 2B aud(nal_unit_type:6)(0x09 0xf0)
     *      annexb 4B header, 19B sps(nal_unit_type:7)
     *      annexb 3B header, 4B pps(nal_unit_type:8)
     *      annexb 3B header, 12B nalu(nal_unit_type:6)
     *      annexb 3B header, 21B nalu(nal_unit_type:6)
     *      annexb 3B header, 2762B nalu(nal_unit_type:5)
     *      annexb 3B header, 3535B nalu(nal_unit_type:5)
     * the second ts message of apple ts sample:
     *      annexb 4B header, 2B aud(nal_unit_type:6)(0x09 0xf0)
     *      annexb 3B header, 21B nalu(nal_unit_type:6)
     *      annexb 3B header, 379B nalu(nal_unit_type:1)
     *      annexb 3B header, 406B nalu(nal_unit_type:1)
     */
    static u_int8_t fresh_nalu_header[] = { 0x00, 0x00, 0x00, 0x01 };
    static u_int8_t cont_nalu_header[] = { 0x00, 0x00, 0x01 };
    
    // the aud(access unit delimiter) before each frame.
    // 7.3.2.4 Access unit delimiter RBSP syntax
    // H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 66.
    //
    // primary_pic_type u(3), the first 3bits, primary_pic_type indicates that the slice_type values
    //      for all slices of the primary coded picture are members of the set listed in Table 7-5 for
    //      the given value of primary_pic_type.
    //      0, slice_type 2, 7
    //      1, slice_type 0, 2, 5, 7
    //      2, slice_type 0, 1, 2, 5, 6, 7
    //      3, slice_type 4, 9
    //      4, slice_type 3, 4, 8, 9
    //      5, slice_type 2, 4, 7, 9
    //      6, slice_type 0, 2, 3, 4, 5, 7, 8, 9
    //      7, slice_type 0, 1, 2, 3, 4, 5, 6, 7, 8, 9
    // 7.4.2.4 Access unit delimiter RBSP semantics
    // H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 102.
    //
    // slice_type specifies the coding type of the slice according to Table 7-6.
    //      0, P (P slice)
    //      1, B (B slice)
    //      2, I (I slice)
    //      3, SP (SP slice)
    //      4, SI (SI slice)
    //      5, P (P slice)
    //      6, B (B slice)
    //      7, I (I slice)
    //      8, SP (SP slice)
    //      9, SI (SI slice)
    // H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 105.
    static u_int8_t aud_nalu_7[] = { 0x09, 0xf0};
    
    // always append a aud nalu for each frame.
    video->payload->append((const char*)fresh_nalu_header, 4);
    video->payload->append((const char*)aud_nalu_7, 2);
    
    // when ts message(samples) contains IDR, insert sps+pps.
    if (sample->has_idr) {
        // fresh nalu header before sps.
        if (codec->sequenceParameterSetLength > 0) {
            // AnnexB prefix, for sps always 4 bytes header
            video->payload->append((const char*)fresh_nalu_header, 4);
            // sps
            video->payload->append(codec->sequenceParameterSetNALUnit, codec->sequenceParameterSetLength);
        }
        // cont nalu header before pps.
        if (codec->pictureParameterSetLength > 0) {
            // AnnexB prefix, for pps always 3 bytes header
            video->payload->append((const char*)cont_nalu_header, 3);
            // pps
            video->payload->append(codec->pictureParameterSetNALUnit, codec->pictureParameterSetLength);
        }
    }
    
    // all sample use cont nalu header, except the sps-pps before IDR frame.
    for (int i = 0; i < sample->nb_sample_units; i++) {
        SrsCodecSampleUnit* sample_unit = &sample->sample_units[i];
        int32_t size = sample_unit->size;
        
        if (!sample_unit->bytes || size <= 0) {
            ret = ERROR_HLS_AVC_SAMPLE_SIZE;
            srs_error("invalid avc sample length=%d, ret=%d", size, ret);
            return ret;
        }
        
        // 5bits, 7.3.1 NAL unit syntax,
        // H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
        SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(sample_unit->bytes[0] & 0x1f);
        
        // ignore SPS/PPS/AUD
        switch (nal_unit_type) {
            case SrsAvcNaluTypeSPS:
            case SrsAvcNaluTypePPS:
            case SrsAvcNaluTypeAccessUnitDelimiter:
                continue;
            default:
                break;
        }
        
        // insert cont nalu header before frame.
        video->payload->append((const char*)cont_nalu_header, 3);
        
        // sample data
        video->payload->append(sample_unit->bytes, sample_unit->size);
    }
    
    return ret;
}

SrsTsEncoder::SrsTsEncoder()
{
    _fs = NULL;
    codec = new SrsAvcAacCodec();
    sample = new SrsCodecSample();
    cache = new SrsTsCache();
    context = new SrsTsContext();
    muxer = NULL;
}

SrsTsEncoder::~SrsTsEncoder()
{
    srs_freep(codec);
    srs_freep(sample);
    srs_freep(cache);
    srs_freep(muxer);
    srs_freep(context);
}

int SrsTsEncoder::initialize(SrsFileWriter* fs)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(fs);
    
    if (!fs->is_open()) {
        ret = ERROR_KERNEL_FLV_STREAM_CLOSED;
        srs_warn("stream is not open for encoder. ret=%d", ret);
        return ret;
    }
    
    _fs = fs;

    srs_freep(muxer);
    muxer = new SrsTSMuxer(fs, context, SrsCodecAudioAAC, SrsCodecVideoAVC);

    if ((ret = muxer->open("")) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int SrsTsEncoder::write_audio(int64_t timestamp, char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    sample->clear();
    if ((ret = codec->audio_aac_demux(data, size, sample)) != ERROR_SUCCESS) {
        if (ret != ERROR_HLS_TRY_MP3) {
            srs_error("http: ts aac demux audio failed. ret=%d", ret);
            return ret;
        }
        if ((ret = codec->audio_mp3_demux(data, size, sample)) != ERROR_SUCCESS) {
            srs_error("http: ts mp3 demux audio failed. ret=%d", ret);
            return ret;
        }
    }
    SrsCodecAudio acodec = (SrsCodecAudio)codec->audio_codec_id;
    
    // ts support audio codec: aac/mp3
    if (acodec != SrsCodecAudioAAC && acodec != SrsCodecAudioMP3) {
        return ret;
    }

    // when codec changed, write new header.
    if ((ret = muxer->update_acodec(acodec)) != ERROR_SUCCESS) {
        srs_error("http: ts audio write header failed. ret=%d", ret);
        return ret;
    }
    
    // for aac: ignore sequence header
    if (acodec == SrsCodecAudioAAC && sample->aac_packet_type == SrsCodecAudioTypeSequenceHeader) {
        return ret;
    }

    // the dts calc from rtmp/flv header.
    // @remark for http ts stream, the timestamp is always monotonically increase,
    //      for the packet is filtered by consumer.
    int64_t dts = timestamp * 90;
    
    // write audio to cache.
    if ((ret = cache->cache_audio(codec, dts, sample)) != ERROR_SUCCESS) {
        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;
}

int SrsTsEncoder::write_video(int64_t timestamp, char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    sample->clear();
    if ((ret = codec->video_avc_demux(data, size, sample)) != ERROR_SUCCESS) {
        srs_error("http: ts codec demux video failed. ret=%d", ret);
        return ret;
    }
    
    // ignore info frame,
    // @see https://github.com/simple-rtmp-server/srs/issues/288#issuecomment-69863909
    if (sample->frame_type == SrsCodecVideoAVCFrameVideoInfoFrame) {
        return ret;
    }
    
    if (codec->video_codec_id != SrsCodecVideoAVC) {
        return ret;
    }
    
    // ignore sequence header
    if (sample->frame_type == SrsCodecVideoAVCFrameKeyFrame
         && sample->avc_packet_type == SrsCodecVideoAVCTypeSequenceHeader) {
        return ret;
    }
    
    int64_t dts = timestamp * 90;
    
    // write video to cache.
    if ((ret = cache->cache_video(codec, dts, sample)) != ERROR_SUCCESS) {
        return ret;
    }

    return flush_video();
}

int SrsTsEncoder::flush_audio()
{
    int ret = ERROR_SUCCESS;

    if ((ret = muxer->write_audio(cache->audio)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // write success, clear and free the ts message.
    srs_freep(cache->audio);

    return ret;
}

int SrsTsEncoder::flush_video()
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = muxer->write_video(cache->video)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // write success, clear and free the ts message.
    srs_freep(cache->video);

    return ret;
}


// following is generated by src/kernel/srs_kernel_buffer.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 <srs_kernel_buffer.hpp>

//#include <srs_kernel_error.hpp>
//#include <srs_kernel_log.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_core_performance.hpp>

SrsSimpleBuffer::SrsSimpleBuffer()
{
}

SrsSimpleBuffer::~SrsSimpleBuffer()
{
}

int SrsSimpleBuffer::length()
{
    int len = (int)data.size();
    srs_assert(len >= 0);
    return len;
}

char* SrsSimpleBuffer::bytes()
{
    return (length() == 0)? NULL : &data.at(0);
}

void SrsSimpleBuffer::erase(int size)
{
    if (size <= 0) {
        return;
    }
    
    if (size >= length()) {
        data.clear();
        return;
    }
    
    data.erase(data.begin(), data.begin() + size);
}

void SrsSimpleBuffer::append(const char* bytes, int size)
{
    srs_assert(size > 0);

    data.insert(data.end(), bytes, bytes + size);
}
// following is generated by src/protocol/srs_rtmp_amf0.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 <srs_rtmp_amf0.hpp>

#include <utility>
#include <vector>
#include <sstream>
using namespace std;

//#include <srs_kernel_log.hpp>
//#include <srs_kernel_error.hpp>
//#include <srs_kernel_stream.hpp>

using namespace _srs_internal;

// AMF0 marker
#define RTMP_AMF0_Number                     0x00
#define RTMP_AMF0_Boolean                     0x01
#define RTMP_AMF0_String                     0x02
#define RTMP_AMF0_Object                     0x03
#define RTMP_AMF0_MovieClip                 0x04 // reserved, not supported
#define RTMP_AMF0_Null                         0x05
#define RTMP_AMF0_Undefined                 0x06
#define RTMP_AMF0_Reference                 0x07
#define RTMP_AMF0_EcmaArray                 0x08
#define RTMP_AMF0_ObjectEnd                 0x09
#define RTMP_AMF0_StrictArray                 0x0A
#define RTMP_AMF0_Date                         0x0B
#define RTMP_AMF0_LongString                 0x0C
#define RTMP_AMF0_UnSupported                 0x0D
#define RTMP_AMF0_RecordSet                 0x0E // reserved, not supported
#define RTMP_AMF0_XmlDocument                 0x0F
#define RTMP_AMF0_TypedObject                 0x10
// AVM+ object is the AMF3 object.
#define RTMP_AMF0_AVMplusObject             0x11
// origin array whos data takes the same form as LengthValueBytes
#define RTMP_AMF0_OriginStrictArray         0x20

// User defined
#define RTMP_AMF0_Invalid                     0x3F

SrsAmf0Any::SrsAmf0Any()
{
    marker = RTMP_AMF0_Invalid;
}

SrsAmf0Any::~SrsAmf0Any()
{
}

bool SrsAmf0Any::is_string()
{
    return marker == RTMP_AMF0_String;
}

bool SrsAmf0Any::is_boolean()
{
    return marker == RTMP_AMF0_Boolean;
}

bool SrsAmf0Any::is_number()
{
    return marker == RTMP_AMF0_Number;
}

bool SrsAmf0Any::is_null()
{
    return marker == RTMP_AMF0_Null;
}

bool SrsAmf0Any::is_undefined()
{
    return marker == RTMP_AMF0_Undefined;
}

bool SrsAmf0Any::is_object()
{
    return marker == RTMP_AMF0_Object;
}

bool SrsAmf0Any::is_ecma_array()
{
    return marker == RTMP_AMF0_EcmaArray;
}

bool SrsAmf0Any::is_strict_array()
{
    return marker == RTMP_AMF0_StrictArray;
}

bool SrsAmf0Any::is_date()
{
    return marker == RTMP_AMF0_Date;
}

bool SrsAmf0Any::is_complex_object()
{
    return is_object() || is_object_eof() || is_ecma_array() || is_strict_array();
}

string SrsAmf0Any::to_str()
{
    SrsAmf0String* p = dynamic_cast<SrsAmf0String*>(this);
    srs_assert(p != NULL);
    return p->value;
}

const char* SrsAmf0Any::to_str_raw()
{
    SrsAmf0String* p = dynamic_cast<SrsAmf0String*>(this);
    srs_assert(p != NULL);
    return p->value.data();
}

bool SrsAmf0Any::to_boolean()
{
    SrsAmf0Boolean* p = dynamic_cast<SrsAmf0Boolean*>(this);
    srs_assert(p != NULL);
    return p->value;
}

double SrsAmf0Any::to_number()
{
    SrsAmf0Number* p = dynamic_cast<SrsAmf0Number*>(this);
    srs_assert(p != NULL);
    return p->value;
}

int64_t SrsAmf0Any::to_date()
{
    SrsAmf0Date* p = dynamic_cast<SrsAmf0Date*>(this);
    srs_assert(p != NULL);
    return p->date();
}

int16_t SrsAmf0Any::to_date_time_zone()
{
    SrsAmf0Date* p = dynamic_cast<SrsAmf0Date*>(this);
    srs_assert(p != NULL);
    return p->time_zone();
}

SrsAmf0Object* SrsAmf0Any::to_object()
{
    SrsAmf0Object* p = dynamic_cast<SrsAmf0Object*>(this);
    srs_assert(p != NULL);
    return p;
}

SrsAmf0EcmaArray* SrsAmf0Any::to_ecma_array()
{
    SrsAmf0EcmaArray* p = dynamic_cast<SrsAmf0EcmaArray*>(this);
    srs_assert(p != NULL);
    return p;
}

SrsAmf0StrictArray* SrsAmf0Any::to_strict_array()
{
    SrsAmf0StrictArray* p = dynamic_cast<SrsAmf0StrictArray*>(this);
    srs_assert(p != NULL);
    return p;
}

void SrsAmf0Any::set_number(double value)
{
    SrsAmf0Number* p = dynamic_cast<SrsAmf0Number*>(this);
    srs_assert(p != NULL);
    p->value = value;
}

bool SrsAmf0Any::is_object_eof()
{
    return marker == RTMP_AMF0_ObjectEnd;
}

void srs_fill_level_spaces(stringstream& ss, int level)
{
    for (int i = 0; i < level; i++) {
        ss << "    ";
    }
}
void srs_amf0_do_print(SrsAmf0Any* any, stringstream& ss, int level)
{
    if (any->is_boolean()) {
        ss << "Boolean " << (any->to_boolean()? "true":"false") << endl;
    } else if (any->is_number()) {
        ss << "Number " << std::fixed << any->to_number() << endl;
    } else if (any->is_string()) {
        ss << "String " << any->to_str() << endl;
    } else if (any->is_date()) {
        ss << "Date " << std::hex << any->to_date() 
            << "/" << std::hex << any->to_date_time_zone() << endl;
    } else if (any->is_null()) {
        ss << "Null" << endl;
    } else if (any->is_ecma_array()) {
        SrsAmf0EcmaArray* obj = any->to_ecma_array();
        ss << "EcmaArray " << "(" << obj->count() << " items)" << endl;
        for (int i = 0; i < obj->count(); i++) {
            srs_fill_level_spaces(ss, level + 1);
            ss << "Elem '" << obj->key_at(i) << "' ";
            if (obj->value_at(i)->is_complex_object()) {
                srs_amf0_do_print(obj->value_at(i), ss, level + 1);
            } else {
                srs_amf0_do_print(obj->value_at(i), ss, 0);
            }
        }
    } else if (any->is_strict_array()) {
        SrsAmf0StrictArray* obj = any->to_strict_array();
        ss << "StrictArray " << "(" << obj->count() << " items)" << endl;
        for (int i = 0; i < obj->count(); i++) {
            srs_fill_level_spaces(ss, level + 1);
            ss << "Elem ";
            if (obj->at(i)->is_complex_object()) {
                srs_amf0_do_print(obj->at(i), ss, level + 1);
            } else {
                srs_amf0_do_print(obj->at(i), ss, 0);
            }
        }
    } else if (any->is_object()) {
        SrsAmf0Object* obj = any->to_object();
        ss << "Object " << "(" << obj->count() << " items)" << endl;
        for (int i = 0; i < obj->count(); i++) {
            srs_fill_level_spaces(ss, level + 1);
            ss << "Property '" << obj->key_at(i) << "' ";
            if (obj->value_at(i)->is_complex_object()) {
                srs_amf0_do_print(obj->value_at(i), ss, level + 1);
            } else {
                srs_amf0_do_print(obj->value_at(i), ss, 0);
            }
        }
    } else {
        ss << "Unknown" << endl;
    }
}

char* SrsAmf0Any::human_print(char** pdata, int* psize)
{
    stringstream ss;
    
    ss.precision(1);
    
    srs_amf0_do_print(this, ss, 0);
    
    string str = ss.str();
    if (str.empty()) {
        return NULL;
    }
    
    char* data = new char[str.length() + 1];
    memcpy(data, str.data(), str.length());
    data[str.length()] = 0;
    
    if (pdata) {
        *pdata = data;
    }
    if (psize) {
        *psize = str.length();
    }
    
    return data;
}

SrsAmf0Any* SrsAmf0Any::str(const char* value)
{
    return new SrsAmf0String(value);
}

SrsAmf0Any* SrsAmf0Any::boolean(bool value)
{
    return new SrsAmf0Boolean(value);
}

SrsAmf0Any* SrsAmf0Any::number(double value)
{
    return new SrsAmf0Number(value);
}

SrsAmf0Any* SrsAmf0Any::null()
{
    return new SrsAmf0Null();
}

SrsAmf0Any* SrsAmf0Any::undefined()
{
    return new SrsAmf0Undefined();
}

SrsAmf0Object* SrsAmf0Any::object()
{
    return new SrsAmf0Object();
}

SrsAmf0Any* SrsAmf0Any::object_eof()
{
    return new SrsAmf0ObjectEOF();
}

SrsAmf0EcmaArray* SrsAmf0Any::ecma_array()
{
    return new SrsAmf0EcmaArray();
}

SrsAmf0StrictArray* SrsAmf0Any::strict_array()
{
    return new SrsAmf0StrictArray();
}

SrsAmf0Any* SrsAmf0Any::date(int64_t value)
{
    return new SrsAmf0Date(value);
}

int SrsAmf0Any::discovery(SrsStream* stream, SrsAmf0Any** ppvalue)
{
    int ret = ERROR_SUCCESS;
    
    // detect the object-eof specially
    if (srs_amf0_is_object_eof(stream)) {
        *ppvalue = new SrsAmf0ObjectEOF();
        return ret;
    }
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read any marker failed. ret=%d", ret);
        return ret;
    }
    
    char marker = stream->read_1bytes();
    srs_verbose("amf0 any marker success");
    
    // backward the 1byte marker.
    stream->skip(-1);
    
    switch (marker) {
        case RTMP_AMF0_String: {
            *ppvalue = SrsAmf0Any::str();
            return ret;
        }
        case RTMP_AMF0_Boolean: {
            *ppvalue = SrsAmf0Any::boolean();
            return ret;
        }
        case RTMP_AMF0_Number: {
            *ppvalue = SrsAmf0Any::number();
            return ret;
        }
        case RTMP_AMF0_Null: {
            *ppvalue = SrsAmf0Any::null();
            return ret;
        }
        case RTMP_AMF0_Undefined: {
            *ppvalue = SrsAmf0Any::undefined();
            return ret;
        }
        case RTMP_AMF0_Object: {
            *ppvalue = SrsAmf0Any::object();
            return ret;
        }
        case RTMP_AMF0_EcmaArray: {
            *ppvalue = SrsAmf0Any::ecma_array();
            return ret;
        }
        case RTMP_AMF0_StrictArray: {
            *ppvalue = SrsAmf0Any::strict_array();
            return ret;
        }
        case RTMP_AMF0_Date: {
            *ppvalue = SrsAmf0Any::date();
            return ret;
        }
        case RTMP_AMF0_Invalid:
        default: {
            ret = ERROR_RTMP_AMF0_INVALID;
            srs_error("invalid amf0 message type. marker=%#x, ret=%d", marker, ret);
            return ret;
        }
    }
}

SrsUnSortedHashtable::SrsUnSortedHashtable()
{
}

SrsUnSortedHashtable::~SrsUnSortedHashtable()
{
    clear();
}

int SrsUnSortedHashtable::count()
{
    return (int)properties.size();
}

void SrsUnSortedHashtable::clear()
{
    std::vector<SrsAmf0ObjectPropertyType>::iterator it;
    for (it = properties.begin(); it != properties.end(); ++it) {
        SrsAmf0ObjectPropertyType& elem = *it;
        SrsAmf0Any* any = elem.second;
        srs_freep(any);
    }
    properties.clear();
}

string SrsUnSortedHashtable::key_at(int index)
{
    srs_assert(index < count());
    SrsAmf0ObjectPropertyType& elem = properties[index];
    return elem.first;
}

const char* SrsUnSortedHashtable::key_raw_at(int index)
{
    srs_assert(index < count());
    SrsAmf0ObjectPropertyType& elem = properties[index];
    return elem.first.data();
}

SrsAmf0Any* SrsUnSortedHashtable::value_at(int index)
{
    srs_assert(index < count());
    SrsAmf0ObjectPropertyType& elem = properties[index];
    return elem.second;
}

void SrsUnSortedHashtable::set(string key, SrsAmf0Any* value)
{
    std::vector<SrsAmf0ObjectPropertyType>::iterator it;
    
    for (it = properties.begin(); it != properties.end(); ++it) {
        SrsAmf0ObjectPropertyType& elem = *it;
        std::string name = elem.first;
        SrsAmf0Any* any = elem.second;
        
        if (key == name) {
            srs_freep(any);
            properties.erase(it);
            break;
        }
    }
    
    if (value) {
        properties.push_back(std::make_pair(key, value));
    }
}

SrsAmf0Any* SrsUnSortedHashtable::get_property(string name)
{
    std::vector<SrsAmf0ObjectPropertyType>::iterator it;
    
    for (it = properties.begin(); it != properties.end(); ++it) {
        SrsAmf0ObjectPropertyType& elem = *it;
        std::string key = elem.first;
        SrsAmf0Any* any = elem.second;
        if (key == name) {
            return any;
        }
    }
    
    return NULL;
}

SrsAmf0Any* SrsUnSortedHashtable::ensure_property_string(string name)
{
    SrsAmf0Any* prop = get_property(name);
    
    if (!prop) {
        return NULL;
    }
    
    if (!prop->is_string()) {
        return NULL;
    }
    
    return prop;
}

SrsAmf0Any* SrsUnSortedHashtable::ensure_property_number(string name)
{
    SrsAmf0Any* prop = get_property(name);
    
    if (!prop) {
        return NULL;
    }
    
    if (!prop->is_number()) {
        return NULL;
    }
    
    return prop;
}

void SrsUnSortedHashtable::copy(SrsUnSortedHashtable* src)
{
    std::vector<SrsAmf0ObjectPropertyType>::iterator it;
    for (it = src->properties.begin(); it != src->properties.end(); ++it) {
        SrsAmf0ObjectPropertyType& elem = *it;
        std::string key = elem.first;
        SrsAmf0Any* any = elem.second;
        set(key, any->copy());
    }
}

SrsAmf0ObjectEOF::SrsAmf0ObjectEOF()
{
    marker = RTMP_AMF0_ObjectEnd;
}

SrsAmf0ObjectEOF::~SrsAmf0ObjectEOF()
{
}

int SrsAmf0ObjectEOF::total_size()
{
    return SrsAmf0Size::object_eof();
}

int SrsAmf0ObjectEOF::read(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // value
    if (!stream->require(2)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read object eof value failed. ret=%d", ret);
        return ret;
    }
    int16_t temp = stream->read_2bytes();
    if (temp != 0x00) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read object eof value check failed. "
            "must be 0x00, actual is %#x, ret=%d", temp, ret);
        return ret;
    }
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read object eof marker failed. ret=%d", ret);
        return ret;
    }
    
    char marker = stream->read_1bytes();
    if (marker != RTMP_AMF0_ObjectEnd) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 check object eof marker failed. "
            "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_ObjectEnd, ret);
        return ret;
    }
    srs_verbose("amf0 read object eof marker success");
    
    srs_verbose("amf0 read object eof success");
    
    return ret;
}
int SrsAmf0ObjectEOF::write(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // value
    if (!stream->require(2)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write object eof value failed. ret=%d", ret);
        return ret;
    }
    stream->write_2bytes(0x00);
    srs_verbose("amf0 write object eof value success");
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write object eof marker failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_1bytes(RTMP_AMF0_ObjectEnd);
    
    srs_verbose("amf0 read object eof success");
    
    return ret;
}

SrsAmf0Any* SrsAmf0ObjectEOF::copy()
{
    return new SrsAmf0ObjectEOF();
}

SrsAmf0Object::SrsAmf0Object()
{
    properties = new SrsUnSortedHashtable();
    eof = new SrsAmf0ObjectEOF();
    marker = RTMP_AMF0_Object;
}

SrsAmf0Object::~SrsAmf0Object()
{
    srs_freep(properties);
    srs_freep(eof);
}

int SrsAmf0Object::total_size()
{
    int size = 1;
    
    for (int i = 0; i < properties->count(); i++){
        std::string name = key_at(i);
        SrsAmf0Any* value = value_at(i);
        
        size += SrsAmf0Size::utf8(name);
        size += SrsAmf0Size::any(value);
    }
    
    size += SrsAmf0Size::object_eof();
    
    return size;
}

int SrsAmf0Object::read(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read object marker failed. ret=%d", ret);
        return ret;
    }
    
    char marker = stream->read_1bytes();
    if (marker != RTMP_AMF0_Object) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 check object marker failed. "
            "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Object, ret);
        return ret;
    }
    srs_verbose("amf0 read object marker success");
    
    // value
    while (!stream->empty()) {
        // detect whether is eof.
        if (srs_amf0_is_object_eof(stream)) {
            SrsAmf0ObjectEOF pbj_eof;
            if ((ret = pbj_eof.read(stream)) != ERROR_SUCCESS) {
                srs_error("amf0 object read eof failed. ret=%d", ret);
                return ret;
            }
            srs_info("amf0 read object EOF.");
            break;
        }
        
        // property-name: utf8 string
        std::string property_name;
        if ((ret = srs_amf0_read_utf8(stream, property_name)) != ERROR_SUCCESS) {
            srs_error("amf0 object read property name failed. ret=%d", ret);
            return ret;
        }
        // property-value: any
        SrsAmf0Any* property_value = NULL;
        if ((ret = srs_amf0_read_any(stream, &property_value)) != ERROR_SUCCESS) {
            srs_error("amf0 object read property_value failed. "
                "name=%s, ret=%d", property_name.c_str(), ret);
            srs_freep(property_value);
            return ret;
        }
        
        // add property
        this->set(property_name, property_value);
    }
    
    return ret;
}

int SrsAmf0Object::write(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write object marker failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_1bytes(RTMP_AMF0_Object);
    srs_verbose("amf0 write object marker success");
    
    // value
    for (int i = 0; i < properties->count(); i++) {
        std::string name = this->key_at(i);
        SrsAmf0Any* any = this->value_at(i);
        
        if ((ret = srs_amf0_write_utf8(stream, name)) != ERROR_SUCCESS) {
            srs_error("write object property name failed. ret=%d", ret);
            return ret;
        }
        
        if ((ret = srs_amf0_write_any(stream, any)) != ERROR_SUCCESS) {
            srs_error("write object property value failed. ret=%d", ret);
            return ret;
        }
        
        srs_verbose("write amf0 property success. name=%s", name.c_str());
    }
    
    if ((ret = eof->write(stream)) != ERROR_SUCCESS) {
        srs_error("write object eof failed. ret=%d", ret);
        return ret;
    }
    
    srs_verbose("write amf0 object success.");
    
    return ret;
}

SrsAmf0Any* SrsAmf0Object::copy()
{
    SrsAmf0Object* copy = new SrsAmf0Object();
    copy->properties->copy(properties);
    return copy;
}

void SrsAmf0Object::clear()
{
    properties->clear();
}

int SrsAmf0Object::count()
{
    return properties->count();
}

string SrsAmf0Object::key_at(int index)
{
    return properties->key_at(index);
}

const char* SrsAmf0Object::key_raw_at(int index)
{
    return properties->key_raw_at(index);
}

SrsAmf0Any* SrsAmf0Object::value_at(int index)
{
    return properties->value_at(index);
}

void SrsAmf0Object::set(string key, SrsAmf0Any* value)
{
    properties->set(key, value);
}

SrsAmf0Any* SrsAmf0Object::get_property(string name)
{
    return properties->get_property(name);
}

SrsAmf0Any* SrsAmf0Object::ensure_property_string(string name)
{
    return properties->ensure_property_string(name);
}

SrsAmf0Any* SrsAmf0Object::ensure_property_number(string name)
{
    return properties->ensure_property_number(name);
}

SrsAmf0EcmaArray::SrsAmf0EcmaArray()
{
    _count = 0;
    properties = new SrsUnSortedHashtable();
    eof = new SrsAmf0ObjectEOF();
    marker = RTMP_AMF0_EcmaArray;
}

SrsAmf0EcmaArray::~SrsAmf0EcmaArray()
{
    srs_freep(properties);
    srs_freep(eof);
}

int SrsAmf0EcmaArray::total_size()
{
    int size = 1 + 4;
    
    for (int i = 0; i < properties->count(); i++){
        std::string name = key_at(i);
        SrsAmf0Any* value = value_at(i);
        
        size += SrsAmf0Size::utf8(name);
        size += SrsAmf0Size::any(value);
    }
    
    size += SrsAmf0Size::object_eof();
    
    return size;
}

int SrsAmf0EcmaArray::read(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read ecma_array marker failed. ret=%d", ret);
        return ret;
    }
    
    char marker = stream->read_1bytes();
    if (marker != RTMP_AMF0_EcmaArray) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 check ecma_array marker failed. "
            "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_EcmaArray, ret);
        return ret;
    }
    srs_verbose("amf0 read ecma_array marker success");

    // count
    if (!stream->require(4)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read ecma_array count failed. ret=%d", ret);
        return ret;
    }
    
    int32_t count = stream->read_4bytes();
    srs_verbose("amf0 read ecma_array count success. count=%d", count);
    
    // value
    this->_count = count;

    while (!stream->empty()) {
        // detect whether is eof.
        if (srs_amf0_is_object_eof(stream)) {
            SrsAmf0ObjectEOF pbj_eof;
            if ((ret = pbj_eof.read(stream)) != ERROR_SUCCESS) {
                srs_error("amf0 ecma_array read eof failed. ret=%d", ret);
                return ret;
            }
            srs_info("amf0 read ecma_array EOF.");
            break;
        }
        
        // property-name: utf8 string
        std::string property_name;
        if ((ret =srs_amf0_read_utf8(stream, property_name)) != ERROR_SUCCESS) {
            srs_error("amf0 ecma_array read property name failed. ret=%d", ret);
            return ret;
        }
        // property-value: any
        SrsAmf0Any* property_value = NULL;
        if ((ret = srs_amf0_read_any(stream, &property_value)) != ERROR_SUCCESS) {
            srs_error("amf0 ecma_array read property_value failed. "
                "name=%s, ret=%d", property_name.c_str(), ret);
            return ret;
        }
        
        // add property
        this->set(property_name, property_value);
    }
    
    return ret;
}
int SrsAmf0EcmaArray::write(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write ecma_array marker failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_1bytes(RTMP_AMF0_EcmaArray);
    srs_verbose("amf0 write ecma_array marker success");

    // count
    if (!stream->require(4)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write ecma_array count failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_4bytes(this->_count);
    srs_verbose("amf0 write ecma_array count success. count=%d", _count);
    
    // value
    for (int i = 0; i < properties->count(); i++) {
        std::string name = this->key_at(i);
        SrsAmf0Any* any = this->value_at(i);
        
        if ((ret = srs_amf0_write_utf8(stream, name)) != ERROR_SUCCESS) {
            srs_error("write ecma_array property name failed. ret=%d", ret);
            return ret;
        }
        
        if ((ret = srs_amf0_write_any(stream, any)) != ERROR_SUCCESS) {
            srs_error("write ecma_array property value failed. ret=%d", ret);
            return ret;
        }
        
        srs_verbose("write amf0 property success. name=%s", name.c_str());
    }
    
    if ((ret = eof->write(stream)) != ERROR_SUCCESS) {
        srs_error("write ecma_array eof failed. ret=%d", ret);
        return ret;
    }
    
    srs_verbose("write ecma_array object success.");
    
    return ret;
}

SrsAmf0Any* SrsAmf0EcmaArray::copy()
{
    SrsAmf0EcmaArray* copy = new SrsAmf0EcmaArray();
    copy->properties->copy(properties);
    copy->_count = _count;
    return copy;
}

void SrsAmf0EcmaArray::clear()
{
    properties->clear();
}

int SrsAmf0EcmaArray::count()
{
    return properties->count();
}

string SrsAmf0EcmaArray::key_at(int index)
{
    return properties->key_at(index);
}

const char* SrsAmf0EcmaArray::key_raw_at(int index)
{
    return properties->key_raw_at(index);
}

SrsAmf0Any* SrsAmf0EcmaArray::value_at(int index)
{
    return properties->value_at(index);
}

void SrsAmf0EcmaArray::set(string key, SrsAmf0Any* value)
{
    properties->set(key, value);
}

SrsAmf0Any* SrsAmf0EcmaArray::get_property(string name)
{
    return properties->get_property(name);
}

SrsAmf0Any* SrsAmf0EcmaArray::ensure_property_string(string name)
{
    return properties->ensure_property_string(name);
}

SrsAmf0Any* SrsAmf0EcmaArray::ensure_property_number(string name)
{
    return properties->ensure_property_number(name);
}

SrsAmf0StrictArray::SrsAmf0StrictArray()
{
    marker = RTMP_AMF0_StrictArray;
    _count = 0;
}

SrsAmf0StrictArray::~SrsAmf0StrictArray()
{
    std::vector<SrsAmf0Any*>::iterator it;
    for (it = properties.begin(); it != properties.end(); ++it) {
        SrsAmf0Any* any = *it;
        srs_freep(any);
    }
    properties.clear();
}

int SrsAmf0StrictArray::total_size()
{
    int size = 1 + 4;
    
    for (int i = 0; i < (int)properties.size(); i++){
        SrsAmf0Any* any = properties[i];
        size += any->total_size();
    }
    
    return size;
}

int SrsAmf0StrictArray::read(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read strict_array marker failed. ret=%d", ret);
        return ret;
    }
    
    char marker = stream->read_1bytes();
    if (marker != RTMP_AMF0_StrictArray) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 check strict_array marker failed. "
            "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_StrictArray, ret);
        return ret;
    }
    srs_verbose("amf0 read strict_array marker success");

    // count
    if (!stream->require(4)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read strict_array count failed. ret=%d", ret);
        return ret;
    }
    
    int32_t count = stream->read_4bytes();
    srs_verbose("amf0 read strict_array count success. count=%d", count);
    
    // value
    this->_count = count;

    for (int i = 0; i < count && !stream->empty(); i++) {
        // property-value: any
        SrsAmf0Any* elem = NULL;
        if ((ret = srs_amf0_read_any(stream, &elem)) != ERROR_SUCCESS) {
            srs_error("amf0 strict_array read value failed. ret=%d", ret);
            return ret;
        }
        
        // add property
        properties.push_back(elem);
    }
    
    return ret;
}
int SrsAmf0StrictArray::write(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write strict_array marker failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_1bytes(RTMP_AMF0_StrictArray);
    srs_verbose("amf0 write strict_array marker success");

    // count
    if (!stream->require(4)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write strict_array count failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_4bytes(this->_count);
    srs_verbose("amf0 write strict_array count success. count=%d", _count);
    
    // value
    for (int i = 0; i < (int)properties.size(); i++) {
        SrsAmf0Any* any = properties[i];
        
        if ((ret = srs_amf0_write_any(stream, any)) != ERROR_SUCCESS) {
            srs_error("write strict_array property value failed. ret=%d", ret);
            return ret;
        }
        
        srs_verbose("write amf0 property success.");
    }
    
    srs_verbose("write strict_array object success.");
    
    return ret;
}

SrsAmf0Any* SrsAmf0StrictArray::copy()
{
    SrsAmf0StrictArray* copy = new SrsAmf0StrictArray();
    
    std::vector<SrsAmf0Any*>::iterator it;
    for (it = properties.begin(); it != properties.end(); ++it) {
        SrsAmf0Any* any = *it;
        copy->append(any->copy());
    }
    
    copy->_count = _count;
    return copy;
}

void SrsAmf0StrictArray::clear()
{
    properties.clear();
}

int SrsAmf0StrictArray::count()
{
    return properties.size();
}

SrsAmf0Any* SrsAmf0StrictArray::at(int index)
{
    srs_assert(index < (int)properties.size());
    return properties.at(index);
}

void SrsAmf0StrictArray::append(SrsAmf0Any* any)
{
    properties.push_back(any);
    _count = (int32_t)properties.size();
}

int SrsAmf0Size::utf8(string value)
{
    return 2 + value.length();
}

int SrsAmf0Size::str(string value)
{
    return 1 + SrsAmf0Size::utf8(value);
}

int SrsAmf0Size::number()
{
    return 1 + 8;
}

int SrsAmf0Size::date()
{
    return 1 + 8 + 2;
}

int SrsAmf0Size::null()
{
    return 1;
}

int SrsAmf0Size::undefined()
{
    return 1;
}

int SrsAmf0Size::boolean()
{
    return 1 + 1;
}

int SrsAmf0Size::object(SrsAmf0Object* obj)
{
    if (!obj) {
        return 0;
    }
    
    return obj->total_size();
}

int SrsAmf0Size::object_eof()
{
    return 2 + 1;
}

int SrsAmf0Size::ecma_array(SrsAmf0EcmaArray* arr)
{
    if (!arr) {
        return 0;
    }
    
    return arr->total_size();
}

int SrsAmf0Size::strict_array(SrsAmf0StrictArray* arr)
{
    if (!arr) {
        return 0;
    }
    
    return arr->total_size();
}

int SrsAmf0Size::any(SrsAmf0Any* o)
{
    if (!o) {
        return 0;
    }
    
    return o->total_size();
}

SrsAmf0String::SrsAmf0String(const char* _value)
{
    marker = RTMP_AMF0_String;
    if (_value) {
        value = _value;
    }
}

SrsAmf0String::~SrsAmf0String()
{
}

int SrsAmf0String::total_size()
{
    return SrsAmf0Size::str(value);
}

int SrsAmf0String::read(SrsStream* stream)
{
    return srs_amf0_read_string(stream, value);
}

int SrsAmf0String::write(SrsStream* stream)
{
    return srs_amf0_write_string(stream, value);
}

SrsAmf0Any* SrsAmf0String::copy()
{
    SrsAmf0String* copy = new SrsAmf0String(value.c_str());
    return copy;
}

SrsAmf0Boolean::SrsAmf0Boolean(bool _value)
{
    marker = RTMP_AMF0_Boolean;
    value = _value;
}

SrsAmf0Boolean::~SrsAmf0Boolean()
{
}

int SrsAmf0Boolean::total_size()
{
    return SrsAmf0Size::boolean();
}

int SrsAmf0Boolean::read(SrsStream* stream)
{
    return srs_amf0_read_boolean(stream, value);
}

int SrsAmf0Boolean::write(SrsStream* stream)
{
    return srs_amf0_write_boolean(stream, value);
}

SrsAmf0Any* SrsAmf0Boolean::copy()
{
    SrsAmf0Boolean* copy = new SrsAmf0Boolean(value);
    return copy;
}

SrsAmf0Number::SrsAmf0Number(double _value)
{
    marker = RTMP_AMF0_Number;
    value = _value;
}

SrsAmf0Number::~SrsAmf0Number()
{
}

int SrsAmf0Number::total_size()
{
    return SrsAmf0Size::number();
}

int SrsAmf0Number::read(SrsStream* stream)
{
    return srs_amf0_read_number(stream, value);
}

int SrsAmf0Number::write(SrsStream* stream)
{
    return srs_amf0_write_number(stream, value);
}

SrsAmf0Any* SrsAmf0Number::copy()
{
    SrsAmf0Number* copy = new SrsAmf0Number(value);
    return copy;
}

SrsAmf0Date::SrsAmf0Date(int64_t value)
{
    marker = RTMP_AMF0_Date;
    _date_value = value;
    _time_zone = 0;
}

SrsAmf0Date::~SrsAmf0Date()
{
}

int SrsAmf0Date::total_size()
{
    return SrsAmf0Size::date();
}

int SrsAmf0Date::read(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read date marker failed. ret=%d", ret);
        return ret;
    }
    
    char marker = stream->read_1bytes();
    if (marker != RTMP_AMF0_Date) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 check date marker failed. "
            "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Date, ret);
        return ret;
    }
    srs_verbose("amf0 read date marker success");

    // date value
    // An ActionScript Date is serialized as the number of milliseconds 
    // elapsed since the epoch of midnight on 1st Jan 1970 in the UTC 
    // time zone.
    if (!stream->require(8)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read date failed. ret=%d", ret);
        return ret;
    }
    
    _date_value = stream->read_8bytes();
    srs_verbose("amf0 read date success. date=%"PRId64, _date_value);
    
    // time zone
    // While the design of this type reserves room for time zone offset 
    // information, it should not be filled in, nor used, as it is unconventional 
    // to change time zones when serializing dates on a network. It is suggested 
    // that the time zone be queried independently as needed.
    if (!stream->require(2)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read time zone failed. ret=%d", ret);
        return ret;
    }
    
    _time_zone = stream->read_2bytes();
    srs_verbose("amf0 read time zone success. zone=%d", _time_zone);
    
    return ret;
}
int SrsAmf0Date::write(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write date marker failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_1bytes(RTMP_AMF0_Date);
    srs_verbose("amf0 write date marker success");

    // date value
    if (!stream->require(8)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write date failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_8bytes(_date_value);
    srs_verbose("amf0 write date success. date=%"PRId64, _date_value);

    // time zone
    if (!stream->require(2)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write time zone failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_2bytes(_time_zone);
    srs_verbose("amf0 write time zone success. date=%d", _time_zone);
    
    srs_verbose("write date object success.");
    
    return ret;
}

SrsAmf0Any* SrsAmf0Date::copy()
{
    SrsAmf0Date* copy = new SrsAmf0Date(0);
    
    copy->_date_value = _date_value;
    copy->_time_zone = _time_zone;
    
    return copy;
}

int64_t SrsAmf0Date::date()
{
    return _date_value;
}

int16_t SrsAmf0Date::time_zone()
{
    return _time_zone;
}

SrsAmf0Null::SrsAmf0Null()
{
    marker = RTMP_AMF0_Null;
}

SrsAmf0Null::~SrsAmf0Null()
{
}

int SrsAmf0Null::total_size()
{
    return SrsAmf0Size::null();
}

int SrsAmf0Null::read(SrsStream* stream)
{
    return srs_amf0_read_null(stream);
}

int SrsAmf0Null::write(SrsStream* stream)
{
    return srs_amf0_write_null(stream);
}

SrsAmf0Any* SrsAmf0Null::copy()
{
    SrsAmf0Null* copy = new SrsAmf0Null();
    return copy;
}

SrsAmf0Undefined::SrsAmf0Undefined()
{
    marker = RTMP_AMF0_Undefined;
}

SrsAmf0Undefined::~SrsAmf0Undefined()
{
}

int SrsAmf0Undefined::total_size()
{
    return SrsAmf0Size::undefined();
}

int SrsAmf0Undefined::read(SrsStream* stream)
{
    return srs_amf0_read_undefined(stream);
}

int SrsAmf0Undefined::write(SrsStream* stream)
{
    return srs_amf0_write_undefined(stream);
}

SrsAmf0Any* SrsAmf0Undefined::copy()
{
    SrsAmf0Undefined* copy = new SrsAmf0Undefined();
    return copy;
}

int srs_amf0_read_any(SrsStream* stream, SrsAmf0Any** ppvalue)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = SrsAmf0Any::discovery(stream, ppvalue)) != ERROR_SUCCESS) {
        srs_error("amf0 discovery any elem failed. ret=%d", ret);
        return ret;
    }
    
    srs_assert(*ppvalue);
    
    if ((ret = (*ppvalue)->read(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 parse elem failed. ret=%d", ret);
        srs_freep(*ppvalue);
        return ret;
    }
    
    return ret;
}

int srs_amf0_read_string(SrsStream* stream, string& value)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read string marker failed. ret=%d", ret);
        return ret;
    }
    
    char marker = stream->read_1bytes();
    if (marker != RTMP_AMF0_String) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 check string marker failed. "
            "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_String, ret);
        return ret;
    }
    srs_verbose("amf0 read string marker success");
    
    return srs_amf0_read_utf8(stream, value);
}

int srs_amf0_write_string(SrsStream* stream, string value)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write string marker failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_1bytes(RTMP_AMF0_String);
    srs_verbose("amf0 write string marker success");
    
    return srs_amf0_write_utf8(stream, value);
}

int srs_amf0_read_boolean(SrsStream* stream, bool& value)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read bool marker failed. ret=%d", ret);
        return ret;
    }
    
    char marker = stream->read_1bytes();
    if (marker != RTMP_AMF0_Boolean) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 check bool marker failed. "
            "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Boolean, ret);
        return ret;
    }
    srs_verbose("amf0 read bool marker success");

    // value
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read bool value failed. ret=%d", ret);
        return ret;
    }

    value = (stream->read_1bytes() != 0);
    
    srs_verbose("amf0 read bool value success. value=%d", value);
    
    return ret;
}
int srs_amf0_write_boolean(SrsStream* stream, bool value)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write bool marker failed. ret=%d", ret);
        return ret;
    }
    stream->write_1bytes(RTMP_AMF0_Boolean);
    srs_verbose("amf0 write bool marker success");

    // value
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write bool value failed. ret=%d", ret);
        return ret;
    }

    if (value) {
        stream->write_1bytes(0x01);
    } else {
        stream->write_1bytes(0x00);
    }
    
    srs_verbose("amf0 write bool value success. value=%d", value);
    
    return ret;
}

int srs_amf0_read_number(SrsStream* stream, double& value)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read number marker failed. ret=%d", ret);
        return ret;
    }
    
    char marker = stream->read_1bytes();
    if (marker != RTMP_AMF0_Number) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 check number marker failed. "
            "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Number, ret);
        return ret;
    }
    srs_verbose("amf0 read number marker success");

    // value
    if (!stream->require(8)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read number value failed. ret=%d", ret);
        return ret;
    }

    int64_t temp = stream->read_8bytes();
    memcpy(&value, &temp, 8);
    
    srs_verbose("amf0 read number value success. value=%.2f", value);
    
    return ret;
}
int srs_amf0_write_number(SrsStream* stream, double value)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write number marker failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_1bytes(RTMP_AMF0_Number);
    srs_verbose("amf0 write number marker success");

    // value
    if (!stream->require(8)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write number value failed. ret=%d", ret);
        return ret;
    }

    int64_t temp = 0x00;
    memcpy(&temp, &value, 8);
    stream->write_8bytes(temp);
    
    srs_verbose("amf0 write number value success. value=%.2f", value);
    
    return ret;
}

int srs_amf0_read_null(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read null marker failed. ret=%d", ret);
        return ret;
    }
    
    char marker = stream->read_1bytes();
    if (marker != RTMP_AMF0_Null) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 check null marker failed. "
            "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Null, ret);
        return ret;
    }
    srs_verbose("amf0 read null success");
    
    return ret;
}
int srs_amf0_write_null(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write null marker failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_1bytes(RTMP_AMF0_Null);
    srs_verbose("amf0 write null marker success");
    
    return ret;
}

int srs_amf0_read_undefined(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 read undefined marker failed. ret=%d", ret);
        return ret;
    }
    
    char marker = stream->read_1bytes();
    if (marker != RTMP_AMF0_Undefined) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 check undefined marker failed. "
            "marker=%#x, required=%#x, ret=%d", marker, RTMP_AMF0_Undefined, ret);
        return ret;
    }
    srs_verbose("amf0 read undefined success");
    
    return ret;
}
int srs_amf0_write_undefined(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    // marker
    if (!stream->require(1)) {
        ret = ERROR_RTMP_AMF0_ENCODE;
        srs_error("amf0 write undefined marker failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_1bytes(RTMP_AMF0_Undefined);
    srs_verbose("amf0 write undefined marker success");
    
    return ret;
}


namespace _srs_internal
{
    int srs_amf0_read_utf8(SrsStream* stream, string& value)
    {
        int ret = ERROR_SUCCESS;
        
        // len
        if (!stream->require(2)) {
            ret = ERROR_RTMP_AMF0_DECODE;
            srs_error("amf0 read string length failed. ret=%d", ret);
            return ret;
        }
        int16_t len = stream->read_2bytes();
        srs_verbose("amf0 read string length success. len=%d", len);
        
        // empty string
        if (len <= 0) {
            srs_verbose("amf0 read empty string. ret=%d", ret);
            return ret;
        }
        
        // data
        if (!stream->require(len)) {
            ret = ERROR_RTMP_AMF0_DECODE;
            srs_error("amf0 read string data failed. ret=%d", ret);
            return ret;
        }
        std::string str = stream->read_string(len);
        
        // support utf8-1 only
        // 1.3.1 Strings and UTF-8
        // UTF8-1 = %x00-7F
        // TODO: support other utf-8 strings
        /*for (int i = 0; i < len; i++) {
            char ch = *(str.data() + i);
            if ((ch & 0x80) != 0) {
                ret = ERROR_RTMP_AMF0_DECODE;
                srs_error("ignored. only support utf8-1, 0x00-0x7F, actual is %#x. ret=%d", (int)ch, ret);
                ret = ERROR_SUCCESS;
            }
        }*/
        
        value = str;
        srs_verbose("amf0 read string data success. str=%s", str.c_str());
        
        return ret;
    }
    int srs_amf0_write_utf8(SrsStream* stream, string value)
    {
        int ret = ERROR_SUCCESS;
        
        // len
        if (!stream->require(2)) {
            ret = ERROR_RTMP_AMF0_ENCODE;
            srs_error("amf0 write string length failed. ret=%d", ret);
            return ret;
        }
        stream->write_2bytes(value.length());
        srs_verbose("amf0 write string length success. len=%d", (int)value.length());
        
        // empty string
        if (value.length() <= 0) {
            srs_verbose("amf0 write empty string. ret=%d", ret);
            return ret;
        }
        
        // data
        if (!stream->require(value.length())) {
            ret = ERROR_RTMP_AMF0_ENCODE;
            srs_error("amf0 write string data failed. ret=%d", ret);
            return ret;
        }
        stream->write_string(value);
        srs_verbose("amf0 write string data success. str=%s", value.c_str());
        
        return ret;
    }
    
    bool srs_amf0_is_object_eof(SrsStream* stream) 
    {
        // detect the object-eof specially
        if (stream->require(3)) {
            int32_t flag = stream->read_3bytes();
            stream->skip(-3);
            
            return 0x09 == flag;
        }
        
        return false;
    }
    
    int srs_amf0_write_object_eof(SrsStream* stream, SrsAmf0ObjectEOF* value)
    {
        int ret = ERROR_SUCCESS;
        
        srs_assert(value != NULL);
        
        // value
        if (!stream->require(2)) {
            ret = ERROR_RTMP_AMF0_ENCODE;
            srs_error("amf0 write object eof value failed. ret=%d", ret);
            return ret;
        }
        stream->write_2bytes(0x00);
        srs_verbose("amf0 write object eof value success");
        
        // marker
        if (!stream->require(1)) {
            ret = ERROR_RTMP_AMF0_ENCODE;
            srs_error("amf0 write object eof marker failed. ret=%d", ret);
            return ret;
        }
        
        stream->write_1bytes(RTMP_AMF0_ObjectEnd);
        
        srs_verbose("amf0 read object eof success");
        
        return ret;
    }

    int srs_amf0_write_any(SrsStream* stream, SrsAmf0Any* value)
    {
        srs_assert(value != NULL);
        return value->write(stream);
    }
}

// following is generated by src/protocol/srs_rtmp_io.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 <srs_rtmp_io.hpp>

ISrsBufferReader::ISrsBufferReader()
{
}

ISrsBufferReader::~ISrsBufferReader()
{
}

ISrsBufferWriter::ISrsBufferWriter()
{
}

ISrsBufferWriter::~ISrsBufferWriter()
{
}

ISrsProtocolStatistic::ISrsProtocolStatistic()
{
}

ISrsProtocolStatistic::~ISrsProtocolStatistic()
{
}

ISrsProtocolReader::ISrsProtocolReader()
{
}

ISrsProtocolReader::~ISrsProtocolReader()
{
}

ISrsProtocolWriter::ISrsProtocolWriter()
{
}

ISrsProtocolWriter::~ISrsProtocolWriter()
{
}

ISrsProtocolReaderWriter::ISrsProtocolReaderWriter()
{
}

ISrsProtocolReaderWriter::~ISrsProtocolReaderWriter()
{
}

// following is generated by src/protocol/srs_rtmp_stack.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 <srs_rtmp_stack.hpp>

//#include <srs_rtmp_amf0.hpp>
//#include <srs_rtmp_io.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_core_autofree.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_protocol_buffer.hpp>
//#include <srs_rtmp_utility.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#endif

#include <stdlib.h>
using namespace std;

// 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)

/****************************************************************************
*****************************************************************************
****************************************************************************/
/**
* 6.1.2. Chunk Message Header
* There are four different formats for the chunk message header,
* selected by the "fmt" field in the chunk basic header.
*/
// 6.1.2.1. Type 0
// Chunks of Type 0 are 11 bytes long. This type MUST be used at the
// start of a chunk stream, and whenever the stream timestamp goes
// backward (e.g., because of a backward seek).
#define RTMP_FMT_TYPE0                          0
// 6.1.2.2. Type 1
// Chunks of Type 1 are 7 bytes long. The message stream ID is not
// included; this chunk takes the same stream ID as the preceding chunk.
// Streams with variable-sized messages (for example, many video
// formats) SHOULD use this format for the first chunk of each new
// message after the first.
#define RTMP_FMT_TYPE1                          1
// 6.1.2.3. Type 2
// Chunks of Type 2 are 3 bytes long. Neither the stream ID nor the
// message length is included; this chunk has the same stream ID and
// message length as the preceding chunk. Streams with constant-sized
// messages (for example, some audio and data formats) SHOULD use this
// format for the first chunk of each message after the first.
#define RTMP_FMT_TYPE2                          2
// 6.1.2.4. Type 3
// Chunks of Type 3 have no header. Stream ID, message length and
// timestamp delta are not present; chunks of this type take values from
// the preceding chunk. When a single message is split into chunks, all
// chunks of a message except the first one, SHOULD use this type. Refer
// to example 2 in section 6.2.2. Stream consisting of messages of
// exactly the same size, stream ID and spacing in time SHOULD use this
// type for all chunks after chunk of Type 2. Refer to example 1 in
// section 6.2.1. If the delta between the first message and the second
// message is same as the time stamp of first message, then chunk of
// type 3 would immediately follow the chunk of type 0 as there is no
// need for a chunk of type 2 to register the delta. If Type 3 chunk
// follows a Type 0 chunk, then timestamp delta for this Type 3 chunk is
// the same as the timestamp of Type 0 chunk.
#define RTMP_FMT_TYPE3                          3

/****************************************************************************
*****************************************************************************
****************************************************************************/
/**
* band width check method name, which will be invoked by client.
* band width check mothods use SrsBandwidthPacket as its internal packet type,
* so ensure you set command name when you use it.
*/
// server play control
#define SRS_BW_CHECK_START_PLAY                 "onSrsBandCheckStartPlayBytes"
#define SRS_BW_CHECK_STARTING_PLAY              "onSrsBandCheckStartingPlayBytes"
#define SRS_BW_CHECK_STOP_PLAY                  "onSrsBandCheckStopPlayBytes"
#define SRS_BW_CHECK_STOPPED_PLAY               "onSrsBandCheckStoppedPlayBytes"

// server publish control
#define SRS_BW_CHECK_START_PUBLISH              "onSrsBandCheckStartPublishBytes"
#define SRS_BW_CHECK_STARTING_PUBLISH           "onSrsBandCheckStartingPublishBytes"
#define SRS_BW_CHECK_STOP_PUBLISH               "onSrsBandCheckStopPublishBytes"
// @remark, flash never send out this packet, for its queue is full.
#define SRS_BW_CHECK_STOPPED_PUBLISH            "onSrsBandCheckStoppedPublishBytes"

// EOF control.
// the report packet when check finished.
#define SRS_BW_CHECK_FINISHED                   "onSrsBandCheckFinished"
// @remark, flash never send out this packet, for its queue is full.
#define SRS_BW_CHECK_FINAL                      "finalClientPacket"

// data packets
#define SRS_BW_CHECK_PLAYING                    "onSrsBandCheckPlaying"
#define SRS_BW_CHECK_PUBLISHING                 "onSrsBandCheckPublishing"

/****************************************************************************
*****************************************************************************
****************************************************************************/

SrsPacket::SrsPacket()
{
}

SrsPacket::~SrsPacket()
{
}

int SrsPacket::encode(int& psize, char*& ppayload)
{
    int ret = ERROR_SUCCESS;
    
    int size = get_size();
    char* payload = NULL;
    
    SrsStream stream;
    
    if (size > 0) {
        payload = new char[size];
        
        if ((ret = stream.initialize(payload, size)) != ERROR_SUCCESS) {
            srs_error("initialize the stream failed. ret=%d", ret);
            srs_freep(payload);
            return ret;
        }
    }
    
    if ((ret = encode_packet(&stream)) != ERROR_SUCCESS) {
        srs_error("encode the packet failed. ret=%d", ret);
        srs_freep(payload);
        return ret;
    }
    
    psize = size;
    ppayload = payload;
    srs_verbose("encode the packet success. size=%d", size);
    
    return ret;
}

int SrsPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(stream != NULL);
    
    ret = ERROR_SYSTEM_PACKET_INVALID;
    srs_error("current packet is not support to decode. ret=%d", ret);
    
    return ret;
}

int SrsPacket::get_prefer_cid()
{
    return 0;
}

int SrsPacket::get_message_type()
{
    return 0;
}

int SrsPacket::get_size()
{
    return 0;
}

int SrsPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(stream != NULL);
    
    ret = ERROR_SYSTEM_PACKET_INVALID;
    srs_error("current packet is not support to encode. ret=%d", ret);
    
    return ret;
}

SrsProtocol::AckWindowSize::AckWindowSize()
{
    ack_window_size = 0;
    acked_size = 0;
}

SrsProtocol::SrsProtocol(ISrsProtocolReaderWriter* io)
{
    in_buffer = new SrsFastBuffer();
    skt = io;
    
    in_chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE;
    out_chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE;
    
    nb_out_iovs = SRS_CONSTS_IOVS_MAX;
    out_iovs = (iovec*)malloc(sizeof(iovec) * nb_out_iovs);
    // each chunk consumers atleast 2 iovs
    srs_assert(nb_out_iovs >= 2);
    
    warned_c0c3_cache_dry = false;
    auto_response_when_recv = true;
    
    cs_cache = NULL;
    if (SRS_PERF_CHUNK_STREAM_CACHE > 0) {
        cs_cache = new SrsChunkStream*[SRS_PERF_CHUNK_STREAM_CACHE];
    }
    for (int cid = 0; cid < SRS_PERF_CHUNK_STREAM_CACHE; cid++) {
        SrsChunkStream* cs = new SrsChunkStream(cid);
        // set the perfer cid of chunk,
        // which will copy to the message received.
        cs->header.perfer_cid = cid;
        
        cs_cache[cid] = cs;
    }
}

SrsProtocol::~SrsProtocol()
{
    if (true) {
        std::map<int, SrsChunkStream*>::iterator it;
        
        for (it = chunk_streams.begin(); it != chunk_streams.end(); ++it) {
            SrsChunkStream* stream = it->second;
            srs_freep(stream);
        }
    
        chunk_streams.clear();
    }
    
    if (true) {
        std::vector<SrsPacket*>::iterator it;
        for (it = manual_response_queue.begin(); it != manual_response_queue.end(); ++it) {
            SrsPacket* pkt = *it;
            srs_freep(pkt);
        }
        manual_response_queue.clear();
    }
    
    srs_freep(in_buffer);
    
    // alloc by malloc, use free directly.
    if (out_iovs) {
        free(out_iovs);
        out_iovs = NULL;
    }
    
    // free all chunk stream cache.
    for (int i = 0; i < SRS_PERF_CHUNK_STREAM_CACHE; i++) {
        SrsChunkStream* cs = cs_cache[i];
        srs_freep(cs);
    }
    srs_freep(cs_cache);
}

void SrsProtocol::set_auto_response(bool v)
{
    auto_response_when_recv = v;
}

int SrsProtocol::manual_response_flush()
{
    int ret = ERROR_SUCCESS;
    
    if (manual_response_queue.empty()) {
        return ret;
    }
    
    std::vector<SrsPacket*>::iterator it;
    for (it = manual_response_queue.begin(); it != manual_response_queue.end();) {
        SrsPacket* pkt = *it;
        
        // erase this packet, the send api always free it.
        it = manual_response_queue.erase(it);
        
        // use underlayer api to send, donot flush again.
        if ((ret = do_send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
            return ret;
        }
    }
    
    return ret;
}

#ifdef SRS_PERF_MERGED_READ
void SrsProtocol::set_merge_read(bool v, IMergeReadHandler* handler)
{
    in_buffer->set_merge_read(v, handler);
}

void SrsProtocol::set_recv_buffer(int buffer_size)
{
    in_buffer->set_buffer(buffer_size);
}
#endif

void SrsProtocol::set_recv_timeout(int64_t timeout_us)
{
    return skt->set_recv_timeout(timeout_us);
}

int64_t SrsProtocol::get_recv_timeout()
{
    return skt->get_recv_timeout();
}

void SrsProtocol::set_send_timeout(int64_t timeout_us)
{
    return skt->set_send_timeout(timeout_us);
}

int64_t SrsProtocol::get_send_timeout()
{
    return skt->get_send_timeout();
}

int64_t SrsProtocol::get_recv_bytes()
{
    return skt->get_recv_bytes();
}

int64_t SrsProtocol::get_send_bytes()
{
    return skt->get_send_bytes();
}

int SrsProtocol::recv_message(SrsCommonMessage** pmsg)
{
    *pmsg = NULL;
    
    int ret = ERROR_SUCCESS;
    
    while (true) {
        SrsCommonMessage* msg = NULL;
        
        if ((ret = recv_interlaced_message(&msg)) != ERROR_SUCCESS) {
            if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                srs_error("recv interlaced message failed. ret=%d", ret);
            }
            srs_freep(msg);
            return ret;
        }
        srs_verbose("entire msg received");
        
        if (!msg) {
            srs_info("got empty message without error.");
            continue;
        }
        
        if (msg->size <= 0 || msg->header.payload_length <= 0) {
            srs_trace("ignore empty message(type=%d, size=%d, time=%"PRId64", sid=%d).",
                msg->header.message_type, msg->header.payload_length,
                msg->header.timestamp, msg->header.stream_id);
            srs_freep(msg);
            continue;
        }
        
        if ((ret = on_recv_message(msg)) != ERROR_SUCCESS) {
            srs_error("hook the received msg failed. ret=%d", ret);
            srs_freep(msg);
            return ret;
        }
        
        srs_verbose("got a msg, cid=%d, type=%d, size=%d, time=%"PRId64, 
            msg->header.perfer_cid, msg->header.message_type, msg->header.payload_length, 
            msg->header.timestamp);
        *pmsg = msg;
        break;
    }
    
    return ret;
}

int SrsProtocol::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket)
{
    *ppacket = NULL;
    
    int ret = ERROR_SUCCESS;
    
    srs_assert(msg != NULL);
    srs_assert(msg->payload != NULL);
    srs_assert(msg->size > 0);
    
    SrsStream stream;

    // initialize the decode stream for all message,
    // it's ok for the initialize if fast and without memory copy.
    if ((ret = stream.initialize(msg->payload, msg->size)) != ERROR_SUCCESS) {
        srs_error("initialize stream failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("decode stream initialized success");
    
    // decode the packet.
    SrsPacket* packet = NULL;
    if ((ret = do_decode_message(msg->header, &stream, &packet)) != ERROR_SUCCESS) {
        srs_freep(packet);
        return ret;
    }
    
    // set to output ppacket only when success.
    *ppacket = packet;
    
    return ret;
}

int SrsProtocol::do_send_messages(SrsSharedPtrMessage** msgs, int nb_msgs)
{
    int ret = ERROR_SUCCESS;
    
#ifdef SRS_PERF_COMPLEX_SEND
    int iov_index = 0;
    iovec* iovs = out_iovs + iov_index;
    
    int c0c3_cache_index = 0;
    char* c0c3_cache = out_c0c3_caches + c0c3_cache_index;

    // try to send use the c0c3 header cache,
    // if cache is consumed, try another loop.
    for (int i = 0; i < nb_msgs; i++) {
        SrsSharedPtrMessage* msg = msgs[i];
        
        if (!msg) {
            continue;
        }
    
        // ignore empty message.
        if (!msg->payload || msg->size <= 0) {
            srs_info("ignore empty message.");
            continue;
        }
    
        // p set to current write position,
        // it's ok when payload is NULL and size is 0.
        char* p = msg->payload;
        char* pend = msg->payload + msg->size;
        
        // always write the header event payload is empty.
        while (p < pend) {
            // always has header
            int nb_cache = SRS_CONSTS_C0C3_HEADERS_MAX - c0c3_cache_index;
            int nbh = msg->chunk_header(c0c3_cache, nb_cache, p == msg->payload);
            srs_assert(nbh > 0);
            
            // header iov
            iovs[0].iov_base = c0c3_cache;
            iovs[0].iov_len = nbh;
            
            // payload iov
            int payload_size = srs_min(out_chunk_size, (int)(pend - p));
            iovs[1].iov_base = p;
            iovs[1].iov_len = payload_size;
            
            // consume sendout bytes.
            p += payload_size;
            
            // realloc the iovs if exceed,
            // for we donot know how many messges maybe to send entirely,
            // we just alloc the iovs, it's ok.
            if (iov_index >= nb_out_iovs - 2) {
                srs_warn("resize iovs %d => %d, max_msgs=%d", 
                    nb_out_iovs, nb_out_iovs + SRS_CONSTS_IOVS_MAX, 
                    SRS_PERF_MW_MSGS);
                    
                nb_out_iovs += SRS_CONSTS_IOVS_MAX;
                int realloc_size = sizeof(iovec) * nb_out_iovs;
                out_iovs = (iovec*)realloc(out_iovs, realloc_size);
            }
            
            // to next pair of iovs
            iov_index += 2;
            iovs = out_iovs + iov_index;

            // to next c0c3 header cache
            c0c3_cache_index += nbh;
            c0c3_cache = out_c0c3_caches + c0c3_cache_index;
            
            // the cache header should never be realloc again,
            // for the ptr is set to iovs, so we just warn user to set larger
            // and use another loop to send again.
            int c0c3_left = SRS_CONSTS_C0C3_HEADERS_MAX - c0c3_cache_index;
            if (c0c3_left < SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE) {
                // only warn once for a connection.
                if (!warned_c0c3_cache_dry) {
                    srs_warn("c0c3 cache header too small, recoment to %d", 
                        SRS_CONSTS_C0C3_HEADERS_MAX + SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE);
                    warned_c0c3_cache_dry = true;
                }
                
                // when c0c3 cache dry,
                // sendout all messages and reset the cache, then send again.
                if ((ret = do_iovs_send(out_iovs, iov_index)) != ERROR_SUCCESS) {
                    return ret;
                }
    
                // reset caches, while these cache ensure 
                // atleast we can sendout a chunk.
                iov_index = 0;
                iovs = out_iovs + iov_index;
                
                c0c3_cache_index = 0;
                c0c3_cache = out_c0c3_caches + c0c3_cache_index;
            }
        }
    }
    
    // maybe the iovs already sendout when c0c3 cache dry,
    // so just ignore when no iovs to send.
    if (iov_index <= 0) {
        return ret;
    }
    srs_info("mw %d msgs in %d iovs, max_msgs=%d, nb_out_iovs=%d",
        nb_msgs, iov_index, SRS_PERF_MW_MSGS, nb_out_iovs);

    return do_iovs_send(out_iovs, iov_index);
#else
    // try to send use the c0c3 header cache,
    // if cache is consumed, try another loop.
    for (int i = 0; i < nb_msgs; i++) {
        SrsSharedPtrMessage* msg = msgs[i];
        
        if (!msg) {
            continue;
        }
    
        // ignore empty message.
        if (!msg->payload || msg->size <= 0) {
            srs_info("ignore empty message.");
            continue;
        }
    
        // p set to current write position,
        // it's ok when payload is NULL and size is 0.
        char* p = msg->payload;
        char* pend = msg->payload + msg->size;
        
        // always write the header event payload is empty.
        while (p < pend) {
            // for simple send, send each chunk one by one
            iovec* iovs = out_iovs;
            char* c0c3_cache = out_c0c3_caches;
            int nb_cache = SRS_CONSTS_C0C3_HEADERS_MAX;
            
            // always has header
            int nbh = msg->chunk_header(c0c3_cache, nb_cache, p == msg->payload);
            srs_assert(nbh > 0);
            
            // header iov
            iovs[0].iov_base = c0c3_cache;
            iovs[0].iov_len = nbh;
            
            // payload iov
            int payload_size = srs_min(out_chunk_size, pend - p);
            iovs[1].iov_base = p;
            iovs[1].iov_len = payload_size;
            
            // consume sendout bytes.
            p += payload_size;

            if ((ret = skt->writev(iovs, 2, NULL)) != ERROR_SUCCESS) {
                if (!srs_is_client_gracefully_close(ret)) {
                    srs_error("send packet with writev failed. ret=%d", ret);
                }
                return ret;
            }
        }
    }
    
    return ret;
#endif   
}

int SrsProtocol::do_iovs_send(iovec* iovs, int size)
{
    return srs_write_large_iovs(skt, iovs, size);
}

int SrsProtocol::do_send_and_free_packet(SrsPacket* packet, int stream_id)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(packet);
    SrsAutoFree(SrsPacket, packet);
    
    int size = 0;
    char* payload = NULL;
    if ((ret = packet->encode(size, payload)) != ERROR_SUCCESS) {
        srs_error("encode RTMP packet to bytes oriented RTMP message failed. ret=%d", ret);
        return ret;
    }
    
    // encode packet to payload and size.
    if (size <= 0 || payload == NULL) {
        srs_warn("packet is empty, ignore empty message.");
        return ret;
    }
    
    // to message
    SrsMessageHeader header;
    header.payload_length = size;
    header.message_type = packet->get_message_type();
    header.stream_id = stream_id;
    header.perfer_cid = packet->get_prefer_cid();
    
    ret = do_simple_send(&header, payload, size);
    srs_freep(payload);
    if (ret == ERROR_SUCCESS) {
        ret = on_send_packet(&header, packet);
    }
    
    return ret;
}

int SrsProtocol::do_simple_send(SrsMessageHeader* mh, char* payload, int size)
{
    int ret = ERROR_SUCCESS;
    
    // we directly send out the packet,
    // use very simple algorithm, not very fast,
    // but it's ok.
    char* p = payload;
    char* end = p + size;
    char c0c3[SRS_CONSTS_RTMP_MAX_FMT0_HEADER_SIZE];
    while (p < end) {
        int nbh = 0;
        if (p == payload) {
            nbh = srs_chunk_header_c0(
                mh->perfer_cid, mh->timestamp, mh->payload_length,
                mh->message_type, mh->stream_id,
                c0c3, sizeof(c0c3));
        } else {
            nbh = srs_chunk_header_c3(
                mh->perfer_cid, mh->timestamp,
                c0c3, sizeof(c0c3));
        }
        srs_assert(nbh > 0);;
        
        iovec iovs[2];
        iovs[0].iov_base = c0c3;
        iovs[0].iov_len = nbh;
        
        int payload_size = srs_min(end - p, out_chunk_size);
        iovs[1].iov_base = p;
        iovs[1].iov_len = payload_size;
        p += payload_size;
        
        if ((ret = skt->writev(iovs, 2, NULL)) != ERROR_SUCCESS) {
            if (!srs_is_client_gracefully_close(ret)) {
                srs_error("send packet with writev failed. ret=%d", ret);
            }
            return ret;
        }
    }
    
    return ret;
}

int SrsProtocol::do_decode_message(SrsMessageHeader& header, SrsStream* stream, SrsPacket** ppacket)
{
    int ret = ERROR_SUCCESS;
    
    SrsPacket* packet = NULL;
    
    // decode specified packet type
    if (header.is_amf0_command() || header.is_amf3_command() || header.is_amf0_data() || header.is_amf3_data()) {
        srs_verbose("start to decode AMF0/AMF3 command message.");
        
        // skip 1bytes to decode the amf3 command.
        if (header.is_amf3_command() && stream->require(1)) {
            srs_verbose("skip 1bytes to decode AMF3 command");
            stream->skip(1);
        }
        
        // amf0 command message.
        // need to read the command name.
        std::string command;
        if ((ret = srs_amf0_read_string(stream, command)) != ERROR_SUCCESS) {
            srs_error("decode AMF0/AMF3 command name failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("AMF0/AMF3 command message, command_name=%s", command.c_str());
        
        // result/error packet
        if (command == RTMP_AMF0_COMMAND_RESULT || command == RTMP_AMF0_COMMAND_ERROR) {
            double transactionId = 0.0;
            if ((ret = srs_amf0_read_number(stream, transactionId)) != ERROR_SUCCESS) {
                srs_error("decode AMF0/AMF3 transcationId failed. ret=%d", ret);
                return ret;
            }
            srs_verbose("AMF0/AMF3 command id, transcationId=%.2f", transactionId);
            
            // reset stream, for header read completed.
            stream->skip(-1 * stream->pos());
            if (header.is_amf3_command()) {
                stream->skip(1);
            }
            
            // find the call name
            if (requests.find(transactionId) == requests.end()) {
                ret = ERROR_RTMP_NO_REQUEST;
                srs_error("decode AMF0/AMF3 request failed. ret=%d", ret);
                return ret;
            }
            
            std::string request_name = requests[transactionId];
            srs_verbose("AMF0/AMF3 request parsed. request_name=%s", request_name.c_str());

            if (request_name == RTMP_AMF0_COMMAND_CONNECT) {
                srs_info("decode the AMF0/AMF3 response command(%s message).", request_name.c_str());
                *ppacket = packet = new SrsConnectAppResPacket();
                return packet->decode(stream);
            } else if (request_name == RTMP_AMF0_COMMAND_CREATE_STREAM) {
                srs_info("decode the AMF0/AMF3 response command(%s message).", request_name.c_str());
                *ppacket = packet = new SrsCreateStreamResPacket(0, 0);
                return packet->decode(stream);
            } else if (request_name == RTMP_AMF0_COMMAND_RELEASE_STREAM
                || request_name == RTMP_AMF0_COMMAND_FC_PUBLISH
                || request_name == RTMP_AMF0_COMMAND_UNPUBLISH) {
                srs_info("decode the AMF0/AMF3 response command(%s message).", request_name.c_str());
                *ppacket = packet = new SrsFMLEStartResPacket(0);
                return packet->decode(stream);
            } else {
                ret = ERROR_RTMP_NO_REQUEST;
                srs_error("decode AMF0/AMF3 request failed. "
                    "request_name=%s, transactionId=%.2f, ret=%d", 
                    request_name.c_str(), transactionId, ret);
                return ret;
            }
        }
        
        // reset to zero(amf3 to 1) to restart decode.
        stream->skip(-1 * stream->pos());
        if (header.is_amf3_command()) {
            stream->skip(1);
        }
        
        // decode command object.
        if (command == RTMP_AMF0_COMMAND_CONNECT) {
            srs_info("decode the AMF0/AMF3 command(connect vhost/app message).");
            *ppacket = packet = new SrsConnectAppPacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_CREATE_STREAM) {
            srs_info("decode the AMF0/AMF3 command(createStream message).");
            *ppacket = packet = new SrsCreateStreamPacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_PLAY) {
            srs_info("decode the AMF0/AMF3 command(paly message).");
            *ppacket = packet = new SrsPlayPacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_PAUSE) {
            srs_info("decode the AMF0/AMF3 command(pause message).");
            *ppacket = packet = new SrsPausePacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_RELEASE_STREAM) {
            srs_info("decode the AMF0/AMF3 command(FMLE releaseStream message).");
            *ppacket = packet = new SrsFMLEStartPacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_FC_PUBLISH) {
            srs_info("decode the AMF0/AMF3 command(FMLE FCPublish message).");
            *ppacket = packet = new SrsFMLEStartPacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_PUBLISH) {
            srs_info("decode the AMF0/AMF3 command(publish message).");
            *ppacket = packet = new SrsPublishPacket();
            return packet->decode(stream);
        } else if(command == RTMP_AMF0_COMMAND_UNPUBLISH) {
            srs_info("decode the AMF0/AMF3 command(unpublish message).");
            *ppacket = packet = new SrsFMLEStartPacket();
            return packet->decode(stream);
        } else if(command == SRS_CONSTS_RTMP_SET_DATAFRAME || command == SRS_CONSTS_RTMP_ON_METADATA) {
            srs_info("decode the AMF0/AMF3 data(onMetaData message).");
            *ppacket = packet = new SrsOnMetaDataPacket();
            return packet->decode(stream);
        } else if(command == SRS_BW_CHECK_FINISHED
            || command == SRS_BW_CHECK_PLAYING
            || command == SRS_BW_CHECK_PUBLISHING
            || command == SRS_BW_CHECK_STARTING_PLAY
            || command == SRS_BW_CHECK_STARTING_PUBLISH
            || command == SRS_BW_CHECK_START_PLAY
            || command == SRS_BW_CHECK_START_PUBLISH
            || command == SRS_BW_CHECK_STOPPED_PLAY
            || command == SRS_BW_CHECK_STOP_PLAY
            || command == SRS_BW_CHECK_STOP_PUBLISH
            || command == SRS_BW_CHECK_STOPPED_PUBLISH
            || command == SRS_BW_CHECK_FINAL)
        {
            srs_info("decode the AMF0/AMF3 band width check message.");
            *ppacket = packet = new SrsBandwidthPacket();
            return packet->decode(stream);
        } else if (command == RTMP_AMF0_COMMAND_CLOSE_STREAM) {
            srs_info("decode the AMF0/AMF3 closeStream message.");
            *ppacket = packet = new SrsCloseStreamPacket();
            return packet->decode(stream);
        } else if (header.is_amf0_command() || header.is_amf3_command()) {
            srs_info("decode the AMF0/AMF3 call message.");
            *ppacket = packet = new SrsCallPacket();
            return packet->decode(stream);
        }
        
        // default packet to drop message.
        srs_info("drop the AMF0/AMF3 command message, command_name=%s", command.c_str());
        *ppacket = packet = new SrsPacket();
        return ret;
    } else if(header.is_user_control_message()) {
        srs_verbose("start to decode user control message.");
        *ppacket = packet = new SrsUserControlPacket();
        return packet->decode(stream);
    } else if(header.is_window_ackledgement_size()) {
        srs_verbose("start to decode set ack window size message.");
        *ppacket = packet = new SrsSetWindowAckSizePacket();
        return packet->decode(stream);
    } else if(header.is_set_chunk_size()) {
        srs_verbose("start to decode set chunk size message.");
        *ppacket = packet = new SrsSetChunkSizePacket();
        return packet->decode(stream);
    } else {
        if (!header.is_set_peer_bandwidth() && !header.is_ackledgement()) {
            srs_trace("drop unknown message, type=%d", header.message_type);
        }
    }
    
    return ret;
}

int SrsProtocol::send_and_free_message(SrsSharedPtrMessage* msg, int stream_id)
{
    return send_and_free_messages(&msg, 1, stream_id);
}

int SrsProtocol::send_and_free_messages(SrsSharedPtrMessage** msgs, int nb_msgs, int stream_id)
{
    // always not NULL msg.
    srs_assert(msgs);
    srs_assert(nb_msgs > 0);
    
    // update the stream id in header.
    for (int i = 0; i < nb_msgs; i++) {
        SrsSharedPtrMessage* msg = msgs[i];
        
        if (!msg) {
            continue;
        }
        
        // check perfer cid and stream,
        // when one msg stream id is ok, ignore left.
        if (msg->check(stream_id)) {
            break;
        }
    }
    
    // donot use the auto free to free the msg,
    // for performance issue.
    int ret = do_send_messages(msgs, nb_msgs);
    
    for (int i = 0; i < nb_msgs; i++) {
        SrsSharedPtrMessage* msg = msgs[i];
        srs_freep(msg);
    }
    
    // donot flush when send failed
    if (ret != ERROR_SUCCESS) {
        return ret;
    }
    
    // flush messages in manual queue
    if ((ret = manual_response_flush()) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int SrsProtocol::send_and_free_packet(SrsPacket* packet, int stream_id)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = do_send_and_free_packet(packet, stream_id)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // flush messages in manual queue
    if ((ret = manual_response_flush()) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int SrsProtocol::recv_interlaced_message(SrsCommonMessage** pmsg)
{
    int ret = ERROR_SUCCESS;
    
    // chunk stream basic header.
    char fmt = 0;
    int cid = 0;
    if ((ret = read_basic_header(fmt, cid)) != ERROR_SUCCESS) {
        if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
            srs_error("read basic header failed. ret=%d", ret);
        }
        return ret;
    }
    srs_verbose("read basic header success. fmt=%d, cid=%d", fmt, cid);
    
    // the cid must not negative.
    srs_assert(cid >= 0);
    
    // get the cached chunk stream.
    SrsChunkStream* chunk = NULL;
    
    // use chunk stream cache to get the chunk info.
    // @see https://github.com/simple-rtmp-server/srs/issues/249
    if (cid < SRS_PERF_CHUNK_STREAM_CACHE) {
        // chunk stream cache hit.
        srs_verbose("cs-cache hit, cid=%d", cid);
        // already init, use it direclty
        chunk = cs_cache[cid];
        srs_verbose("cached chunk stream: fmt=%d, cid=%d, size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)",
            chunk->fmt, chunk->cid, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, chunk->header.payload_length,
            chunk->header.timestamp, chunk->header.stream_id);
    } else {
        // chunk stream cache miss, use map.
        if (chunk_streams.find(cid) == chunk_streams.end()) {
            chunk = chunk_streams[cid] = new SrsChunkStream(cid);
            // set the perfer cid of chunk,
            // which will copy to the message received.
            chunk->header.perfer_cid = cid;
            srs_verbose("cache new chunk stream: fmt=%d, cid=%d", fmt, cid);
        } else {
            chunk = chunk_streams[cid];
            srs_verbose("cached chunk stream: fmt=%d, cid=%d, size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)",
                chunk->fmt, chunk->cid, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, chunk->header.payload_length,
                chunk->header.timestamp, chunk->header.stream_id);
        }
    }

    // chunk stream message header
    if ((ret = read_message_header(chunk, fmt)) != ERROR_SUCCESS) {
        if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
            srs_error("read message header failed. ret=%d", ret);
        }
        return ret;
    }
    srs_verbose("read message header success. "
            "fmt=%d, ext_time=%d, size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)", 
            fmt, chunk->extended_timestamp, (chunk->msg? chunk->msg->size : 0), chunk->header.message_type, 
            chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id);
    
    // read msg payload from chunk stream.
    SrsCommonMessage* msg = NULL;
    if ((ret = read_message_payload(chunk, &msg)) != ERROR_SUCCESS) {
        if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
            srs_error("read message payload failed. ret=%d", ret);
        }
        return ret;
    }
    
    // not got an entire RTMP message, try next chunk.
    if (!msg) {
        srs_verbose("get partial message success. size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)",
                (msg? msg->size : (chunk->msg? chunk->msg->size : 0)), chunk->header.message_type, chunk->header.payload_length,
                chunk->header.timestamp, chunk->header.stream_id);
        return ret;
    }
    
    *pmsg = msg;
    srs_info("get entire message success. size=%d, message(type=%d, size=%d, time=%"PRId64", sid=%d)",
            (msg? msg->size : (chunk->msg? chunk->msg->size : 0)), chunk->header.message_type, chunk->header.payload_length,
            chunk->header.timestamp, chunk->header.stream_id);
            
    return ret;
}

/**
* 6.1.1. Chunk Basic Header
* The Chunk Basic Header encodes the chunk stream ID and the chunk
* type(represented by fmt field in the figure below). Chunk type
* determines the format of the encoded message header. Chunk Basic
* Header field may be 1, 2, or 3 bytes, depending on the chunk stream
* ID.
* 
* The bits 0-5 (least significant) in the chunk basic header represent
* the chunk stream ID.
*
* Chunk stream IDs 2-63 can be encoded in the 1-byte version of this
* field.
*    0 1 2 3 4 5 6 7
*   +-+-+-+-+-+-+-+-+
*   |fmt|   cs id   |
*   +-+-+-+-+-+-+-+-+
*   Figure 6 Chunk basic header 1
*
* Chunk stream IDs 64-319 can be encoded in the 2-byte version of this
* field. ID is computed as (the second byte + 64).
*   0                   1
*   0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
*   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*   |fmt|    0      | cs id - 64    |
*   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*   Figure 7 Chunk basic header 2
*
* Chunk stream IDs 64-65599 can be encoded in the 3-byte version of
* this field. ID is computed as ((the third byte)*256 + the second byte
* + 64).
*    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3
*   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*   |fmt|     1     |         cs id - 64            |
*   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
*   Figure 8 Chunk basic header 3
*
* cs id: 6 bits
* fmt: 2 bits
* cs id - 64: 8 or 16 bits
* 
* Chunk stream IDs with values 64-319 could be represented by both 2-
* byte version and 3-byte version of this field.
*/
int SrsProtocol::read_basic_header(char& fmt, int& cid)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = in_buffer->grow(skt, 1)) != ERROR_SUCCESS) {
        if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
            srs_error("read 1bytes basic header failed. required_size=%d, ret=%d", 1, ret);
        }
        return ret;
    }
    
    fmt = in_buffer->read_1byte();
    cid = fmt & 0x3f;
    fmt = (fmt >> 6) & 0x03;
    
    // 2-63, 1B chunk header
    if (cid > 1) {
        srs_verbose("basic header parsed. fmt=%d, cid=%d", fmt, cid);
        return ret;
    }

    // 64-319, 2B chunk header
    if (cid == 0) {
        if ((ret = in_buffer->grow(skt, 1)) != ERROR_SUCCESS) {
            if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                srs_error("read 2bytes basic header failed. required_size=%d, ret=%d", 1, ret);
            }
            return ret;
        }
        
        cid = 64;
        cid += (u_int8_t)in_buffer->read_1byte();
        srs_verbose("2bytes basic header parsed. fmt=%d, cid=%d", fmt, cid);
    // 64-65599, 3B chunk header
    } else if (cid == 1) {
        if ((ret = in_buffer->grow(skt, 2)) != ERROR_SUCCESS) {
            if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                srs_error("read 3bytes basic header failed. required_size=%d, ret=%d", 2, ret);
            }
            return ret;
        }
        
        cid = 64;
        cid += (u_int8_t)in_buffer->read_1byte();
        cid += ((u_int8_t)in_buffer->read_1byte()) * 256;
        srs_verbose("3bytes basic header parsed. fmt=%d, cid=%d", fmt, cid);
    } else {
        srs_error("invalid path, impossible basic header.");
        srs_assert(false);
    }
    
    return ret;
}

/**
* parse the message header.
*   3bytes: timestamp delta,    fmt=0,1,2
*   3bytes: payload length,     fmt=0,1
*   1bytes: message type,       fmt=0,1
*   4bytes: stream id,          fmt=0
* where:
*   fmt=0, 0x0X
*   fmt=1, 0x4X
*   fmt=2, 0x8X
*   fmt=3, 0xCX
*/
int SrsProtocol::read_message_header(SrsChunkStream* chunk, char fmt)
{
    int ret = ERROR_SUCCESS;
    
    /**
    * we should not assert anything about fmt, for the first packet.
    * (when first packet, the chunk->msg is NULL).
    * the fmt maybe 0/1/2/3, the FMLE will send a 0xC4 for some audio packet.
    * the previous packet is:
    *     04                // fmt=0, cid=4
    *     00 00 1a          // timestamp=26
    *     00 00 9d          // payload_length=157
    *     08                // message_type=8(audio)
    *     01 00 00 00       // stream_id=1
    * the current packet maybe:
    *     c4             // fmt=3, cid=4
    * it's ok, for the packet is audio, and timestamp delta is 26.
    * the current packet must be parsed as:
    *     fmt=0, cid=4
    *     timestamp=26+26=52
    *     payload_length=157
    *     message_type=8(audio)
    *     stream_id=1
    * so we must update the timestamp even fmt=3 for first packet.
    */
    // fresh packet used to update the timestamp even fmt=3 for first packet.
    // fresh packet always means the chunk is the first one of message.
    bool is_first_chunk_of_msg = !chunk->msg;
    
    // but, we can ensure that when a chunk stream is fresh, 
    // the fmt must be 0, a new stream.
    if (chunk->msg_count == 0 && fmt != RTMP_FMT_TYPE0) {
        // for librtmp, if ping, it will send a fresh stream with fmt=1,
        // 0x42             where: fmt=1, cid=2, protocol contorl user-control message
        // 0x00 0x00 0x00   where: timestamp=0
        // 0x00 0x00 0x06   where: payload_length=6
        // 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
        if (chunk->cid == RTMP_CID_ProtocolControl && fmt == RTMP_FMT_TYPE1) {
            srs_warn("accept cid=2, fmt=1 to make librtmp happy.");
        } else {
            // must be a RTMP protocol level error.
            ret = ERROR_RTMP_CHUNK_START;
            srs_error("chunk stream is fresh, fmt must be %d, actual is %d. cid=%d, ret=%d", 
                RTMP_FMT_TYPE0, fmt, chunk->cid, ret);
            return ret;
        }
    }

    // when exists cache msg, means got an partial message,
    // the fmt must not be type0 which means new message.
    if (chunk->msg && fmt == RTMP_FMT_TYPE0) {
        ret = ERROR_RTMP_CHUNK_START;
        srs_error("chunk stream exists, "
            "fmt must not be %d, actual is %d. ret=%d", RTMP_FMT_TYPE0, fmt, ret);
        return ret;
    }
    
    // create msg when new chunk stream start
    if (!chunk->msg) {
        chunk->msg = new SrsCommonMessage();
        srs_verbose("create message for new chunk, fmt=%d, cid=%d", fmt, chunk->cid);
    }

    // read message header from socket to buffer.
    static char mh_sizes[] = {11, 7, 3, 0};
    int mh_size = mh_sizes[(int)fmt];
    srs_verbose("calc chunk message header size. fmt=%d, mh_size=%d", fmt, mh_size);
    
    if (mh_size > 0 && (ret = in_buffer->grow(skt, mh_size)) != ERROR_SUCCESS) {
        if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
            srs_error("read %dbytes message header failed. ret=%d", mh_size, ret);
        }
        return ret;
    }
    
    /**
    * parse the message header.
    *   3bytes: timestamp delta,    fmt=0,1,2
    *   3bytes: payload length,     fmt=0,1
    *   1bytes: message type,       fmt=0,1
    *   4bytes: stream id,          fmt=0
    * where:
    *   fmt=0, 0x0X
    *   fmt=1, 0x4X
    *   fmt=2, 0x8X
    *   fmt=3, 0xCX
    */
    // see also: ngx_rtmp_recv
    if (fmt <= RTMP_FMT_TYPE2) {
        char* p = in_buffer->read_slice(mh_size);
    
        char* pp = (char*)&chunk->header.timestamp_delta;
        pp[2] = *p++;
        pp[1] = *p++;
        pp[0] = *p++;
        pp[3] = 0;
        
        // fmt: 0
        // timestamp: 3 bytes
        // If the timestamp is greater than or equal to 16777215
        // (hexadecimal 0x00ffffff), this value MUST be 16777215, and the
        // 'extended timestamp header' MUST be present. Otherwise, this value
        // SHOULD be the entire timestamp.
        //
        // fmt: 1 or 2
        // timestamp delta: 3 bytes
        // If the delta is greater than or equal to 16777215 (hexadecimal
        // 0x00ffffff), this value MUST be 16777215, and the 'extended
        // timestamp header' MUST be present. Otherwise, this value SHOULD be
        // the entire delta.
        chunk->extended_timestamp = (chunk->header.timestamp_delta >= RTMP_EXTENDED_TIMESTAMP);
        if (!chunk->extended_timestamp) {
            // Extended timestamp: 0 or 4 bytes
            // This field MUST be sent when the normal timsestamp is set to
            // 0xffffff, it MUST NOT be sent if the normal timestamp is set to
            // anything else. So for values less than 0xffffff the normal
            // timestamp field SHOULD be used in which case the extended timestamp
            // MUST NOT be present. For values greater than or equal to 0xffffff
            // the normal timestamp field MUST NOT be used and MUST be set to
            // 0xffffff and the extended timestamp MUST be sent.
            if (fmt == RTMP_FMT_TYPE0) {
                // 6.1.2.1. Type 0
                // For a type-0 chunk, the absolute timestamp of the message is sent
                // here.
                chunk->header.timestamp = chunk->header.timestamp_delta;
            } else {
                // 6.1.2.2. Type 1
                // 6.1.2.3. Type 2
                // For a type-1 or type-2 chunk, the difference between the previous
                // chunk's timestamp and the current chunk's timestamp is sent here.
                chunk->header.timestamp += chunk->header.timestamp_delta;
            }
        }
        
        if (fmt <= RTMP_FMT_TYPE1) {
            int32_t payload_length = 0;
            pp = (char*)&payload_length;
            pp[2] = *p++;
            pp[1] = *p++;
            pp[0] = *p++;
            pp[3] = 0;
            
            // for a message, if msg exists in cache, the size must not changed.
            // always use the actual msg size to compare, for the cache payload length can changed,
            // for the fmt type1(stream_id not changed), user can change the payload 
            // length(it's not allowed in the continue chunks).
            if (!is_first_chunk_of_msg && chunk->header.payload_length != payload_length) {
                ret = ERROR_RTMP_PACKET_SIZE;
                srs_error("msg exists in chunk cache, "
                    "size=%d cannot change to %d, ret=%d", 
                    chunk->header.payload_length, payload_length, ret);
                return ret;
            }
            
            chunk->header.payload_length = payload_length;
            chunk->header.message_type = *p++;
            
            if (fmt == RTMP_FMT_TYPE0) {
                pp = (char*)&chunk->header.stream_id;
                pp[0] = *p++;
                pp[1] = *p++;
                pp[2] = *p++;
                pp[3] = *p++;
                srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%"PRId64", payload=%d, type=%d, sid=%d", 
                    fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp, chunk->header.payload_length, 
                    chunk->header.message_type, chunk->header.stream_id);
            } else {
                srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%"PRId64", payload=%d, type=%d", 
                    fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp, chunk->header.payload_length, 
                    chunk->header.message_type);
            }
        } else {
            srs_verbose("header read completed. fmt=%d, mh_size=%d, ext_time=%d, time=%"PRId64"", 
                fmt, mh_size, chunk->extended_timestamp, chunk->header.timestamp);
        }
    } else {
        // update the timestamp even fmt=3 for first chunk packet
        if (is_first_chunk_of_msg && !chunk->extended_timestamp) {
            chunk->header.timestamp += chunk->header.timestamp_delta;
        }
        srs_verbose("header read completed. fmt=%d, size=%d, ext_time=%d", 
            fmt, mh_size, chunk->extended_timestamp);
    }
    
    // read extended-timestamp
    if (chunk->extended_timestamp) {
        mh_size += 4;
        srs_verbose("read header ext time. fmt=%d, ext_time=%d, mh_size=%d", fmt, chunk->extended_timestamp, mh_size);
        if ((ret = in_buffer->grow(skt, 4)) != ERROR_SUCCESS) {
            if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
                srs_error("read %dbytes message header failed. required_size=%d, ret=%d", mh_size, 4, ret);
            }
            return ret;
        }
        // the ptr to the slice maybe invalid when grow()
        // reset the p to get 4bytes slice.
        char* p = in_buffer->read_slice(4);

        u_int32_t timestamp = 0x00;
        char* pp = (char*)&timestamp;
        pp[3] = *p++;
        pp[2] = *p++;
        pp[1] = *p++;
        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
        timestamp &= 0x7fffffff;
        
        /**
        * RTMP specification and ffmpeg/librtmp is false,
        * but, adobe changed the specification, so flash/FMLE/FMS always true.
        * default to true to support flash/FMLE/FMS.
        * 
        * ffmpeg/librtmp may donot send this filed, need to detect the value.
        * @see also: http://blog.csdn.net/win_lin/article/details/13363699
        * compare to the chunk timestamp, which is set by chunk message header
        * type 0,1 or 2.
        *
        * @remark, nginx send the extended-timestamp in sequence-header,
        * and timestamp delta in continue C1 chunks, and so compatible with ffmpeg,
        * that is, there is no continue chunks and extended-timestamp in nginx-rtmp.
        *
        * @remark, srs always send the extended-timestamp, to keep simple,
        * and compatible with adobe products.
        */
        u_int32_t chunk_timestamp = (u_int32_t)chunk->header.timestamp;
        
        /**
        * if chunk_timestamp<=0, the chunk previous packet has no extended-timestamp,
        * always use the extended timestamp.
        */
        /**
        * about the is_first_chunk_of_msg.
        * @remark, for the first chunk of message, always use the extended timestamp.
        */
        if (!is_first_chunk_of_msg && chunk_timestamp > 0 && chunk_timestamp != timestamp) {
            mh_size -= 4;
            in_buffer->skip(-4);
            srs_info("no 4bytes extended timestamp in the continued chunk");
        } else {
            chunk->header.timestamp = timestamp;
        }
        srs_verbose("header read ext_time completed. time=%"PRId64"", chunk->header.timestamp);
    }
    
    // the extended-timestamp must be unsigned-int,
    //         24bits timestamp: 0xffffff = 16777215ms = 16777.215s = 4.66h
    //         32bits timestamp: 0xffffffff = 4294967295ms = 4294967.295s = 1193.046h = 49.71d
    // because the rtmp protocol says the 32bits timestamp is about "50 days":
    //         3. Byte Order, Alignment, and Time Format
    //                Because timestamps are generally only 32 bits long, they will roll
    //                over after fewer than 50 days.
    // 
    // but, its sample says the timestamp is 31bits:
    //         An application could assume, for example, that all 
    //        adjacent timestamps are within 2^31 milliseconds of each other, so
    //        10000 comes after 4000000000, while 3000000000 comes before
    //        4000000000.
    // and flv specification says timestamp is 31bits:
    //        Extension of the Timestamp field to form a SI32 value. This
    //        field represents the upper 8 bits, while the previous
    //        Timestamp field represents the lower 24 bits of the time in
    //        milliseconds.
    // in a word, 31bits timestamp is ok.
    // convert extended timestamp to 31bits.
    chunk->header.timestamp &= 0x7fffffff;
    
    // valid message, the payload_length is 24bits,
    // so it should never be negative.
    srs_assert(chunk->header.payload_length >= 0);
    
    // copy header to msg
    chunk->msg->header = chunk->header;
    
    // increase the msg count, the chunk stream can accept fmt=1/2/3 message now.
    chunk->msg_count++;
    
    return ret;
}

int SrsProtocol::read_message_payload(SrsChunkStream* chunk, SrsCommonMessage** pmsg)
{
    int ret = ERROR_SUCCESS;
    
    // empty message
    if (chunk->header.payload_length <= 0) {
        srs_trace("get an empty RTMP "
                "message(type=%d, size=%d, time=%"PRId64", sid=%d)", chunk->header.message_type, 
                chunk->header.payload_length, chunk->header.timestamp, chunk->header.stream_id);
        
        *pmsg = chunk->msg;
        chunk->msg = NULL;
                
        return ret;
    }
    srs_assert(chunk->header.payload_length > 0);
    
    // the chunk payload size.
    int payload_size = chunk->header.payload_length - chunk->msg->size;
    payload_size = srs_min(payload_size, in_chunk_size);
    srs_verbose("chunk payload size is %d, message_size=%d, received_size=%d, in_chunk_size=%d", 
        payload_size, chunk->header.payload_length, chunk->msg->size, in_chunk_size);

    // create msg payload if not initialized
    if (!chunk->msg->payload) {
        chunk->msg->create_payload(chunk->header.payload_length);
    }
    
    // read payload to buffer
    if ((ret = in_buffer->grow(skt, payload_size)) != ERROR_SUCCESS) {
        if (ret != ERROR_SOCKET_TIMEOUT && !srs_is_client_gracefully_close(ret)) {
            srs_error("read payload failed. required_size=%d, ret=%d", payload_size, ret);
        }
        return ret;
    }
    memcpy(chunk->msg->payload + chunk->msg->size, in_buffer->read_slice(payload_size), payload_size);
    chunk->msg->size += payload_size;
    
    srs_verbose("chunk payload read completed. payload_size=%d", payload_size);
    
    // got entire RTMP message?
    if (chunk->header.payload_length == chunk->msg->size) {
        *pmsg = chunk->msg;
        chunk->msg = NULL;
        srs_verbose("get entire RTMP message(type=%d, size=%d, time=%"PRId64", sid=%d)", 
                chunk->header.message_type, chunk->header.payload_length, 
                chunk->header.timestamp, chunk->header.stream_id);
        return ret;
    }
    
    srs_verbose("get partial RTMP message(type=%d, size=%d, time=%"PRId64", sid=%d), partial size=%d", 
            chunk->header.message_type, chunk->header.payload_length, 
            chunk->header.timestamp, chunk->header.stream_id,
            chunk->msg->size);
            
    return ret;
}

int SrsProtocol::on_recv_message(SrsCommonMessage* msg)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(msg != NULL);
        
    // acknowledgement
    if (in_ack_size.ack_window_size > 0 
        && skt->get_recv_bytes() - in_ack_size.acked_size > in_ack_size.ack_window_size
    ) {
        if ((ret = response_acknowledgement_message()) != ERROR_SUCCESS) {
            return ret;
        }
    }
    
    SrsPacket* packet = NULL;
    switch (msg->header.message_type) {
        case RTMP_MSG_SetChunkSize:
        case RTMP_MSG_UserControlMessage:
        case RTMP_MSG_WindowAcknowledgementSize:
            if ((ret = decode_message(msg, &packet)) != ERROR_SUCCESS) {
                srs_error("decode packet from message payload failed. ret=%d", ret);
                return ret;
            }
            srs_verbose("decode packet from message payload success.");
            break;
        default:
            return ret;
    }
    
    srs_assert(packet);
    
    // always free the packet.
    SrsAutoFree(SrsPacket, packet);
    
    switch (msg->header.message_type) {
        case RTMP_MSG_WindowAcknowledgementSize: {
            SrsSetWindowAckSizePacket* pkt = dynamic_cast<SrsSetWindowAckSizePacket*>(packet);
            srs_assert(pkt != NULL);
            
            if (pkt->ackowledgement_window_size > 0) {
                in_ack_size.ack_window_size = pkt->ackowledgement_window_size;
                // @remark, we ignore this message, for user noneed to care.
                // but it's important for dev, for client/server will block if required 
                // ack msg not arrived.
                srs_info("set ack window size to %d", pkt->ackowledgement_window_size);
            } else {
                srs_warn("ignored. set ack window size is %d", pkt->ackowledgement_window_size);
            }
            break;
        }
        case RTMP_MSG_SetChunkSize: {
            SrsSetChunkSizePacket* pkt = dynamic_cast<SrsSetChunkSizePacket*>(packet);
            srs_assert(pkt != NULL);

            // 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
            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);
            }

            in_chunk_size = pkt->chunk_size;
            srs_trace("input chunk size to %d", pkt->chunk_size);

            break;
        }
        case RTMP_MSG_UserControlMessage: {
            SrsUserControlPacket* pkt = dynamic_cast<SrsUserControlPacket*>(packet);
            srs_assert(pkt != NULL);
            
            if (pkt->event_type == SrcPCUCSetBufferLength) {
                srs_trace("ignored. set buffer length to %d", pkt->extra_data);
            }
            if (pkt->event_type == SrcPCUCPingRequest) {
                if ((ret = response_ping_message(pkt->event_data)) != ERROR_SUCCESS) {
                    return ret;
                }
            }
            break;
        }
        default:
            break;
    }
    
    return ret;
}

int SrsProtocol::on_send_packet(SrsMessageHeader* mh, SrsPacket* packet)
{
    int ret = ERROR_SUCCESS;
    
    // ignore raw bytes oriented RTMP message.
    if (packet == NULL) {
        return ret;
    }
    
    switch (mh->message_type) {
        case RTMP_MSG_SetChunkSize: {
            SrsSetChunkSizePacket* pkt = dynamic_cast<SrsSetChunkSizePacket*>(packet);
            srs_assert(pkt != NULL);
            
            out_chunk_size = pkt->chunk_size;
            
            srs_trace("out chunk size to %d", pkt->chunk_size);
            break;
        }
        case RTMP_MSG_AMF0CommandMessage:
        case RTMP_MSG_AMF3CommandMessage: {
            if (true) {
                SrsConnectAppPacket* pkt = dynamic_cast<SrsConnectAppPacket*>(packet);
                if (pkt) {
                    requests[pkt->transaction_id] = pkt->command_name;
                    break;
                }
            }
            if (true) {
                SrsCreateStreamPacket* pkt = dynamic_cast<SrsCreateStreamPacket*>(packet);
                if (pkt) {
                    requests[pkt->transaction_id] = pkt->command_name;
                    break;
                }
            }
            if (true) {
                SrsFMLEStartPacket* pkt = dynamic_cast<SrsFMLEStartPacket*>(packet);
                if (pkt) {
                    requests[pkt->transaction_id] = pkt->command_name;
                    break;
                }
            }
            break;
        }
        default:
            break;
    }
    
    return ret;
}

int SrsProtocol::response_acknowledgement_message()
{
    int ret = ERROR_SUCCESS;
    
    SrsAcknowledgementPacket* pkt = new SrsAcknowledgementPacket();
    in_ack_size.acked_size = skt->get_recv_bytes();
    pkt->sequence_number = (int32_t)in_ack_size.acked_size;
    
    // cache the message and use flush to send.
    if (!auto_response_when_recv) {
        manual_response_queue.push_back(pkt);
        return ret;
    }
    
    // use underlayer api to send, donot flush again.
    if ((ret = do_send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
        srs_error("send acknowledgement failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("send acknowledgement success.");
    
    return ret;
}

int SrsProtocol::response_ping_message(int32_t timestamp)
{
    int ret = ERROR_SUCCESS;
    
    srs_trace("get a ping request, response it. timestamp=%d", timestamp);
    
    SrsUserControlPacket* pkt = new SrsUserControlPacket();
    
    pkt->event_type = SrcPCUCPingResponse;
    pkt->event_data = timestamp;
    
    // cache the message and use flush to send.
    if (!auto_response_when_recv) {
        manual_response_queue.push_back(pkt);
        return ret;
    }
    
    // use underlayer api to send, donot flush again.
    if ((ret = do_send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
        srs_error("send ping response failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("send ping response success.");
    
    return ret;
}

SrsChunkStream::SrsChunkStream(int _cid)
{
    fmt = 0;
    cid = _cid;
    extended_timestamp = false;
    msg = NULL;
    msg_count = 0;
}

SrsChunkStream::~SrsChunkStream()
{
    srs_freep(msg);
}

SrsConnectAppPacket::SrsConnectAppPacket()
{
    command_name = RTMP_AMF0_COMMAND_CONNECT;
    transaction_id = 1;
    command_object = SrsAmf0Any::object();
    // optional
    args = NULL;
}

SrsConnectAppPacket::~SrsConnectAppPacket()
{
    srs_freep(command_object);
    srs_freep(args);
}

int SrsConnectAppPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode connect command_name failed. ret=%d", ret);
        return ret;
    }
    if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_CONNECT) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 decode connect command_name failed. "
            "command_name=%s, ret=%d", command_name.c_str(), ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("amf0 decode connect transaction_id failed. ret=%d", ret);
        return ret;
    }
    
    // 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;
    }
    
    if (!stream->empty()) {
        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();
        }
    }
    
    srs_info("amf0 decode connect packet success");
    
    return ret;
}

int SrsConnectAppPacket::get_prefer_cid()
{
    return RTMP_CID_OverConnection;
}

int SrsConnectAppPacket::get_message_type()
{
    return RTMP_MSG_AMF0CommandMessage;
}

int SrsConnectAppPacket::get_size()
{
    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 SrsConnectAppPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = command_object->write(stream)) != ERROR_SUCCESS) {
        srs_error("encode command_object failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_object success.");
    
    if (args && (ret = args->write(stream)) != ERROR_SUCCESS) {
        srs_error("encode args failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode args success.");
    
    srs_info("encode connect app request packet 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 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);
        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;
    }
    
    // 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 = props->read(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode connect props failed. ret=%d", ret);
        return ret;
    }
    
    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 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 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 = props->write(stream)) != ERROR_SUCCESS) {
        srs_error("encode props failed. ret=%d", ret);
        return ret;
    }

    srs_verbose("encode props success.");
    
    if ((ret = info->write(stream)) != ERROR_SUCCESS) {
        srs_error("encode info failed. ret=%d", ret);
        return ret;
    }

    srs_verbose("encode info success.");
    
    srs_info("encode connect app response packet success.");
    
    return ret;
}

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 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 SrsCallPacket::get_prefer_cid()
{
    return RTMP_CID_OverConnection;
}

int SrsCallPacket::get_message_type()
{
    return RTMP_MSG_AMF0CommandMessage;
}

int SrsCallPacket::get_size()
{
    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 SrsCallPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if (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;
}

SrsCallResPacket::SrsCallResPacket(double _transaction_id)
{
    command_name = RTMP_AMF0_COMMAND_RESULT;
    transaction_id = _transaction_id;
    command_object = NULL;
    response = NULL;
}

SrsCallResPacket::~SrsCallResPacket()
{
    srs_freep(command_object);
    srs_freep(response);
}

int SrsCallResPacket::get_prefer_cid()
{
    return RTMP_CID_OverConnection;
}

int SrsCallResPacket::get_message_type()
{
    return RTMP_MSG_AMF0CommandMessage;
}

int SrsCallResPacket::get_size()
{
    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 SrsCallResPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if (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 (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 call response packet success.");
    
    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 ret = ERROR_SUCCESS;

    if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode createStream command_name failed. ret=%d", ret);
        return ret;
    }
    if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_CREATE_STREAM) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 decode createStream command_name failed. "
            "command_name=%s, ret=%d", command_name.c_str(), ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("amf0 decode createStream transaction_id failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode createStream command_object failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("amf0 decode createStream packet success");
    
    return ret;
}

int SrsCreateStreamPacket::get_prefer_cid()
{
    return RTMP_CID_OverConnection;
}

int SrsCreateStreamPacket::get_message_type()
{
    return RTMP_MSG_AMF0CommandMessage;
}

int SrsCreateStreamPacket::get_size()
{
    return SrsAmf0Size::str(command_name) + SrsAmf0Size::number()
        + SrsAmf0Size::null();
}

int SrsCreateStreamPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode command_object failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_object success.");
    
    srs_info("encode create stream request packet success.");
    
    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()
{
    srs_freep(command_object);
}

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 ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("amf0 decode createStream transaction_id failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode createStream command_object failed. ret=%d", ret);
        return ret;
    }
    
    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 SrsCreateStreamResPacket::get_prefer_cid()
{
    return RTMP_CID_OverConnection;
}

int SrsCreateStreamResPacket::get_message_type()
{
    return RTMP_MSG_AMF0CommandMessage;
}

int SrsCreateStreamResPacket::get_size()
{
    return SrsAmf0Size::str(command_name) + SrsAmf0Size::number()
        + SrsAmf0Size::null() + SrsAmf0Size::number();
}

int SrsCreateStreamResPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode command_object failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_object success.");
    
    if ((ret = srs_amf0_write_number(stream, stream_id)) != ERROR_SUCCESS) {
        srs_error("encode stream_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode stream_id success.");
    
    
    srs_info("encode createStream response packet success.");
    
    return ret;
}

SrsCloseStreamPacket::SrsCloseStreamPacket()
{
    command_name = RTMP_AMF0_COMMAND_CLOSE_STREAM;
    transaction_id = 0;
    command_object = SrsAmf0Any::null();
}

SrsCloseStreamPacket::~SrsCloseStreamPacket()
{
    srs_freep(command_object);
}

int SrsCloseStreamPacket::decode(SrsStream* stream)
{
    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;
}

SrsFMLEStartPacket::SrsFMLEStartPacket()
{
    command_name = RTMP_AMF0_COMMAND_RELEASE_STREAM;
    transaction_id = 0;
    command_object = SrsAmf0Any::null();
}

SrsFMLEStartPacket::~SrsFMLEStartPacket()
{
    srs_freep(command_object);
}

int SrsFMLEStartPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start command_name failed. ret=%d", ret);
        return ret;
    }
    if (command_name.empty() 
        || (command_name != RTMP_AMF0_COMMAND_RELEASE_STREAM 
        && command_name != RTMP_AMF0_COMMAND_FC_PUBLISH
        && command_name != RTMP_AMF0_COMMAND_UNPUBLISH)
    ) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 decode FMLE start command_name failed. "
            "command_name=%s, ret=%d", command_name.c_str(), ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start transaction_id failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start command_object failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start stream_name failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("amf0 decode FMLE start packet success");
    
    return ret;
}

int SrsFMLEStartPacket::get_prefer_cid()
{
    return RTMP_CID_OverConnection;
}

int SrsFMLEStartPacket::get_message_type()
{
    return RTMP_MSG_AMF0CommandMessage;
}

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 ((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.");
    
    
    srs_info("encode FMLE start response packet success.");
    
    return ret;
}

SrsFMLEStartPacket* SrsFMLEStartPacket::create_release_stream(string stream)
{
    SrsFMLEStartPacket* pkt = new SrsFMLEStartPacket();
    
    pkt->command_name = RTMP_AMF0_COMMAND_RELEASE_STREAM;
    pkt->transaction_id = 2;
    pkt->stream_name = stream;
    
    return pkt;
}

SrsFMLEStartPacket* SrsFMLEStartPacket::create_FC_publish(string stream)
{
    SrsFMLEStartPacket* pkt = new SrsFMLEStartPacket();
    
    pkt->command_name = RTMP_AMF0_COMMAND_FC_PUBLISH;
    pkt->transaction_id = 3;
    pkt->stream_name = stream;
    
    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);
        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 ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode FMLE start response command_object failed. ret=%d", ret);
        return ret;
    }
    
    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_info("amf0 decode FMLE start packet success");
    
    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 ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode command_object failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_object success.");
    
    if ((ret = srs_amf0_write_undefined(stream)) != ERROR_SUCCESS) {
        srs_error("encode args failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode args success.");
    
    
    srs_info("encode FMLE start response packet success.");
    
    return ret;
}

SrsPublishPacket::SrsPublishPacket()
{
    command_name = RTMP_AMF0_COMMAND_PUBLISH;
    transaction_id = 0;
    command_object = SrsAmf0Any::null();
    type = "live";
}

SrsPublishPacket::~SrsPublishPacket()
{
    srs_freep(command_object);
}

int SrsPublishPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode publish command_name failed. ret=%d", ret);
        return ret;
    }
    if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_PUBLISH) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 decode publish command_name failed. "
            "command_name=%s, ret=%d", command_name.c_str(), ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("amf0 decode publish transaction_id failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode publish command_object failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode publish stream_name failed. ret=%d", ret);
        return ret;
    }
    
    if (!stream->empty() && (ret = srs_amf0_read_string(stream, type)) != ERROR_SUCCESS) {
        srs_error("amf0 decode publish type failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("amf0 decode publish packet success");
    
    return ret;
}

int SrsPublishPacket::get_prefer_cid()
{
    return RTMP_CID_OverStream;
}

int SrsPublishPacket::get_message_type()
{
    return RTMP_MSG_AMF0CommandMessage;
}

int SrsPublishPacket::get_size()
{
    return SrsAmf0Size::str(command_name) + SrsAmf0Size::number()
        + SrsAmf0Size::null() + SrsAmf0Size::str(stream_name)
        + SrsAmf0Size::str(type);
}

int SrsPublishPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode command_object failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_object success.");
    
    if ((ret = srs_amf0_write_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;
}

SrsPausePacket::SrsPausePacket()
{
    command_name = RTMP_AMF0_COMMAND_PAUSE;
    transaction_id = 0;
    command_object = SrsAmf0Any::null();

    time_ms = 0;
    is_pause = true;
}

SrsPausePacket::~SrsPausePacket()
{
    srs_freep(command_object);
}

int SrsPausePacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode pause command_name failed. ret=%d", ret);
        return ret;
    }
    if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_PAUSE) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 decode pause command_name failed. "
            "command_name=%s, ret=%d", command_name.c_str(), ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("amf0 decode pause transaction_id failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode pause command_object failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_boolean(stream, is_pause)) != ERROR_SUCCESS) {
        srs_error("amf0 decode pause is_pause failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_number(stream, time_ms)) != ERROR_SUCCESS) {
        srs_error("amf0 decode pause time_ms failed. ret=%d", ret);
        return ret;
    }
    
    srs_info("amf0 decode pause packet success");
    
    return ret;
}

SrsPlayPacket::SrsPlayPacket()
{
    command_name = RTMP_AMF0_COMMAND_PLAY;
    transaction_id = 0;
    command_object = SrsAmf0Any::null();

    start = -2;
    duration = -1;
    reset = true;
}

SrsPlayPacket::~SrsPlayPacket()
{
    srs_freep(command_object);
}

int SrsPlayPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    if ((ret = srs_amf0_read_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode play command_name failed. ret=%d", ret);
        return ret;
    }
    if (command_name.empty() || command_name != RTMP_AMF0_COMMAND_PLAY) {
        ret = ERROR_RTMP_AMF0_DECODE;
        srs_error("amf0 decode play command_name failed. "
            "command_name=%s, ret=%d", command_name.c_str(), ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("amf0 decode play transaction_id failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode play command_object failed. ret=%d", ret);
        return ret;
    }
    
    if ((ret = srs_amf0_read_string(stream, stream_name)) != ERROR_SUCCESS) {
        srs_error("amf0 decode play stream_name failed. ret=%d", ret);
        return ret;
    }
    
    if (!stream->empty() && (ret = srs_amf0_read_number(stream, start)) != ERROR_SUCCESS) {
        srs_error("amf0 decode play start failed. ret=%d", ret);
        return ret;
    }
    if (!stream->empty() && (ret = srs_amf0_read_number(stream, duration)) != ERROR_SUCCESS) {
        srs_error("amf0 decode play duration failed. ret=%d", ret);
        return ret;
    }

    if (stream->empty()) {
        return 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;
    }
    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 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 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;
    }
    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;
    }
    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;
    }
    srs_verbose("encode reset success.");
    
    srs_info("encode play request packet 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()
{
    return SrsAmf0Size::str(command_name) + SrsAmf0Size::number()
        + SrsAmf0Size::null() + SrsAmf0Size::object(desc);
}

int SrsPlayResPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode command_object failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_object success.");
    
    if ((ret = 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;
}

SrsOnBWDonePacket::SrsOnBWDonePacket()
{
    command_name = RTMP_AMF0_COMMAND_ON_BW_DONE;
    transaction_id = 0;
    args = SrsAmf0Any::null();
}

SrsOnBWDonePacket::~SrsOnBWDonePacket()
{
    srs_freep(args);
}

int SrsOnBWDonePacket::get_prefer_cid()
{
    return RTMP_CID_OverConnection;
}

int SrsOnBWDonePacket::get_message_type()
{
    return RTMP_MSG_AMF0CommandMessage;
}

int SrsOnBWDonePacket::get_size()
{
    return SrsAmf0Size::str(command_name) + SrsAmf0Size::number()
        + SrsAmf0Size::null();
}

int SrsOnBWDonePacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode args failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode args success.");
    
    srs_info("encode onBWDone packet success.");
    
    return ret;
}

SrsOnStatusCallPacket::SrsOnStatusCallPacket()
{
    command_name = RTMP_AMF0_COMMAND_ON_STATUS;
    transaction_id = 0;
    args = SrsAmf0Any::null();
    data = SrsAmf0Any::object();
}

SrsOnStatusCallPacket::~SrsOnStatusCallPacket()
{
    srs_freep(args);
    srs_freep(data);
}

int SrsOnStatusCallPacket::get_prefer_cid()
{
    return RTMP_CID_OverStream;
}

int SrsOnStatusCallPacket::get_message_type()
{
    return RTMP_MSG_AMF0CommandMessage;
}

int SrsOnStatusCallPacket::get_size()
{
    return SrsAmf0Size::str(command_name) + SrsAmf0Size::number()
        + SrsAmf0Size::null() + SrsAmf0Size::object(data);
}

int SrsOnStatusCallPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode args failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode args success.");;
    
    if ((ret = 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;
}

SrsBandwidthPacket::SrsBandwidthPacket()
{
    command_name = RTMP_AMF0_COMMAND_ON_STATUS;
    transaction_id = 0;
    args = SrsAmf0Any::null();
    data = SrsAmf0Any::object();
}

SrsBandwidthPacket::~SrsBandwidthPacket()
{
    srs_freep(args);
    srs_freep(data);
}

int SrsBandwidthPacket::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);
        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;
    }

    if ((ret = srs_amf0_read_null(stream)) != ERROR_SUCCESS) {
        srs_error("amf0 decode bwtc command_object 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);
            return ret;
        }
    }

    srs_info("decode SrsBandwidthPacket success.");

    return ret;
}

int SrsBandwidthPacket::get_prefer_cid()
{
    return RTMP_CID_OverStream;
}

int SrsBandwidthPacket::get_message_type()
{
    return RTMP_MSG_AMF0CommandMessage;
}

int SrsBandwidthPacket::get_size()
{
    return SrsAmf0Size::str(command_name) + SrsAmf0Size::number()
        + SrsAmf0Size::null() + SrsAmf0Size::object(data);
}

int SrsBandwidthPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_number(stream, transaction_id)) != ERROR_SUCCESS) {
        srs_error("encode transaction_id failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode transaction_id success.");
    
    if ((ret = srs_amf0_write_null(stream)) != ERROR_SUCCESS) {
        srs_error("encode args failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode args success.");;
    
    if ((ret = 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;
}

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)
{
    command_name = command;
    
    return this;
}

SrsOnStatusDataPacket::SrsOnStatusDataPacket()
{
    command_name = RTMP_AMF0_COMMAND_ON_STATUS;
    data = SrsAmf0Any::object();
}

SrsOnStatusDataPacket::~SrsOnStatusDataPacket()
{
    srs_freep(data);
}

int SrsOnStatusDataPacket::get_prefer_cid()
{
    return RTMP_CID_OverStream;
}

int SrsOnStatusDataPacket::get_message_type()
{
    return RTMP_MSG_AMF0DataMessage;
}

int SrsOnStatusDataPacket::get_size()
{
    return SrsAmf0Size::str(command_name) + SrsAmf0Size::object(data);
}

int SrsOnStatusDataPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = data->write(stream)) != ERROR_SUCCESS) {
        srs_error("encode data failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode data success.");
    
    srs_info("encode onStatus(Data) packet success.");
    
    return ret;
}

SrsSampleAccessPacket::SrsSampleAccessPacket()
{
    command_name = RTMP_AMF0_DATA_SAMPLE_ACCESS;
    video_sample_access = false;
    audio_sample_access = false;
}

SrsSampleAccessPacket::~SrsSampleAccessPacket()
{
}

int SrsSampleAccessPacket::get_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;
    
    if ((ret = srs_amf0_write_string(stream, command_name)) != ERROR_SUCCESS) {
        srs_error("encode command_name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode command_name success.");
    
    if ((ret = srs_amf0_write_boolean(stream, video_sample_access)) != ERROR_SUCCESS) {
        srs_error("encode video_sample_access failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode video_sample_access success.");
    
    if ((ret = srs_amf0_write_boolean(stream, audio_sample_access)) != ERROR_SUCCESS) {
        srs_error("encode audio_sample_access failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode audio_sample_access success.");;
    
    srs_info("encode |RtmpSampleAccess packet success.");
    
    return ret;
}

SrsOnMetaDataPacket::SrsOnMetaDataPacket()
{
    name = SRS_CONSTS_RTMP_ON_METADATA;
    metadata = SrsAmf0Any::object();
}

SrsOnMetaDataPacket::~SrsOnMetaDataPacket()
{
    srs_freep(metadata);
}

int SrsOnMetaDataPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_read_string(stream, name)) != ERROR_SUCCESS) {
        srs_error("decode metadata name failed. ret=%d", ret);
        return ret;
    }

    // ignore the @setDataFrame
    if (name == 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;
        }
    }
    
    srs_verbose("decode metadata name success. name=%s", name.c_str());
    
    // the metadata maybe object or ecma array
    SrsAmf0Any* any = NULL;
    if ((ret = srs_amf0_read_any(stream, &any)) != ERROR_SUCCESS) {
        srs_error("decode metadata metadata failed. ret=%d", ret);
        return ret;
    }
    
    srs_assert(any);
    if (any->is_object()) {
        srs_freep(metadata);
        metadata = any->to_object();
        srs_info("decode metadata object success");
        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");
    }
    
    return ret;
}

int SrsOnMetaDataPacket::get_prefer_cid()
{
    return RTMP_CID_OverConnection2;
}

int SrsOnMetaDataPacket::get_message_type()
{
    return RTMP_MSG_AMF0DataMessage;
}

int SrsOnMetaDataPacket::get_size()
{
    return SrsAmf0Size::str(name) + SrsAmf0Size::object(metadata);
}

int SrsOnMetaDataPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_amf0_write_string(stream, name)) != ERROR_SUCCESS) {
        srs_error("encode name failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("encode name success.");
    
    if ((ret = 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;
}

SrsSetWindowAckSizePacket::SrsSetWindowAckSizePacket()
{
    ackowledgement_window_size = 0;
}

SrsSetWindowAckSizePacket::~SrsSetWindowAckSizePacket()
{
}

int SrsSetWindowAckSizePacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if (!stream->require(4)) {
        ret = ERROR_RTMP_MESSAGE_DECODE;
        srs_error("decode ack window size failed. ret=%d", ret);
        return ret;
    }
    
    ackowledgement_window_size = stream->read_4bytes();
    srs_info("decode ack window size success");
    
    return ret;
}

int SrsSetWindowAckSizePacket::get_prefer_cid()
{
    return RTMP_CID_ProtocolControl;
}

int SrsSetWindowAckSizePacket::get_message_type()
{
    return RTMP_MSG_WindowAcknowledgementSize;
}

int SrsSetWindowAckSizePacket::get_size()
{
    return 4;
}

int SrsSetWindowAckSizePacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if (!stream->require(4)) {
        ret = ERROR_RTMP_MESSAGE_ENCODE;
        srs_error("encode ack size packet failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_4bytes(ackowledgement_window_size);
    
    srs_verbose("encode ack size packet "
        "success. ack_size=%d", ackowledgement_window_size);
    
    return ret;
}

SrsAcknowledgementPacket::SrsAcknowledgementPacket()
{
    sequence_number = 0;
}

SrsAcknowledgementPacket::~SrsAcknowledgementPacket()
{
}

int SrsAcknowledgementPacket::get_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 (!stream->require(4)) {
        ret = ERROR_RTMP_MESSAGE_ENCODE;
        srs_error("encode acknowledgement packet failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_4bytes(sequence_number);
    
    srs_verbose("encode acknowledgement packet "
        "success. sequence_number=%d", sequence_number);
    
    return ret;
}

SrsSetChunkSizePacket::SrsSetChunkSizePacket()
{
    chunk_size = SRS_CONSTS_RTMP_PROTOCOL_CHUNK_SIZE;
}

SrsSetChunkSizePacket::~SrsSetChunkSizePacket()
{
}

int SrsSetChunkSizePacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if (!stream->require(4)) {
        ret = ERROR_RTMP_MESSAGE_DECODE;
        srs_error("decode chunk size failed. ret=%d", ret);
        return ret;
    }
    
    chunk_size = stream->read_4bytes();
    srs_info("decode chunk size success. chunk_size=%d", chunk_size);
    
    return ret;
}

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;
    
    if (!stream->require(4)) {
        ret = ERROR_RTMP_MESSAGE_ENCODE;
        srs_error("encode chunk packet failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_4bytes(chunk_size);
    
    srs_verbose("encode chunk packet success. ack_size=%d", chunk_size);
    
    return ret;
}

SrsSetPeerBandwidthPacket::SrsSetPeerBandwidthPacket()
{
    bandwidth = 0;
    type = 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;
    
    if (!stream->require(5)) {
        ret = ERROR_RTMP_MESSAGE_ENCODE;
        srs_error("encode set bandwidth packet failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_4bytes(bandwidth);
    stream->write_1bytes(type);
    
    srs_verbose("encode set bandwidth packet "
        "success. bandwidth=%d, type=%d", bandwidth, type);
    
    return ret;
}

SrsUserControlPacket::SrsUserControlPacket()
{
    event_type = 0;
    event_data = 0;
    extra_data = 0;
}

SrsUserControlPacket::~SrsUserControlPacket()
{
}

int SrsUserControlPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if (!stream->require(6)) {
        ret = ERROR_RTMP_MESSAGE_DECODE;
        srs_error("decode user control failed. ret=%d", ret);
        return ret;
    }
    
    event_type = stream->read_2bytes();
    event_data = stream->read_4bytes();
    
    if (event_type == SrcPCUCSetBufferLength) {
        if (!stream->require(4)) {
            ret = ERROR_RTMP_MESSAGE_ENCODE;
            srs_error("decode user control packet failed. ret=%d", ret);
            return ret;
        }
        extra_data = stream->read_4bytes();
    }
    
    srs_info("decode user control success. "
        "event_type=%d, event_data=%d, extra_data=%d", 
        event_type, event_data, extra_data);
    
    return ret;
}

int SrsUserControlPacket::get_prefer_cid()
{
    return RTMP_CID_ProtocolControl;
}

int SrsUserControlPacket::get_message_type()
{
    return RTMP_MSG_UserControlMessage;
}

int SrsUserControlPacket::get_size()
{
    if (event_type == SrcPCUCSetBufferLength) {
        return 2 + 4 + 4;
    } else {
        return 2 + 4;
    }
}

int SrsUserControlPacket::encode_packet(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;
    
    if (!stream->require(get_size())) {
        ret = ERROR_RTMP_MESSAGE_ENCODE;
        srs_error("encode user control packet failed. ret=%d", ret);
        return ret;
    }
    
    stream->write_2bytes(event_type);
    stream->write_4bytes(event_data);

    // when event type is set buffer length,
    // write the extra buffer length.
    if (event_type == SrcPCUCSetBufferLength) {
        stream->write_4bytes(extra_data);
        srs_verbose("user control message, buffer_length=%d", extra_data);
    }
    
    srs_verbose("encode user control packet success. "
        "event_type=%d, event_data=%d", event_type, event_data);
    
    return ret;
}


// 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 <srs_rtmp_sdk.hpp>

//#include <srs_core_autofree.hpp>
//#include <srs_rtmp_io.hpp>
//#include <srs_rtmp_amf0.hpp>
//#include <srs_rtmp_handshake.hpp>
//#include <srs_rtmp_utility.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_kernel_utility.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#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()
{
    objectEncoding = RTMP_SIG_AMF0_VER;
    duration = -1;
    args = NULL;
}

SrsRequest::~SrsRequest()
{
    srs_freep(args);
}

SrsRequest* SrsRequest::copy()
{
    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();
    }
    
    return cp;
}

void SrsRequest::update_auth(SrsRequest* req)
{
    pageUrl = req->pageUrl;
    swfUrl = req->swfUrl;
    tcUrl = req->tcUrl;
    
    if (args) {
        srs_freep(args);
    }
    if (req->args) {
        args = req->args->copy()->to_object();
    }
    
    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");
    
    // 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, "/");
}

SrsResponse::SrsResponse()
{
    stream_id = SRS_DEFAULT_SID;
}

SrsResponse::~SrsResponse()
{
}

string srs_client_type_string(SrsRtmpConnType type)
{
    switch (type) {
        case SrsRtmpConnPlay: return "Play";
        case SrsRtmpConnFlashPublish: return "publish(FlashPublish)";
        case SrsRtmpConnFMLEPublish: return "publish(FMLEPublish)";
        default: return "Unknown";
    }
}

SrsHandshakeBytes::SrsHandshakeBytes() 
{
    c0c1 = s0s1s2 = c2 = NULL;
}

SrsHandshakeBytes::~SrsHandshakeBytes() 
{
    srs_freep(c0c1);
    srs_freep(s0s1s2);
    srs_freep(c2);
}

int SrsHandshakeBytes::read_c0c1(ISrsProtocolReaderWriter* io)
{
    int ret = ERROR_SUCCESS;
    
    if (c0c1) {
        return 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("read c0c1 success.");
    
    return ret;
}

int SrsHandshakeBytes::read_s0s1s2(ISrsProtocolReaderWriter* io)
{
    int ret = ERROR_SUCCESS;
    
    if (s0s1s2) {
        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);
        return ret;
    }
    srs_verbose("read s0s1s2 success.");
    
    return ret;
}

int SrsHandshakeBytes::read_c2(ISrsProtocolReaderWriter* io)
{
    int ret = ERROR_SUCCESS;
    
    if (c2) {
        return 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.");
    
    return ret;
}

int SrsHandshakeBytes::create_c0c1()
{
    int ret = ERROR_SUCCESS;
    
    if (c0c1) {
        return 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;
    }
    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;
    
    if (s0s1s2) {
        return ret;
    }
    
    s0s1s2 = new char[3073];
    srs_random_generate(s0s1s2, 3073);
    
    // plain text required.
    SrsStream stream;
    if ((ret = stream.initialize(s0s1s2, 9)) != ERROR_SUCCESS) {
        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);
    }
    
    // if c1 specified, copy c1 to s2.
    // @see: https://github.com/simple-rtmp-server/srs/issues/46
    if (c1) {
        memcpy(s0s1s2 + 1537, c1, 1536);
    }
    
    return ret;
}

int SrsHandshakeBytes::create_c2()
{
    int ret = ERROR_SUCCESS;
    
    if (c2) {
        return ret;
    }
    
    c2 = new char[1536];
    srs_random_generate(c2, 1536);
    
    // time
    SrsStream stream;
    if ((ret = stream.initialize(c2, 8)) != ERROR_SUCCESS) {
        return ret;
    }
    stream.write_4bytes((int32_t)::time(NULL));
    // c2 time2 copy from s1
    if (s0s1s2) {
        stream.write_bytes(s0s1s2 + 1, 4);
    }
    
    return ret;
}

SrsRtmpClient::SrsRtmpClient(ISrsProtocolReaderWriter* skt)
{
    io = skt;
    protocol = new SrsProtocol(skt);
    hs_bytes = new SrsHandshakeBytes();
}

SrsRtmpClient::~SrsRtmpClient()
{
    srs_freep(protocol);
    srs_freep(hs_bytes);
}

void SrsRtmpClient::set_recv_timeout(int64_t timeout_us)
{
    protocol->set_recv_timeout(timeout_us);
}

void SrsRtmpClient::set_send_timeout(int64_t timeout_us)
{
    protocol->set_send_timeout(timeout_us);
}

int64_t SrsRtmpClient::get_recv_bytes()
{
    return protocol->get_recv_bytes();
}

int64_t SrsRtmpClient::get_send_bytes()
{
    return protocol->get_send_bytes();
}

int SrsRtmpClient::recv_message(SrsCommonMessage** pmsg)
{
    return protocol->recv_message(pmsg);
}

int SrsRtmpClient::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket)
{
    return protocol->decode_message(msg, ppacket);
}

int SrsRtmpClient::send_and_free_message(SrsSharedPtrMessage* msg, int stream_id)
{
    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;
    
    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;
            }
        }
        return ret;
    }
    
    srs_freep(hs_bytes);
    
    return ret;
}

int SrsRtmpClient::simple_handshake()
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(hs_bytes);
    
    SrsSimpleHandshake simple_hs;
    if ((ret = simple_hs.handshake_with_server(hs_bytes, io)) != ERROR_SUCCESS) {
        return ret;
    }
    
    srs_freep(hs_bytes);
    
    return ret;
}

int SrsRtmpClient::complex_handshake()
{
    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;
}

int SrsRtmpClient::connect_app(string app, 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 = 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 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;
    
    // 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;
        }
    }
    
    // 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;
        }
    }
    
    // expect connect _result
    SrsCommonMessage* msg = NULL;
    SrsConnectAppResPacket* pkt = NULL;
    if ((ret = expect_message<SrsConnectAppResPacket>(&msg, &pkt)) != ERROR_SUCCESS) {
        srs_error("expect connect app response message 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();
        }
    }
    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;
}

int SrsRtmpClient::create_stream(int& stream_id)
{
    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<SrsCreateStreamResPacket>(&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 SrsRtmpClient::play(string stream, int stream_id)
{
    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;
        }
    }
    
    // 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;
        }
    }
    
    // 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 ret;
}

int SrsRtmpClient::publish(string stream, int stream_id)
{
    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 SrsRtmpClient::fmle_publish(string stream, int& stream_id)
{
    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<SrsCreateStreamResPacket>(&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;
    }

    // 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;
}

SrsRtmpServer::SrsRtmpServer(ISrsProtocolReaderWriter* skt)
{
    io = skt;
    protocol = new SrsProtocol(skt);
    hs_bytes = new SrsHandshakeBytes();
}

SrsRtmpServer::~SrsRtmpServer()
{
    srs_freep(protocol);
    srs_freep(hs_bytes);
}

void SrsRtmpServer::set_auto_response(bool v)
{
    protocol->set_auto_response(v);
}

#ifdef SRS_PERF_MERGED_READ
void SrsRtmpServer::set_merge_read(bool v, IMergeReadHandler* handler)
{
    protocol->set_merge_read(v, handler);
}

void SrsRtmpServer::set_recv_buffer(int buffer_size)
{
    protocol->set_recv_buffer(buffer_size);
}
#endif

void SrsRtmpServer::set_recv_timeout(int64_t timeout_us)
{
    protocol->set_recv_timeout(timeout_us);
}

int64_t SrsRtmpServer::get_recv_timeout()
{
    return protocol->get_recv_timeout();
}

void SrsRtmpServer::set_send_timeout(int64_t timeout_us)
{
    protocol->set_send_timeout(timeout_us);
}

int64_t SrsRtmpServer::get_send_timeout()
{
    return protocol->get_send_timeout();
}

int64_t SrsRtmpServer::get_recv_bytes()
{
    return protocol->get_recv_bytes();
}

int64_t SrsRtmpServer::get_send_bytes()
{
    return protocol->get_send_bytes();
}

int SrsRtmpServer::recv_message(SrsCommonMessage** pmsg)
{
    return protocol->recv_message(pmsg);
}

int SrsRtmpServer::decode_message(SrsCommonMessage* msg, SrsPacket** ppacket)
{
    return protocol->decode_message(msg, ppacket);
}

int SrsRtmpServer::send_and_free_message(SrsSharedPtrMessage* msg, int stream_id)
{
    return protocol->send_and_free_message(msg, stream_id);
}

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;
    
    srs_assert(hs_bytes);
    
    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_freep(hs_bytes);
    
    return ret;
}

int SrsRtmpServer::connect_app(SrsRequest* req)
{
    int ret = ERROR_SUCCESS;
    
    SrsCommonMessage* msg = NULL;
    SrsConnectAppPacket* pkt = NULL;
    if ((ret = expect_message<SrsConnectAppPacket>(&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");
    
    SrsAmf0Any* prop = NULL;
    
    if ((prop = pkt->command_object->ensure_property_string("tcUrl")) == NULL) {
        ret = ERROR_RTMP_REQ_CONNECT;
        srs_error("invalid request, must specifies the tcUrl. ret=%d", ret);
        return ret;
    }
    req->tcUrl = 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();
    }
    
    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("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 SrsRtmpServer::set_window_ack_size(int ack_size)
{
    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;
}

int SrsRtmpServer::set_peer_bandwidth(int bandwidth, int type)
{
    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);
        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;
    
    SrsConnectAppResPacket* pkt = new SrsConnectAppResPacket();
    
    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));
    
    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 ((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("send connect app response message success.");
    
    return ret;
}

void SrsRtmpServer::response_connect_reject(SrsRequest* /*req*/, const char* desc)
{
    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.");

    return;
}

int SrsRtmpServer::on_bw_done()
{
    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);
        return ret;
    }
    srs_info("send onBWDone message success.");
    
    return ret;
}

int SrsRtmpServer::identify_client(int stream_id, SrsRtmpConnType& type, string& stream_name, double& duration)
{
    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;
        }

        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<SrsCreateStreamPacket*>(pkt)) {
            srs_info("identify client by create stream, play or flash publish.");
            return identify_create_stream_client(dynamic_cast<SrsCreateStreamPacket*>(pkt), stream_id, type, stream_name, duration);
        }
        if (dynamic_cast<SrsFMLEStartPacket*>(pkt)) {
            srs_info("identify client by releaseStream, fmle publish.");
            return identify_fmle_publish_client(dynamic_cast<SrsFMLEStartPacket*>(pkt), type, stream_name);
        }
        if (dynamic_cast<SrsPlayPacket*>(pkt)) {
            srs_info("level0 identify client by play.");
            return identify_play_client(dynamic_cast<SrsPlayPacket*>(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<SrsCallPacket*>(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;
}

int SrsRtmpServer::set_chunk_size(int chunk_size)
{
    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 SrsRtmpServer::start_play(int stream_id)
{
    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();

        // 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.");
    }
    
    // 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("start play success.");
    
    return ret;
}

int SrsRtmpServer::on_play_client_pause(int stream_id, bool is_pause)
{
    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 SrsRtmpServer::start_fmle_publish(int stream_id)
{
    int ret = ERROR_SUCCESS;
    
    // FCPublish
    double fc_publish_tid = 0;
    if (true) {
        SrsCommonMessage* msg = NULL;
        SrsFMLEStartPacket* pkt = NULL;
        if ((ret = expect_message<SrsFMLEStartPacket>(&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<SrsCreateStreamPacket>(&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<SrsPublishPacket>(&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.");
    }
    
    srs_info("FMLE publish success.");
    
    return ret;
}

int SrsRtmpServer::fmle_unpublish(int stream_id, double unpublish_tid)
{
    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.");
    }
    
    srs_info("FMLE unpublish success.");
    
    return ret;
}

int SrsRtmpServer::start_flash_publish(int stream_id)
{
    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 SrsRtmpServer::identify_create_stream_client(SrsCreateStreamPacket* req, int stream_id, SrsRtmpConnType& type, string& stream_name, double& duration)
{
    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.");
    }
    
    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 (!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<SrsPlayPacket*>(pkt)) {
            srs_info("level1 identify client by play.");
            return identify_play_client(dynamic_cast<SrsPlayPacket*>(pkt), type, stream_name, duration);
        }
        if (dynamic_cast<SrsPublishPacket*>(pkt)) {
            srs_info("identify client by publish, falsh publish.");
            return identify_flash_publish_client(dynamic_cast<SrsPublishPacket*>(pkt), type, stream_name);
        }
        if (dynamic_cast<SrsCreateStreamPacket*>(pkt)) {
            srs_info("identify client by create stream, play or flash publish.");
            return identify_create_stream_client(dynamic_cast<SrsCreateStreamPacket*>(pkt), stream_id, type, stream_name, duration);
        }
        
        srs_trace("ignore AMF0/AMF3 command message.");
    }
    
    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;
    
    // 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;
}

int SrsRtmpServer::identify_flash_publish_client(SrsPublishPacket* req, SrsRtmpConnType& type, string& stream_name)
{
    int ret = ERROR_SUCCESS;
    
    type = SrsRtmpConnFlashPublish;
    stream_name = req->stream_name;
    
    return ret;
}

int SrsRtmpServer::identify_play_client(SrsPlayPacket* req, SrsRtmpConnType& type, string& stream_name, double& duration)
{
    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);

    return ret;
}


// following is generated by src/protocol/srs_rtmp_handshake.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 <srs_rtmp_handshake.hpp>

#include <time.h>

//#include <srs_core_autofree.hpp>
//#include <srs_kernel_error.hpp>
//#include <srs_kernel_log.hpp>
//#include <srs_rtmp_io.hpp>
//#include <srs_rtmp_utility.hpp>
//#include <srs_rtmp_sdk.hpp>
//#include <srs_kernel_stream.hpp>

#ifdef SRS_AUTO_SSL

using namespace _srs_internal;

// for openssl_HMACsha256
#include <openssl/evp.h>
#include <openssl/hmac.h>
// for openssl_generate_key
#include <openssl/dh.h>

namespace _srs_internal
{
    // 68bytes FMS key which is used to sign the sever packet.
    u_int8_t SrsGenuineFMSKey[] = {
        0x47, 0x65, 0x6e, 0x75, 0x69, 0x6e, 0x65, 0x20,
        0x41, 0x64, 0x6f, 0x62, 0x65, 0x20, 0x46, 0x6c,
        0x61, 0x73, 0x68, 0x20, 0x4d, 0x65, 0x64, 0x69,
        0x61, 0x20, 0x53, 0x65, 0x72, 0x76, 0x65, 0x72,
        0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Media Server 001
        0xf0, 0xee, 0xc2, 0x4a, 0x80, 0x68, 0xbe, 0xe8,
        0x2e, 0x00, 0xd0, 0xd1, 0x02, 0x9e, 0x7e, 0x57,
        0x6e, 0xec, 0x5d, 0x2d, 0x29, 0x80, 0x6f, 0xab,
        0x93, 0xb8, 0xe6, 0x36, 0xcf, 0xeb, 0x31, 0xae
    }; // 68
    
    // 62bytes FP key which is used to sign the client packet.
    u_int8_t SrsGenuineFPKey[] = {
        0x47, 0x65, 0x6E, 0x75, 0x69, 0x6E, 0x65, 0x20,
        0x41, 0x64, 0x6F, 0x62, 0x65, 0x20, 0x46, 0x6C,
        0x61, 0x73, 0x68, 0x20, 0x50, 0x6C, 0x61, 0x79,
        0x65, 0x72, 0x20, 0x30, 0x30, 0x31, // Genuine Adobe Flash Player 001
        0xF0, 0xEE, 0xC2, 0x4A, 0x80, 0x68, 0xBE, 0xE8,
        0x2E, 0x00, 0xD0, 0xD1, 0x02, 0x9E, 0x7E, 0x57,
        0x6E, 0xEC, 0x5D, 0x2D, 0x29, 0x80, 0x6F, 0xAB,
        0x93, 0xB8, 0xE6, 0x36, 0xCF, 0xEB, 0x31, 0xAE
    }; // 62
    
    int do_openssl_HMACsha256(HMAC_CTX* ctx, const void* data, int data_size, void* digest, unsigned int* digest_size) 
    {
        int ret = ERROR_SUCCESS;
        
        if (HMAC_Update(ctx, (unsigned char *) data, data_size) < 0) {
            ret = ERROR_OpenSslSha256Update;
            return ret;
        }
    
        if (HMAC_Final(ctx, (unsigned char *) digest, digest_size) < 0) {
            ret = ERROR_OpenSslSha256Final;
            return ret;
        }
        
        return ret;
    }
    /**
    * sha256 digest algorithm.
    * @param key the sha256 key, NULL to use EVP_Digest, for instance,
    *       hashlib.sha256(data).digest().
    */
    int openssl_HMACsha256(const void* key, int key_size, const void* data, int data_size, void* digest) 
    {
        int ret = ERROR_SUCCESS;
        
        unsigned int digest_size = 0;
        
        unsigned char* temp_key = (unsigned char*)key;
        unsigned char* temp_digest = (unsigned char*)digest;
        
        if (key == NULL) {
            // use data to digest.
            // @see ./crypto/sha/sha256t.c
            // @see ./crypto/evp/digest.c
            if (EVP_Digest(data, data_size, temp_digest, &digest_size, EVP_sha256(), NULL) < 0)
            {
                ret = ERROR_OpenSslSha256EvpDigest;
                return ret;
            }
        } else {
            // use key-data to digest.
            HMAC_CTX ctx;
            
            // @remark, if no key, use EVP_Digest to digest,
            // for instance, in python, hashlib.sha256(data).digest().
            HMAC_CTX_init(&ctx);
            if (HMAC_Init_ex(&ctx, temp_key, key_size, EVP_sha256(), NULL) < 0) {
                ret = ERROR_OpenSslSha256Init;
                return ret;
            }
            
            ret = do_openssl_HMACsha256(&ctx, data, data_size, temp_digest, &digest_size);
            HMAC_CTX_cleanup(&ctx);
            
            if (ret != ERROR_SUCCESS) {
                return ret;
            }
        }
        
        if (digest_size != 32) {
            ret = ERROR_OpenSslSha256DigestSize;
            return ret;
        }
        
        return ret;
    }
    
    #define RFC2409_PRIME_1024 \
            "FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1" \
            "29024E088A67CC74020BBEA63B139B22514A08798E3404DD" \
            "EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245" \
            "E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED" \
            "EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE65381" \
            "FFFFFFFFFFFFFFFF"
    
    SrsDH::SrsDH()
    {
        pdh = NULL;
    }
    
    SrsDH::~SrsDH()
    {
        if (pdh != NULL) {
            if (pdh->p != NULL) {
                BN_free(pdh->p);
                pdh->p = NULL;
            }
            if (pdh->g != NULL) {
                BN_free(pdh->g);
                pdh->g = NULL;
            }
            DH_free(pdh);
            pdh = NULL;
        }
    }
    
    int SrsDH::initialize(bool ensure_128bytes_public_key)
    {
        int ret = ERROR_SUCCESS;
        
        for (;;) {
            if ((ret = do_initialize()) != ERROR_SUCCESS) {
                return ret;
            }
            
            if (ensure_128bytes_public_key) {
                int32_t key_size = BN_num_bytes(pdh->pub_key);
                if (key_size != 128) {
                    srs_warn("regenerate 128B key, current=%dB", key_size);
                    continue;
                }
            }
            
            break;
        }
        
        return ret;
    }
    
    int SrsDH::copy_public_key(char* pkey, int32_t& pkey_size)
    {
        int ret = ERROR_SUCCESS;
        
        // copy public key to bytes.
        // sometimes, the key_size is 127, seems ok.
        int32_t key_size = BN_num_bytes(pdh->pub_key);
        srs_assert(key_size > 0);
        
        // 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
        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
        srs_assert(key_size <= pkey_size);
        pkey_size = key_size;
        
        return ret;
    }
    
    int SrsDH::copy_shared_key(const char* ppkey, int32_t ppkey_size, char* skey, int32_t& skey_size)
    {
        int ret = ERROR_SUCCESS;
        
        BIGNUM* ppk = NULL;
        if ((ppk = BN_bin2bn((const unsigned char*)ppkey, ppkey_size, 0)) == NULL) {
            ret = ERROR_OpenSslGetPeerPublicKey;
            return ret;
        }
        
        // 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
        int32_t key_size = DH_compute_key((unsigned char*)skey, ppk, pdh);
        
        if (key_size < ppkey_size) {
            srs_warn("shared key size=%d, ppk_size=%d", key_size, ppkey_size);
        }
        
        if (key_size < 0 || key_size > skey_size) {
            ret = ERROR_OpenSslComputeSharedKey;
        } else {
            skey_size = key_size;
        }
        
        if (ppk) {
            BN_free(ppk);
        }
        
        return ret;
    }
    
    int SrsDH::do_initialize()
    {
        int ret = ERROR_SUCCESS;
        
        int32_t bits_count = 1024; 
        
        //1. Create the DH
        if ((pdh = DH_new()) == NULL) {
            ret = ERROR_OpenSslCreateDH; 
            return ret;
        }
    
        //2. Create his internal p and g
        if ((pdh->p = BN_new()) == NULL) {
            ret = ERROR_OpenSslCreateP; 
            return ret;
        }
        if ((pdh->g = BN_new()) == NULL) {
            ret = ERROR_OpenSslCreateG; 
            return ret;
        }
    
        //3. initialize p and g, @see ./test/ectest.c:260
        if (!BN_hex2bn(&pdh->p, RFC2409_PRIME_1024)) {
            ret = ERROR_OpenSslParseP1024; 
            return ret;
        }
        // @see ./test/bntest.c:1764
        if (!BN_set_word(pdh->g, 2)) {
            ret = ERROR_OpenSslSetG;
            return ret;
        }
    
        // 4. Set the key length
        pdh->length = bits_count;
    
        // 5. Generate private and public key
        // @see ./test/dhtest.c:152
        if (!DH_generate_key(pdh)) {
            ret = ERROR_OpenSslGenerateDHKeys;
            return ret;
        }
        
        return ret;
    }
    
    key_block::key_block()
    {
        offset = (int32_t)rand();
        random0 = NULL;
        random1 = NULL;
        
        int valid_offset = calc_valid_offset();
        srs_assert(valid_offset >= 0);
        
        random0_size = valid_offset;
        if (random0_size > 0) {
            random0 = new char[random0_size];
            srs_random_generate(random0, random0_size);
            snprintf(random0, random0_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
        }
        
        srs_random_generate(key, sizeof(key));
        
        random1_size = 764 - valid_offset - 128 - 4;
        if (random1_size > 0) {
            random1 = new char[random1_size];
            srs_random_generate(random1, random1_size);
            snprintf(random1, random1_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
        }
    }
    
    key_block::~key_block()
    {
        srs_freep(random0);
        srs_freep(random1);
    }
    
    int key_block::parse(SrsStream* stream)
    {
        int ret = ERROR_SUCCESS;
        
        // the key must be 764 bytes.
        srs_assert(stream->require(764));
    
        // read the last offset first, 760-763
        stream->skip(764 - sizeof(int32_t));
        offset = stream->read_4bytes();
        
        // reset stream to read others.
        stream->skip(-764);
        
        int valid_offset = calc_valid_offset();
        srs_assert(valid_offset >= 0);
        
        random0_size = valid_offset;
        if (random0_size > 0) {
            srs_freep(random0);
            random0 = new char[random0_size];
            stream->read_bytes(random0, random0_size);
        }
        
        stream->read_bytes(key, 128);
        
        random1_size = 764 - valid_offset - 128 - 4;
        if (random1_size > 0) {
            srs_freep(random1);
            random1 = new char[random1_size];
            stream->read_bytes(random1, random1_size);
        }
        
        return ret;
    }
    
    int key_block::calc_valid_offset()
    {
        int max_offset_size = 764 - 128 - 4;
        
        int valid_offset = 0;
        u_int8_t* pp = (u_int8_t*)&offset;
        valid_offset += *pp++;
        valid_offset += *pp++;
        valid_offset += *pp++;
        valid_offset += *pp++;
    
        return valid_offset % max_offset_size;
    }
    
    digest_block::digest_block()
    {
        offset = (int32_t)rand();
        random0 = NULL;
        random1 = NULL;
        
        int valid_offset = calc_valid_offset();
        srs_assert(valid_offset >= 0);
        
        random0_size = valid_offset;
        if (random0_size > 0) {
            random0 = new char[random0_size];
            srs_random_generate(random0, random0_size);
            snprintf(random0, random0_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
        }
        
        srs_random_generate(digest, sizeof(digest));
        
        random1_size = 764 - 4 - valid_offset - 32;
        if (random1_size > 0) {
            random1 = new char[random1_size];
            srs_random_generate(random1, random1_size);
            snprintf(random1, random1_size, "%s", RTMP_SIG_SRS_HANDSHAKE);
        }
    }
    
    digest_block::~digest_block()
    {
        srs_freep(random0);
        srs_freep(random1);
    }

    int digest_block::parse(SrsStream* stream)
    {
        int ret = ERROR_SUCCESS;
        
        // the digest must be 764 bytes.
        srs_assert(stream->require(764));
        
        offset = stream->read_4bytes();
        
        int valid_offset = calc_valid_offset();
        srs_assert(valid_offset >= 0);
        
        random0_size = valid_offset;
        if (random0_size > 0) {
            srs_freep(random0);
            random0 = new char[random0_size];
            stream->read_bytes(random0, random0_size);
        }
        
        stream->read_bytes(digest, 32);
        
        random1_size = 764 - 4 - valid_offset - 32;
        if (random1_size > 0) {
            srs_freep(random1);
            random1 = new char[random1_size];
            stream->read_bytes(random1, random1_size);
        }
        
        return ret;
    }
    
    int digest_block::calc_valid_offset()
    {
        int max_offset_size = 764 - 32 - 4;
        
        int valid_offset = 0;
        u_int8_t* pp = (u_int8_t*)&offset;
        valid_offset += *pp++;
        valid_offset += *pp++;
        valid_offset += *pp++;
        valid_offset += *pp++;
    
        return valid_offset % max_offset_size;
    }
    
    c1s1_strategy::c1s1_strategy()
    {
    }
    
    c1s1_strategy::~c1s1_strategy()
    {
    }
    
    char* c1s1_strategy::get_digest()
    {
        return digest.digest;
    }
    
    char* c1s1_strategy::get_key()
    {
        return key.key;
    }
    
    int c1s1_strategy::dump(c1s1* owner, char* _c1s1, int size)
    {
        srs_assert(size == 1536);
        return copy_to(owner, _c1s1, size, true);
    }
    
    int c1s1_strategy::c1_create(c1s1* owner)
    {
        int ret = ERROR_SUCCESS;
        
        // generate digest
        char* c1_digest = NULL;
        
        if ((ret = calc_c1_digest(owner, c1_digest)) != ERROR_SUCCESS) {
            srs_error("sign c1 error, failed to calc digest. ret=%d", ret);
            return ret;
        }
        
        srs_assert(c1_digest != NULL);
        SrsAutoFree(char, c1_digest);
        
        memcpy(digest.digest, c1_digest, 32);
        
        return ret;
    }
    
    int c1s1_strategy::c1_validate_digest(c1s1* owner, bool& is_valid)
    {
        int ret = ERROR_SUCCESS;
        
        char* c1_digest = NULL;
        
        if ((ret = calc_c1_digest(owner, c1_digest)) != ERROR_SUCCESS) {
            srs_error("validate c1 error, failed to calc digest. ret=%d", ret);
            return ret;
        }
        
        srs_assert(c1_digest != NULL);
        SrsAutoFree(char, c1_digest);
        
        is_valid = srs_bytes_equals(digest.digest, c1_digest, 32);
        
        return ret;
    }
    
    int c1s1_strategy::s1_create(c1s1* owner, c1s1* c1)
    {
        int ret = ERROR_SUCCESS;

        SrsDH dh;
        
        // ensure generate 128bytes public key.
        if ((ret = dh.initialize(true)) != ERROR_SUCCESS) {
            return ret;
        }
        
        // directly generate the public key.
        // @see: https://github.com/simple-rtmp-server/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);
            return ret;
        }

        // altough the public key is always 128bytes, but the share key maybe not.
        // we just ignore the actual key size, but if need to use the key, must use the actual size.
        // TODO: FIXME: use the actual key size.
        //srs_assert(pkey_size == 128);
        srs_verbose("calc s1 key success.");
            
        char* s1_digest = NULL;
        if ((ret = calc_s1_digest(owner, s1_digest))  != ERROR_SUCCESS) {
            srs_error("calc s1 digest failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("calc s1 digest success.");
        
        srs_assert(s1_digest != NULL);
        SrsAutoFree(char, s1_digest);
        
        memcpy(digest.digest, s1_digest, 32);
        srs_verbose("copy s1 key success.");
        
        return ret;
    }
    
    int c1s1_strategy::s1_validate_digest(c1s1* owner, bool& is_valid)
    {
        int ret = ERROR_SUCCESS;
        
        char* s1_digest = NULL;
        
        if ((ret = calc_s1_digest(owner, s1_digest)) != ERROR_SUCCESS) {
            srs_error("validate s1 error, failed to calc digest. ret=%d", ret);
            return ret;
        }
        
        srs_assert(s1_digest != NULL);
        SrsAutoFree(char, s1_digest);
        
        is_valid = srs_bytes_equals(digest.digest, s1_digest, 32);
        
        return ret;
    }
    
    int c1s1_strategy::calc_c1_digest(c1s1* owner, char*& c1_digest)
    {
        int ret = ERROR_SUCCESS;

        /**
        * c1s1 is splited by digest:
        *     c1s1-part1: n bytes (time, version, key and digest-part1).
        *     digest-data: 32bytes
        *     c1s1-part2: (1536-n-32)bytes (digest-part2)
        * @return a new allocated bytes, user must free it.
        */
        char* c1s1_joined_bytes = new char[1536 -32];
        SrsAutoFree(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_error("calc digest for c1 failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("digest calculated for c1");
        
        return ret;
    }
    
    int c1s1_strategy::calc_s1_digest(c1s1* owner, char*& s1_digest)
    {
        int ret = ERROR_SUCCESS;

        /**
        * c1s1 is splited by digest:
        *     c1s1-part1: n bytes (time, version, key and digest-part1).
        *     digest-data: 32bytes
        *     c1s1-part2: (1536-n-32)bytes (digest-part2)
        * @return a new allocated bytes, user must free it.
        */
        char* c1s1_joined_bytes = new char[1536 -32];
        SrsAutoFree(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_error("calc digest for s1 failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("digest calculated for s1");

        return ret;
    }
    
    void c1s1_strategy::copy_time_version(SrsStream* stream, c1s1* owner)
    {
        srs_assert(stream->require(8));
        
        // 4bytes time
        stream->write_4bytes(owner->time);

        // 4bytes version
        stream->write_4bytes(owner->version);
    }
    void c1s1_strategy::copy_key(SrsStream* stream)
    {
        srs_assert(key.random0_size >= 0);
        srs_assert(key.random1_size >= 0);
        
        int total = key.random0_size + 128 + key.random1_size + 4;
        srs_assert(stream->require(total));
        
        // 764bytes key block
        if (key.random0_size > 0) {
            stream->write_bytes(key.random0, key.random0_size);
        }
        
        stream->write_bytes(key.key, 128);
        
        if (key.random1_size > 0) {
            stream->write_bytes(key.random1, key.random1_size);
        }
        
        stream->write_4bytes(key.offset);
    }
    void c1s1_strategy::copy_digest(SrsStream* stream, bool with_digest)
    {
        srs_assert(key.random0_size >= 0);
        srs_assert(key.random1_size >= 0);
        
        int total = 4 + digest.random0_size + digest.random1_size;
        if (with_digest) {
            total += 32;
        }
        srs_assert(stream->require(total));
        
        // 732bytes digest block without the 32bytes digest-data
        // nbytes digest block part1
        stream->write_4bytes(digest.offset);
        
        // digest random padding.
        if (digest.random0_size > 0) {
            stream->write_bytes(digest.random0, digest.random0_size);
        }
        
        // digest
        if (with_digest) {
            stream->write_bytes(digest.digest, 32);
        }
        
        // nbytes digest block part2
        if (digest.random1_size > 0) {
            stream->write_bytes(digest.random1, digest.random1_size);
        }
    }
    
    c1s1_strategy_schema0::c1s1_strategy_schema0()
    {
    }
    
    c1s1_strategy_schema0::~c1s1_strategy_schema0()
    {
    }
    
    srs_schema_type c1s1_strategy_schema0::schema()
    {
        return srs_schema0;
    }
    
    int c1s1_strategy_schema0::parse(char* _c1s1, int size)
    {
        int ret = ERROR_SUCCESS;
        
        srs_assert(size == 1536);
        
        SrsStream stream;
        
        if ((ret = stream.initialize(_c1s1 + 8, 764)) != ERROR_SUCCESS) {
            return ret;
        }
        
        if ((ret = key.parse(&stream)) != ERROR_SUCCESS) {
            srs_error("parse the c1 key failed. ret=%d", ret);
            return ret;
        }
        
        if ((ret = stream.initialize(_c1s1 + 8 + 764, 764)) != ERROR_SUCCESS) {
            return ret;
        }

        if ((ret = digest.parse(&stream)) != ERROR_SUCCESS) {
            srs_error("parse the c1 digest failed. ret=%d", ret);
            return ret;
        }
        
        srs_verbose("parse c1 key-digest success");
        
        return ret;
    }
    
    int c1s1_strategy_schema0::copy_to(c1s1* owner, char* bytes, int size, bool with_digest)
    {
        int ret = ERROR_SUCCESS;
        
        if (with_digest) {
            srs_assert(size == 1536);
        } else {
            srs_assert(size == 1504);
        }
        
        SrsStream stream;
        
        if ((ret = stream.initialize(bytes, size)) != ERROR_SUCCESS) {
            return ret;
        }
        
        copy_time_version(&stream, owner);
        copy_key(&stream);
        copy_digest(&stream, with_digest);
        
        srs_assert(stream.empty());
        
        return ret;
    }
    
    c1s1_strategy_schema1::c1s1_strategy_schema1()
    {
    }
    
    c1s1_strategy_schema1::~c1s1_strategy_schema1()
    {
    }
    
    srs_schema_type c1s1_strategy_schema1::schema()
    {
        return srs_schema1;
    }
    
    int c1s1_strategy_schema1::parse(char* _c1s1, int size)
    {
        int ret = ERROR_SUCCESS;
        
        srs_assert(size == 1536);
        
        SrsStream stream;
        
        if ((ret = stream.initialize(_c1s1 + 8, 764)) != ERROR_SUCCESS) {
            return ret;
        }

        if ((ret = digest.parse(&stream)) != ERROR_SUCCESS) {
            srs_error("parse the c1 digest failed. ret=%d", ret);
            return ret;
        }
        
        if ((ret = stream.initialize(_c1s1 + 8 + 764, 764)) != ERROR_SUCCESS) {
            return ret;
        }
        
        if ((ret = key.parse(&stream)) != ERROR_SUCCESS) {
            srs_error("parse the c1 key failed. ret=%d", ret);
            return ret;
        }
        
        srs_verbose("parse c1 digest-key success");
        
        return ret;
    }
    
    int c1s1_strategy_schema1::copy_to(c1s1* owner, char* bytes, int size, bool with_digest)
    {
        int ret = ERROR_SUCCESS;
        
        if (with_digest) {
            srs_assert(size == 1536);
        } else {
            srs_assert(size == 1504);
        }
        
        SrsStream stream;
        
        if ((ret = stream.initialize(bytes, size)) != ERROR_SUCCESS) {
            return ret;
        }
        
        copy_time_version(&stream, owner);
        copy_digest(&stream, with_digest);
        copy_key(&stream);
        
        srs_assert(stream.empty());
        
        return ret;
    }
    
    c1s1::c1s1()
    {
        payload = NULL;
    }
    c1s1::~c1s1()
    {
        srs_freep(payload);
    }
    
    srs_schema_type c1s1::schema()
    {
        srs_assert(payload != NULL);
        return payload->schema();
    }
    
    char* c1s1::get_digest()
    {
        srs_assert(payload != NULL);
        return payload->get_digest();
    }
    
    char* c1s1::get_key()
    {
        srs_assert(payload != NULL);
        return payload->get_key();
    }
    
    int c1s1::dump(char* _c1s1, int size)
    {
        srs_assert(size == 1536);
        srs_assert(payload != NULL);
        return payload->dump(this, _c1s1, size);
    }
    
    int c1s1::parse(char* _c1s1, int size, srs_schema_type schema)
    {
        int ret = ERROR_SUCCESS;
        
        srs_assert(size == 1536);
        
        if (schema != srs_schema0 && schema != srs_schema1) {
            ret = ERROR_RTMP_CH_SCHEMA;
            srs_error("parse c1 failed. invalid schema=%d, ret=%d", schema, ret);
            return ret;
        }
        
        SrsStream stream;
        
        if ((ret = stream.initialize(_c1s1, size)) != ERROR_SUCCESS) {
            return ret;
        }
        
        time = stream.read_4bytes();
        version = stream.read_4bytes(); // client c1 version
        
        srs_freep(payload);
        if (schema == srs_schema0) {
            payload = new c1s1_strategy_schema0();
        } else {
            payload = new c1s1_strategy_schema1();
        }

        return payload->parse(_c1s1, size);
    }
    
    int c1s1::c1_create(srs_schema_type schema)
    {
        int ret = ERROR_SUCCESS;
        
        if (schema != srs_schema0 && schema != srs_schema1) {
            ret = ERROR_RTMP_CH_SCHEMA;
            srs_error("create c1 failed. invalid schema=%d, ret=%d", schema, ret);
            return ret;
        }
        
        // client c1 time and version
        time = ::time(NULL);
        version = 0x80000702; // client c1 version

        // generate signature by schema
        srs_freep(payload);
        if (schema == srs_schema0) {
            payload = new c1s1_strategy_schema0();
        } else {
            payload = new c1s1_strategy_schema1();
        }
        
        return payload->c1_create(this);
    }
    
    int c1s1::c1_validate_digest(bool& is_valid)
    {
        is_valid = false;
        srs_assert(payload);
        return payload->c1_validate_digest(this, is_valid);
    }
    
    int c1s1::s1_create(c1s1* c1)
    {
        int ret = ERROR_SUCCESS;
        
        if (c1->schema() != srs_schema0 && c1->schema() != srs_schema1) {
            ret = ERROR_RTMP_CH_SCHEMA;
            srs_error("create s1 failed. invalid schema=%d, ret=%d", c1->schema(), ret);
            return ret;
        }
        
        time = ::time(NULL);
        version = 0x01000504; // server s1 version
        
        srs_freep(payload);
        if (c1->schema() == srs_schema0) {
            payload = new c1s1_strategy_schema0();
        } else {
            payload = new c1s1_strategy_schema1();
        }
        
        return payload->s1_create(this, c1);
    }
    
    int c1s1::s1_validate_digest(bool& is_valid)
    {
        is_valid = false;
        srs_assert(payload);
        return payload->s1_validate_digest(this, is_valid);
    }
    
    c2s2::c2s2()
    {
        srs_random_generate(random, 1504);
        
        int size = snprintf(random, 1504, "%s", RTMP_SIG_SRS_HANDSHAKE);
        srs_assert(++size < 1504);
        snprintf(random + 1504 - size, size, "%s", RTMP_SIG_SRS_HANDSHAKE);
        
        srs_random_generate(digest, 32);
    }
    
    c2s2::~c2s2()
    {
    }
    
    int c2s2::dump(char* _c2s2, int size)
    {
        srs_assert(size == 1536);
        
        memcpy(_c2s2, random, 1504);
        memcpy(_c2s2 + 1504, digest, 32);
        
        return ERROR_SUCCESS;
    }
    
    int c2s2::parse(char* _c2s2, int size)
    {
        srs_assert(size == 1536);
        
        memcpy(random, _c2s2, 1504);
        memcpy(digest, _c2s2 + 1504, 32);
        
        return ERROR_SUCCESS;
    }
    
    int c2s2::c2_create(c1s1* s1)
    {
        int ret = ERROR_SUCCESS;
        
        char temp_key[SRS_OpensslHashSize];
        if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 62, s1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) {
            srs_error("create c2 temp key failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("generate c2 temp key success.");
        
        char _digest[SRS_OpensslHashSize];
        if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) {
            srs_error("create c2 digest failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("generate c2 digest success.");
        
        memcpy(digest, _digest, 32);
        
        return ret;
    }
    
    int c2s2::c2_validate(c1s1* s1, bool& is_valid)
    {
        is_valid = false;
        int ret = ERROR_SUCCESS;
        
        char temp_key[SRS_OpensslHashSize];
        if ((ret = openssl_HMACsha256(SrsGenuineFPKey, 62, s1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) {
            srs_error("create c2 temp key failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("generate c2 temp key success.");
        
        char _digest[SRS_OpensslHashSize];
        if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) {
            srs_error("create c2 digest failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("generate c2 digest success.");
        
        is_valid = srs_bytes_equals(digest, _digest, 32);
        
        return ret;
    }
    
    int c2s2::s2_create(c1s1* c1)
    {
        int ret = ERROR_SUCCESS;
        
        char temp_key[SRS_OpensslHashSize];
        if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 68, c1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) {
            srs_error("create s2 temp key failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("generate s2 temp key success.");
        
        char _digest[SRS_OpensslHashSize];
        if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) {
            srs_error("create s2 digest failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("generate s2 digest success.");
        
        memcpy(digest, _digest, 32);
        
        return ret;
    }
    
    int c2s2::s2_validate(c1s1* c1, bool& is_valid)
    {
        is_valid = false;
        int ret = ERROR_SUCCESS;
        
        char temp_key[SRS_OpensslHashSize];
        if ((ret = openssl_HMACsha256(SrsGenuineFMSKey, 68, c1->get_digest(), 32, temp_key)) != ERROR_SUCCESS) {
            srs_error("create s2 temp key failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("generate s2 temp key success.");
        
        char _digest[SRS_OpensslHashSize];
        if ((ret = openssl_HMACsha256(temp_key, 32, random, 1504, _digest)) != ERROR_SUCCESS) {
            srs_error("create s2 digest failed. ret=%d", ret);
            return ret;
        }
        srs_verbose("generate s2 digest success.");
        
        is_valid = srs_bytes_equals(digest, _digest, 32);
        
        return ret;
    }
}

#endif

SrsSimpleHandshake::SrsSimpleHandshake()
{
}

SrsSimpleHandshake::~SrsSimpleHandshake()
{
}

int SrsSimpleHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrsProtocolReaderWriter* io)
{
    int ret = ERROR_SUCCESS;
    
    ssize_t nsize;
    
    if ((ret = hs_bytes->read_c0c1(io)) != ERROR_SUCCESS) {
        return ret;
    }

    // plain text required.
    if (hs_bytes->c0c1[0] != 0x03) {
        ret = ERROR_RTMP_PLAIN_REQUIRED;
        srs_warn("only support rtmp plain text. ret=%d", ret);
        return ret;
    }
    srs_verbose("check c0 success, required plain text.");
    
    if ((ret = hs_bytes->create_s0s1s2(hs_bytes->c0c1 + 1)) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = io->write(hs_bytes->s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) {
        srs_warn("simple handshake send s0s1s2 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("simple handshake send s0s1s2 success.");
    
    if ((ret = hs_bytes->read_c2(io)) != ERROR_SUCCESS) {
        return ret;
    }
    
    srs_trace("simple handshake success.");
    
    return ret;
}

int SrsSimpleHandshake::handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrsProtocolReaderWriter* io)
{
    int ret = ERROR_SUCCESS;
    
    ssize_t nsize;
    
    // simple handshake
    if ((ret = hs_bytes->create_c0c1()) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = io->write(hs_bytes->c0c1, 1537, &nsize)) != ERROR_SUCCESS) {
        srs_warn("write c0c1 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("write c0c1 success.");
    
    if ((ret = hs_bytes->read_s0s1s2(io)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // plain text required.
    if (hs_bytes->s0s1s2[0] != 0x03) {
        ret = ERROR_RTMP_HANDSHAKE;
        srs_warn("handshake failed, plain text required. ret=%d", ret);
        return ret;
    }
    
    if ((ret = hs_bytes->create_c2()) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = io->write(hs_bytes->c2, 1536, &nsize)) != ERROR_SUCCESS) {
        srs_warn("simple handshake write c2 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("simple handshake write c2 success.");
    
    srs_trace("simple handshake success.");
    
    return ret;
}

SrsComplexHandshake::SrsComplexHandshake()
{
}

SrsComplexHandshake::~SrsComplexHandshake()
{
}

#ifndef SRS_AUTO_SSL
int SrsComplexHandshake::handshake_with_client(SrsHandshakeBytes* /*hs_bytes*/, ISrsProtocolReaderWriter* /*io*/)
{
    srs_trace("directly use simple handshake for ssl disabled.");
    return ERROR_RTMP_TRY_SIMPLE_HS;
}
#else
int SrsComplexHandshake::handshake_with_client(SrsHandshakeBytes* hs_bytes, ISrsProtocolReaderWriter* io)
{
    int ret = ERROR_SUCCESS;

    ssize_t nsize;
    
    if ((ret = hs_bytes->read_c0c1(io)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // decode c1
    c1s1 c1;
    // try schema0.
    // @remark, use schema0 to make flash player happy.
    if ((ret = c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema0)) != ERROR_SUCCESS) {
        srs_error("parse c1 schema%d error. ret=%d", srs_schema0, ret);
        return ret;
    }
    // try schema1
    bool is_valid = false;
    if ((ret = c1.c1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) {
        srs_info("schema0 failed, try schema1.");
        if ((ret = c1.parse(hs_bytes->c0c1 + 1, 1536, srs_schema1)) != ERROR_SUCCESS) {
            srs_error("parse c1 schema%d error. ret=%d", srs_schema1, ret);
            return ret;
        }
        
        if ((ret = c1.c1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) {
            ret = ERROR_RTMP_TRY_SIMPLE_HS;
            srs_info("all schema valid failed, try simple handshake. ret=%d", ret);
            return ret;
        }
    } else {
        srs_info("schema0 is ok.");
    }
    srs_verbose("decode c1 success.");
    
    // encode s1
    c1s1 s1;
    if ((ret = s1.s1_create(&c1)) != ERROR_SUCCESS) {
        srs_error("create s1 from c1 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("create s1 from c1 success.");
    // verify s1
    if ((ret = s1.s1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) {
        ret = ERROR_RTMP_TRY_SIMPLE_HS;
        srs_info("verify s1 failed, try simple handshake. ret=%d", ret);
        return ret;
    }
    srs_verbose("verify s1 success.");
    
    c2s2 s2;
    if ((ret = s2.s2_create(&c1)) != ERROR_SUCCESS) {
        srs_error("create s2 from c1 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("create s2 from c1 success.");
    // verify s2
    if ((ret = s2.s2_validate(&c1, is_valid)) != ERROR_SUCCESS || !is_valid) {
        ret = ERROR_RTMP_TRY_SIMPLE_HS;
        srs_info("verify s2 failed, try simple handshake. ret=%d", ret);
        return ret;
    }
    srs_verbose("verify s2 success.");
    
    // sendout s0s1s2
    if ((ret = hs_bytes->create_s0s1s2()) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = s1.dump(hs_bytes->s0s1s2 + 1, 1536)) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = s2.dump(hs_bytes->s0s1s2 + 1537, 1536)) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = io->write(hs_bytes->s0s1s2, 3073, &nsize)) != ERROR_SUCCESS) {
        srs_warn("complex handshake send s0s1s2 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("complex handshake send s0s1s2 success.");
    
    // recv c2
    if ((ret = hs_bytes->read_c2(io)) != ERROR_SUCCESS) {
        return ret;
    }
    c2s2 c2;
    if ((ret = c2.parse(hs_bytes->c2, 1536)) != ERROR_SUCCESS) {
        return ret;
    }
    srs_verbose("complex handshake read c2 success.");
    
    // verify c2
    // never verify c2, for ffmpeg will failed.
    // it's ok for flash.
    
    srs_trace("complex handshake success");
    
    return ret;
}
#endif

#ifndef SRS_AUTO_SSL
int SrsComplexHandshake::handshake_with_server(SrsHandshakeBytes* /*hs_bytes*/, ISrsProtocolReaderWriter* /*io*/)
{
    return ERROR_RTMP_TRY_SIMPLE_HS;
}
#else
int SrsComplexHandshake::handshake_with_server(SrsHandshakeBytes* hs_bytes, ISrsProtocolReaderWriter* io)
{
    int ret = ERROR_SUCCESS;

    ssize_t nsize;
    
    // complex handshake
    if ((ret = hs_bytes->create_c0c1()) != ERROR_SUCCESS) {
        return ret;
    }
    
    // sign c1
    c1s1 c1;
    // @remark, FMS requires the schema1(digest-key), or connect failed.
    if ((ret = c1.c1_create(srs_schema1)) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = c1.dump(hs_bytes->c0c1 + 1, 1536)) != ERROR_SUCCESS) {
        return ret;
    }

    // verify c1
    bool is_valid;
    if ((ret = c1.c1_validate_digest(is_valid)) != ERROR_SUCCESS || !is_valid) {
        ret = ERROR_RTMP_TRY_SIMPLE_HS;
        return ret;
    }
    
    if ((ret = io->write(hs_bytes->c0c1, 1537, &nsize)) != ERROR_SUCCESS) {
        srs_warn("write c0c1 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("write c0c1 success.");
    
    // s0s1s2
    if ((ret = hs_bytes->read_s0s1s2(io)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // plain text required.
    if (hs_bytes->s0s1s2[0] != 0x03) {
        ret = ERROR_RTMP_HANDSHAKE;
        srs_warn("handshake failed, plain text required. ret=%d", ret);
        return ret;
    }
    
    // verify s1s2
    c1s1 s1;
    if ((ret = s1.parse(hs_bytes->s0s1s2 + 1, 1536, c1.schema())) != ERROR_SUCCESS) {
        return ret;
    }
    
    // never verify the s1,
    // for if forward to nginx-rtmp, verify s1 will failed,
    // TODO: FIXME: find the handshake schema of nginx-rtmp.
    
    // c2
    if ((ret = hs_bytes->create_c2()) != ERROR_SUCCESS) {
        return ret;
    }

    c2s2 c2;
    if ((ret = c2.c2_create(&s1)) != ERROR_SUCCESS) {
        return ret;
    }

    if ((ret = c2.dump(hs_bytes->c2, 1536)) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = io->write(hs_bytes->c2, 1536, &nsize)) != ERROR_SUCCESS) {
        srs_warn("complex handshake write c2 failed. ret=%d", ret);
        return ret;
    }
    srs_verbose("complex handshake write c2 success.");
    
    srs_trace("complex handshake success.");
    
    return ret;
}
#endif


// following is generated by src/protocol/srs_rtmp_utility.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 <srs_rtmp_utility.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#endif

#include <stdlib.h>
using namespace std;

//#include <srs_kernel_log.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_rtmp_stack.hpp>
//#include <srs_kernel_codec.hpp>
//#include <srs_kernel_consts.hpp>
//#include <srs_rtmp_stack.hpp>
//#include <srs_rtmp_io.hpp>

void srs_discovery_tc_url(
    string tcUrl, 
    string& schema, string& host, string& vhost, 
    string& app, string& port, std::string& param
) {
    size_t pos = std::string::npos;
    std::string url = tcUrl;
    
    if ((pos = url.find("://")) != std::string::npos) {
        schema = url.substr(0, pos);
        url = url.substr(schema.length() + 3);
        srs_info("discovery schema=%s", schema.c_str());
    }
    
    if ((pos = url.find("/")) != std::string::npos) {
        host = url.substr(0, pos);
        url = url.substr(host.length() + 1);
        srs_info("discovery host=%s", host.c_str());
    }

    port = SRS_CONSTS_RTMP_DEFAULT_PORT;
    if ((pos = host.find(":")) != std::string::npos) {
        port = host.substr(pos + 1);
        host = host.substr(0, pos);
        srs_info("discovery host=%s, port=%s", host.c_str(), port.c_str());
    }
    
    app = url;
    vhost = host;
    srs_vhost_resolve(vhost, app, param);
}

void srs_vhost_resolve(string& vhost, string& app, string& param)
{
    // get original param
    size_t pos = 0;
    if ((pos = app.find("?")) != std::string::npos) {
        param = app.substr(pos);
    }
    
    // filter tcUrl
    app = srs_string_replace(app, ",", "?");
    app = srs_string_replace(app, "...", "?");
    app = srs_string_replace(app, "&&", "?");
    app = srs_string_replace(app, "=", "?");
    
    if ((pos = app.find("?")) != std::string::npos) {
        std::string query = app.substr(pos + 1);
        app = app.substr(0, pos);
        
        if ((pos = query.find("vhost?")) != std::string::npos) {
            query = query.substr(pos + 6);
            if (!query.empty()) {
                vhost = query;
            }
            if ((pos = vhost.find("?")) != std::string::npos) {
                vhost = vhost.substr(0, pos);
            }
        }
    }
    
    /* others */
}

void srs_random_generate(char* bytes, int size)
{
    static bool _random_initialized = false;
    if (!_random_initialized) {
        srand(0);
        _random_initialized = true;
        srs_trace("srand initialized the random.");
    }
    
    for (int i = 0; i < size; i++) {
        // the common value in [0x0f, 0xf0]
        bytes[i] = 0x0f + (rand() % (256 - 0x0f - 0x0f));
    }
}

string srs_generate_tc_url(string ip, string vhost, string app, string port, string param)
{
    string tcUrl = "rtmp://";
    
    if (vhost == SRS_CONSTS_RTMP_DEFAULT_VHOST) {
        tcUrl += ip;
    } else {
        tcUrl += vhost;
    }
    
    if (port != SRS_CONSTS_RTMP_DEFAULT_PORT) {
        tcUrl += ":";
        tcUrl += port;
    }
    
    tcUrl += "/";
    tcUrl += app;
    tcUrl += param;
    
    return tcUrl;
}

/**
* compare the memory in bytes.
*/
bool srs_bytes_equals(void* pa, void* pb, int size)
{
    u_int8_t* a = (u_int8_t*)pa;
    u_int8_t* b = (u_int8_t*)pb;
    
    if (!a && !b) {
        return true;
    }
    
    if (!a || !b) {
        return false;
    }
    
    for(int i = 0; i < size; i++){
        if(a[i] != b[i]){
            return false;
        }
    }

    return true;
}

int srs_do_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg)
{
    int ret = ERROR_SUCCESS;
    
    *ppmsg = NULL;
    SrsSharedPtrMessage* msg = NULL;
    
    if (type == SrsCodecFlvTagAudio) {
        SrsMessageHeader header;
        header.initialize_audio(size, timestamp, stream_id);
        
        msg = new SrsSharedPtrMessage();
        if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) {
            srs_freep(msg);
            return ret;
        }
    } else if (type == SrsCodecFlvTagVideo) {
        SrsMessageHeader header;
        header.initialize_video(size, timestamp, stream_id);
        
        msg = new SrsSharedPtrMessage();
        if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) {
            srs_freep(msg);
            return ret;
        }
    } else if (type == SrsCodecFlvTagScript) {
        SrsMessageHeader header;
        header.initialize_amf0_script(size, stream_id);
        
        msg = new SrsSharedPtrMessage();
        if ((ret = msg->create(&header, data, size)) != ERROR_SUCCESS) {
            srs_freep(msg);
            return ret;
        }
    } else {
        ret = ERROR_STREAM_CASTER_FLV_TAG;
        srs_error("rtmp unknown tag type=%#x. ret=%d", type, ret);
        return ret;
    }

    *ppmsg = msg;

    return ret;
}

int srs_rtmp_create_msg(char type, u_int32_t timestamp, char* data, int size, int stream_id, SrsSharedPtrMessage** ppmsg)
{
    int ret = ERROR_SUCCESS;

    // 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);
        return ret;
    }

    return ret;
}

std::string srs_generate_stream_url(std::string vhost, std::string app, std::string stream) 
{
    std::string url = "";
    
    if (SRS_CONSTS_RTMP_DEFAULT_VHOST != vhost){
    	url += vhost;
    }
    url += "/";
    url += app;
    url += "/";
    url += stream;

    return url;
}

int srs_write_large_iovs(ISrsProtocolReaderWriter* skt, iovec* iovs, int size, ssize_t* pnwrite)
{
    int ret = ERROR_SUCCESS;
    
    // the limits of writev iovs.
    // for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
    // for linux, generally it's 1024.
    static int limits = sysconf(_SC_IOV_MAX);
#else
    static int limits = 1024;
#endif
    
    // send in a time.
    if (size < limits) {
        if ((ret = skt->writev(iovs, size, pnwrite)) != ERROR_SUCCESS) {
            if (!srs_is_client_gracefully_close(ret)) {
                srs_error("send with writev failed. ret=%d", ret);
            }
            return ret;
        }
        return ret;
    }
    
    // send in multiple times.
    int cur_iov = 0;
    while (cur_iov < size) {
        int cur_count = srs_min(limits, size - cur_iov);
        if ((ret = skt->writev(iovs + cur_iov, cur_count, pnwrite)) != ERROR_SUCCESS) {
            if (!srs_is_client_gracefully_close(ret)) {
                srs_error("send with writev failed. ret=%d", ret);
            }
            return ret;
        }
        cur_iov += cur_count;
    }
    
    return ret;
}

// following is generated by src/protocol/srs_rtmp_msg_array.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 <srs_rtmp_msg_array.hpp>

//#include <srs_rtmp_stack.hpp>

SrsMessageArray::SrsMessageArray(int max_msgs)
{
    srs_assert(max_msgs > 0);
    
    msgs = new SrsSharedPtrMessage*[max_msgs];
    max = max_msgs;
    
    zero(max_msgs);
}

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);
}

void SrsMessageArray::free(int count)
{
    // initialize
    for (int i = 0; i < count; i++) {
        SrsSharedPtrMessage* msg = msgs[i];
        srs_freep(msg);
        
        msgs[i] = NULL;
    }
}

void SrsMessageArray::zero(int count)
{
    // initialize
    for (int i = 0; i < count; i++) {
        msgs[i] = NULL;
    }
}


// following is generated by src/protocol/srs_protocol_buffer.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 <srs_protocol_buffer.hpp>

#include <stdlib.h>

//#include <srs_kernel_error.hpp>
//#include <srs_kernel_log.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_core_performance.hpp>

// the default recv buffer size, 128KB.
#define SRS_DEFAULT_RECV_BUFFER_SIZE 131072

// limit user-space buffer to 256KB, for 3Mbps stream delivery.
//      800*2000/8=200000B(about 195KB).
// @remark it's ok for higher stream, the buffer is ok for one chunk is 256KB.
#define SRS_MAX_SOCKET_BUFFER 262144

// the max header size,
// @see SrsProtocol::read_message_header().
#define SRS_RTMP_MAX_MESSAGE_HEADER 11

#ifdef SRS_PERF_MERGED_READ
IMergeReadHandler::IMergeReadHandler()
{
}

IMergeReadHandler::~IMergeReadHandler()
{
}
#endif

SrsFastBuffer::SrsFastBuffer()
{
#ifdef SRS_PERF_MERGED_READ
    merged_read = false;
    _handler = NULL;
#endif
    
    nb_buffer = SRS_DEFAULT_RECV_BUFFER_SIZE;
    buffer = (char*)malloc(nb_buffer);
    p = end = buffer;
}

SrsFastBuffer::~SrsFastBuffer()
{
    free(buffer);
    buffer = NULL;
}

int SrsFastBuffer::size()
{
    return (int)(end - p);
}

char* SrsFastBuffer::bytes()
{
    return p;
}

void SrsFastBuffer::set_buffer(int buffer_size)
{
    // never exceed the max size.
    if (buffer_size > SRS_MAX_SOCKET_BUFFER) {
        srs_warn("limit the user-space buffer from %d to %d", 
            buffer_size, SRS_MAX_SOCKET_BUFFER);
    }
    
    // the user-space buffer size limit to a max value.
    int nb_resize_buf = srs_min(buffer_size, SRS_MAX_SOCKET_BUFFER);

    // only realloc when buffer changed bigger
    if (nb_resize_buf <= nb_buffer) {
        return;
    }
    
    // realloc for buffer change bigger.
    int start = (int)(p - buffer);
    int nb_bytes = (int)(end - p);
    
    buffer = (char*)realloc(buffer, nb_resize_buf);
    nb_buffer = nb_resize_buf;
    p = buffer + start;
    end = p + nb_bytes;
}

char SrsFastBuffer::read_1byte()
{
    srs_assert(end - p >= 1);
    return *p++;
}

char* SrsFastBuffer::read_slice(int size)
{
    srs_assert(end - p >= size);
    srs_assert(p + size > buffer);
    
    char* ptr = p;
    p += size;

    return ptr;
}

void SrsFastBuffer::skip(int size)
{
    srs_assert(end - p >= size);
    srs_assert(p + size > buffer);
    p += size;
}

int SrsFastBuffer::grow(ISrsBufferReader* reader, int required_size)
{
    int ret = ERROR_SUCCESS;

    // already got required size of bytes.
    if (end - p >= required_size) {
        return ret;
    }

    // must be positive.
    srs_assert(required_size > 0);

    // the free space of buffer, 
    //      buffer = consumed_bytes + exists_bytes + free_space.
    int nb_free_space = (int)(buffer + nb_buffer - end);
    // resize the space when no left space.
    if (nb_free_space < required_size) {
        // the bytes already in buffer
        int nb_exists_bytes = (int)(end - p);
        srs_assert(nb_exists_bytes >= 0);
        srs_verbose("move fast buffer %d bytes", nb_exists_bytes);

        // reset or move to get more space.
        if (!nb_exists_bytes) {
            // reset when buffer is empty.
            p = end = buffer;
            srs_verbose("all consumed, reset fast buffer");
        } else {
            // move the left bytes to start of buffer.
            srs_assert(nb_exists_bytes < nb_buffer);
            buffer = (char*)memmove(buffer, p, nb_exists_bytes);
            p = buffer;
            end = p + nb_exists_bytes;
        }
        
        // check whether enough free space in buffer.
        nb_free_space = (int)(buffer + nb_buffer - end);
        if (nb_free_space < required_size) {
            ret = ERROR_READER_BUFFER_OVERFLOW;
            srs_error("buffer overflow, required=%d, max=%d, left=%d, ret=%d", 
                required_size, nb_buffer, nb_free_space, ret);
            return ret;
        }
    }

    // buffer is ok, read required size of bytes.
    while (end - p < required_size) {
        ssize_t nread;
        if ((ret = reader->read(end, nb_free_space, &nread)) != ERROR_SUCCESS) {
            return ret;
        }
        
#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.
        * @see https://github.com/simple-rtmp-server/srs/issues/241
        */
        if (merged_read && _handler) {
            _handler->on_read(nread);
        }
#endif
        
        // we just move the ptr to next.
        srs_assert((int)nread > 0);
        end += nread;
        nb_free_space -= nread;
    }
    
    return ret;
}

#ifdef SRS_PERF_MERGED_READ
void SrsFastBuffer::set_merge_read(bool v, IMergeReadHandler* handler)
{
    merged_read = v;
    _handler = handler;
}
#endif

// following is generated by src/protocol/srs_raw_avc.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 <srs_raw_avc.hpp>

#include <string.h>
using namespace std;

//#include <srs_kernel_error.hpp>
//#include <srs_kernel_log.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_core_autofree.hpp>
//#include <srs_kernel_codec.hpp>

SrsRawH264Stream::SrsRawH264Stream()
{
}

SrsRawH264Stream::~SrsRawH264Stream()
{
}

int SrsRawH264Stream::annexb_demux(SrsStream* stream, char** pframe, int* pnb_frame)
{
    int ret = ERROR_SUCCESS;

    *pframe = NULL;
    *pnb_frame = 0;

    while (!stream->empty()) {
        // each frame must prefixed by annexb format.
        // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
        int pnb_start_code = 0;
        if (!srs_avc_startswith_annexb(stream, &pnb_start_code)) {
            return ERROR_H264_API_NO_PREFIXED;
        }
        int start = stream->pos() + pnb_start_code;
        
        // find the last frame prefixed by annexb format.
        stream->skip(pnb_start_code);
        while (!stream->empty()) {
            if (srs_avc_startswith_annexb(stream, NULL)) {
                break;
            }
            stream->skip(1);
        }
        
        // demux the frame.
        *pnb_frame = stream->pos() - start;
        *pframe = stream->data() + start;
        break;
    }

    return ret;
}

bool SrsRawH264Stream::is_sps(char* frame, int nb_frame)
{
    srs_assert(nb_frame > 0);
    
    // 5bits, 7.3.1 NAL unit syntax, 
    // H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
    //  7: SPS, 8: PPS, 5: I Frame, 1: P Frame
    u_int8_t nal_unit_type = (char)frame[0] & 0x1f;

    return nal_unit_type == 7;
}

bool SrsRawH264Stream::is_pps(char* frame, int nb_frame)
{
    srs_assert(nb_frame > 0);
    
    // 5bits, 7.3.1 NAL unit syntax, 
    // H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
    //  7: SPS, 8: PPS, 5: I Frame, 1: P Frame
    u_int8_t nal_unit_type = (char)frame[0] & 0x1f;

    return nal_unit_type == 8;
}

int SrsRawH264Stream::sps_demux(char* frame, int nb_frame, string& sps)
{
    int ret = ERROR_SUCCESS;

    // atleast 1bytes for SPS to decode the type, profile, constrain and level.
    if (nb_frame < 4) {
        return ret;
    }
        
    sps = "";
    if (nb_frame > 0) {
        sps.append(frame, nb_frame);
    }
    
    // should never be empty.
    if (sps.empty()) {
        return ERROR_STREAM_CASTER_AVC_SPS;
    }
        
    return ret;
}

int SrsRawH264Stream::pps_demux(char* frame, int nb_frame, string& pps)
{
    int ret = ERROR_SUCCESS;

    pps = "";
    if (nb_frame > 0) {
        pps.append(frame, nb_frame);
    }

    // should never be empty.
    if (pps.empty()) {
        return ERROR_STREAM_CASTER_AVC_PPS;
    }

    return ret;
}

int SrsRawH264Stream::mux_sequence_header(string sps, string pps, u_int32_t dts, u_int32_t pts, string& sh)
{
    int ret = ERROR_SUCCESS;

    // 5bytes sps/pps header:
    //      configurationVersion, AVCProfileIndication, profile_compatibility,
    //      AVCLevelIndication, lengthSizeMinusOne
    // 3bytes size of sps:
    //      numOfSequenceParameterSets, sequenceParameterSetLength(2B)
    // Nbytes of sps.
    //      sequenceParameterSetNALUnit
    // 3bytes size of pps:
    //      numOfPictureParameterSets, pictureParameterSetLength
    // Nbytes of pps:
    //      pictureParameterSetNALUnit
    int nb_packet = 5 
        + 3 + (int)sps.length() 
        + 3 + (int)pps.length();
    char* packet = new char[nb_packet];
    SrsAutoFree(char, packet);

    // use stream to generate the h264 packet.
    SrsStream stream;
    if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // decode the SPS: 
    // @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62
    if (true) {
        srs_assert((int)sps.length() >= 4);
        char* frame = (char*)sps.data();
    
        // @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205
        //      Baseline profile profile_idc is 66(0x42).
        //      Main profile profile_idc is 77(0x4d).
        //      Extended profile profile_idc is 88(0x58).
        u_int8_t profile_idc = frame[1];
        //u_int8_t constraint_set = frame[2];
        u_int8_t level_idc = frame[3];
        
        // generate the sps/pps header
        // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
        // configurationVersion
        stream.write_1bytes(0x01);
        // AVCProfileIndication
        stream.write_1bytes(profile_idc);
        // profile_compatibility
        stream.write_1bytes(0x00);
        // AVCLevelIndication
        stream.write_1bytes(level_idc);
        // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size,
        // so we always set it to 0x03.
        stream.write_1bytes(0x03);
    }
    
    // sps
    if (true) {
        // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
        // numOfSequenceParameterSets, always 1
        stream.write_1bytes(0x01);
        // sequenceParameterSetLength
        stream.write_2bytes(sps.length());
        // sequenceParameterSetNALUnit
        stream.write_string(sps);
    }
    
    // pps
    if (true) {
        // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
        // numOfPictureParameterSets, always 1
        stream.write_1bytes(0x01);
        // pictureParameterSetLength
        stream.write_2bytes(pps.length());
        // pictureParameterSetNALUnit
        stream.write_string(pps);
    }

    // TODO: FIXME: for more profile.
    // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
    // profile_idc == 100 || profile_idc == 110 || profile_idc == 122 || profile_idc == 144

    sh = "";
    sh.append(packet, nb_packet);

    return ret;
}

int SrsRawH264Stream::mux_ipb_frame(char* frame, int nb_frame, string& ibp)
{
    int ret = ERROR_SUCCESS;
    
    // 4bytes size of nalu:
    //      NALUnitLength
    // Nbytes of nalu.
    //      NALUnit
    int nb_packet = 4 + nb_frame;
    char* packet = new char[nb_packet];
    SrsAutoFree(char, packet);
    
    // use stream to generate the h264 packet.
    SrsStream stream;
    if ((ret = stream.initialize(packet, nb_packet)) != ERROR_SUCCESS) {
        return ret;
    }

    // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
    // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size
    u_int32_t NAL_unit_length = nb_frame;
    
    // mux the avc NALU in "ISO Base Media File Format" 
    // from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
    // NALUnitLength
    stream.write_4bytes(NAL_unit_length);
    // NALUnit
    stream.write_bytes(frame, nb_frame);

    ibp = "";
    ibp.append(packet, nb_packet);

    return ret;
}

int SrsRawH264Stream::mux_avc2flv(string video, int8_t frame_type, int8_t avc_packet_type, u_int32_t dts, u_int32_t pts, char** flv, int* nb_flv)
{
    int ret = ERROR_SUCCESS;
    
    // for h264 in RTMP video payload, there is 5bytes header:
    //      1bytes, FrameType | CodecID
    //      1bytes, AVCPacketType
    //      3bytes, CompositionTime, the cts.
    // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
    int size = (int)video.length() + 5;
    char* data = new char[size];
    char* p = data;
    
    // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
    // Frame Type, Type of video frame.
    // CodecID, Codec Identifier.
    // set the rtmp header
    *p++ = (frame_type << 4) | SrsCodecVideoAVC;
    
    // AVCPacketType
    *p++ = avc_packet_type;

    // CompositionTime
    // pts = dts + cts, or 
    // cts = pts - dts.
    // where cts is the header in rtmp video packet payload header.
    u_int32_t cts = pts - dts;
    char* pp = (char*)&cts;
    *p++ = pp[2];
    *p++ = pp[1];
    *p++ = pp[0];
    
    // h.264 raw data.
    memcpy(p, video.data(), video.length());

    *flv = data;
    *nb_flv = size;

    return ret;
}

SrsRawAacStream::SrsRawAacStream()
{
}

SrsRawAacStream::~SrsRawAacStream()
{
}

int SrsRawAacStream::adts_demux(SrsStream* stream, char** pframe, int* pnb_frame, SrsRawAacStreamCodec& codec)
{
    int ret = ERROR_SUCCESS;
    
    while (!stream->empty()) {
        int adts_header_start = stream->pos();
        
        // 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
        // byte_alignment()
        
        // adts_fixed_header:
        //      12bits syncword,
        //      16bits left.
        // adts_variable_header:
        //      28bits
        //      12+16+28=56bits
        // adts_error_check:
        //      16bits if protection_absent
        //      56+16=72bits
        // if protection_absent:
        //      require(7bytes)=56bits
        // else
        //      require(9bytes)=72bits
        if (!stream->require(7)) {
            return ERROR_AAC_ADTS_HEADER;
        }
        
        // for aac, the frame must be ADTS format.
        if (!srs_aac_startswith_adts(stream)) {
            return ERROR_AAC_REQUIRED_ADTS;
        }
        
        // syncword 12 bslbf
        stream->read_1bytes();
        // 4bits left.
        // adts_fixed_header(), 1.A.2.2.1 Fixed Header of ADTS
        // ID 1 bslbf
        // layer 2 uimsbf
        // protection_absent 1 bslbf
        int8_t pav = (stream->read_1bytes() & 0x0f);
        int8_t id = (pav >> 3) & 0x01;
        /*int8_t layer = (pav >> 1) & 0x03;*/
        int8_t protection_absent = pav & 0x01;
        
        /**
        * ID: MPEG identifier, set to '1' if the audio data in the ADTS stream are MPEG-2 AAC (See ISO/IEC 13818-7)
        * and set to '0' if the audio data are MPEG-4. See also ISO/IEC 11172-3, subclause 2.4.2.3.
        */
        if (id != 0x01) {
            srs_info("adts: id must be 1(aac), actual 0(mp4a). ret=%d", ret);
            
            // well, some system always use 0, but actually is aac format.
            // for example, houjian vod ts always set the aac id to 0, actually 1.
            // we just ignore it, and alwyas use 1(aac) to demux.
            id = 0x01;
        }
        
        int16_t sfiv = stream->read_2bytes();
        // profile 2 uimsbf
        // sampling_frequency_index 4 uimsbf
        // private_bit 1 bslbf
        // channel_configuration 3 uimsbf
        // original/copy 1 bslbf
        // home 1 bslbf
        int8_t profile = (sfiv >> 14) & 0x03;
        int8_t sampling_frequency_index = (sfiv >> 10) & 0x0f;
        /*int8_t private_bit = (sfiv >> 9) & 0x01;*/
        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
        // 4bits left.
        // adts_variable_header(), 1.A.2.2.2 Variable Header of ADTS
        // copyright_identification_bit 1 bslbf
        // copyright_identification_start 1 bslbf
        /*int8_t fh_copyright_identification_bit = (fh1 >> 3) & 0x01;*/
        /*int8_t fh_copyright_identification_start = (fh1 >> 2) & 0x01;*/
        // frame_length 13 bslbf: Length of the frame including headers and error_check in bytes.
        // use the left 2bits as the 13 and 12 bit,
        // the frame_length is 13bits, so we move 13-2=11.
        int16_t frame_length = (sfiv << 11) & 0x1800;
        
        int32_t abfv = stream->read_3bytes();
        // frame_length 13 bslbf: consume the first 13-2=11bits
        // the fh2 is 24bits, so we move right 24-11=13.
        frame_length |= (abfv >> 13) & 0x07ff;
        // adts_buffer_fullness 11 bslbf
        /*int16_t fh_adts_buffer_fullness = (abfv >> 2) & 0x7ff;*/
        // number_of_raw_data_blocks_in_frame 2 uimsbf
        /*int16_t number_of_raw_data_blocks_in_frame = abfv & 0x03;*/
        // adts_error_check(), 1.A.2.2.3 Error detection
        if (!protection_absent) {
            if (!stream->require(2)) {
                return ERROR_AAC_ADTS_HEADER;
            }
            // crc_check 16 Rpchof
            /*int16_t crc_check = */stream->read_2bytes();
        }
        
        // TODO: check the sampling_frequency_index
        // TODO: check the channel_configuration
        
        // raw_data_blocks
        int adts_header_size = stream->pos() - adts_header_start;
        int raw_data_size = frame_length - adts_header_size;
        if (!stream->require(raw_data_size)) {
            return ERROR_AAC_ADTS_HEADER;
        }
        
        // the codec info.
        codec.protection_absent = protection_absent;
        codec.aac_object = srs_codec_aac_ts2rtmp((SrsAacProfile)profile);
        codec.sampling_frequency_index = sampling_frequency_index;
        codec.channel_configuration = channel_configuration;
        codec.frame_length = frame_length;
        
        // @see srs_audio_write_raw_frame().
        // TODO: FIXME: maybe need to resample audio.
        codec.sound_format = 10; // AAC
        if (sampling_frequency_index <= 0x0c && sampling_frequency_index > 0x0a) {
            codec.sound_rate = SrsCodecAudioSampleRate5512;
        } else if (sampling_frequency_index <= 0x0a && sampling_frequency_index > 0x07) {
            codec.sound_rate = SrsCodecAudioSampleRate11025;
        } else if (sampling_frequency_index <= 0x07 && sampling_frequency_index > 0x04) {
            codec.sound_rate = SrsCodecAudioSampleRate22050;
        } else if (sampling_frequency_index <= 0x04) {
            codec.sound_rate = SrsCodecAudioSampleRate44100;
        } else {
            codec.sound_rate = SrsCodecAudioSampleRate44100;
            srs_warn("adts invalid sample rate for flv, rate=%#x", sampling_frequency_index);
        }
        codec.sound_type = srs_max(0, srs_min(1, channel_configuration - 1));
        // TODO: FIXME: finger it out the sound size by adts.
        codec.sound_size = 1; // 0(8bits) or 1(16bits).

        // frame data.
        *pframe = stream->data() + stream->pos();
        *pnb_frame = raw_data_size;
        stream->skip(raw_data_size);

        break;
    }

    return ret;
}

int SrsRawAacStream::mux_sequence_header(SrsRawAacStreamCodec* codec, string& sh)
{
    int ret = ERROR_SUCCESS;

    // only support aac profile 1-4.
    if (codec->aac_object == SrsAacObjectTypeReserved) {
        return ERROR_AAC_DATA_INVALID;
    }
    
    SrsAacObjectType audioObjectType = codec->aac_object;
    char channelConfiguration = codec->channel_configuration;
    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
    switch (codec->sound_rate) {
        case SrsCodecAudioSampleRate11025: 
            samplingFrequencyIndex = 0x0a; break;
        case SrsCodecAudioSampleRate22050: 
            samplingFrequencyIndex = 0x07; break;
        case SrsCodecAudioSampleRate44100: 
            samplingFrequencyIndex = 0x04; break;
        default:
            break;
    }

    sh = "";

    char ch = 0;
    // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf
    // AudioSpecificConfig (), page 33
    // 1.6.2.1 AudioSpecificConfig
    // audioObjectType; 5 bslbf
    ch = (audioObjectType << 3) & 0xf8;
    // 3bits left.
        
    // samplingFrequencyIndex; 4 bslbf
    ch |= (samplingFrequencyIndex >> 1) & 0x07;
    sh += ch;
    ch = (samplingFrequencyIndex << 7) & 0x80;
    if (samplingFrequencyIndex == 0x0f) {
        return ERROR_AAC_DATA_INVALID;
    }
    // 7bits left.
        
    // channelConfiguration; 4 bslbf
    ch |= (channelConfiguration << 3) & 0x78;
    // 3bits left.
        
    // GASpecificConfig(), page 451
    // 4.4.1 Decoder configuration (GASpecificConfig)
    // frameLengthFlag; 1 bslbf
    // dependsOnCoreCoder; 1 bslbf
    // extensionFlag; 1 bslbf
    sh += ch;

    return ret;
}

int SrsRawAacStream::mux_aac2flv(char* frame, int nb_frame, SrsRawAacStreamCodec* codec, u_int32_t dts, char** flv, int* nb_flv)
{
    int ret = ERROR_SUCCESS;

    char sound_format = codec->sound_format;
    char sound_type = codec->sound_type;
    char sound_size = codec->sound_size;
    char sound_rate = codec->sound_rate;
    char aac_packet_type = codec->aac_packet_type;

    // for audio frame, there is 1 or 2 bytes header:
    //      1bytes, SoundFormat|SoundRate|SoundSize|SoundType
    //      1bytes, AACPacketType for SoundFormat == 10, 0 is sequence header.
    int size = nb_frame + 1;
    if (sound_format == SrsCodecAudioAAC) {
        size += 1;
    }
    char* data = new char[size];
    char* p = data;
    
    u_int8_t audio_header = sound_type & 0x01;
    audio_header |= (sound_size << 1) & 0x02;
    audio_header |= (sound_rate << 2) & 0x0c;
    audio_header |= (sound_format << 4) & 0xf0;
    
    *p++ = audio_header;
    
    if (sound_format == SrsCodecAudioAAC) {
        *p++ = aac_packet_type;
    }
    
    memcpy(p, frame, nb_frame);

    *flv = data;
    *nb_flv = size;

    return ret;
}

// following is generated by src/protocol/srs_rtsp_stack.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 <srs_rtsp_stack.hpp>

#include <stdlib.h>
#include <map>
using namespace std;

//#include <srs_rtmp_io.hpp>
//#include <srs_kernel_buffer.hpp>
//#include <srs_kernel_error.hpp>
//#include <srs_kernel_log.hpp>
//#include <srs_kernel_consts.hpp>
//#include <srs_core_autofree.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_kernel_codec.hpp>

#ifdef SRS_AUTO_STREAM_CASTER

#define SRS_RTSP_BUFFER 4096

// get the status text of code.
string srs_generate_rtsp_status_text(int status)
{
    static std::map<int, std::string> _status_map;
    if (_status_map.empty()) {
        _status_map[SRS_CONSTS_RTSP_Continue                       ] = SRS_CONSTS_RTSP_Continue_str                        ;      
        _status_map[SRS_CONSTS_RTSP_OK                             ] = SRS_CONSTS_RTSP_OK_str                              ;      
        _status_map[SRS_CONSTS_RTSP_Created                        ] = SRS_CONSTS_RTSP_Created_str                         ;      
        _status_map[SRS_CONSTS_RTSP_LowOnStorageSpace              ] = SRS_CONSTS_RTSP_LowOnStorageSpace_str               ;      
        _status_map[SRS_CONSTS_RTSP_MultipleChoices                ] = SRS_CONSTS_RTSP_MultipleChoices_str                 ;      
        _status_map[SRS_CONSTS_RTSP_MovedPermanently               ] = SRS_CONSTS_RTSP_MovedPermanently_str                ;      
        _status_map[SRS_CONSTS_RTSP_MovedTemporarily               ] = SRS_CONSTS_RTSP_MovedTemporarily_str                ;      
        _status_map[SRS_CONSTS_RTSP_SeeOther                       ] = SRS_CONSTS_RTSP_SeeOther_str                        ;      
        _status_map[SRS_CONSTS_RTSP_NotModified                    ] = SRS_CONSTS_RTSP_NotModified_str                     ;      
        _status_map[SRS_CONSTS_RTSP_UseProxy                       ] = SRS_CONSTS_RTSP_UseProxy_str                        ;      
        _status_map[SRS_CONSTS_RTSP_BadRequest                     ] = SRS_CONSTS_RTSP_BadRequest_str                      ;      
        _status_map[SRS_CONSTS_RTSP_Unauthorized                   ] = SRS_CONSTS_RTSP_Unauthorized_str                    ;      
        _status_map[SRS_CONSTS_RTSP_PaymentRequired                ] = SRS_CONSTS_RTSP_PaymentRequired_str                 ;      
        _status_map[SRS_CONSTS_RTSP_Forbidden                      ] = SRS_CONSTS_RTSP_Forbidden_str                       ;      
        _status_map[SRS_CONSTS_RTSP_NotFound                       ] = SRS_CONSTS_RTSP_NotFound_str                        ;      
        _status_map[SRS_CONSTS_RTSP_MethodNotAllowed               ] = SRS_CONSTS_RTSP_MethodNotAllowed_str                ;      
        _status_map[SRS_CONSTS_RTSP_NotAcceptable                  ] = SRS_CONSTS_RTSP_NotAcceptable_str                   ;      
        _status_map[SRS_CONSTS_RTSP_ProxyAuthenticationRequired    ] = SRS_CONSTS_RTSP_ProxyAuthenticationRequired_str     ;      
        _status_map[SRS_CONSTS_RTSP_RequestTimeout                 ] = SRS_CONSTS_RTSP_RequestTimeout_str                  ;      
        _status_map[SRS_CONSTS_RTSP_Gone                           ] = SRS_CONSTS_RTSP_Gone_str                            ;      
        _status_map[SRS_CONSTS_RTSP_LengthRequired                 ] = SRS_CONSTS_RTSP_LengthRequired_str                  ;      
        _status_map[SRS_CONSTS_RTSP_PreconditionFailed             ] = SRS_CONSTS_RTSP_PreconditionFailed_str              ;      
        _status_map[SRS_CONSTS_RTSP_RequestEntityTooLarge          ] = SRS_CONSTS_RTSP_RequestEntityTooLarge_str           ;      
        _status_map[SRS_CONSTS_RTSP_RequestURITooLarge             ] = SRS_CONSTS_RTSP_RequestURITooLarge_str              ;      
        _status_map[SRS_CONSTS_RTSP_UnsupportedMediaType           ] = SRS_CONSTS_RTSP_UnsupportedMediaType_str            ;      
        _status_map[SRS_CONSTS_RTSP_ParameterNotUnderstood         ] = SRS_CONSTS_RTSP_ParameterNotUnderstood_str          ;      
        _status_map[SRS_CONSTS_RTSP_ConferenceNotFound             ] = SRS_CONSTS_RTSP_ConferenceNotFound_str              ;      
        _status_map[SRS_CONSTS_RTSP_NotEnoughBandwidth             ] = SRS_CONSTS_RTSP_NotEnoughBandwidth_str              ;      
        _status_map[SRS_CONSTS_RTSP_SessionNotFound                ] = SRS_CONSTS_RTSP_SessionNotFound_str                 ;      
        _status_map[SRS_CONSTS_RTSP_MethodNotValidInThisState      ] = SRS_CONSTS_RTSP_MethodNotValidInThisState_str       ;      
        _status_map[SRS_CONSTS_RTSP_HeaderFieldNotValidForResource ] = SRS_CONSTS_RTSP_HeaderFieldNotValidForResource_str  ;      
        _status_map[SRS_CONSTS_RTSP_InvalidRange                   ] = SRS_CONSTS_RTSP_InvalidRange_str                    ;      
        _status_map[SRS_CONSTS_RTSP_ParameterIsReadOnly            ] = SRS_CONSTS_RTSP_ParameterIsReadOnly_str             ;      
        _status_map[SRS_CONSTS_RTSP_AggregateOperationNotAllowed   ] = SRS_CONSTS_RTSP_AggregateOperationNotAllowed_str    ;      
        _status_map[SRS_CONSTS_RTSP_OnlyAggregateOperationAllowed  ] = SRS_CONSTS_RTSP_OnlyAggregateOperationAllowed_str   ;      
        _status_map[SRS_CONSTS_RTSP_UnsupportedTransport           ] = SRS_CONSTS_RTSP_UnsupportedTransport_str            ;      
        _status_map[SRS_CONSTS_RTSP_DestinationUnreachable         ] = SRS_CONSTS_RTSP_DestinationUnreachable_str          ;      
        _status_map[SRS_CONSTS_RTSP_InternalServerError            ] = SRS_CONSTS_RTSP_InternalServerError_str             ;      
        _status_map[SRS_CONSTS_RTSP_NotImplemented                 ] = SRS_CONSTS_RTSP_NotImplemented_str                  ;      
        _status_map[SRS_CONSTS_RTSP_BadGateway                     ] = SRS_CONSTS_RTSP_BadGateway_str                      ;     
        _status_map[SRS_CONSTS_RTSP_ServiceUnavailable             ] = SRS_CONSTS_RTSP_ServiceUnavailable_str              ;     
        _status_map[SRS_CONSTS_RTSP_GatewayTimeout                 ] = SRS_CONSTS_RTSP_GatewayTimeout_str                  ;     
        _status_map[SRS_CONSTS_RTSP_RTSPVersionNotSupported        ] = SRS_CONSTS_RTSP_RTSPVersionNotSupported_str         ;     
        _status_map[SRS_CONSTS_RTSP_OptionNotSupported             ] = SRS_CONSTS_RTSP_OptionNotSupported_str              ;        
    }
    
    std::string status_text;
    if (_status_map.find(status) == _status_map.end()) {
        status_text = "Status Unknown";
    } else {
        status_text = _status_map[status];
    }
    
    return status_text;
}

std::string srs_generate_rtsp_method_str(SrsRtspMethod method) 
{
    switch (method) {
        case SrsRtspMethodDescribe: return SRS_METHOD_DESCRIBE;
        case SrsRtspMethodAnnounce: return SRS_METHOD_ANNOUNCE;
        case SrsRtspMethodGetParameter: return SRS_METHOD_GET_PARAMETER;
        case SrsRtspMethodOptions: return SRS_METHOD_OPTIONS;
        case SrsRtspMethodPause: return SRS_METHOD_PAUSE;
        case SrsRtspMethodPlay: return SRS_METHOD_PLAY;
        case SrsRtspMethodRecord: return SRS_METHOD_RECORD;
        case SrsRtspMethodRedirect: return SRS_METHOD_REDIRECT;
        case SrsRtspMethodSetup: return SRS_METHOD_SETUP;
        case SrsRtspMethodSetParameter: return SRS_METHOD_SET_PARAMETER;
        case SrsRtspMethodTeardown: return SRS_METHOD_TEARDOWN;
        default: return "Unknown";
    }
}

SrsRtpPacket::SrsRtpPacket()
{
    version = 2;
    padding = 0;
    extension = 0;
    csrc_count = 0;
    marker = 1;

    payload_type = 0;
    sequence_number = 0;
    timestamp = 0;
    ssrc = 0;

    payload = new SrsSimpleBuffer();
    audio_samples = new SrsCodecSample();
    chunked = false;
    completed = false;
}

SrsRtpPacket::~SrsRtpPacket()
{
    srs_freep(payload);
    srs_freep(audio_samples);
}

void SrsRtpPacket::copy(SrsRtpPacket* src)
{
    version = src->version;
    padding = src->padding;
    extension = src->extension;
    csrc_count = src->csrc_count;
    marker = src->marker;
    payload_type = src->payload_type;
    sequence_number = src->sequence_number;
    timestamp = src->timestamp;
    ssrc = src->ssrc;

    chunked = src->chunked;
    completed = src->completed;
    audio_samples = new SrsCodecSample();
}

void SrsRtpPacket::reap(SrsRtpPacket* src)
{
    copy(src);

    srs_freep(payload);
    payload = src->payload;
    src->payload = NULL;
    
    srs_freep(audio_samples);
    audio_samples = src->audio_samples;
    src->audio_samples = NULL;
}

int SrsRtpPacket::decode(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // 12bytes header
    if (!stream->require(12)) {
        ret = ERROR_RTP_HEADER_CORRUPT;
        srs_error("rtsp: rtp header corrupt. ret=%d", ret);
        return ret;
    }

    int8_t vv = stream->read_1bytes();
    version = (vv >> 6) & 0x03;
    padding = (vv >> 5) & 0x01;
    extension = (vv >> 4) & 0x01;
    csrc_count = vv & 0x0f;

    int8_t mv = stream->read_1bytes();
    marker = (mv >> 7) & 0x01;
    payload_type = mv & 0x7f;

    sequence_number = stream->read_2bytes();
    timestamp = stream->read_4bytes();
    ssrc = stream->read_4bytes();

    // TODO: FIXME: check sequence number.

    // video codec.
    if (payload_type == 96) {
        return decode_96(stream);
    } else if (payload_type == 97) {
        return decode_97(stream);
    }

    return ret;
}

int SrsRtpPacket::decode_97(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // atleast 2bytes content.
    if (!stream->require(2)) {
        ret = ERROR_RTP_TYPE97_CORRUPT;
        srs_error("rtsp: rtp type97 corrupt. ret=%d", ret);
        return ret;
    }

    int8_t hasv = stream->read_1bytes();
    int8_t lasv = stream->read_1bytes();
    u_int16_t au_size = ((hasv << 5) & 0xE0) | ((lasv >> 3) & 0x1f);

    if (!stream->require(au_size)) {
        ret = ERROR_RTP_TYPE97_CORRUPT;
        srs_error("rtsp: rtp type97 au_size corrupt. ret=%d", ret);
        return ret;
    }

    int required_size = 0;

    // append left bytes to payload.
    payload->append(
        stream->data() + stream->pos() + au_size, 
        stream->size() - stream->pos() - au_size
    );
    char* p = payload->bytes();

    for (int i = 0; i < au_size; i += 2) {
        hasv = stream->read_1bytes();
        lasv = stream->read_1bytes();

        u_int16_t sample_size = ((hasv << 5) & 0xE0) | ((lasv >> 3) & 0x1f);
        // TODO: FIXME: finger out how to parse the size of sample.
        if (sample_size < 0x100 && stream->require(required_size + sample_size + 0x100)) {
            sample_size = sample_size | 0x100;
        }

        char* sample = p + required_size;
        required_size += sample_size;

        if (!stream->require(required_size)) {
            ret = ERROR_RTP_TYPE97_CORRUPT;
            srs_error("rtsp: rtp type97 samples corrupt. ret=%d", ret);
            return ret;
        }

        if ((ret = audio_samples->add_sample_unit(sample, sample_size)) != ERROR_SUCCESS) {
            srs_error("rtsp: rtp type97 add sample failed. ret=%d", ret);
            return ret;
        }
    }

    // parsed ok.
    completed = true;

    return ret;
}

int SrsRtpPacket::decode_96(SrsStream* stream)
{
    int ret = ERROR_SUCCESS;

    // atleast 2bytes content.
    if (!stream->require(2)) {
        ret = ERROR_RTP_TYPE96_CORRUPT;
        srs_error("rtsp: rtp type96 corrupt. ret=%d", ret);
        return ret;
    }

    // frame type
    // 0... .... reserverd
    // .11. .... NALU[0]&0x60
    // ...1 11.. FU indicator
    // .... ..00 reserverd
    int8_t ftv = stream->read_1bytes();
    int8_t nalu_0x60 = ftv & 0x60;
    int8_t fu_indicator = ftv & 0x1c;

    // nri, whatever
    // 10.. .... first chunk.
    // 00.. .... continous chunk.
    // 01.. .... last chunk.
    // ...1 1111 NALU[0]&0x1f
    int8_t nriv = stream->read_1bytes();
    bool first_chunk = (nriv & 0xC0) == 0x80;
    bool last_chunk = (nriv & 0xC0) == 0x40;
    bool contious_chunk = (nriv & 0xC0) == 0x00;
    int8_t nalu_0x1f = nriv & 0x1f;

    // chunked, generate the first byte NALU.
    if (fu_indicator == 0x1c && (first_chunk || last_chunk || contious_chunk)) {
        chunked = true;
        completed = last_chunk;

        // generate and append the first byte NALU.
        if (first_chunk) {
            int8_t nalu_byte0 = nalu_0x60 | nalu_0x1f;
            payload->append((char*)&nalu_byte0, 1);
        }
        
        payload->append(stream->data() + stream->pos(), stream->size() - stream->pos());
        return ret;
    }

    // no chunked, append to payload.
    stream->skip(-2);
    payload->append(stream->data() + stream->pos(), stream->size() - stream->pos());
    completed = true;

    return ret;
}

SrsRtspSdp::SrsRtspSdp()
{
    state = SrsRtspSdpStateOthers;
}

SrsRtspSdp::~SrsRtspSdp()
{
}

int SrsRtspSdp::parse(string token)
{
    int ret = ERROR_SUCCESS;

    if (token.empty()) {
        srs_info("rtsp: ignore empty token.");
        return ret;
    }
    
    size_t pos = string::npos;

    char* start = (char*)token.data();
    char* end = start + (int)token.length();
    char* p = start;

    // key, first 2bytes.
    // v=0
    // o=- 0 0 IN IP4 127.0.0.1
    // s=No Name
    // c=IN IP4 192.168.43.23
    // t=0 0
    // a=tool:libavformat 53.9.0
    // m=video 0 RTP/AVP 96
    // b=AS:850
    // a=rtpmap:96 H264/90000
    // a=fmtp:96 packetization-mode=1; sprop-parameter-sets=Z2QAKKzRwFAFu/8ALQAiEAAAAwAQAAADAwjxgxHg,aOmrLIs=
    // a=control:streamid=0
    // m=audio 0 RTP/AVP 97
    // b=AS:49
    // a=rtpmap:97 MPEG4-GENERIC/44100/2
    // a=fmtp:97 profile-level-id=1;mode=AAC-hbr;sizelength=13;indexlength=3;indexdeltalength=3; config=139056E5A0
    // a=control:streamid=1
    char key = p[0];
    p += 2;

    // left bytes as attr string.
    std::string attr_str;
    if (end - p) {
        attr_str.append(p, end - p);
    }

    // parse the attributes from left bytes.
    std::vector<std::string> attrs;
    while (p < end) {
        // parse an attribute, split by SP.
        char* pa = p;
        for (; p < end && p[0] != SRS_RTSP_SP; p++) {
        }
        std::string attr;
        if (p > pa) {
            attr.append(pa, p - pa);
            attrs.push_back(attr);
        }
        p++;
    }

    // parse the first attr as desc, update the first elem for desc.
    // for example, the value can be "tool", "AS", "rtpmap", "fmtp", "control"
    std::string desc_key;
    if (attrs.size() > 0) {
        std::string attr = attrs.at(0);
        if ((pos = attr.find(":")) != string::npos) {
            desc_key = attr.substr(0, pos);
            attr = attr.substr(pos + 1);
            attr_str = attr_str.substr(pos + 1);
            attrs[0] = attr;
        } else {
            desc_key = attr;
        }
    }

    // interpret the attribute according by key.
    switch (key) {
        case 'v': version = attr_str; break;
        case 'o':
            owner_username = (attrs.size() > 0)? attrs[0]:"";
            owner_session_id = (attrs.size() > 1)? attrs[1]:"";
            owner_session_version = (attrs.size() > 2)? attrs[2]:"";
            owner_network_type = (attrs.size() > 3)? attrs[3]:"";
            owner_address_type = (attrs.size() > 4)? attrs[4]:"";
            owner_address = (attrs.size() > 5)? attrs[5]:"";
            break;
        case 's': session_name = attr_str; break;
        case 'c':
            connection_network_type = (attrs.size() > 0)? attrs[0]:"";
            connection_address_type = (attrs.size() > 0)? attrs[0]:"";
            connection_address = (attrs.size() > 0)? attrs[0]:"";
            break;
        case 'a':
            if (desc_key == "tool") {
                tool = attr_str;
            } else if (desc_key == "rtpmap") {
                if (state == SrsRtspSdpStateVideo) {
                    video_codec = (attrs.size() > 1)? attrs[1]:"";
                    if ((pos = video_codec.find("/")) != string::npos) {
                        video_sample_rate = video_codec.substr(pos + 1);
                        video_codec = video_codec.substr(0, pos);
                    }
                } else if (state == SrsRtspSdpStateAudio) {
                    audio_codec = (attrs.size() > 1)? attrs[1]:"";
                    if ((pos = audio_codec.find("/")) != string::npos) {
                        audio_sample_rate = audio_codec.substr(pos + 1);
                        audio_codec = audio_codec.substr(0, pos);
                    }
                    if ((pos = audio_sample_rate.find("/")) != string::npos) {
                        audio_channel = audio_sample_rate.substr(pos + 1);
                        audio_sample_rate = audio_sample_rate.substr(0, pos);
                    }
                }
            } else if (desc_key == "fmtp") {
                for (int i = 1; i < (int)attrs.size(); i++) {
                    std::string attr = attrs.at(i);
                    if ((ret = parse_fmtp_attribute(attr)) != ERROR_SUCCESS) {
                        srs_error("rtsp: parse fmtp failed, attr=%s. ret=%d", attr.c_str(), ret);
                        return ret;
                    }
                }
            } else if (desc_key == "control") {
                for (int i = 0; i < (int)attrs.size(); i++) {
                    std::string attr = attrs.at(i);
                    if ((ret = parse_control_attribute(attr)) != ERROR_SUCCESS) {
                        srs_error("rtsp: parse control failed, attr=%s. ret=%d", attr.c_str(), ret);
                        return ret;
                    }
                }
            }
            break;
        case 'm':
            if (desc_key == "video") {
                state = SrsRtspSdpStateVideo;
                video_port = (attrs.size() > 1)? attrs[1]:"";
                video_protocol = (attrs.size() > 2)? attrs[2]:"";
                video_transport_format = (attrs.size() > 3)? attrs[3]:"";
            } else if (desc_key == "audio") {
                state = SrsRtspSdpStateAudio;
                audio_port = (attrs.size() > 1)? attrs[1]:"";
                audio_protocol = (attrs.size() > 2)? attrs[2]:"";
                audio_transport_format = (attrs.size() > 3)? attrs[3]:"";
            }
            break;
        case 'b':
            if (desc_key == "AS") {
                if (state == SrsRtspSdpStateVideo) {
                    video_bandwidth_kbps = (attrs.size() > 0)? attrs[0]:"";
                } else if (state == SrsRtspSdpStateAudio) {
                    audio_bandwidth_kbps = (attrs.size() > 0)? attrs[0]:"";
                }
            }
            break;
        case 't':
        default: break;
    }

    return ret;
}

int SrsRtspSdp::parse_fmtp_attribute(string attr)
{
    int ret = ERROR_SUCCESS;
    
    size_t pos = string::npos;
    std::string token = attr;

    while (!token.empty()) {
        std::string item = token;
        if ((pos = item.find(";")) != string::npos) {
            item = token.substr(0, pos);
            token = token.substr(pos + 1);
        } else {
            token = "";
        }

        std::string item_key = item, item_value;
        if ((pos = item.find("=")) != string::npos) {
            item_key = item.substr(0, pos);
            item_value = item.substr(pos + 1);
        }

        if (state == SrsRtspSdpStateVideo) {
            if (item_key == "packetization-mode") {
                video_packetization_mode = item_value;
            } else if (item_key == "sprop-parameter-sets") {
                video_sps = item_value;
                if ((pos = video_sps.find(",")) != string::npos) {
                    video_pps = video_sps.substr(pos + 1);
                    video_sps = video_sps.substr(0, pos);
                }
                // decode the sps/pps by base64
                video_sps = base64_decode(video_sps);
                video_pps = base64_decode(video_pps);
            }
        } else if (state == SrsRtspSdpStateAudio) {
            if (item_key == "profile-level-id") {
                audio_profile_level_id = item_value;
            } else if (item_key == "mode") {
                audio_mode = item_value;
            } else if (item_key == "sizelength") {
                audio_size_length = item_value;
            } else if (item_key == "indexlength") {
                audio_index_length = item_value;
            } else if (item_key == "indexdeltalength") {
                audio_index_delta_length = item_value;
            } else if (item_key == "config") {
                if (item_value.length() <= 0) {
                    ret = ERROR_RTSP_AUDIO_CONFIG;
                    srs_error("rtsp: audio config failed. ret=%d", ret);
                    return ret;
                }

                char* tmp_sh = new char[item_value.length()];
                SrsAutoFree(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);
            }
        }
    }

    return ret;
}

int SrsRtspSdp::parse_control_attribute(string attr)
{
    int ret = ERROR_SUCCESS;
    
    size_t pos = string::npos;
    std::string token = attr;

    while (!token.empty()) {
        std::string item = token;
        if ((pos = item.find(";")) != string::npos) {
            item = token.substr(0, pos);
            token = token.substr(pos + 1);
        } else {
            token = "";
        }

        std::string item_key = item, item_value;
        if ((pos = item.find("=")) != string::npos) {
            item_key = item.substr(0, pos);
            item_value = item.substr(pos + 1);
        }

        if (state == SrsRtspSdpStateVideo) {
            if (item_key == "streamid") {
                video_stream_id = item_value;
            }
        } else if (state == SrsRtspSdpStateAudio) {
            if (item_key == "streamid") {
                audio_stream_id = item_value;
            }
        }
    }

    return ret;
}

string SrsRtspSdp::base64_decode(string value)
{
    if (value.empty()) {
        return "";
    }

    int nb_output = (int)(value.length() * 2);
    u_int8_t* output = new u_int8_t[nb_output];
    SrsAutoFree(u_int8_t, output);

    int ret = srs_av_base64_decode(output, (char*)value.c_str(), nb_output);
    if (ret <= 0) {
        return "";
    }

    std::string plaintext;
    plaintext.append((char*)output, ret);
    return plaintext;
}

SrsRtspTransport::SrsRtspTransport()
{
    client_port_min = 0;
    client_port_max = 0;
}

SrsRtspTransport::~SrsRtspTransport()
{
}

int SrsRtspTransport::parse(string attr)
{
    int ret = ERROR_SUCCESS;
    
    size_t pos = string::npos;
    std::string token = attr;

    while (!token.empty()) {
        std::string item = token;
        if ((pos = item.find(";")) != string::npos) {
            item = token.substr(0, pos);
            token = token.substr(pos + 1);
        } else {
            token = "";
        }

        std::string item_key = item, item_value;
        if ((pos = item.find("=")) != string::npos) {
            item_key = item.substr(0, pos);
            item_value = item.substr(pos + 1);
        }

        if (transport.empty()) {
            transport = item_key;
            if ((pos = transport.find("/")) != string::npos) {
                profile = transport.substr(pos + 1);
                transport = transport.substr(0, pos);
            }
            if ((pos = profile.find("/")) != string::npos) {
                lower_transport = profile.substr(pos + 1);
                profile = profile.substr(0, pos);
            }
        }

        if (item_key == "unicast" || item_key == "multicast") {
            cast_type = item_key;
        } else if (item_key == "mode") {
            mode = item_value;
        } else if (item_key == "client_port") {
            std::string sport = item_value;
            std::string eport = item_value;
            if ((pos = eport.find("-")) != string::npos) {
                sport = eport.substr(0, pos);
                eport = eport.substr(pos + 1);
            }
            client_port_min = ::atoi(sport.c_str());
            client_port_max = ::atoi(eport.c_str());
        }
    }

    return ret;
}

SrsRtspRequest::SrsRtspRequest()
{
    seq = 0;
    content_length = 0;
    stream_id = 0;
    sdp = NULL;
    transport = NULL;
}

SrsRtspRequest::~SrsRtspRequest()
{
    srs_freep(sdp);
    srs_freep(transport);
}

bool SrsRtspRequest::is_options()
{
    return method == SRS_METHOD_OPTIONS;
}

bool SrsRtspRequest::is_announce()
{
    return method == SRS_METHOD_ANNOUNCE;
}

bool SrsRtspRequest::is_setup()
{
    return method == SRS_METHOD_SETUP;
}

bool SrsRtspRequest::is_record()
{
    return method == SRS_METHOD_RECORD;
}

SrsRtspResponse::SrsRtspResponse(int cseq)
{
    seq = cseq;
    status = SRS_CONSTS_RTSP_OK;
}

SrsRtspResponse::~SrsRtspResponse()
{
}

int SrsRtspResponse::encode(stringstream& ss)
{
    int ret = ERROR_SUCCESS;

    // status line
    ss << SRS_RTSP_VERSION << SRS_RTSP_SP 
        << status << SRS_RTSP_SP 
        << srs_generate_rtsp_status_text(status) << SRS_RTSP_CRLF;

    // cseq
    ss << SRS_RTSP_TOKEN_CSEQ << ":" << SRS_RTSP_SP << seq << SRS_RTSP_CRLF;

    // others.
    ss << "Cache-Control: no-store" << SRS_RTSP_CRLF
        << "Pragma: no-cache" << SRS_RTSP_CRLF
        << "Server: " << RTMP_SIG_SRS_SERVER << SRS_RTSP_CRLF;

    // session if specified.
    if (!session.empty()) {
        ss << SRS_RTSP_TOKEN_SESSION << ":" << session << SRS_RTSP_CRLF;
    }

    if ((ret = encode_header(ss)) != ERROR_SUCCESS) {
        srs_error("rtsp: encode header failed. ret=%d", ret);
        return ret;
    };

    // header EOF.
    ss << SRS_RTSP_CRLF;

    return ret;
}

int SrsRtspResponse::encode_header(std::stringstream& ss)
{
    return ERROR_SUCCESS;
}

SrsRtspOptionsResponse::SrsRtspOptionsResponse(int cseq) : SrsRtspResponse(cseq)
{
    methods = (SrsRtspMethod)(SrsRtspMethodDescribe | SrsRtspMethodOptions 
        | SrsRtspMethodPause | SrsRtspMethodPlay | SrsRtspMethodSetup | SrsRtspMethodTeardown
        | SrsRtspMethodAnnounce | SrsRtspMethodRecord);
}

SrsRtspOptionsResponse::~SrsRtspOptionsResponse()
{
}

int SrsRtspOptionsResponse::encode_header(stringstream& ss)
{
    SrsRtspMethod rtsp_methods[] = {
        SrsRtspMethodDescribe,
        SrsRtspMethodAnnounce,
        SrsRtspMethodGetParameter,
        SrsRtspMethodOptions,
        SrsRtspMethodPause,
        SrsRtspMethodPlay,
        SrsRtspMethodRecord,
        SrsRtspMethodRedirect,
        SrsRtspMethodSetup,
        SrsRtspMethodSetParameter,
        SrsRtspMethodTeardown,
    };

    ss << SRS_RTSP_TOKEN_PUBLIC << ":" << SRS_RTSP_SP;

    bool appended = false;
    int nb_methods = (int)(sizeof(rtsp_methods) / sizeof(SrsRtspMethod));
    for (int i = 0; i < nb_methods; i++) {
        SrsRtspMethod method = rtsp_methods[i];
        if (((int)methods & (int)method) != (int)method) {
            continue;
        }

        if (appended) {
            ss << ", ";
        }
        ss << srs_generate_rtsp_method_str(method);
        appended = true;
    }
    ss << SRS_RTSP_CRLF;

    return ERROR_SUCCESS;
}

SrsRtspSetupResponse::SrsRtspSetupResponse(int seq) : SrsRtspResponse(seq)
{
    local_port_min = 0;
    local_port_max = 0;
}

SrsRtspSetupResponse::~SrsRtspSetupResponse()
{
}

int SrsRtspSetupResponse::encode_header(stringstream& ss)
{
    ss << SRS_RTSP_TOKEN_SESSION << ":" << SRS_RTSP_SP << session << SRS_RTSP_CRLF;
    ss << SRS_RTSP_TOKEN_TRANSPORT << ":" << SRS_RTSP_SP 
        << "RTP/AVP;unicast;client_port=" << client_port_min << "-" << client_port_max << ";"
        << "server_port=" << local_port_min << "-" << local_port_max
        << SRS_RTSP_CRLF;
    return ERROR_SUCCESS;
}

SrsRtspStack::SrsRtspStack(ISrsProtocolReaderWriter* s)
{
    buf = new SrsSimpleBuffer();
    skt = s;
}

SrsRtspStack::~SrsRtspStack()
{
    srs_freep(buf);
}

int SrsRtspStack::recv_message(SrsRtspRequest** preq)
{
    int ret = ERROR_SUCCESS;

    SrsRtspRequest* req = new SrsRtspRequest();
    if ((ret = do_recv_message(req)) != ERROR_SUCCESS) {
        srs_freep(req);
        return ret;
    }

    *preq = req;

    return ret;
}

int SrsRtspStack::send_message(SrsRtspResponse* res)
{
    int ret = ERROR_SUCCESS;

    std::stringstream ss;
    // encode the message to string.
    res->encode(ss);

    std::string str = ss.str();
    srs_assert(!str.empty());

    if ((ret = skt->write((char*)str.c_str(), (int)str.length(), NULL)) != ERROR_SUCCESS) {
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("rtsp: send response failed. ret=%d", ret);
        }
        return ret;
    }
    srs_info("rtsp: send response ok");

    return ret;
}

int SrsRtspStack::do_recv_message(SrsRtspRequest* req)
{
    int ret = ERROR_SUCCESS;

    // parse request line.
    if ((ret = recv_token_normal(req->method)) != ERROR_SUCCESS) {
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("rtsp: parse method failed. ret=%d", ret);
        }
        return ret;
    }

    if ((ret = recv_token_normal(req->uri)) != ERROR_SUCCESS) {
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("rtsp: parse uri failed. ret=%d", ret);
        }
        return ret;
    }

    if ((ret = recv_token_eof(req->version)) != ERROR_SUCCESS) {
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("rtsp: parse version failed. ret=%d", ret);
        }
        return ret;
    }

    // parse headers.
    for (;;) {
        // parse the header name
        std::string token;
        if ((ret = recv_token_normal(token)) != ERROR_SUCCESS) {
            if (ret == ERROR_RTSP_REQUEST_HEADER_EOF) {
                ret = ERROR_SUCCESS;
                srs_info("rtsp: message header parsed");
                break;
            }
            if (!srs_is_client_gracefully_close(ret)) {
                srs_error("rtsp: parse token failed. ret=%d", ret);
            }
            return ret;
        }

        // parse the header value according by header name
        if (token == SRS_RTSP_TOKEN_CSEQ) {
            std::string seq;
            if ((ret = recv_token_eof(seq)) != ERROR_SUCCESS) {
                if (!srs_is_client_gracefully_close(ret)) {
                    srs_error("rtsp: parse %s failed. ret=%d", SRS_RTSP_TOKEN_CSEQ, ret);
                }
                return ret;
            }
            req->seq = ::atol(seq.c_str());
        } else if (token == SRS_RTSP_TOKEN_CONTENT_TYPE) {
            std::string ct;
            if ((ret = recv_token_eof(ct)) != ERROR_SUCCESS) {
                if (!srs_is_client_gracefully_close(ret)) {
                    srs_error("rtsp: parse %s failed. ret=%d", SRS_RTSP_TOKEN_CONTENT_TYPE, ret);
                }
                return ret;
            }
            req->content_type = ct;
        } else if (token == SRS_RTSP_TOKEN_CONTENT_LENGTH) {
            std::string cl;
            if ((ret = recv_token_eof(cl)) != ERROR_SUCCESS) {
                if (!srs_is_client_gracefully_close(ret)) {
                    srs_error("rtsp: parse %s failed. ret=%d", SRS_RTSP_TOKEN_CONTENT_LENGTH, ret);
                }
                return ret;
            }
            req->content_length = ::atol(cl.c_str());
        } else if (token == SRS_RTSP_TOKEN_TRANSPORT) {
            std::string transport;
            if ((ret = recv_token_eof(transport)) != ERROR_SUCCESS) {
                if (!srs_is_client_gracefully_close(ret)) {
                    srs_error("rtsp: parse %s failed. ret=%d", SRS_RTSP_TOKEN_TRANSPORT, ret);
                }
                return ret;
            }
            if (!req->transport) {
                req->transport = new SrsRtspTransport();
            }
            if ((ret = req->transport->parse(transport)) != ERROR_SUCCESS) {
                srs_error("rtsp: parse transport failed, transport=%s. ret=%d", transport.c_str(), ret);
                return ret;
            }
        } else if (token == SRS_RTSP_TOKEN_SESSION) {
            if ((ret = recv_token_eof(req->session)) != ERROR_SUCCESS) {
                if (!srs_is_client_gracefully_close(ret)) {
                    srs_error("rtsp: parse %s failed. ret=%d", SRS_RTSP_TOKEN_SESSION, ret);
                }
                return ret;
            }
        } else {
            // unknown header name, parse util EOF.
            SrsRtspTokenState state = SrsRtspTokenStateNormal;
            while (state == SrsRtspTokenStateNormal) {
                std::string value;
                if ((ret = recv_token(value, state)) != ERROR_SUCCESS) {
                    if (!srs_is_client_gracefully_close(ret)) {
                        srs_error("rtsp: parse token failed. ret=%d", ret);
                    }
                    return ret;
                }
                srs_trace("rtsp: ignore header %s=%s", token.c_str(), value.c_str());
            }
        }
    }

    // for setup, parse the stream id from uri.
    if (req->is_setup()) {
        size_t pos = string::npos;
        std::string stream_id;
        if ((pos = req->uri.rfind("/")) != string::npos) {
            stream_id = req->uri.substr(pos + 1);
        }
        if ((pos = stream_id.find("=")) != string::npos) {
            stream_id = stream_id.substr(pos + 1);
        }
        req->stream_id = ::atoi(stream_id.c_str());
        srs_info("rtsp: setup stream id=%d", req->stream_id);
    }

    // parse rdp body.
    long consumed = 0;
    while (consumed < req->content_length) {
        if (!req->sdp) {
            req->sdp = new SrsRtspSdp();
        }

        int nb_token = 0;
        std::string token;
        if ((ret = recv_token_util_eof(token, &nb_token)) != ERROR_SUCCESS) {
            if (!srs_is_client_gracefully_close(ret)) {
                srs_error("rtsp: parse sdp token failed. ret=%d", ret);
            }
            return ret;
        }
        consumed += nb_token;

        if ((ret = req->sdp->parse(token)) != ERROR_SUCCESS) {
            srs_error("rtsp: sdp parse token failed, token=%s. ret=%d", token.c_str(), ret);
            return ret;
        }
        srs_info("rtsp: %s", token.c_str());
    }
    srs_info("rtsp: sdp parsed, size=%d", consumed);

    return ret;
}

int SrsRtspStack::recv_token_normal(std::string& token)
{
    int ret = ERROR_SUCCESS;

    SrsRtspTokenState state;

    if ((ret = recv_token(token, state)) != ERROR_SUCCESS) {
        if (ret == ERROR_RTSP_REQUEST_HEADER_EOF) {
            return ret;
        }
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("rtsp: parse token failed. ret=%d", ret);
        }
        return ret;
    }

    if (state != SrsRtspTokenStateNormal) {
        ret = ERROR_RTSP_TOKEN_NOT_NORMAL;
        srs_error("rtsp: parse normal token failed, state=%d. ret=%d", state, ret);
        return ret;
    }

    return ret;
}

int SrsRtspStack::recv_token_eof(std::string& token)
{
    int ret = ERROR_SUCCESS;

    SrsRtspTokenState state;

    if ((ret = recv_token(token, state)) != ERROR_SUCCESS) {
        if (ret == ERROR_RTSP_REQUEST_HEADER_EOF) {
            return ret;
        }
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("rtsp: parse token failed. ret=%d", ret);
        }
        return ret;
    }

    if (state != SrsRtspTokenStateEOF) {
        ret = ERROR_RTSP_TOKEN_NOT_NORMAL;
        srs_error("rtsp: parse eof token failed, state=%d. ret=%d", state, ret);
        return ret;
    }

    return ret;
}

int SrsRtspStack::recv_token_util_eof(std::string& token, int* pconsumed)
{
    int ret = ERROR_SUCCESS;

    SrsRtspTokenState state;

    // use 0x00 as ignore the normal token flag.
    if ((ret = recv_token(token, state, 0x00, pconsumed)) != ERROR_SUCCESS) {
        if (ret == ERROR_RTSP_REQUEST_HEADER_EOF) {
            return ret;
        }
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("rtsp: parse token failed. ret=%d", ret);
        }
        return ret;
    }

    if (state != SrsRtspTokenStateEOF) {
        ret = ERROR_RTSP_TOKEN_NOT_NORMAL;
        srs_error("rtsp: parse eof token failed, state=%d. ret=%d", state, ret);
        return ret;
    }

    return ret;
}

int SrsRtspStack::recv_token(std::string& token, SrsRtspTokenState& state, char normal_ch, int* pconsumed)
{
    int ret = ERROR_SUCCESS;

    // whatever, default to error state.
    state = SrsRtspTokenStateError;

    // when buffer is empty, append bytes first.
    bool append_bytes = buf->length() == 0;

    // parse util token.
    for (;;) {
        // append bytes if required.
        if (append_bytes) {
            append_bytes = false;

            char buffer[SRS_RTSP_BUFFER];
            ssize_t nb_read = 0;
            if ((ret = skt->read(buffer, SRS_RTSP_BUFFER, &nb_read)) != ERROR_SUCCESS) {
                if (!srs_is_client_gracefully_close(ret)) {
                    srs_error("rtsp: io read failed. ret=%d", ret);
                }
                return ret;
            }
            srs_info("rtsp: io read %d bytes", nb_read);

            buf->append(buffer, nb_read);
        }

        // parse one by one.
        char* start = buf->bytes();
        char* end = start + buf->length();
        char* p = start;

        // find util SP/CR/LF, max 2 EOF, to finger out the EOF of message.
        for (; p < end && p[0] != normal_ch && p[0] != SRS_RTSP_CR && p[0] != SRS_RTSP_LF; p++) {
        }

        // matched.
        if (p < end) {
            // finger out the state.
            if (p[0] == normal_ch) {
                state = SrsRtspTokenStateNormal;
            } else {
                state = SrsRtspTokenStateEOF;
            }
            
            // got the token.
            int nb_token = p - start;
            // trim last ':' character.
            if (nb_token && p[-1] == ':') {
                nb_token--;
            }
            if (nb_token) {
                token.append(start, nb_token);
            } else {
                ret = ERROR_RTSP_REQUEST_HEADER_EOF;
            }

            // ignore SP/CR/LF
            for (int i = 0; i < 2 && p < end && (p[0] == normal_ch || p[0] == SRS_RTSP_CR || p[0] == SRS_RTSP_LF); p++, i++) {
            }

            // consume the token bytes.
            srs_assert(p - start);
            buf->erase(p - start);
            if (pconsumed) {
                *pconsumed = p - start;
            }
            break;
        }

        // append more and parse again.
        append_bytes = true;
    }

    return ret;
}

#endif

// following is generated by src/protocol/srs_http_stack.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 <srs_http_stack.hpp>

#include <stdlib.h>
#include <sstream>
#include <algorithm>
using namespace std;

//#include <srs_kernel_error.hpp>
//#include <srs_kernel_log.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_kernel_file.hpp>

#define SRS_HTTP_DEFAULT_PAGE "index.html"

// get the status text of code.
string srs_generate_http_status_text(int status)
{
    static std::map<int, std::string> _status_map;
    if (_status_map.empty()) {
        _status_map[SRS_CONSTS_HTTP_Continue                       ] = SRS_CONSTS_HTTP_Continue_str                     ;
        _status_map[SRS_CONSTS_HTTP_SwitchingProtocols             ] = SRS_CONSTS_HTTP_SwitchingProtocols_str           ;
        _status_map[SRS_CONSTS_HTTP_OK                             ] = SRS_CONSTS_HTTP_OK_str                           ;
        _status_map[SRS_CONSTS_HTTP_Created                        ] = SRS_CONSTS_HTTP_Created_str                      ;
        _status_map[SRS_CONSTS_HTTP_Accepted                       ] = SRS_CONSTS_HTTP_Accepted_str                     ;
        _status_map[SRS_CONSTS_HTTP_NonAuthoritativeInformation    ] = SRS_CONSTS_HTTP_NonAuthoritativeInformation_str  ;
        _status_map[SRS_CONSTS_HTTP_NoContent                      ] = SRS_CONSTS_HTTP_NoContent_str                    ;
        _status_map[SRS_CONSTS_HTTP_ResetContent                   ] = SRS_CONSTS_HTTP_ResetContent_str                 ;
        _status_map[SRS_CONSTS_HTTP_PartialContent                 ] = SRS_CONSTS_HTTP_PartialContent_str               ;
        _status_map[SRS_CONSTS_HTTP_MultipleChoices                ] = SRS_CONSTS_HTTP_MultipleChoices_str              ;
        _status_map[SRS_CONSTS_HTTP_MovedPermanently               ] = SRS_CONSTS_HTTP_MovedPermanently_str             ;
        _status_map[SRS_CONSTS_HTTP_Found                          ] = SRS_CONSTS_HTTP_Found_str                        ;
        _status_map[SRS_CONSTS_HTTP_SeeOther                       ] = SRS_CONSTS_HTTP_SeeOther_str                     ;
        _status_map[SRS_CONSTS_HTTP_NotModified                    ] = SRS_CONSTS_HTTP_NotModified_str                  ;
        _status_map[SRS_CONSTS_HTTP_UseProxy                       ] = SRS_CONSTS_HTTP_UseProxy_str                     ;
        _status_map[SRS_CONSTS_HTTP_TemporaryRedirect              ] = SRS_CONSTS_HTTP_TemporaryRedirect_str            ;
        _status_map[SRS_CONSTS_HTTP_BadRequest                     ] = SRS_CONSTS_HTTP_BadRequest_str                   ;
        _status_map[SRS_CONSTS_HTTP_Unauthorized                   ] = SRS_CONSTS_HTTP_Unauthorized_str                 ;
        _status_map[SRS_CONSTS_HTTP_PaymentRequired                ] = SRS_CONSTS_HTTP_PaymentRequired_str              ;
        _status_map[SRS_CONSTS_HTTP_Forbidden                      ] = SRS_CONSTS_HTTP_Forbidden_str                    ;
        _status_map[SRS_CONSTS_HTTP_NotFound                       ] = SRS_CONSTS_HTTP_NotFound_str                     ;
        _status_map[SRS_CONSTS_HTTP_MethodNotAllowed               ] = SRS_CONSTS_HTTP_MethodNotAllowed_str             ;
        _status_map[SRS_CONSTS_HTTP_NotAcceptable                  ] = SRS_CONSTS_HTTP_NotAcceptable_str                ;
        _status_map[SRS_CONSTS_HTTP_ProxyAuthenticationRequired    ] = SRS_CONSTS_HTTP_ProxyAuthenticationRequired_str  ;
        _status_map[SRS_CONSTS_HTTP_RequestTimeout                 ] = SRS_CONSTS_HTTP_RequestTimeout_str               ;
        _status_map[SRS_CONSTS_HTTP_Conflict                       ] = SRS_CONSTS_HTTP_Conflict_str                     ;
        _status_map[SRS_CONSTS_HTTP_Gone                           ] = SRS_CONSTS_HTTP_Gone_str                         ;
        _status_map[SRS_CONSTS_HTTP_LengthRequired                 ] = SRS_CONSTS_HTTP_LengthRequired_str               ;
        _status_map[SRS_CONSTS_HTTP_PreconditionFailed             ] = SRS_CONSTS_HTTP_PreconditionFailed_str           ;
        _status_map[SRS_CONSTS_HTTP_RequestEntityTooLarge          ] = SRS_CONSTS_HTTP_RequestEntityTooLarge_str        ;
        _status_map[SRS_CONSTS_HTTP_RequestURITooLarge             ] = SRS_CONSTS_HTTP_RequestURITooLarge_str           ;
        _status_map[SRS_CONSTS_HTTP_UnsupportedMediaType           ] = SRS_CONSTS_HTTP_UnsupportedMediaType_str         ;
        _status_map[SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable   ] = SRS_CONSTS_HTTP_RequestedRangeNotSatisfiable_str ;
        _status_map[SRS_CONSTS_HTTP_ExpectationFailed              ] = SRS_CONSTS_HTTP_ExpectationFailed_str            ;
        _status_map[SRS_CONSTS_HTTP_InternalServerError            ] = SRS_CONSTS_HTTP_InternalServerError_str          ;
        _status_map[SRS_CONSTS_HTTP_NotImplemented                 ] = SRS_CONSTS_HTTP_NotImplemented_str               ;
        _status_map[SRS_CONSTS_HTTP_BadGateway                     ] = SRS_CONSTS_HTTP_BadGateway_str                   ;
        _status_map[SRS_CONSTS_HTTP_ServiceUnavailable             ] = SRS_CONSTS_HTTP_ServiceUnavailable_str           ;
        _status_map[SRS_CONSTS_HTTP_GatewayTimeout                 ] = SRS_CONSTS_HTTP_GatewayTimeout_str               ;
        _status_map[SRS_CONSTS_HTTP_HTTPVersionNotSupported        ] = SRS_CONSTS_HTTP_HTTPVersionNotSupported_str      ;
    }
    
    std::string status_text;
    if (_status_map.find(status) == _status_map.end()) {
        status_text = "Status Unknown";
    } else {
        status_text = _status_map[status];
    }
    
    return status_text;
}

// bodyAllowedForStatus reports whether a given response status code
// permits a body.  See RFC2616, section 4.4.
bool srs_go_http_body_allowd(int status)
{
    if (status >= 100 && status <= 199) {
        return false;
    } else if (status == 204 || status == 304) {
        return false;
    }
    
    return true;
}

// DetectContentType implements the algorithm described
// at http://mimesniff.spec.whatwg.org/ to determine the
// Content-Type of the given data.  It considers at most the
// first 512 bytes of data.  DetectContentType always returns
// a valid MIME type: if it cannot determine a more specific one, it
// returns "application/octet-stream".
string srs_go_http_detect(char* data, int size)
{
    // detect only when data specified.
    if (data) {
    }
    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, string error)
{
    int ret = ERROR_SUCCESS;
    
    w->header()->set_content_type("text/plain; charset=utf-8");
    w->header()->set_content_length(error.length());
    w->write_header(code);
    w->write((char*)error.data(), (int)error.length());
    
    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()
{
}

SrsHttpHeader::~SrsHttpHeader()
{
}

void SrsHttpHeader::set(string key, string value)
{
    headers[key] = value;
}

string SrsHttpHeader::get(string key)
{
    std::string v;
    
    if (headers.find(key) != headers.end()) {
        v = headers[key];
    }
    
    return v;
}

int64_t SrsHttpHeader::content_length()
{
    std::string cl = get("Content-Length");
    
    if (cl.empty()) {
        return -1;
    }
    
    return (int64_t)::atof(cl.c_str());
}

void SrsHttpHeader::set_content_length(int64_t size)
{
    char buf[64];
    snprintf(buf, sizeof(buf), "%"PRId64, size);
    set("Content-Length", buf);
}

string SrsHttpHeader::content_type()
{
    return get("Content-Type");
}

void SrsHttpHeader::set_content_type(string ct)
{
    set("Content-Type", ct);
}

void SrsHttpHeader::write(stringstream& ss)
{
    std::map<std::string, std::string>::iterator it;
    for (it = headers.begin(); it != headers.end(); ++it) {
        ss << it->first << ": " << it->second << SRS_HTTP_CRLF;
    }
}

ISrsHttpResponseWriter::ISrsHttpResponseWriter()
{
}

ISrsHttpResponseWriter::~ISrsHttpResponseWriter()
{
}

ISrsHttpResponseReader::ISrsHttpResponseReader()
{
}

ISrsHttpResponseReader::~ISrsHttpResponseReader()
{
}

ISrsHttpHandler::ISrsHttpHandler()
{
    entry = NULL;
}

ISrsHttpHandler::~ISrsHttpHandler()
{
}

SrsHttpRedirectHandler::SrsHttpRedirectHandler(string u, int c)
{
    url = u;
    code = c;
}

SrsHttpRedirectHandler::~SrsHttpRedirectHandler()
{
}

int SrsHttpRedirectHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
    int ret = ERROR_SUCCESS;
    // TODO: FIXME: implements it.
    return ret;
}

SrsHttpNotFoundHandler::SrsHttpNotFoundHandler()
{
}

SrsHttpNotFoundHandler::~SrsHttpNotFoundHandler()
{
}

int SrsHttpNotFoundHandler::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
    return srs_go_http_error(w, SRS_CONSTS_HTTP_NotFound, SRS_CONSTS_HTTP_NotFound_str);
}

SrsHttpFileServer::SrsHttpFileServer(string root_dir)
{
    dir = root_dir;
}

SrsHttpFileServer::~SrsHttpFileServer()
{
}

int SrsHttpFileServer::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
    string upath = r->path();
    
    // add default pages.
    if (srs_string_ends_with(upath, "/")) {
        upath += SRS_HTTP_DEFAULT_PAGE;
    }
    
    string fullpath = dir + "/";
    
    // remove the virtual directory.
    srs_assert(entry);
    size_t pos = entry->pattern.find("/");
    if (upath.length() > entry->pattern.length() && pos != string::npos) {
        fullpath += upath.substr(entry->pattern.length() - pos);
    } else {
        fullpath += upath;
    }
    
    // stat current dir, if exists, return error.
    if (!srs_path_exists(fullpath)) {
        srs_warn("http miss file=%s, pattern=%s, upath=%s",
                 fullpath.c_str(), entry->pattern.c_str(), upath.c_str());
        return SrsHttpNotFoundHandler().serve_http(w, r);
    }
    srs_trace("http match file=%s, pattern=%s, upath=%s",
              fullpath.c_str(), entry->pattern.c_str(), upath.c_str());
    
    // handle file according to its extension.
    // use vod stream for .flv/.fhv
    if (srs_string_ends_with(fullpath, ".flv") || srs_string_ends_with(fullpath, ".fhv")) {
        return serve_flv_file(w, r, fullpath);
    } else if (srs_string_ends_with(fullpath, ".mp4")) {
        return serve_mp4_file(w, r, fullpath);
    }
    
    // serve common static file.
    return serve_file(w, r, fullpath);
}

int SrsHttpFileServer::serve_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath)
{
    int ret = ERROR_SUCCESS;
    
    // open the target file.
    SrsFileReader fs;
    
    if ((ret = fs.open(fullpath)) != ERROR_SUCCESS) {
        srs_warn("open file %s failed, ret=%d", fullpath.c_str(), ret);
        return ret;
    }
    
    int64_t length = fs.filesize();
    
    // unset the content length to encode in chunked encoding.
    w->header()->set_content_length(length);
    
    static std::map<std::string, std::string> _mime;
    if (_mime.empty()) {
        _mime[".ts"] = "video/MP2T";
        _mime[".flv"] = "video/x-flv";
        _mime[".m4v"] = "video/x-m4v";
        _mime[".3gpp"] = "video/3gpp";
        _mime[".3gp"] = "video/3gpp";
        _mime[".mp4"] = "video/mp4";
        _mime[".aac"] = "audio/x-aac";
        _mime[".mp3"] = "audio/mpeg";
        _mime[".m4a"] = "audio/x-m4a";
        _mime[".ogg"] = "audio/ogg";
        // @see hls-m3u8-draft-pantos-http-live-streaming-12.pdf, page 5.
        _mime[".m3u8"] = "application/vnd.apple.mpegurl"; // application/x-mpegURL
        _mime[".rss"] = "application/rss+xml";
        _mime[".json"] = "application/json";
        _mime[".swf"] = "application/x-shockwave-flash";
        _mime[".doc"] = "application/msword";
        _mime[".zip"] = "application/zip";
        _mime[".rar"] = "application/x-rar-compressed";
        _mime[".xml"] = "text/xml";
        _mime[".html"] = "text/html";
        _mime[".js"] = "text/javascript";
        _mime[".css"] = "text/css";
        _mime[".ico"] = "image/x-icon";
        _mime[".png"] = "image/png";
        _mime[".jpeg"] = "image/jpeg";
        _mime[".jpg"] = "image/jpeg";
        _mime[".gif"] = "image/gif";
    }
    
    if (true) {
        size_t pos;
        std::string ext = fullpath;
        if ((pos = ext.rfind(".")) != string::npos) {
            ext = ext.substr(pos);
        }
        
        if (_mime.find(ext) == _mime.end()) {
            w->header()->set_content_type("application/octet-stream");
        } else {
            w->header()->set_content_type(_mime[ext]);
        }
    }
    
    // write body.
    int64_t left = length;
    if ((ret = copy(w, &fs, r, (int)left)) != ERROR_SUCCESS) {
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("read file=%s size=%d failed, ret=%d", fullpath.c_str(), left, ret);
        }
        return ret;
    }
    
    return w->final_request();
}

int SrsHttpFileServer::serve_flv_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath)
{
    std::string start = r->query_get("start");
    if (start.empty()) {
        return serve_file(w, r, fullpath);
    }
    
    int offset = ::atoi(start.c_str());
    if (offset <= 0) {
        return serve_file(w, r, fullpath);
    }
    
    return serve_flv_stream(w, r, fullpath, offset);
}

int SrsHttpFileServer::serve_mp4_file(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath)
{
    // for flash to request mp4 range in query string.
    // for example, http://digitalprimates.net/dash/DashTest.html?url=http://dashdemo.edgesuite.net/digitalprimates/nexus/oops-20120802-manifest.mpd
    std::string range = r->query_get("range");
    // or, use bytes to request range,
    // for example, http://dashas.castlabs.com/demo/try.html
    if (range.empty()) {
        range = r->query_get("bytes");
    }
    
    // rollback to serve whole file.
    size_t pos = string::npos;
    if (range.empty() || (pos = range.find("-")) == string::npos) {
        return serve_file(w, r, fullpath);
    }
    
    // parse the start in query string
    int start = 0;
    if (pos > 0) {
        start = ::atoi(range.substr(0, pos).c_str());
    }
    
    // parse end in query string.
    int end = -1;
    if (pos < range.length() - 1) {
        end = ::atoi(range.substr(pos + 1).c_str());
    }
    
    // invalid param, serve as whole mp4 file.
    if (start < 0 || (end != -1 && start > end)) {
        return serve_file(w, r, fullpath);
    }
    
    return serve_mp4_stream(w, r, fullpath, start, end);
}

int SrsHttpFileServer::serve_flv_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int offset)
{
    return serve_file(w, r, fullpath);
}

int SrsHttpFileServer::serve_mp4_stream(ISrsHttpResponseWriter* w, ISrsHttpMessage* r, string fullpath, int start, int end)
{
    return serve_file(w, r, fullpath);
}

int SrsHttpFileServer::copy(ISrsHttpResponseWriter* w, SrsFileReader* fs, ISrsHttpMessage* r, int size)
{
    int ret = ERROR_SUCCESS;
    
    int left = size;
    char* buf = r->http_ts_send_buffer();
    
    while (left > 0) {
        ssize_t nread = -1;
        int max_read = srs_min(left, SRS_HTTP_TS_SEND_BUFFER_SIZE);
        if ((ret = fs->read(buf, max_read, &nread)) != ERROR_SUCCESS) {
            break;
        }
        
        left -= nread;
        if ((ret = w->write(buf, (int)nread)) != ERROR_SUCCESS) {
            break;
        }
    }
    
    return ret;
}

SrsHttpMuxEntry::SrsHttpMuxEntry()
{
    enabled = true;
    explicit_match = false;
    handler = NULL;
}

SrsHttpMuxEntry::~SrsHttpMuxEntry()
{
    srs_freep(handler);
}

ISrsHttpMatchHijacker::ISrsHttpMatchHijacker()
{
}

ISrsHttpMatchHijacker::~ISrsHttpMatchHijacker()
{
}

SrsHttpServeMux::SrsHttpServeMux()
{
}

SrsHttpServeMux::~SrsHttpServeMux()
{
    std::map<std::string, SrsHttpMuxEntry*>::iterator it;
    for (it = entries.begin(); it != entries.end(); ++it) {
        SrsHttpMuxEntry* entry = it->second;
        srs_freep(entry);
    }
    entries.clear();
    
    vhosts.clear();
    hijackers.clear();
}

int SrsHttpServeMux::initialize()
{
    int ret = ERROR_SUCCESS;
    // TODO: FIXME: implements it.
    return ret;
}

void SrsHttpServeMux::hijack(ISrsHttpMatchHijacker* h)
{
    std::vector<ISrsHttpMatchHijacker*>::iterator it = ::find(hijackers.begin(), hijackers.end(), h);
    if (it != hijackers.end()) {
        return;
    }
    hijackers.push_back(h);
}

void SrsHttpServeMux::unhijack(ISrsHttpMatchHijacker* h)
{
    std::vector<ISrsHttpMatchHijacker*>::iterator it = ::find(hijackers.begin(), hijackers.end(), h);
    if (it == hijackers.end()) {
        return;
    }
    hijackers.erase(it);
}

int SrsHttpServeMux::handle(std::string pattern, ISrsHttpHandler* handler)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(handler);
    
    if (pattern.empty()) {
        ret = ERROR_HTTP_PATTERN_EMPTY;
        srs_error("http: empty pattern. ret=%d", ret);
        return ret;
    }
    
    if (entries.find(pattern) != entries.end()) {
        SrsHttpMuxEntry* exists = entries[pattern];
        if (exists->explicit_match) {
            ret = ERROR_HTTP_PATTERN_DUPLICATED;
            srs_error("http: multiple registrations for %s. ret=%d", pattern.c_str(), ret);
            return ret;
        }
    }
    
    std::string vhost = pattern;
    if (pattern.at(0) != '/') {
        if (pattern.find("/") != string::npos) {
            vhost = pattern.substr(0, pattern.find("/"));
        }
        vhosts[vhost] = handler;
    }
    
    if (true) {
        SrsHttpMuxEntry* entry = new SrsHttpMuxEntry();
        entry->explicit_match = true;
        entry->handler = handler;
        entry->pattern = pattern;
        entry->handler->entry = entry;
        
        if (entries.find(pattern) != entries.end()) {
            SrsHttpMuxEntry* exists = entries[pattern];
            srs_freep(exists);
        }
        entries[pattern] = entry;
    }
    
    // Helpful behavior:
    // If pattern is /tree/, insert an implicit permanent redirect for /tree.
    // It can be overridden by an explicit registration.
    if (pattern != "/" && !pattern.empty() && pattern.at(pattern.length() - 1) == '/') {
        std::string rpattern = pattern.substr(0, pattern.length() - 1);
        SrsHttpMuxEntry* entry = NULL;
        
        // free the exists not explicit entry
        if (entries.find(rpattern) != entries.end()) {
            SrsHttpMuxEntry* exists = entries[rpattern];
            if (!exists->explicit_match) {
                entry = exists;
            }
        }
        
        // create implicit redirect.
        if (!entry || entry->explicit_match) {
            srs_freep(entry);
            
            entry = new SrsHttpMuxEntry();
            entry->explicit_match = false;
            entry->handler = new SrsHttpRedirectHandler(pattern, SRS_CONSTS_HTTP_MovedPermanently);
            entry->pattern = pattern;
            entry->handler->entry = entry;
            
            entries[rpattern] = entry;
        }
    }
    
    return ret;
}

int SrsHttpServeMux::serve_http(ISrsHttpResponseWriter* w, ISrsHttpMessage* r)
{
    int ret = ERROR_SUCCESS;
    
    ISrsHttpHandler* h = NULL;
    if ((ret = find_handler(r, &h)) != ERROR_SUCCESS) {
        srs_error("find handler failed. ret=%d", ret);
        return ret;
    }
    
    srs_assert(h);
    if ((ret = h->serve_http(w, r)) != ERROR_SUCCESS) {
        if (!srs_is_client_gracefully_close(ret)) {
            srs_error("handler serve http failed. ret=%d", ret);
        }
        return ret;
    }
    
    return ret;
}

int SrsHttpServeMux::find_handler(ISrsHttpMessage* r, ISrsHttpHandler** ph)
{
    int ret = ERROR_SUCCESS;
    
    // TODO: FIXME: support the path . and ..
    if (r->url().find("..") != std::string::npos) {
        ret = ERROR_HTTP_URL_NOT_CLEAN;
        srs_error("htt url not canonical, url=%s. ret=%d", r->url().c_str(), ret);
        return ret;
    }
    
    if ((ret = match(r, ph)) != ERROR_SUCCESS) {
        srs_error("http match handler failed. ret=%d", ret);
        return ret;
    }
    
    // always hijack.
    if (!hijackers.empty()) {
        // notice all hijacker the match failed.
        std::vector<ISrsHttpMatchHijacker*>::iterator it;
        for (it = hijackers.begin(); it != hijackers.end(); ++it) {
            ISrsHttpMatchHijacker* hijacker = *it;
            if ((ret = hijacker->hijack(r, ph)) != ERROR_SUCCESS) {
                srs_error("hijacker match failed. ret=%d", ret);
                return ret;
            }
        }
    }
    
    if (*ph == NULL) {
        // TODO: FIXME: memory leak.
        *ph = new SrsHttpNotFoundHandler();
    }
    
    return ret;
}

int SrsHttpServeMux::match(ISrsHttpMessage* r, ISrsHttpHandler** ph)
{
    int ret = ERROR_SUCCESS;
    
    std::string path = r->path();
    
    // Host-specific pattern takes precedence over generic ones
    if (!vhosts.empty() && vhosts.find(r->host()) != vhosts.end()) {
        path = r->host() + path;
    }
    
    int nb_matched = 0;
    ISrsHttpHandler* h = NULL;
    
    std::map<std::string, SrsHttpMuxEntry*>::iterator it;
    for (it = entries.begin(); it != entries.end(); ++it) {
        std::string pattern = it->first;
        SrsHttpMuxEntry* entry = it->second;
        
        if (!entry->enabled) {
            continue;
        }
        
        if (!path_match(pattern, path)) {
            continue;
        }
        
        if (!h || (int)pattern.length() > nb_matched) {
            nb_matched = (int)pattern.length();
            h = entry->handler;
        }
    }
    
    *ph = h;
    
    return ret;
}

bool SrsHttpServeMux::path_match(string pattern, string path)
{
    if (pattern.empty()) {
        return false;
    }
    
    int n = (int)pattern.length();
    
    // not endswith '/', exactly match.
    if (pattern.at(n - 1) != '/') {
        return pattern == path;
    }
    
    // endswith '/', match any,
    // for example, '/api/' match '/api/[N]'
    if ((int)path.length() >= n) {
        if (memcmp(pattern.data(), path.data(), n) == 0) {
            return true;
        }
    }
    
    return false;
}

ISrsHttpMessage::ISrsHttpMessage()
{
    _http_ts_send_buffer = new char[SRS_HTTP_TS_SEND_BUFFER_SIZE];
}

ISrsHttpMessage::~ISrsHttpMessage()
{
    srs_freep(_http_ts_send_buffer);
}

char* ISrsHttpMessage::http_ts_send_buffer()
{
    return _http_ts_send_buffer;
}
// following is generated by src/protocol/srs_protocol_kbps.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 <srs_protocol_kbps.hpp>

//#include <srs_kernel_utility.hpp>

SrsKbpsSample::SrsKbpsSample()
{
    bytes = time = 0;
    kbps = 0;
}

SrsKbpsSlice::SrsKbpsSlice()
{
    io.in = NULL;
    io.out = NULL;
    last_bytes = io_bytes_base = starttime = bytes = delta_bytes = 0;
}

SrsKbpsSlice::~SrsKbpsSlice()
{
}

int64_t SrsKbpsSlice::get_total_bytes()
{
    return bytes + last_bytes - io_bytes_base;
}

void SrsKbpsSlice::sample()
{
    int64_t now = srs_get_system_time_ms();
    int64_t total_bytes = get_total_bytes();
    
    if (sample_30s.time <= 0) {
        sample_30s.kbps = 0;
        sample_30s.time = now;
        sample_30s.bytes = total_bytes;
    }
    if (sample_1m.time <= 0) {
        sample_1m.kbps = 0;
        sample_1m.time = now;
        sample_1m.bytes = total_bytes;
    }
    if (sample_5m.time <= 0) {
        sample_5m.kbps = 0;
        sample_5m.time = now;
        sample_5m.bytes = total_bytes;
    }
    if (sample_60m.time <= 0) {
        sample_60m.kbps = 0;
        sample_60m.time = now;
        sample_60m.bytes = total_bytes;
    }
    
    if (now - sample_30s.time > 30 * 1000) {
        sample_30s.kbps = (int)((total_bytes - sample_30s.bytes) * 8 / (now - sample_30s.time));
        sample_30s.time = now;
        sample_30s.bytes = total_bytes;
    }
    if (now - sample_1m.time > 60 * 1000) {
        sample_1m.kbps = (int)((total_bytes - sample_1m.bytes) * 8 / (now - sample_1m.time));
        sample_1m.time = now;
        sample_1m.bytes = total_bytes;
    }
    if (now - sample_5m.time > 300 * 1000) {
        sample_5m.kbps = (int)((total_bytes - sample_5m.bytes) * 8 / (now - sample_5m.time));
        sample_5m.time = now;
        sample_5m.bytes = total_bytes;
    }
    if (now - sample_60m.time > 3600 * 1000) {
        sample_60m.kbps = (int)((total_bytes - sample_60m.bytes) * 8 / (now - sample_60m.time));
        sample_60m.time = now;
        sample_60m.bytes = total_bytes;
    }
}

IKbpsDelta::IKbpsDelta()
{
}

IKbpsDelta::~IKbpsDelta()
{
}

SrsKbps::SrsKbps()
{
}

SrsKbps::~SrsKbps()
{
}

void SrsKbps::set_io(ISrsProtocolStatistic* in, ISrsProtocolStatistic* out)
{
    // set input stream
    // now, set start time.
    if (is.starttime == 0) {
        is.starttime = srs_get_system_time_ms();
    }
    // save the old in bytes.
    if (is.io.in) {
        is.bytes += is.last_bytes - is.io_bytes_base;
    }
    // use new io.
    is.io.in = in;
    is.last_bytes = is.io_bytes_base = 0;
    if (in) {
        is.last_bytes = is.io_bytes_base = in->get_recv_bytes();
    }
    // resample
    is.sample();
    
    // set output stream
    // now, set start time.
    if (os.starttime == 0) {
        os.starttime = srs_get_system_time_ms();
    }
    // save the old in bytes.
    if (os.io.out) {
        os.bytes += os.last_bytes - os.io_bytes_base;
    }
    // use new io.
    os.io.out = out;
    os.last_bytes = os.io_bytes_base = 0;
    if (out) {
        os.last_bytes = os.io_bytes_base = out->get_send_bytes();
    }
    // resample
    os.sample();
}

int SrsKbps::get_send_kbps()
{
    int64_t duration = srs_get_system_time_ms() - is.starttime;
    if (duration <= 0) {
        return 0;
    }
    int64_t bytes = get_send_bytes();
    return (int)(bytes * 8 / duration);
}

int SrsKbps::get_recv_kbps()
{
    int64_t duration = srs_get_system_time_ms() - os.starttime;
    if (duration <= 0) {
        return 0;
    }
    int64_t bytes = get_recv_bytes();
    return (int)(bytes * 8 / duration);
}

int SrsKbps::get_send_kbps_30s()
{
    return os.sample_30s.kbps;
}

int SrsKbps::get_recv_kbps_30s()
{
    return is.sample_30s.kbps;
}

int SrsKbps::get_send_kbps_5m()
{
    return os.sample_5m.kbps;
}

int SrsKbps::get_recv_kbps_5m()
{
    return is.sample_5m.kbps;
}

int64_t SrsKbps::get_send_bytes()
{
    return os.get_total_bytes();
}

int64_t SrsKbps::get_recv_bytes()
{
    return is.get_total_bytes();
}

void SrsKbps::resample()
{
    sample();
}

int64_t SrsKbps::get_send_bytes_delta()
{
    int64_t delta = os.get_total_bytes() - os.delta_bytes;
    return delta;
}

int64_t SrsKbps::get_recv_bytes_delta()
{
    int64_t delta = is.get_total_bytes() - is.delta_bytes;
    return delta;
}

void SrsKbps::cleanup()
{
    os.delta_bytes = os.get_total_bytes();
    is.delta_bytes = is.get_total_bytes();
}

void SrsKbps::add_delta(IKbpsDelta* delta)
{
    srs_assert(delta);
    
    // update the total bytes
    is.last_bytes += delta->get_recv_bytes_delta();
    os.last_bytes += delta->get_send_bytes_delta();
    
    // we donot sample, please use sample() to do resample.
}

void SrsKbps::sample()
{
    // update the total bytes
    if (os.io.out) {
        os.last_bytes = os.io.out->get_send_bytes();
    }
    
    if (is.io.in) {
        is.last_bytes = is.io.in->get_recv_bytes();
    }
    
    // resample
    is.sample();
    os.sample();
}

// following is generated by src/libs/srs_librtmp.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 <srs_librtmp.hpp>

#include <stdlib.h>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <sys/time.h>
#endif

#include <string>
#include <sstream>
using namespace std;

//#include <srs_kernel_error.hpp>
//#include <srs_rtmp_sdk.hpp>
//#include <srs_lib_simple_socket.hpp>
//#include <srs_rtmp_utility.hpp>
//#include <srs_core_autofree.hpp>
//#include <srs_rtmp_stack.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_kernel_stream.hpp>
//#include <srs_rtmp_amf0.hpp>
//#include <srs_kernel_flv.hpp>
//#include <srs_kernel_codec.hpp>
//#include <srs_kernel_file.hpp>
//#include <srs_lib_bandwidth.hpp>
//#include <srs_raw_avc.hpp>

// kernel module.
ISrsLog* _srs_log = new ISrsLog();
ISrsThreadContext* _srs_context = new ISrsThreadContext();

/**
* export runtime context.
*/
struct Context
{
    std::string url;
    std::string tcUrl;
    std::string host;
    std::string ip;
    std::string port;
    std::string vhost;
    std::string app;
    std::string stream;
    std::string param;

    // extra request object for connect to server, NULL to ignore.
    SrsRequest* req;
    
    // the message received cache,
    // for example, when got aggregate message,
    // the context will parse to videos/audios,
    // and return one by one.
    std::vector<SrsCommonMessage*> msgs;
    
    SrsRtmpClient* rtmp;
    SimpleSocketStream* skt;
    int stream_id;
    
    // the remux raw codec.
    SrsRawH264Stream avc_raw;
    SrsRawAacStream aac_raw;

    // for h264 raw stream, 
    // @see: https://github.com/simple-rtmp-server/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
    bool h264_sps_pps_sent;
    // only send the ssp and pps when both changed.
    // @see https://github.com/simple-rtmp-server/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
    SrsStream aac_raw_stream;
    // the aac sequence header.
    std::string aac_specific_config;
    
    Context() {
        rtmp = NULL;
        skt = NULL;
        req = NULL;
        stream_id = 0;
        h264_sps_pps_sent = false;
        h264_sps_changed = false;
        h264_pps_changed = false;
    }
    virtual ~Context() {
        srs_freep(req);
        srs_freep(rtmp);
        srs_freep(skt);
        
        std::vector<SrsCommonMessage*>::iterator it;
        for (it = msgs.begin(); it != msgs.end(); ++it) {
            SrsCommonMessage* msg = *it;
            srs_freep(msg);
        }
        msgs.clear();
    }
};

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifdef _WIN32
    int gettimeofday(struct timeval* tv, struct timezone* tz)
    {  
        time_t clock;
        struct tm tm;
        SYSTEMTIME win_time;
    
        GetLocalTime(&win_time);
    
        tm.tm_year = win_time.wYear - 1900;
        tm.tm_mon = win_time.wMonth - 1;
        tm.tm_mday = win_time.wDay;
        tm.tm_hour = win_time.wHour;
        tm.tm_min = win_time.wMinute;
        tm.tm_sec = win_time.wSecond;
        tm.tm_isdst = -1;
    
        clock = mktime(&tm);
    
        tv->tv_sec = (long)clock;
        tv->tv_usec = win_time.wMilliseconds * 1000;
    
        return 0;
    }
    
    int socket_setup()
    {
        WORD wVersionRequested;
        WSADATA wsaData;
        int err;
    
        /* Use the MAKEWORD(lowbyte, highbyte) macro declared in Windef.h */
        wVersionRequested = MAKEWORD(2, 2);
    
        err = WSAStartup(wVersionRequested, &wsaData);
        if (err != 0) {
            /* Tell the user that we could not find a usable */
            /* Winsock DLL.                                  */
            //printf("WSAStartup failed with error: %d\n", err);
            return -1;
        }
        return 0;
    }
    
    int socket_cleanup()
    {
        WSACleanup();
        return 0;
    }
    
    pid_t getpid(void)
    {
        return (pid_t)GetCurrentProcessId();
    }
    
    int usleep(useconds_t usec)
    {
        Sleep((DWORD)(usec / 1000));
        return 0;
    }
    
    ssize_t writev(int fd, const struct iovec *iov, int iovcnt)
    {
        ssize_t nwrite = 0;
        for (int i = 0; i < iovcnt; i++) {
            const struct iovec* current = iov + i;
    
            int nsent = ::send(fd, (char*)current->iov_base, current->iov_len, 0);
            if (nsent < 0) {
                return nsent;
            }
    
            nwrite += nsent;
            if (nsent == 0) {
                return nwrite;
            }
        }
        return nwrite;
    }
    
    ////////////////////////   strlcpy.c (modified) //////////////////////////
    
    /*    $OpenBSD: strlcpy.c,v 1.11 2006/05/05 15:27:38 millert Exp $    */
    
    /*-
     * Copyright (c) 1998 Todd C. Miller <Todd.Miller@courtesan.com>
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    //#include <sys/cdefs.h> // ****
    //#include <cstddef> // ****
    // __FBSDID("$FreeBSD: stable/9/sys/libkern/strlcpy.c 243811 2012-12-03 18:08:44Z delphij $"); // ****
    
    // #include <sys/types.h> // ****
    // #include <sys/libkern.h> // ****
    
    /*
     * Copy src to string dst of size siz.  At most siz-1 characters
     * will be copied.  Always NUL terminates (unless siz == 0).
     * Returns strlen(src); if retval >= siz, truncation occurred.
     */
    
    //#define __restrict // ****
    
    std::size_t strlcpy(char * __restrict dst, const char * __restrict src, size_t siz)
    {
        char *d = dst;
        const char *s = src;
        size_t n = siz;
    
        /* Copy as many bytes as will fit */
        if (n != 0) {
            while (--n != 0) {
                if ((*d++ = *s++) == '\0')
                    break;
            }
        }
    
        /* Not enough room in dst, add NUL and traverse rest of src */
        if (n == 0) {
            if (siz != 0)
                *d = '\0';        /* NUL-terminate dst */
            while (*s++)
                ;
        }
    
        return(s - src - 1);    /* count does not include NUL */
    }
    
    // http://www.cplusplus.com/forum/general/141779/////////////////////////   inet_ntop.c (modified) //////////////////////////
    /*
     * Copyright (c) 2004 by Internet Systems Consortium, Inc. ("ISC")
     * Copyright (c) 1996-1999 by Internet Software Consortium.
     *
     * Permission to use, copy, modify, and distribute this software for any
     * purpose with or without fee is hereby granted, provided that the above
     * copyright notice and this permission notice appear in all copies.
     *
     * THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES
     * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     * MERCHANTABILITY AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR
     * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT
     * OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     */
    
    // #if defined(LIBC_SCCS) && !defined(lint) // ****
    //static const char rcsid[] = "$Id: inet_ntop.c,v 1.3.18.2 2005/11/03 23:02:22 marka Exp $";
    // #endif /* LIBC_SCCS and not lint */ // ****
    // #include <sys/cdefs.h> // ****
    // __FBSDID("$FreeBSD: stable/9/sys/libkern/inet_ntop.c 213103 2010-09-24 15:01:45Z attilio $"); // ****
    
    //#define _WIN32_WINNT _WIN32_WINNT_WIN8 // ****
    //#include <Ws2tcpip.h> // ****
    #pragma comment(lib, "Ws2_32.lib") // ****
    //#include <cstdio> // ****
    
    // #include <sys/param.h> // ****
    // #include <sys/socket.h> // ****
    // #include <sys/systm.h> // ****
    
    // #include <netinet/in.h> // ****
    
    /*%
     * WARNING: Don't even consider trying to compile this on a system where
     * sizeof(int) < 4.  sizeof(int) > 4 is fine; all the world's not a VAX.
     */
    
    static char *inet_ntop4(const u_char *src, char *dst, socklen_t size);
    static char *inet_ntop6(const u_char *src, char *dst, socklen_t size);
    
    /* char *
     * inet_ntop(af, src, dst, size)
     *    convert a network format address to presentation format.
     * return:
     *    pointer to presentation format address (`dst'), or NULL (see errno).
     * author:
     *    Paul Vixie, 1996.
     */
    const char* inet_ntop(int af, const void *src, char *dst, socklen_t size)
    {
        switch (af) {
        case AF_INET:
            return (inet_ntop4( (unsigned char*)src, (char*)dst, size)); // ****
    #ifdef AF_INET6
        #error "IPv6 not supported"
        //case AF_INET6:
        //    return (char*)(inet_ntop6( (unsigned char*)src, (char*)dst, size)); // ****
    #endif
        default:
            // return (NULL); // ****
            return 0 ; // ****
        }
        /* NOTREACHED */
    }
    
    /* const char *
     * inet_ntop4(src, dst, size)
     *    format an IPv4 address
     * return:
     *    `dst' (as a const)
     * notes:
     *    (1) uses no statics
     *    (2) takes a u_char* not an in_addr as input
     * author:
     *    Paul Vixie, 1996.
     */
    static char * inet_ntop4(const u_char *src, char *dst, socklen_t size)
    {
        static const char fmt[128] = "%u.%u.%u.%u";
        char tmp[sizeof "255.255.255.255"];
        int l;
    
        l = snprintf(tmp, sizeof(tmp), fmt, src[0], src[1], src[2], src[3]); // ****
        if (l <= 0 || (socklen_t) l >= size) {
            return (NULL);
        }
        strlcpy(dst, tmp, size);
        return (dst);
    }
    
    /* const char *
     * inet_ntop6(src, dst, size)
     *    convert IPv6 binary address into presentation (printable) format
     * author:
     *    Paul Vixie, 1996.
     */
    static char * inet_ntop6(const u_char *src, char *dst, socklen_t size)
    {
        /*
         * Note that int32_t and int16_t need only be "at least" large enough
         * to contain a value of the specified size.  On some systems, like
         * Crays, there is no such thing as an integer variable with 16 bits.
         * Keep this in mind if you think this function should have been coded
         * to use pointer overlays.  All the world's not a VAX.
         */
        char tmp[sizeof "ffff:ffff:ffff:ffff:ffff:ffff:255.255.255.255"], *tp;
        struct { int base, len; } best, cur;
    #define NS_IN6ADDRSZ 16
    #define NS_INT16SZ 2
        u_int words[NS_IN6ADDRSZ / NS_INT16SZ];
        int i;
    
        /*
         * Preprocess:
         *    Copy the input (bytewise) array into a wordwise array.
         *    Find the longest run of 0x00's in src[] for :: shorthanding.
         */
        memset(words, '\0', sizeof words);
        for (i = 0; i < NS_IN6ADDRSZ; i++)
            words[i / 2] |= (src[i] << ((1 - (i % 2)) << 3));
        best.base = -1;
        best.len = 0;
        cur.base = -1;
        cur.len = 0;
        for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
            if (words[i] == 0) {
                if (cur.base == -1)
                    cur.base = i, cur.len = 1;
                else
                    cur.len++;
            } else {
                if (cur.base != -1) {
                    if (best.base == -1 || cur.len > best.len)
                        best = cur;
                    cur.base = -1;
                }
            }
        }
        if (cur.base != -1) {
            if (best.base == -1 || cur.len > best.len)
                best = cur;
        }
        if (best.base != -1 && best.len < 2)
            best.base = -1;
    
        /*
         * Format the result.
         */
        tp = tmp;
        for (i = 0; i < (NS_IN6ADDRSZ / NS_INT16SZ); i++) {
            /* Are we inside the best run of 0x00's? */
            if (best.base != -1 && i >= best.base &&
                i < (best.base + best.len)) {
                if (i == best.base)
                    *tp++ = ':';
                continue;
            }
            /* Are we following an initial run of 0x00s or any real hex? */
            if (i != 0)
                *tp++ = ':';
            /* Is this address an encapsulated IPv4? */
            if (i == 6 && best.base == 0 && (best.len == 6 ||
                (best.len == 7 && words[7] != 0x0001) ||
                (best.len == 5 && words[5] == 0xffff))) {
                if (!inet_ntop4(src+12, tp, sizeof tmp - (tp - tmp)))
                    return (NULL);
                tp += strlen(tp);
                break;
            }
            tp += std::sprintf(tp, "%x", words[i]); // ****
        }
        /* Was it a trailing run of 0x00's? */
        if (best.base != -1 && (best.base + best.len) == 
            (NS_IN6ADDRSZ / NS_INT16SZ))
            *tp++ = ':';
        *tp++ = '\0';
    
        /*
         * Check for overflow, copy, and we're done.
         */
        if ((socklen_t)(tp - tmp) > size) {
            return (NULL);
        }
        strcpy(dst, tmp);
        return (dst);
    }
#endif

int srs_librtmp_context_parse_uri(Context* context) 
{
    int ret = ERROR_SUCCESS;
    
    // parse uri
    size_t pos = string::npos;
    string uri = context->url;
    // tcUrl, stream
    if ((pos = uri.rfind("/")) != string::npos) {
        context->stream = uri.substr(pos + 1);
        context->tcUrl = uri = uri.substr(0, pos);
    }
    
    std::string schema;
    srs_discovery_tc_url(context->tcUrl, 
        schema, context->host, context->vhost, context->app, context->port,
        context->param);
    
    return ret;
}

int srs_librtmp_context_resolve_host(Context* context) 
{
    int ret = ERROR_SUCCESS;
    
    // create socket
    srs_freep(context->skt);
    context->skt = new SimpleSocketStream();
    
    if ((ret = context->skt->create_socket()) != ERROR_SUCCESS) {
        return ret;
    }
    
    // connect to server:port
    context->ip = srs_dns_resolve(context->host);
    if (context->ip.empty()) {
        return -1;
    }
    
    return ret;
}

int srs_librtmp_context_connect(Context* context) 
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(context->skt);
    
    std::string ip = context->ip;
    int port = ::atoi(context->port.c_str());
    
    if ((ret = context->skt->connect(ip.c_str(), port)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

#ifdef __cplusplus
extern "C"{
#endif

int srs_version_major()
{
    return VERSION_MAJOR;
}

int srs_version_minor()
{
    return VERSION_MINOR;
}

int srs_version_revision()
{
    return VERSION_REVISION;
}

srs_rtmp_t srs_rtmp_create(const char* url)
{
    Context* context = new Context();
    context->url = url;
    return context;
}

srs_rtmp_t srs_rtmp_create2(const char* url)
{
    Context* context = new Context();
    
    // use url as tcUrl.
    context->url = url;
    // auto append stream.
    context->url += "/livestream";
    
    return context;
}

void srs_rtmp_destroy(srs_rtmp_t rtmp)
{
    if (!rtmp) {
        return;
    }
    
    Context* context = (Context*)rtmp;
    
    srs_freep(context);
}

int srs_rtmp_handshake(srs_rtmp_t rtmp)
{
    int ret = ERROR_SUCCESS;
    
    if ((ret = srs_rtmp_dns_resolve(rtmp)) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = srs_rtmp_connect_server(rtmp)) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = srs_rtmp_do_simple_handshake(rtmp)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int srs_rtmp_dns_resolve(srs_rtmp_t rtmp)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    // parse uri
    if ((ret = srs_librtmp_context_parse_uri(context)) != ERROR_SUCCESS) {
        return ret;
    }
    // resolve host
    if ((ret = srs_librtmp_context_resolve_host(context)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int srs_rtmp_connect_server(srs_rtmp_t rtmp)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    if ((ret = srs_librtmp_context_connect(context)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int srs_rtmp_do_complex_handshake(srs_rtmp_t rtmp)
{
#ifndef SRS_AUTO_SSL
    // complex handshake requires ssl
    return ERROR_RTMP_HS_SSL_REQUIRE;
#endif

    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    srs_assert(context->skt != NULL);
    
    // simple handshake
    srs_freep(context->rtmp);
    context->rtmp = new SrsRtmpClient(context->skt);
    
    if ((ret = context->rtmp->complex_handshake()) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int srs_rtmp_set_connect_args(srs_rtmp_t rtmp, 
    const char* tcUrl, const char* swfUrl, const char* pageUrl, srs_amf0_t args
) {
    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    srs_freep(context->req);
    context->req = new SrsRequest();
    
    if (args) {
        context->req->args = (SrsAmf0Object*)args;
    }
    if (tcUrl) {
        context->req->tcUrl = tcUrl;
    }
    if (swfUrl) {
        context->req->swfUrl = swfUrl;
    }
    if (pageUrl) {
        context->req->pageUrl = pageUrl;
    }
    
    return ret;
}

int srs_rtmp_do_simple_handshake(srs_rtmp_t rtmp)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    srs_assert(context->skt != NULL);
    
    // simple handshake
    srs_freep(context->rtmp);
    context->rtmp = new SrsRtmpClient(context->skt);
    
    if ((ret = context->rtmp->simple_handshake()) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int srs_rtmp_connect_app(srs_rtmp_t rtmp)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    string tcUrl = srs_generate_tc_url(
        context->ip, context->vhost, context->app, context->port,
        context->param
    );
    
    if ((ret = context->rtmp->connect_app(
        context->app, tcUrl, context->req, true)) != ERROR_SUCCESS) 
    {
        return ret;
    }
    
    return ret;
}

int srs_rtmp_connect_app2(srs_rtmp_t rtmp,
    char srs_server_ip[128],char srs_server[128], 
    char srs_primary[128], char srs_authors[128], 
    char srs_version[32], int* srs_id, int* srs_pid
) {
    srs_server_ip[0] = 0;
    srs_server[0] = 0;
    srs_primary[0] = 0;
    srs_authors[0] = 0;
    srs_version[0] = 0;
    *srs_id = 0;
    *srs_pid = 0;

    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    string tcUrl = srs_generate_tc_url(
        context->ip, context->vhost, context->app, context->port,
        context->param
    );
    
    std::string sip, sserver, sprimary, sauthors, sversion;
    
    if ((ret = context->rtmp->connect_app2(context->app, tcUrl, NULL, true,
        sip, sserver, sprimary, sauthors, sversion, *srs_id, *srs_pid)) != ERROR_SUCCESS) {
        return ret;
    }
    
    snprintf(srs_server_ip, 128, "%s", sip.c_str());
    snprintf(srs_server, 128, "%s", sserver.c_str());
    snprintf(srs_primary, 128, "%s", sprimary.c_str());
    snprintf(srs_authors, 128, "%s", sauthors.c_str());
    snprintf(srs_version, 32, "%s", sversion.c_str());
    
    return ret;
}

int srs_rtmp_play_stream(srs_rtmp_t rtmp)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    if ((ret = context->rtmp->create_stream(context->stream_id)) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = context->rtmp->play(context->stream, context->stream_id)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int srs_rtmp_publish_stream(srs_rtmp_t rtmp)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    if ((ret = context->rtmp->fmle_publish(context->stream, context->stream_id)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int srs_rtmp_bandwidth_check(srs_rtmp_t rtmp, 
    int64_t* start_time, int64_t* end_time, 
    int* play_kbps, int* publish_kbps,
    int* play_bytes, int* publish_bytes,
    int* play_duration, int* publish_duration
) {
    *start_time = 0;
    *end_time = 0;
    *play_kbps = 0;
    *publish_kbps = 0;
    *play_bytes = 0;
    *publish_bytes = 0;
    *play_duration = 0;
    *publish_duration = 0;
    
    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    SrsBandwidthClient client;

    if ((ret = client.initialize(context->rtmp)) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = client.bandwidth_check(
        start_time, end_time, play_kbps, publish_kbps,
        play_bytes, publish_bytes, play_duration, publish_duration)) != ERROR_SUCCESS
    ) {
        return ret;
    }
    
    return ret;
}


int srs_rtmp_on_aggregate(Context* context, SrsCommonMessage* msg)
{
    int ret = ERROR_SUCCESS;
    
    SrsStream aggregate_stream;
    SrsStream* stream = &aggregate_stream;
    if ((ret = stream->initialize(msg->payload, msg->size)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // the aggregate message always use abs time.
    int delta = -1;
    
    while (!stream->empty()) {
        if (!stream->require(1)) {
            ret = ERROR_RTMP_AGGREGATE;
            srs_error("invalid aggregate message type. ret=%d", ret);
            return ret;
        }
        int8_t type = stream->read_1bytes();
        
        if (!stream->require(3)) {
            ret = ERROR_RTMP_AGGREGATE;
            srs_error("invalid aggregate message size. ret=%d", ret);
            return ret;
        }
        int32_t data_size = stream->read_3bytes();
        
        if (data_size < 0) {
            ret = ERROR_RTMP_AGGREGATE;
            srs_error("invalid aggregate message size(negative). ret=%d", ret);
            return ret;
        }
        
        if (!stream->require(3)) {
            ret = ERROR_RTMP_AGGREGATE;
            srs_error("invalid aggregate message time. ret=%d", ret);
            return ret;
        }
        int32_t timestamp = stream->read_3bytes();
        
        if (!stream->require(1)) {
            ret = ERROR_RTMP_AGGREGATE;
            srs_error("invalid aggregate message time(high). ret=%d", ret);
            return ret;
        }
        int32_t time_h = stream->read_1bytes();
        
        timestamp |= time_h<<24;
        timestamp &= 0x7FFFFFFF;
        
        // adjust abs timestamp in aggregate msg.
        if (delta < 0) {
            delta = (int)msg->header.timestamp - (int)timestamp;
        }
        timestamp += delta;
        
        if (!stream->require(3)) {
            ret = ERROR_RTMP_AGGREGATE;
            srs_error("invalid aggregate message stream_id. ret=%d", ret);
            return ret;
        }
        int32_t stream_id = stream->read_3bytes();
        
        if (data_size > 0 && !stream->require(data_size)) {
            ret = ERROR_RTMP_AGGREGATE;
            srs_error("invalid aggregate message data. ret=%d", ret);
            return ret;
        }
        
        // to common message.
        SrsCommonMessage o;
        
        o.header.message_type = type;
        o.header.payload_length = data_size;
        o.header.timestamp_delta = timestamp;
        o.header.timestamp = timestamp;
        o.header.stream_id = stream_id;
        o.header.perfer_cid = msg->header.perfer_cid;

        if (data_size > 0) {
            o.size = data_size;
            o.payload = new char[o.size];
            stream->read_bytes(o.payload, o.size);
        }
        
        if (!stream->require(4)) {
            ret = ERROR_RTMP_AGGREGATE;
            srs_error("invalid aggregate message previous tag size. ret=%d", ret);
            return ret;
        }
        stream->read_4bytes();

        // process parsed message
        SrsCommonMessage* parsed_msg = new SrsCommonMessage();
        parsed_msg->header = o.header;
        parsed_msg->payload = o.payload;
        parsed_msg->size = o.size;
        o.payload = NULL;
        context->msgs.push_back(parsed_msg);
    }
    
    return ret;
}

int srs_rtmp_go_packet(Context* context, SrsCommonMessage* msg, 
    char* type, u_int32_t* timestamp, char** data, int* size,
    bool* got_msg
) {
    int ret = ERROR_SUCCESS;

    // generally we got a message.
    *got_msg = true;
    
    if (msg->header.is_audio()) {
        *type = SRS_RTMP_TYPE_AUDIO;
        *timestamp = (u_int32_t)msg->header.timestamp;
        *data = (char*)msg->payload;
        *size = (int)msg->size;
        // detach bytes from packet.
        msg->payload = NULL;
    } else if (msg->header.is_video()) {
        *type = SRS_RTMP_TYPE_VIDEO;
        *timestamp = (u_int32_t)msg->header.timestamp;
        *data = (char*)msg->payload;
        *size = (int)msg->size;
        // detach bytes from packet.
        msg->payload = NULL;
    } else if (msg->header.is_amf0_data() || msg->header.is_amf3_data()) {
        *type = SRS_RTMP_TYPE_SCRIPT;
        *data = (char*)msg->payload;
        *size = (int)msg->size;
        // detach bytes from packet.
        msg->payload = NULL;
    } else if (msg->header.is_aggregate()) {
        if ((ret = srs_rtmp_on_aggregate(context, msg)) != ERROR_SUCCESS) {
            return ret;
        }
        *got_msg = false;
    } else {
        *type = msg->header.message_type;
        *data = (char*)msg->payload;
        *size = (int)msg->size;
        // detach bytes from packet.
        msg->payload = NULL;
    }
    
    return ret;
}

int srs_rtmp_read_packet(srs_rtmp_t rtmp, char* type, u_int32_t* timestamp, char** data, int* size)
{
    *type = 0;
    *timestamp = 0;
    *data = NULL;
    *size = 0;
    
    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    for (;;) {
        SrsCommonMessage* msg = NULL;
        
        // read from cache first.
        if (!context->msgs.empty()) {
            std::vector<SrsCommonMessage*>::iterator it = context->msgs.begin();
            msg = *it;
            context->msgs.erase(it);
        }
        
        // read from protocol sdk.
        if (!msg && (ret = context->rtmp->recv_message(&msg)) != ERROR_SUCCESS) {
            return ret;
        }
        
        // no msg, try again.
        if (!msg) {
            continue;
        }
        
        SrsAutoFree(SrsCommonMessage, msg);
        
        // process the got packet, if nothing, try again.
        bool got_msg;
        if ((ret = srs_rtmp_go_packet(context, msg, type, timestamp, data, size, &got_msg)) != ERROR_SUCCESS) {
            return ret;
        }
        
        // got expected message.
        if (got_msg) {
            break;
        }
    }
    
    return ret;
}

int srs_rtmp_write_packet(srs_rtmp_t rtmp, char type, u_int32_t timestamp, char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    SrsSharedPtrMessage* msg = NULL;

    if ((ret = srs_rtmp_create_msg(type, timestamp, data, size, context->stream_id, &msg)) != ERROR_SUCCESS) {
        return ret;
    }

    srs_assert(msg);

    // send out encoded msg.
    if ((ret = context->rtmp->send_and_free_message(msg, context->stream_id)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

srs_bool srs_rtmp_is_onMetaData(char type, char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    if (type != SRS_RTMP_TYPE_SCRIPT) {
        return false;
    }
    
    SrsStream stream;
    if ((ret = stream.initialize(data, size)) != ERROR_SUCCESS) {
        return false;
    }
    
    std::string name;
    if ((ret = srs_amf0_read_string(&stream, name)) != ERROR_SUCCESS) {
        return false;
    }
    
    if (name == SRS_CONSTS_RTMP_ON_METADATA) {
        return true;
    }
    
    if (name == SRS_CONSTS_RTMP_SET_DATAFRAME) {
        return true;
    }
    
    return false;
}

/**
* directly write a audio frame.
*/
int srs_write_audio_raw_frame(Context* context,
    char* frame, int frame_size, SrsRawAacStreamCodec* codec, u_int32_t timestamp
) {
    int ret = ERROR_SUCCESS;

    char* data = NULL;
    int size = 0;
    if ((ret = context->aac_raw.mux_aac2flv(frame, frame_size, codec, timestamp, &data, &size)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_AUDIO, timestamp, data, size);
}

/**
* write aac frame in adts.
*/
int srs_write_aac_adts_frame(Context* context, 
    SrsRawAacStreamCodec* codec, char* frame, int frame_size, u_int32_t timestamp
) {
    int ret = ERROR_SUCCESS;
    
    // send out aac sequence header if not sent.
    if (context->aac_specific_config.empty()) {
        std::string sh;
        if ((ret = context->aac_raw.mux_sequence_header(codec, sh)) != ERROR_SUCCESS) {
            return ret;
        }
        context->aac_specific_config = sh;

        codec->aac_packet_type = 0;

        if ((ret = srs_write_audio_raw_frame(context, (char*)sh.data(), (int)sh.length(), codec, timestamp)) != ERROR_SUCCESS) {
            return ret;
        }
    }
    
    codec->aac_packet_type = 1;
    return srs_write_audio_raw_frame(context, frame, frame_size, codec, timestamp);
}

/**
* write aac frames in adts.
*/
int srs_write_aac_adts_frames(Context* context,
    char sound_format, char sound_rate, char sound_size, char sound_type,
    char* frames, int frames_size, u_int32_t timestamp
) {
    int ret = ERROR_SUCCESS;
    
    SrsStream* stream = &context->aac_raw_stream;
    if ((ret = stream->initialize(frames, frames_size)) != ERROR_SUCCESS) {
        return ret;
    }
    
    while (!stream->empty()) {
        char* frame = NULL;
        int frame_size = 0;
        SrsRawAacStreamCodec codec;
        if ((ret = context->aac_raw.adts_demux(stream, &frame, &frame_size, codec)) != ERROR_SUCCESS) {
            return ret;
        }

        // override by user specified.
        codec.sound_format = sound_format;
        codec.sound_rate = sound_rate;
        codec.sound_size = sound_size;
        codec.sound_type = sound_type;

        if ((ret = srs_write_aac_adts_frame(context, &codec, frame, frame_size, timestamp)) != ERROR_SUCCESS) {
            return ret;
        }
    }
    
    return ret;
}

/**
* write audio raw frame to SRS.
*/
int srs_audio_write_raw_frame(srs_rtmp_t rtmp, 
    char sound_format, char sound_rate, char sound_size, char sound_type,
    char* frame, int frame_size, u_int32_t timestamp
) {
    int ret = ERROR_SUCCESS;
    
    Context* context = (Context*)rtmp;
    srs_assert(context);
    
    if (sound_format == SrsCodecAudioAAC) {
        // for aac, the frame must be ADTS format.
        if (!srs_aac_is_adts(frame, frame_size)) {
            return ERROR_AAC_REQUIRED_ADTS;
        }
        
        // for aac, demux the ADTS to RTMP format.
        return srs_write_aac_adts_frames(context, 
            sound_format, sound_rate, sound_size, sound_type, 
            frame, frame_size, timestamp);
    } else {
        // use codec info for aac.
        SrsRawAacStreamCodec codec;
        codec.sound_format = sound_format;
        codec.sound_rate = sound_rate;
        codec.sound_size = sound_size;
        codec.sound_type = sound_type;
        codec.aac_packet_type = 0;

        // for other data, directly write frame.
        return srs_write_audio_raw_frame(context, frame, frame_size, &codec, timestamp);
    }
    
    
    return ret;
}

/**
* whether aac raw data is in adts format,
* which bytes sequence matches '1111 1111 1111'B, that is 0xFFF.
*/
srs_bool srs_aac_is_adts(char* aac_raw_data, int ac_raw_size)
{
    SrsStream stream;
    if (stream.initialize(aac_raw_data, ac_raw_size) != ERROR_SUCCESS) {
        return false;
    }
    
    return srs_aac_startswith_adts(&stream);
}

/**
* parse the adts header to get the frame size.
*/
int srs_aac_adts_frame_size(char* aac_raw_data, int ac_raw_size)
{
    int size = -1;
    
    if (!srs_aac_is_adts(aac_raw_data, ac_raw_size)) {
        return size;
    }
    
    // adts always 7bytes.
    if (ac_raw_size <= 7) {
        return size;
    }
    
    // last 2bits
    int16_t ch3 = aac_raw_data[3];
    // whole 8bits
    int16_t ch4 = aac_raw_data[4];
    // first 3bits
    int16_t ch5 = aac_raw_data[5];
    
    size = ((ch3 << 11) & 0x1800) | ((ch4 << 3) & 0x07f8) | ((ch5 >> 5) & 0x0007);
    
    return size;
}
    
/**
* write h264 IPB-frame.
*/
int srs_write_h264_ipb_frame(Context* context, 
    char* frame, int frame_size, u_int32_t dts, u_int32_t pts
) {
    int ret = ERROR_SUCCESS;
    
    // when sps or pps not sent, ignore the packet.
    // @see https://github.com/simple-rtmp-server/srs/issues/203
    if (!context->h264_sps_pps_sent) {
        return ERROR_H264_DROP_BEFORE_SPS_PPS;
    }
    
    // 5bits, 7.3.1 NAL unit syntax,
    // H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
    //  7: SPS, 8: PPS, 5: I Frame, 1: P Frame
    SrsAvcNaluType nal_unit_type = (SrsAvcNaluType)(frame[0] & 0x1f);
    
    // for IDR frame, the frame is keyframe.
    SrsCodecVideoAVCFrame frame_type = SrsCodecVideoAVCFrameInterFrame;
    if (nal_unit_type == SrsAvcNaluTypeIDR) {
        frame_type = SrsCodecVideoAVCFrameKeyFrame;
    }
    
    std::string ibp;
    if ((ret = context->avc_raw.mux_ipb_frame(frame, frame_size, ibp)) != ERROR_SUCCESS) {
        return ret;
    }
    
    int8_t avc_packet_type = SrsCodecVideoAVCTypeNALU;
    char* flv = NULL;
    int nb_flv = 0;
    if ((ret = context->avc_raw.mux_avc2flv(ibp, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // the timestamp in rtmp message header is dts.
    u_int32_t timestamp = dts;
    return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv);
}

/**
* write the h264 sps/pps in context over RTMP.
*/
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) {
        return ret;
    }
    
    // h264 raw to h264 packet.
    std::string sh;
    if ((ret = context->avc_raw.mux_sequence_header(context->h264_sps, context->h264_pps, dts, pts, sh)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // h264 packet to flv packet.
    int8_t frame_type = SrsCodecVideoAVCFrameKeyFrame;
    int8_t avc_packet_type = SrsCodecVideoAVCTypeSequenceHeader;
    char* flv = NULL;
    int nb_flv = 0;
    if ((ret = context->avc_raw.mux_avc2flv(sh, frame_type, avc_packet_type, dts, pts, &flv, &nb_flv)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // reset sps and pps.
    context->h264_sps_changed = false;
    context->h264_pps_changed = false;
    context->h264_sps_pps_sent = true;
    
    // the timestamp in rtmp message header is dts.
    u_int32_t timestamp = dts;
    return srs_rtmp_write_packet(context, SRS_RTMP_TYPE_VIDEO, timestamp, flv, nb_flv);
}

/**
* write h264 raw frame, maybe sps/pps/IPB-frame.
*/
int srs_write_h264_raw_frame(Context* context, 
    char* frame, int frame_size, u_int32_t dts, u_int32_t pts
) {
    int ret = ERROR_SUCCESS;

    // for sps
    if (context->avc_raw.is_sps(frame, frame_size)) {
        std::string sps;
        if ((ret = context->avc_raw.sps_demux(frame, frame_size, sps)) != ERROR_SUCCESS) {
            return ret;
        }
        
        if (context->h264_sps == sps) {
            return ERROR_H264_DUPLICATED_SPS;
        }
        context->h264_sps_changed = true;
        context->h264_sps = sps;
        
        return srs_write_h264_sps_pps(context, dts, pts);
    }

    // for pps
    if (context->avc_raw.is_pps(frame, frame_size)) {
        std::string pps;
        if ((ret = context->avc_raw.pps_demux(frame, frame_size, pps)) != ERROR_SUCCESS) {
            return ret;
        }
        
        if (context->h264_pps == pps) {
            return ERROR_H264_DUPLICATED_PPS;
        }
        context->h264_pps_changed = true;
        context->h264_pps = pps;
        
        return srs_write_h264_sps_pps(context, dts, pts);
    }

    // ibp frame.
    return srs_write_h264_ipb_frame(context, frame, frame_size, dts, pts);
}

/**
* write h264 multiple frames, in annexb format.
*/
int srs_h264_write_raw_frames(srs_rtmp_t rtmp, 
    char* frames, int frames_size, u_int32_t dts, u_int32_t pts
) {
    int ret = ERROR_SUCCESS;
    
    srs_assert(frames != NULL);
    srs_assert(frames_size > 0);
    
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    
    if ((ret = context->h264_raw_stream.initialize(frames, frames_size)) != ERROR_SUCCESS) {
        return ret;
    }
    
    // use the last error
    // @see https://github.com/simple-rtmp-server/srs/issues/203
    // @see https://github.com/simple-rtmp-server/srs/issues/204
    int error_code_return = ret;
    
    // send each frame.
    while (!context->h264_raw_stream.empty()) {
        char* frame = NULL;
        int frame_size = 0;
        if ((ret = context->avc_raw.annexb_demux(&context->h264_raw_stream, &frame, &frame_size)) != ERROR_SUCCESS) {
            return ret;
        }
    
        // ignore invalid frame,
        // atleast 1bytes for SPS to decode the type
        if (frame_size <= 0) {
            continue;
        }

        // it may be return error, but we must process all packets.
        if ((ret = srs_write_h264_raw_frame(context, frame, frame_size, dts, pts)) != ERROR_SUCCESS) {
            error_code_return = ret;
            
            // ignore known error, process all packets.
            if (srs_h264_is_dvbsp_error(ret)
                || srs_h264_is_duplicated_sps_error(ret)
                || srs_h264_is_duplicated_pps_error(ret)
            ) {
                continue;
            }
            
            return ret;
        }
    }
    
    return error_code_return;
}

srs_bool srs_h264_is_dvbsp_error(int error_code)
{
    return error_code == ERROR_H264_DROP_BEFORE_SPS_PPS;
}

srs_bool srs_h264_is_duplicated_sps_error(int error_code)
{
    return error_code == ERROR_H264_DUPLICATED_SPS;
}

srs_bool srs_h264_is_duplicated_pps_error(int error_code)
{
    return error_code == ERROR_H264_DUPLICATED_PPS;
}

srs_bool srs_h264_startswith_annexb(char* h264_raw_data, int h264_raw_size, int* pnb_start_code)
{
    SrsStream stream;
    if (stream.initialize(h264_raw_data, h264_raw_size) != ERROR_SUCCESS) {
        return false;
    }
    
    return srs_avc_startswith_annexb(&stream, pnb_start_code);
}

struct FlvContext
{
    SrsFileReader reader;
    SrsFileWriter writer;
    SrsFlvEncoder enc;
    SrsFlvDecoder dec;
};

srs_flv_t srs_flv_open_read(const char* file)
{
    int ret = ERROR_SUCCESS;
    
    FlvContext* flv = new FlvContext();
    
    if ((ret = flv->reader.open(file)) != ERROR_SUCCESS) {
        srs_freep(flv);
        return NULL;
    }
    
    if ((ret = flv->dec.initialize(&flv->reader)) != ERROR_SUCCESS) {
        srs_freep(flv);
        return NULL;
    }
    
    return flv;
}

srs_flv_t srs_flv_open_write(const char* file)
{
    int ret = ERROR_SUCCESS;
    
    FlvContext* flv = new FlvContext();
    
    if ((ret = flv->writer.open(file)) != ERROR_SUCCESS) {
        srs_freep(flv);
        return NULL;
    }
    
    if ((ret = flv->enc.initialize(&flv->writer)) != ERROR_SUCCESS) {
        srs_freep(flv);
        return NULL;
    }
    
    return flv;
}

void srs_flv_close(srs_flv_t flv)
{
    FlvContext* context = (FlvContext*)flv;
    srs_freep(context);
}

int srs_flv_read_header(srs_flv_t flv, char header[9])
{
    int ret = ERROR_SUCCESS;
    
    FlvContext* context = (FlvContext*)flv;

    if (!context->reader.is_open()) {
        return ERROR_SYSTEM_IO_INVALID;
    }
    
    if ((ret = context->dec.read_header(header)) != ERROR_SUCCESS) {
        return ret;
    }
    
    char ts[4]; // tag size
    if ((ret = context->dec.read_previous_tag_size(ts)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int srs_flv_read_tag_header(srs_flv_t flv, char* ptype, int32_t* pdata_size, u_int32_t* ptime)
{
    int ret = ERROR_SUCCESS;
    
    FlvContext* context = (FlvContext*)flv;

    if (!context->reader.is_open()) {
        return ERROR_SYSTEM_IO_INVALID;
    }
    
    if ((ret = context->dec.read_tag_header(ptype, pdata_size, ptime)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int srs_flv_read_tag_data(srs_flv_t flv, char* data, int32_t size)
{
    int ret = ERROR_SUCCESS;
    
    FlvContext* context = (FlvContext*)flv;

    if (!context->reader.is_open()) {
        return ERROR_SYSTEM_IO_INVALID;
    }
    
    if ((ret = context->dec.read_tag_data(data, size)) != ERROR_SUCCESS) {
        return ret;
    }
    
    char ts[4]; // tag size
    if ((ret = context->dec.read_previous_tag_size(ts)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int srs_flv_write_header(srs_flv_t flv, char header[9])
{
    int ret = ERROR_SUCCESS;
    
    FlvContext* context = (FlvContext*)flv;

    if (!context->writer.is_open()) {
        return ERROR_SYSTEM_IO_INVALID;
    }
    
    if ((ret = context->enc.write_header(header)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

int srs_flv_write_tag(srs_flv_t flv, char type, int32_t time, char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    FlvContext* context = (FlvContext*)flv;

    if (!context->writer.is_open()) {
        return ERROR_SYSTEM_IO_INVALID;
    }
    
    if (type == SRS_RTMP_TYPE_AUDIO) {
        return context->enc.write_audio(time, data, size);
    } else if (type == SRS_RTMP_TYPE_VIDEO) {
        return context->enc.write_video(time, data, size);
    } else {
        return context->enc.write_metadata(type, data, size);
    }

    return ret;
}

int srs_flv_size_tag(int data_size)
{
    return SrsFlvEncoder::size_tag(data_size);
}

int64_t srs_flv_tellg(srs_flv_t flv)
{
    FlvContext* context = (FlvContext*)flv;
    return context->reader.tellg();
}

void srs_flv_lseek(srs_flv_t flv, int64_t offset)
{
    FlvContext* context = (FlvContext*)flv;
    context->reader.lseek(offset);
}

srs_bool srs_flv_is_eof(int error_code)
{
    return error_code == ERROR_SYSTEM_FILE_EOF;
}

srs_bool srs_flv_is_sequence_header(char* data, int32_t size)
{
    return SrsFlvCodec::video_is_sequence_header(data, (int)size);
}

srs_bool srs_flv_is_keyframe(char* data, int32_t size)
{
    return SrsFlvCodec::video_is_keyframe(data, (int)size);
}

srs_amf0_t srs_amf0_parse(char* data, int size, int* nparsed)
{
    int ret = ERROR_SUCCESS;
    
    srs_amf0_t amf0 = NULL;
    
    SrsStream stream;
    if ((ret = stream.initialize(data, size)) != ERROR_SUCCESS) {
        return amf0;
    }
    
    SrsAmf0Any* any = NULL;
    if ((ret = SrsAmf0Any::discovery(&stream, &any)) != ERROR_SUCCESS) {
        return amf0;
    }
    
    stream.skip(-1 * stream.pos());
    if ((ret = any->read(&stream)) != ERROR_SUCCESS) {
        srs_freep(any);
        return amf0;
    }
    
    if (nparsed) {
        *nparsed = stream.pos();
    }
    amf0 = (srs_amf0_t)any;
    
    return amf0;
}

srs_amf0_t srs_amf0_create_string(const char* value)
{
    return SrsAmf0Any::str(value);
}

srs_amf0_t srs_amf0_create_number(srs_amf0_number value)
{
    return SrsAmf0Any::number(value);
}

srs_amf0_t srs_amf0_create_ecma_array()
{
    return SrsAmf0Any::ecma_array();
}

srs_amf0_t srs_amf0_create_strict_array()
{
    return SrsAmf0Any::strict_array();
}

srs_amf0_t srs_amf0_create_object()
{
    return SrsAmf0Any::object();
}

srs_amf0_t srs_amf0_ecma_array_to_object(srs_amf0_t ecma_arr)
{
    srs_assert(srs_amf0_is_ecma_array(ecma_arr));

    SrsAmf0EcmaArray* arr = (SrsAmf0EcmaArray*)ecma_arr;
    SrsAmf0Object* obj = SrsAmf0Any::object();
    
    for (int i = 0; i < arr->count(); i++) {
        std::string key = arr->key_at(i);
        SrsAmf0Any* value = arr->value_at(i);
        obj->set(key, value->copy());
    }
    
    return obj;
}

void srs_amf0_free(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_freep(any);
}

int srs_amf0_size(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    return any->total_size();
}

int srs_amf0_serialize(srs_amf0_t amf0, char* data, int size)
{
    int ret = ERROR_SUCCESS;
    
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    
    SrsStream stream;
    if ((ret = stream.initialize(data, size)) != ERROR_SUCCESS) {
        return ret;
    }
    
    if ((ret = any->write(&stream)) != ERROR_SUCCESS) {
        return ret;
    }
    
    return ret;
}

srs_bool srs_amf0_is_string(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    return any->is_string();
}

srs_bool srs_amf0_is_boolean(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    return any->is_boolean();
}

srs_bool srs_amf0_is_number(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    return any->is_number();
}

srs_bool srs_amf0_is_null(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    return any->is_null();
}

srs_bool srs_amf0_is_object(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    return any->is_object();
}

srs_bool srs_amf0_is_ecma_array(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    return any->is_ecma_array();
}

srs_bool srs_amf0_is_strict_array(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    return any->is_strict_array();
}

const char* srs_amf0_to_string(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    return any->to_str_raw();
}

srs_bool srs_amf0_to_boolean(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    return any->to_boolean();
}

srs_amf0_number srs_amf0_to_number(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    return any->to_number();
}

void srs_amf0_set_number(srs_amf0_t amf0, srs_amf0_number value)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    any->set_number(value);
}

int srs_amf0_object_property_count(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_object());

    SrsAmf0Object* obj = (SrsAmf0Object*)amf0;
    return obj->count();
}

const char* srs_amf0_object_property_name_at(srs_amf0_t amf0, int index)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_object());

    SrsAmf0Object* obj = (SrsAmf0Object*)amf0;
    return obj->key_raw_at(index);
}

srs_amf0_t srs_amf0_object_property_value_at(srs_amf0_t amf0, int index)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_object());

    SrsAmf0Object* obj = (SrsAmf0Object*)amf0;
    return (srs_amf0_t)obj->value_at(index);
}

srs_amf0_t srs_amf0_object_property(srs_amf0_t amf0, const char* name)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_object());

    SrsAmf0Object* obj = (SrsAmf0Object*)amf0;
    return (srs_amf0_t)obj->get_property(name);
}

void srs_amf0_object_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_object());

    SrsAmf0Object* obj = (SrsAmf0Object*)amf0;
    any = (SrsAmf0Any*)value;
    obj->set(name, any);
}

void srs_amf0_object_clear(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_object());

    SrsAmf0Object* obj = (SrsAmf0Object*)amf0;
    obj->clear();
}

int srs_amf0_ecma_array_property_count(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_ecma_array());

    SrsAmf0EcmaArray * obj = (SrsAmf0EcmaArray*)amf0;
    return obj->count();
}

const char* srs_amf0_ecma_array_property_name_at(srs_amf0_t amf0, int index)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_ecma_array());

    SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0;
    return obj->key_raw_at(index);
}

srs_amf0_t srs_amf0_ecma_array_property_value_at(srs_amf0_t amf0, int index)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_ecma_array());

    SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0;
    return (srs_amf0_t)obj->value_at(index);
}

srs_amf0_t srs_amf0_ecma_array_property(srs_amf0_t amf0, const char* name)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_ecma_array());

    SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0;
    return (srs_amf0_t)obj->get_property(name);
}

void srs_amf0_ecma_array_property_set(srs_amf0_t amf0, const char* name, srs_amf0_t value)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_ecma_array());

    SrsAmf0EcmaArray* obj = (SrsAmf0EcmaArray*)amf0;
    any = (SrsAmf0Any*)value;
    obj->set(name, any);
}

int srs_amf0_strict_array_property_count(srs_amf0_t amf0)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_strict_array());

    SrsAmf0StrictArray * obj = (SrsAmf0StrictArray*)amf0;
    return obj->count();
}

srs_amf0_t srs_amf0_strict_array_property_at(srs_amf0_t amf0, int index)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_strict_array());

    SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0;
    return (srs_amf0_t)obj->at(index);
}

void srs_amf0_strict_array_append(srs_amf0_t amf0, srs_amf0_t value)
{
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    srs_assert(any->is_strict_array());

    SrsAmf0StrictArray* obj = (SrsAmf0StrictArray*)amf0;
    any = (SrsAmf0Any*)value;
    obj->append(any);
}

int64_t srs_utils_time_ms()
{
    srs_update_system_time_ms();
    return srs_get_system_time_ms();
}

int64_t srs_utils_send_bytes(srs_rtmp_t rtmp)
{
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    return context->rtmp->get_send_bytes();
}

int64_t srs_utils_recv_bytes(srs_rtmp_t rtmp)
{
    srs_assert(rtmp != NULL);
    Context* context = (Context*)rtmp;
    return context->rtmp->get_recv_bytes();
}

int srs_utils_parse_timestamp(
    u_int32_t time, char type, char* data, int size,
    u_int32_t* ppts
) {
    int ret = ERROR_SUCCESS;
    
    if (type != SRS_RTMP_TYPE_VIDEO) {
        *ppts = time;
        return ret;
    }

    if (!SrsFlvCodec::video_is_h264(data, size)) {
        return ERROR_FLV_INVALID_VIDEO_TAG;
    }

    if (SrsFlvCodec::video_is_sequence_header(data, size)) {
        *ppts = time;
        return ret;
    }
    
    // 1bytes, frame type and codec id.
    // 1bytes, avc packet type.
    // 3bytes, cts, composition time,
    //      pts = dts + cts, or 
    //      cts = pts - dts.
    if (size < 5) {
        return ERROR_FLV_INVALID_VIDEO_TAG;
    }
    
    u_int32_t cts = 0;
    char* p = data + 2;
    char* pp = (char*)&cts;
    pp[2] = *p++;
    pp[1] = *p++;
    pp[0] = *p++;

    *ppts = time + cts;
    
    return ret;
}
    
srs_bool srs_utils_flv_tag_is_ok(char type)
{
    return type == SRS_RTMP_TYPE_AUDIO || type == SRS_RTMP_TYPE_VIDEO || type == SRS_RTMP_TYPE_SCRIPT;
}

srs_bool srs_utils_flv_tag_is_audio(char type)
{
    return type == SRS_RTMP_TYPE_AUDIO;
}
    
srs_bool srs_utils_flv_tag_is_video(char type)
{
    return type == SRS_RTMP_TYPE_VIDEO;
}

srs_bool srs_utils_flv_tag_is_av(char type)
{
    return type == SRS_RTMP_TYPE_AUDIO || type == SRS_RTMP_TYPE_VIDEO;
}

char srs_utils_flv_video_codec_id(char* data, int size)
{
    if (size < 1) {
        return 0;
    }

    char codec_id = data[0];
    codec_id = codec_id & 0x0F;
    
    return codec_id;
}

char srs_utils_flv_video_avc_packet_type(char* data, int size)
{
    if (size < 2) {
        return -1;
    }
    
    if (!SrsFlvCodec::video_is_h264(data, size)) {
        return -1;
    }
    
    u_int8_t avc_packet_type = data[1];
    
    if (avc_packet_type > 2) {
        return -1;
    }
    
    return avc_packet_type;
}

char srs_utils_flv_video_frame_type(char* data, int size)
{
    if (size < 1) {
        return -1;
    }
    
    if (!SrsFlvCodec::video_is_h264(data, size)) {
        return -1;
    }
    
    u_int8_t frame_type = data[0];
    frame_type = (frame_type >> 4) & 0x0f;
    if (frame_type < 1 || frame_type > 5) {
        return -1;
    }
    
    return frame_type;
}

char srs_utils_flv_audio_sound_format(char* data, int size)
{
    if (size < 1) {
        return -1;
    }
    
    u_int8_t sound_format = data[0];
    sound_format = (sound_format >> 4) & 0x0f;
    if (sound_format > 15 || sound_format == 12 || sound_format == 13) {
        return -1;
    }
    
    return sound_format;
}

char srs_utils_flv_audio_sound_rate(char* data, int size)
{
    if (size < 1) {
        return -1;
    }
    
    u_int8_t sound_rate = data[0];
    sound_rate = (sound_rate >> 2) & 0x03;
    if (sound_rate > 3) {
        return -1;
    }
    
    return sound_rate;
}

char srs_utils_flv_audio_sound_size(char* data, int size)
{
    if (size < 1) {
        return -1;
    }
    
    u_int8_t sound_size = data[0];
    sound_size = (sound_size >> 1) & 0x01;
    if (sound_size > 1) {
        return -1;
    }
    
    return sound_size;
}

char srs_utils_flv_audio_sound_type(char* data, int size)
{
    if (size < 1) {
        return -1;
    }
    
    u_int8_t sound_type = data[0];
    sound_type = sound_type & 0x01;
    if (sound_type > 1) {
        return -1;
    }
    
    return sound_type;
}

char srs_utils_flv_audio_aac_packet_type(char* data, int size)
{
    if (size < 2) {
        return -1;
    }
    
    if (srs_utils_flv_audio_sound_format(data, size) != 10) {
        return -1;
    }
    
    u_int8_t aac_packet_type = data[1];
    if (aac_packet_type > 1) {
        return -1;
    }
    
    return aac_packet_type;
}

char* srs_human_amf0_print(srs_amf0_t amf0, char** pdata, int* psize)
{
    if (!amf0) {
        return NULL;
    }
    
    SrsAmf0Any* any = (SrsAmf0Any*)amf0;
    
    return any->human_print(pdata, psize);
}

const char* srs_human_flv_tag_type2string(char type)
{
    static const char* audio = "Audio";
    static const char* video = "Video";
    static const char* data = "Data";
    static const char* unknown = "Unknown";
    
    switch (type) {
        case SRS_RTMP_TYPE_AUDIO: return audio;
        case SRS_RTMP_TYPE_VIDEO: return video;
        case SRS_RTMP_TYPE_SCRIPT: return data;
        default: return unknown;
    }
    
    return unknown;
}

const char* srs_human_flv_video_codec_id2string(char codec_id)
{
    static const char* h263 = "H.263";
    static const char* screen = "Screen";
    static const char* vp6 = "VP6";
    static const char* vp6_alpha = "VP6Alpha";
    static const char* screen2 = "Screen2";
    static const char* h264 = "H.264";
    static const char* unknown = "Unknown";
    
    switch (codec_id) {
        case 2: return h263;
        case 3: return screen;
        case 4: return vp6;
        case 5: return vp6_alpha;
        case 6: return screen2;
        case 7: return h264;
        default: return unknown;
    }
    
    return unknown;
}

const char* srs_human_flv_video_avc_packet_type2string(char avc_packet_type)
{
    static const char* sps_pps = "SH";
    static const char* nalu = "Nalu";
    static const char* sps_pps_end = "SpsPpsEnd";
    static const char* unknown = "Unknown";
    
    switch (avc_packet_type) {
        case 0: return sps_pps;
        case 1: return nalu;
        case 2: return sps_pps_end;
        default: return unknown;
    }
    
    return unknown;
}

const char* srs_human_flv_video_frame_type2string(char frame_type)
{
    static const char* keyframe = "I";
    static const char* interframe = "P/B";
    static const char* disposable_interframe = "DI";
    static const char* generated_keyframe = "GI";
    static const char* video_infoframe = "VI";
    static const char* unknown = "Unknown";
    
    switch (frame_type) {
        case 1: return keyframe;
        case 2: return interframe;
        case 3: return disposable_interframe;
        case 4: return generated_keyframe;
        case 5: return video_infoframe;
        default: return unknown;
    }
    
    return unknown;
}

const char* srs_human_flv_audio_sound_format2string(char sound_format)
{
    static const char* linear_pcm = "LinearPCM";
    static const char* ad_pcm = "ADPCM";
    static const char* mp3 = "MP3";
    static const char* linear_pcm_le = "LinearPCMLe";
    static const char* nellymoser_16khz = "NellymoserKHz16";
    static const char* nellymoser_8khz = "NellymoserKHz8";
    static const char* nellymoser = "Nellymoser";
    static const char* g711_a_pcm = "G711APCM";
    static const char* g711_mu_pcm = "G711MuPCM";
    static const char* reserved = "Reserved";
    static const char* aac = "AAC";
    static const char* speex = "Speex";
    static const char* mp3_8khz = "MP3KHz8";
    static const char* device_specific = "DeviceSpecific";
    static const char* unknown = "Unknown";
    
    switch (sound_format) {
        case 0: return linear_pcm;
        case 1: return ad_pcm;
        case 2: return mp3;
        case 3: return linear_pcm_le;
        case 4: return nellymoser_16khz;
        case 5: return nellymoser_8khz;
        case 6: return nellymoser;
        case 7: return g711_a_pcm;
        case 8: return g711_mu_pcm;
        case 9: return reserved;
        case 10: return aac;
        case 11: return speex;
        case 14: return mp3_8khz;
        case 15: return device_specific;
        default: return unknown;
    }
    
    return unknown;
}

const char* srs_human_flv_audio_sound_rate2string(char sound_rate)
{
    static const char* khz_5_5 = "5.5KHz";
    static const char* khz_11 = "11KHz";
    static const char* khz_22 = "22KHz";
    static const char* khz_44 = "44KHz";
    static const char* unknown = "Unknown";
    
    switch (sound_rate) {
        case 0: return khz_5_5;
        case 1: return khz_11;
        case 2: return khz_22;
        case 3: return khz_44;
        default: return unknown;
    }
    
    return unknown;
}

const char* srs_human_flv_audio_sound_size2string(char sound_size)
{
    static const char* bit_8 = "8bit";
    static const char* bit_16 = "16bit";
    static const char* unknown = "Unknown";
    
    switch (sound_size) {
        case 0: return bit_8;
        case 1: return bit_16;
        default: return unknown;
    }
    
    return unknown;
}

const char* srs_human_flv_audio_sound_type2string(char sound_type)
{
    static const char* mono = "Mono";
    static const char* stereo = "Stereo";
    static const char* unknown = "Unknown";
    
    switch (sound_type) {
        case 0: return mono;
        case 1: return stereo;
        default: return unknown;
    }
    
    return unknown;
}

const char* srs_human_flv_audio_aac_packet_type2string(char aac_packet_type)
{
    static const char* sps_pps = "SH";
    static const char* raw = "Raw";
    static const char* unknown = "Unknown";
    
    switch (aac_packet_type) {
        case 0: return sps_pps;
        case 1: return raw;
        default: return unknown;
    }
    
    return unknown;
}
    
int srs_human_print_rtmp_packet(char type, u_int32_t timestamp, char* data, int size)
{
    return srs_human_print_rtmp_packet2(type, timestamp, data, size, 0);
}

int srs_human_print_rtmp_packet2(char type, u_int32_t timestamp, char* data, int size, u_int32_t pre_timestamp)
{
    int ret = ERROR_SUCCESS;
    
    int diff = 0;
    if (pre_timestamp > 0) {
        diff = (int)timestamp - (int)pre_timestamp;
    }
    
    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
        );
        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_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_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)),
            srs_human_flv_audio_sound_type2string(srs_utils_flv_audio_sound_type(data, size)),
            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);
        int nparsed = 0;
        while (nparsed < size) {
            int nb_parsed_this = 0;
            srs_amf0_t amf0 = srs_amf0_parse(data + nparsed, size - nparsed, &nb_parsed_this);
            if (amf0 == NULL) {
                break;
            }
    
            nparsed += nb_parsed_this;
            
            char* amf0_str = NULL;
            srs_human_raw("%s", srs_human_amf0_print(amf0, &amf0_str, NULL));
            srs_freep(amf0_str);
        }
    } else {
        srs_human_trace("Rtmp packet type=%#x, dts=%d, pts=%d, diff=%d, size=%d",
            type, timestamp, pts, diff, size);
    }
    
    return ret;
}

const char* srs_human_format_time()
{
    struct timeval tv;
    static char buf[23];
    
    memset(buf, 0, sizeof(buf));
    
    // clock time
    if (gettimeofday(&tv, NULL) == -1) {
        return buf;
    }
    
    // to calendar time
    struct tm* tm;
    if ((tm = localtime((const time_t*)&tv.tv_sec)) == NULL) {
        return buf;
    }
    
    snprintf(buf, sizeof(buf), 
        "%d-%02d-%02d %02d:%02d:%02d.%03d", 
        1900 + tm->tm_year, 1 + tm->tm_mon, tm->tm_mday, 
        tm->tm_hour, tm->tm_min, tm->tm_sec, 
        (int)(tv.tv_usec / 1000));
        
    // for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
    buf[sizeof(buf) - 1] = 0;
    
    return buf;
}


#ifdef SRS_HIJACK_IO
srs_hijack_io_t srs_hijack_io_get(srs_rtmp_t rtmp)
{
    if (!rtmp) {
        return NULL;
    }
    
    Context* context = (Context*)rtmp;
    if (!context->skt) {
        return NULL;
    }
    
    return context->skt->hijack_io();
}
#endif

#ifdef __cplusplus
}
#endif

// following is generated by src/libs/srs_lib_simple_socket.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 <srs_lib_simple_socket.hpp>

//#include <srs_kernel_error.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
    #define SOCKET_ETIME ETIME
    #define SOCKET_ECONNRESET ECONNRESET

    #define SOCKET_ERRNO() errno
    #define SOCKET_RESET(fd) fd = -1; (void)0
    #define SOCKET_CLOSE(fd) \
        if (fd > 0) {\
            ::close(fd); \
            fd = -1; \
        } \
        (void)0
    #define SOCKET_VALID(x) (x > 0)
    #define SOCKET_SETUP() (void)0
    #define SOCKET_CLEANUP() (void)0
#else
    #define SOCKET_ETIME WSAETIMEDOUT
    #define SOCKET_ECONNRESET WSAECONNRESET
    #define SOCKET_ERRNO() WSAGetLastError()
    #define SOCKET_RESET(x) x=INVALID_SOCKET
    #define SOCKET_CLOSE(x) if(x!=INVALID_SOCKET){::closesocket(x);x=INVALID_SOCKET;}
    #define SOCKET_VALID(x) (x!=INVALID_SOCKET)
    #define SOCKET_BUFF(x) ((char*)x)
    #define SOCKET_SETUP() socket_setup()
    #define SOCKET_CLEANUP() socket_cleanup()
#endif

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
    #include <unistd.h>
    #include <sys/socket.h>
    #include <netinet/in.h>
    #include <arpa/inet.h>
    #include <sys/uio.h>
#endif

#include <sys/types.h>
#include <errno.h>

//#include <srs_kernel_utility.hpp>

#ifndef ST_UTIME_NO_TIMEOUT
    #define ST_UTIME_NO_TIMEOUT -1
#endif

// when io not hijacked, use simple socket, the block sync stream.
#ifndef SRS_HIJACK_IO
    struct SrsBlockSyncSocket
    {
        SOCKET fd;
        int64_t recv_timeout;
        int64_t send_timeout;
        int64_t recv_bytes;
        int64_t send_bytes;
        
        SrsBlockSyncSocket() {
            send_timeout = recv_timeout = ST_UTIME_NO_TIMEOUT;
            recv_bytes = send_bytes = 0;
            
            SOCKET_RESET(fd);
            SOCKET_SETUP();
        }
        
        virtual ~SrsBlockSyncSocket() {
            SOCKET_CLOSE(fd);
            SOCKET_CLEANUP();
        }
    };
    srs_hijack_io_t srs_hijack_io_create()
    {
        SrsBlockSyncSocket* skt = new SrsBlockSyncSocket();
        return skt;
    }
    void srs_hijack_io_destroy(srs_hijack_io_t ctx)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        srs_freep(skt);
    }
    int srs_hijack_io_create_socket(srs_hijack_io_t ctx)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        
        skt->fd = ::socket(AF_INET, SOCK_STREAM, 0);
        if (!SOCKET_VALID(skt->fd)) {
            return ERROR_SOCKET_CREATE;
        }
    
        return ERROR_SUCCESS;
    }
    int srs_hijack_io_connect(srs_hijack_io_t ctx, const char* server_ip, int port)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        
        sockaddr_in addr;
        addr.sin_family = AF_INET;
        addr.sin_port = htons(port);
        addr.sin_addr.s_addr = inet_addr(server_ip);
        
        if(::connect(skt->fd, (const struct sockaddr*)&addr, sizeof(sockaddr_in)) < 0){
            return ERROR_SOCKET_CONNECT;
        }
        
        return ERROR_SUCCESS;
    }
    int srs_hijack_io_read(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        
        int ret = ERROR_SUCCESS;
        
        ssize_t nb_read = ::recv(skt->fd, (char*)buf, size, 0);
        
        if (nread) {
            *nread = nb_read;
        }
        
        // On success a non-negative integer indicating the number of bytes actually read is returned 
        // (a value of 0 means the network connection is closed or end of file is reached).
        if (nb_read <= 0) {
            if (nb_read < 0 && SOCKET_ERRNO() == SOCKET_ETIME) {
                return ERROR_SOCKET_TIMEOUT;
            }
            
            if (nb_read == 0) {
                errno = SOCKET_ECONNRESET;
            }
            
            return ERROR_SOCKET_READ;
        }
        
        skt->recv_bytes += nb_read;
        
        return ret;
    }
    void srs_hijack_io_set_recv_timeout(srs_hijack_io_t ctx, int64_t timeout_us)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        skt->recv_timeout = timeout_us;
    }
    int64_t srs_hijack_io_get_recv_timeout(srs_hijack_io_t ctx)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        return skt->recv_timeout;
    }
    int64_t srs_hijack_io_get_recv_bytes(srs_hijack_io_t ctx)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        return skt->recv_bytes;
    }
    void srs_hijack_io_set_send_timeout(srs_hijack_io_t ctx, int64_t timeout_us)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        skt->send_timeout = timeout_us;
    }
    int64_t srs_hijack_io_get_send_timeout(srs_hijack_io_t ctx)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        return skt->send_timeout;
    }
    int64_t srs_hijack_io_get_send_bytes(srs_hijack_io_t ctx)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        return skt->send_bytes;
    }
    int srs_hijack_io_writev(srs_hijack_io_t ctx, const iovec *iov, int iov_size, ssize_t* nwrite)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        
        int ret = ERROR_SUCCESS;
        
        ssize_t nb_write = ::writev(skt->fd, iov, iov_size);
        
        if (nwrite) {
            *nwrite = nb_write;
        }
        
        // On  success,  the  readv()  function  returns the number of bytes read; 
        // 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
            if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) {
                return ERROR_SOCKET_TIMEOUT;
            }
            
            return ERROR_SOCKET_WRITE;
        }
        
        skt->send_bytes += nb_write;
        
        return ret;
    }
    bool srs_hijack_io_is_never_timeout(srs_hijack_io_t ctx, int64_t timeout_us)
    {
        return timeout_us == (int64_t)ST_UTIME_NO_TIMEOUT;
    }
    int srs_hijack_io_read_fully(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nread)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        
        int ret = ERROR_SUCCESS;
        
        size_t left = size;
        ssize_t nb_read = 0;
        
        while (left > 0) {
            char* this_buf = (char*)buf + nb_read;
            ssize_t this_nread;
            
            if ((ret = srs_hijack_io_read(ctx, this_buf, left, &this_nread)) != ERROR_SUCCESS) {
                return ret;
            }
            
            nb_read += this_nread;
            left -= (size_t)this_nread;
        }
        
        if (nread) {
            *nread = nb_read;
        }
        skt->recv_bytes += nb_read;
        
        return ret;
    }
    int srs_hijack_io_write(srs_hijack_io_t ctx, void* buf, size_t size, ssize_t* nwrite)
    {
        SrsBlockSyncSocket* skt = (SrsBlockSyncSocket*)ctx;
        
        int ret = ERROR_SUCCESS;
        
        ssize_t nb_write = ::send(skt->fd, (char*)buf, size, 0);
        
        if (nwrite) {
            *nwrite = nb_write;
        }
        
        if (nb_write <= 0) {
            // @see https://github.com/simple-rtmp-server/srs/issues/200
            if (nb_write < 0 && SOCKET_ERRNO() == SOCKET_ETIME) {
                return ERROR_SOCKET_TIMEOUT;
            }
            
            return ERROR_SOCKET_WRITE;
        }
        
        skt->send_bytes += nb_write;
        
        return ret;
    }
#endif

SimpleSocketStream::SimpleSocketStream()
{
    io = srs_hijack_io_create();
}

SimpleSocketStream::~SimpleSocketStream()
{
    if (io) {
        srs_hijack_io_destroy(io);
        io = NULL;
    }
}

srs_hijack_io_t SimpleSocketStream::hijack_io()
{
    return io;
}

int SimpleSocketStream::create_socket()
{
    srs_assert(io);
    return srs_hijack_io_create_socket(io);
}

int SimpleSocketStream::connect(const char* server_ip, int port)
{
    srs_assert(io);
    return srs_hijack_io_connect(io, server_ip, port);
}

// ISrsBufferReader
int SimpleSocketStream::read(void* buf, size_t size, ssize_t* nread)
{
    srs_assert(io);
    return srs_hijack_io_read(io, buf, size, nread);
}

// ISrsProtocolReader
void SimpleSocketStream::set_recv_timeout(int64_t timeout_us)
{
    srs_assert(io);
    srs_hijack_io_set_recv_timeout(io, timeout_us);
}

int64_t SimpleSocketStream::get_recv_timeout()
{
    srs_assert(io);
    return srs_hijack_io_get_recv_timeout(io);
}

int64_t SimpleSocketStream::get_recv_bytes()
{
    srs_assert(io);
    return srs_hijack_io_get_recv_bytes(io);
}

// ISrsProtocolWriter
void SimpleSocketStream::set_send_timeout(int64_t timeout_us)
{
    srs_assert(io);
    srs_hijack_io_set_send_timeout(io, timeout_us);
}

int64_t SimpleSocketStream::get_send_timeout()
{
    srs_assert(io);
    return srs_hijack_io_get_send_timeout(io);
}

int64_t SimpleSocketStream::get_send_bytes()
{
    srs_assert(io);
    return srs_hijack_io_get_send_bytes(io);
}

int SimpleSocketStream::writev(const iovec *iov, int iov_size, ssize_t* nwrite)
{
    srs_assert(io);
    return srs_hijack_io_writev(io, iov, iov_size, nwrite);
}

// ISrsProtocolReaderWriter
bool SimpleSocketStream::is_never_timeout(int64_t timeout_us)
{
    srs_assert(io);
    return srs_hijack_io_is_never_timeout(io, timeout_us);
}

int SimpleSocketStream::read_fully(void* buf, size_t size, ssize_t* nread)
{
    srs_assert(io);
    return srs_hijack_io_read_fully(io, buf, size, nread);
}

int SimpleSocketStream::write(void* buf, size_t size, ssize_t* nwrite)
{
    srs_assert(io);
    return srs_hijack_io_write(io, buf, size, nwrite);
}


// following is generated by src/libs/srs_lib_bandwidth.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 <srs_lib_bandwidth.hpp>

// for srs-librtmp, @see https://github.com/simple-rtmp-server/srs/issues/213
#ifndef _WIN32
#include <unistd.h>
#endif

#include <sstream>
using namespace std;

//#include <srs_kernel_error.hpp>
//#include <srs_rtmp_stack.hpp>
//#include <srs_rtmp_sdk.hpp>
//#include <srs_core_autofree.hpp>
//#include <srs_kernel_utility.hpp>
//#include <srs_rtmp_amf0.hpp>

/**
* recv bandwidth helper.
*/
typedef bool (*_CheckPacketType)(SrsBandwidthPacket* pkt);
bool _bandwidth_is_start_play(SrsBandwidthPacket* pkt)
{
    return pkt->is_start_play();
}
bool _bandwidth_is_stop_play(SrsBandwidthPacket* pkt)
{
    return pkt->is_stop_play();
}
bool _bandwidth_is_start_publish(SrsBandwidthPacket* pkt)
{
    return pkt->is_start_publish();
}
bool _bandwidth_is_stop_publish(SrsBandwidthPacket* pkt)
{
    return pkt->is_stop_publish();
}
bool _bandwidth_is_finish(SrsBandwidthPacket* pkt)
{
    return pkt->is_finish();
}
int _srs_expect_bandwidth_packet(SrsRtmpClient* rtmp, _CheckPacketType pfn)
{
    int ret = ERROR_SUCCESS;
    
    while (true) {
        SrsCommonMessage* msg = NULL;
        SrsBandwidthPacket* pkt = NULL;
        if ((ret = rtmp->expect_message<SrsBandwidthPacket>(&msg, &pkt)) != ERROR_SUCCESS) {
            return ret;
        }
        SrsAutoFree(SrsCommonMessage, msg);
        SrsAutoFree(SrsBandwidthPacket, pkt);
        srs_info("get final message success.");
        
        if (pfn(pkt)) {
            return ret;
        }
    }
    
    return ret;
}
int _srs_expect_bandwidth_packet2(SrsRtmpClient* rtmp, _CheckPacketType pfn, SrsBandwidthPacket** ppkt)
{
    int ret = ERROR_SUCCESS;
    
    while (true) {
        SrsCommonMessage* msg = NULL;
        SrsBandwidthPacket* pkt = NULL;
        if ((ret = rtmp->expect_message<SrsBandwidthPacket>(&msg, &pkt)) != ERROR_SUCCESS) {
            return ret;
        }
        SrsAutoFree(SrsCommonMessage, msg);
        srs_info("get final message success.");
        
        if (pfn(pkt)) {
            *ppkt = pkt;
            return ret;
        }
        
        srs_freep(pkt);
    }
    
    return ret;
}

SrsBandwidthClient::SrsBandwidthClient()
{
    _rtmp = NULL;
}

SrsBandwidthClient::~SrsBandwidthClient()
{
}

int SrsBandwidthClient::initialize(SrsRtmpClient* rtmp)
{
    _rtmp = rtmp;

    return ERROR_SUCCESS;
}

int SrsBandwidthClient::bandwidth_check(
    int64_t* start_time, int64_t* end_time, 
    int* play_kbps, int* publish_kbps,
    int* play_bytes, int* publish_bytes,
    int* play_duration, int* publish_duration
) {
    int ret = ERROR_SUCCESS;

    srs_update_system_time_ms();
    *start_time = srs_get_system_time_ms();
    
    // play
    if ((ret = play_start()) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = play_checking()) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = play_stop()) != ERROR_SUCCESS) {
        return ret;
    }
    
    // publish
    int duration_ms = 0;
    int actual_play_kbps = 0;
    if ((ret = publish_start(duration_ms, actual_play_kbps)) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = publish_checking(duration_ms, actual_play_kbps)) != ERROR_SUCCESS) {
        return ret;
    }
    if ((ret = publish_stop()) != ERROR_SUCCESS) {
        return ret;
    }
    
    SrsBandwidthPacket* pkt = NULL;
    if ((ret = final(&pkt)) != ERROR_SUCCESS) {
        return ret;
    }
    SrsAutoFree(SrsBandwidthPacket, pkt);
    
    // get data
    if (true ) {
        SrsAmf0Any* prop = NULL;
        if ((prop = pkt->data->ensure_property_number("play_kbps")) != NULL) {
            *play_kbps = (int)prop->to_number();
        }
        if ((prop = pkt->data->ensure_property_number("publish_kbps")) != NULL) {
            *publish_kbps = (int)prop->to_number();
        }
        if ((prop = pkt->data->ensure_property_number("play_bytes")) != NULL) {
            *play_bytes = (int)prop->to_number();
        }
        if ((prop = pkt->data->ensure_property_number("publish_bytes")) != NULL) {
            *publish_bytes = (int)prop->to_number();
        }
        if ((prop = pkt->data->ensure_property_number("play_time")) != NULL) {
            *play_duration = (int)prop->to_number();
        }
        if ((prop = pkt->data->ensure_property_number("publish_time")) != NULL) {
            *publish_duration = (int)prop->to_number();
        }
    }

    srs_update_system_time_ms();
    *end_time = srs_get_system_time_ms();
    
    return ret;
}

int SrsBandwidthClient::play_start()
{
    int ret = ERROR_SUCCESS;

    if ((ret = _srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_start_play)) != ERROR_SUCCESS) {
        return ret;
    }
    srs_info("BW check recv play begin request.");
    
    if (true) {
        // send start play response to server.
        SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_starting_play();
    
        if ((ret = _rtmp->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
            srs_error("send bandwidth check start play message failed. ret=%d", ret);
            return ret;
        }
    }
    srs_info("BW check play begin.");
    
    return ret;
}

int SrsBandwidthClient::play_checking()
{
    int ret = ERROR_SUCCESS;
    return ret;
}

int SrsBandwidthClient::play_stop()
{
    int ret = ERROR_SUCCESS;

    if ((ret = _srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_stop_play)) != ERROR_SUCCESS) {
        return ret;
    }
    srs_info("BW check recv play stop request.");
    
    if (true) {
        // send stop play response to server.
        SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_stopped_play();
    
        if ((ret = _rtmp->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
            srs_error("send bandwidth check stop play message failed. ret=%d", ret);
            return ret;
        }
    }
    srs_info("BW check play stop.");
    
    return ret;
}

int SrsBandwidthClient::publish_start(int& duration_ms, int& play_kbps)
{
    int ret = ERROR_SUCCESS;

    if (true) {
        SrsBandwidthPacket* pkt = NULL;
        if ((ret = _srs_expect_bandwidth_packet2(_rtmp, _bandwidth_is_start_publish, &pkt)) != ERROR_SUCCESS) {
            return ret;
        }
        SrsAutoFree(SrsBandwidthPacket, pkt);
        
        SrsAmf0Any* prop = NULL;
        if ((prop = pkt->data->ensure_property_number("duration_ms")) != NULL) {
            duration_ms = (int)prop->to_number();
        }
        if ((prop = pkt->data->ensure_property_number("limit_kbps")) != NULL) {
            play_kbps = (int)prop->to_number();
        }
    }
    srs_info("BW check recv publish begin request.");
    
    if (true) {
        // send start publish response to server.
        SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_starting_publish();
    
        if ((ret = _rtmp->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
            srs_error("send bandwidth check start publish message failed. ret=%d", ret);
            return ret;
        }
    }
    srs_info("BW check publish begin.");
    
    return ret;
}

int SrsBandwidthClient::publish_checking(int duration_ms, int play_kbps)
{
    int ret = ERROR_SUCCESS;
    
    if (duration_ms <= 0) {
        ret = ERROR_RTMP_BWTC_DATA;
        srs_error("server must specifies the duration, ret=%d", ret);
        return ret;
    }
    
    if (play_kbps <= 0) {
        ret = ERROR_RTMP_BWTC_DATA;
        srs_error("server must specifies the play kbp, ret=%d", ret);
        return ret;
    }

    int data_count = 1;
    srs_update_system_time_ms();
    int64_t starttime = srs_get_system_time_ms();
    while ((srs_get_system_time_ms() - starttime) < duration_ms) {
        // TODO: FIXME: use shared ptr message.
        SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_publishing();

        // TODO: FIXME: magic number
        for (int i = 0; i < data_count; ++i) {
            std::stringstream seq;
            seq << i;
            std::string play_data = "SRS band check data from server's publishing......";
            pkt->data->set(seq.str(), SrsAmf0Any::str(play_data.c_str()));
        }
        data_count += 2;

        if ((ret = _rtmp->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
            srs_error("send bandwidth check publish messages failed. ret=%d", ret);
            return ret;
        }
        
        // use the play kbps to control the publish
        srs_update_system_time_ms();
        int elaps = (int)(srs_get_system_time_ms() - starttime);
        if (elaps > 0) {
            int current_kbps = (int)(_rtmp->get_send_bytes() * 8 / elaps);
            while (current_kbps > play_kbps) {
                srs_update_system_time_ms();
                elaps = (int)(srs_get_system_time_ms() - starttime);
                current_kbps = (int)(_rtmp->get_send_bytes() * 8 / elaps);
                usleep(100 * 1000); // TODO: FIXME: magic number.
            }
        }
    }
    srs_info("BW check send publish bytes over.");
    
    return ret;
}

int SrsBandwidthClient::publish_stop()
{
    int ret = ERROR_SUCCESS;
    
    if (true) {
        // send start publish response to server.
        SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_stop_publish();
    
        if ((ret = _rtmp->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
            srs_error("send bandwidth check stop publish message failed. ret=%d", ret);
            return ret;
        }
    }
    srs_info("BW client stop publish request.");

    if ((ret = _srs_expect_bandwidth_packet(_rtmp, _bandwidth_is_stop_publish)) != ERROR_SUCCESS) {
        return ret;
    }
    srs_info("BW check recv publish stop request.");
    
    if (true) {
        // send start publish response to server.
        SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_stopped_publish();
    
        if ((ret = _rtmp->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
            srs_error("send bandwidth check stop publish message failed. ret=%d", ret);
            return ret;
        }
    }
    srs_info("BW check publish stop.");
    
    return ret;
}

int SrsBandwidthClient::final(SrsBandwidthPacket** ppkt)
{
    int ret = ERROR_SUCCESS;

    if ((ret = _srs_expect_bandwidth_packet2(_rtmp, _bandwidth_is_finish, ppkt)) != ERROR_SUCCESS) {
        return ret;
    }
    srs_info("BW check recv finish/report request.");
    
    if (true) {
        // send final response to server.
        SrsBandwidthPacket* pkt = SrsBandwidthPacket::create_final();
    
        if ((ret = _rtmp->send_and_free_packet(pkt, 0)) != ERROR_SUCCESS) {
            srs_error("send bandwidth check final message failed. ret=%d", ret);
            return ret;
        }
    }
    srs_info("BW check final.");
    
    return ret;
}

