├── Makefile ├── README.md ├── build-librtmp.sh ├── lib └── sn.txt ├── rtmp.c └── rtmp.h /Makefile: -------------------------------------------------------------------------------- 1 | VERSION=v2.4 2 | 3 | prefix=/usr/local 4 | 5 | incdir=$(prefix)/include/librtmp 6 | bindir=$(prefix)/bin 7 | libdir=$(prefix)/lib 8 | mandir=$(prefix)/man 9 | BINDIR=$(DESTDIR)$(bindir) 10 | INCDIR=$(DESTDIR)$(incdir) 11 | LIBDIR=$(DESTDIR)$(libdir) 12 | MANDIR=$(DESTDIR)$(mandir) 13 | 14 | LD=$(CROSS_COMPILE)ld 15 | AR=/usr/bin/ar 16 | 17 | SYS=posix 18 | CRYPTO= 19 | #CRYPTO=GNUTLS 20 | DEF_POLARSSL=-DUSE_POLARSSL 21 | DEF_OPENSSL=-DUSE_OPENSSL 22 | DEF_GNUTLS=-DUSE_GNUTLS 23 | DEF_=-DNO_CRYPTO 24 | REQ_GNUTLS=gnutls,hogweed,nettle 25 | REQ_OPENSSL=libcrypto 26 | PUB_GNUTLS=-lgmp 27 | LIBZ=-lz 28 | LIBS_posix= 29 | LIBS_darwin= 30 | LIBS_mingw=-lws2_32 -lwinmm -lgdi32 31 | LIB_GNUTLS=-lgnutls -lhogweed -lnettle -lgmp $(LIBZ) 32 | LIB_OPENSSL=-lssl -lcrypto $(LIBZ) 33 | LIB_POLARSSL=-lpolarssl $(LIBZ) 34 | PRIVATE_LIBS=$(LIBS_$(SYS)) 35 | CRYPTO_LIB=$(LIB_$(CRYPTO)) $(PRIVATE_LIBS) 36 | CRYPTO_REQ=$(REQ_$(CRYPTO)) 37 | CRYPTO_DEF=$(DEF_$(CRYPTO)) 38 | PUBLIC_LIBS=$(PUB_$(CRYPTO)) 39 | 40 | SO_VERSION=1 41 | SOX_posix=so 42 | SOX_darwin=dylib 43 | SOX_mingw=dll 44 | SOX=$(SOX_$(SYS)) 45 | SO_posix=.$(SOX).$(SO_VERSION) 46 | SO_darwin=.$(SO_VERSION).$(SOX) 47 | SO_mingw=-$(SO_VERSION).$(SOX) 48 | SO_EXT=$(SO_$(SYS)) 49 | 50 | SODIR_posix=$(LIBDIR) 51 | SODIR_darwin=$(LIBDIR) 52 | SODIR_mingw=$(BINDIR) 53 | SODIR=$(SODIR_$(SYS)) 54 | 55 | SO_LDFLAGS_posix=-shared -Wl,-soname,$@ 56 | SO_LDFLAGS_darwin=-dynamiclib -twolevel_namespace -undefined dynamic_lookup \ 57 | -fno-common -headerpad_max_install_names -install_name $(libdir)/$@ 58 | SO_LDFLAGS_mingw=-shared -Wl,--out-implib,librtmp.dll.a 59 | SO_LDFLAGS=$(SO_LDFLAGS_$(SYS)) 60 | 61 | INSTALL_IMPLIB_posix= 62 | INSTALL_IMPLIB_darwin= 63 | INSTALL_IMPLIB_mingw=cp librtmp.dll.a $(LIBDIR) 64 | INSTALL_IMPLIB=$(INSTALL_IMPLIB_$(SYS)) 65 | 66 | SHARED=yes 67 | SODEF_yes=-fPIC 68 | SOLIB_yes=librtmp$(SO_EXT) 69 | SOINST_yes=install_so 70 | SO_DEF=$(SODEF_$(SHARED)) 71 | SO_LIB=$(SOLIB_$(SHARED)) 72 | SO_INST=$(SOINST_$(SHARED)) 73 | 74 | DEF=-DRTMPDUMP_VERSION=\"$(VERSION)\" $(CRYPTO_DEF) $(XDEF) 75 | OPT=-O2 76 | CFLAGS=-Wall $(XCFLAGS) $(INC) $(DEF) $(OPT) $(SO_DEF) 77 | LDFLAGS=$(XLDFLAGS) 78 | 79 | 80 | OBJS=rtmp.o log.o amf.o hashswf.o parseurl.o 81 | 82 | all: librtmp.a $(SO_LIB) 83 | 84 | clean: 85 | rm -f *.o *.a *.$(SOX) *$(SO_EXT) librtmp.pc 86 | 87 | librtmp.a: $(OBJS) 88 | $(AR) rs $@ $? 89 | 90 | librtmp$(SO_EXT): $(OBJS) 91 | $(CC) $(SO_LDFLAGS) $(LDFLAGS) -o $@ $^ $> $(CRYPTO_LIB) 92 | ln -sf $@ librtmp.$(SOX) 93 | 94 | log.o: log.c log.h Makefile 95 | rtmp.o: rtmp.c rtmp.h rtmp_sys.h handshake.h dh.h log.h amf.h Makefile 96 | amf.o: amf.c amf.h bytes.h log.h Makefile 97 | hashswf.o: hashswf.c http.h rtmp.h rtmp_sys.h Makefile 98 | parseurl.o: parseurl.c rtmp.h rtmp_sys.h log.h Makefile 99 | 100 | librtmp.pc: librtmp.pc.in Makefile 101 | sed -e "s;@prefix@;$(prefix);" -e "s;@libdir@;$(libdir);" \ 102 | -e "s;@VERSION@;$(VERSION);" \ 103 | -e "s;@CRYPTO_REQ@;$(CRYPTO_REQ);" \ 104 | -e "s;@PUBLIC_LIBS@;$(PUBLIC_LIBS);" \ 105 | -e "s;@PRIVATE_LIBS@;$(PRIVATE_LIBS);" librtmp.pc.in > $@ 106 | 107 | install: install_base $(SO_INST) 108 | 109 | install_base: librtmp.a librtmp.pc 110 | -mkdir -p $(INCDIR) $(LIBDIR)/pkgconfig $(MANDIR)/man3 $(SODIR) 111 | cp amf.h http.h log.h rtmp.h $(INCDIR) 112 | cp librtmp.a $(LIBDIR) 113 | cp librtmp.pc $(LIBDIR)/pkgconfig 114 | cp librtmp.3 $(MANDIR)/man3 115 | 116 | install_so: librtmp$(SO_EXT) 117 | cp librtmp$(SO_EXT) $(SODIR) 118 | $(INSTALL_IMPLIB) 119 | cd $(SODIR); ln -sf librtmp$(SO_EXT) librtmp.$(SOX) 120 | 121 | CC=$(CROSS_COMPILE)gcc -arch arm64 122 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # librtmp-for-ipv6 2 | librtmp 运行在ipv6和ipv4的网络下 3 | compile: 4 | ./build-librtmp.sh 5 | 6 | 最后会编译到lib文件夹下,支持i386 x86_64 armv7 armv7s arm64,不打包openssl库 7 | -------------------------------------------------------------------------------- /build-librtmp.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # build-librtmp.sh 4 | # Automated librtmp build script for iPhoneOS and iPhoneSimulator 5 | # 6 | # Created by Min Kim on 10/1/13. 7 | # Copyright (c) 2013 iFactory Lab Limited. All rights reserved. 8 | # 9 | # Licensed under the Apache License, Version 2.0 (the "License"); 10 | # you may not use this file except in compliance with the License. 11 | # You may obtain a copy of the License at 12 | # 13 | # http://www.apache.org/licenses/LICENSE-2.0 14 | # 15 | # Unless required by applicable law or agreed to in writing, software 16 | # distributed under the License is distributed on an "AS IS" BASIS, 17 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 18 | # See the License for the specific language governing permissions and 19 | # limitations under the License. 20 | # 21 | ########################################################################### 22 | # Change values here # 23 | # # 24 | SDKVERSION=9.3 # 25 | # # 26 | ########################################################################### 27 | # # 28 | # Don't change anything under this line! # 29 | # # 30 | ########################################################################### 31 | 32 | CURRENTPATH=`pwd` 33 | ARCHS="i386 x86_64 armv7 armv7s arm64" 34 | LIBRTMPREPO="git://git.ffmpeg.org/rtmpdump" 35 | BUILDPATH="${CURRENTPATH}/build" 36 | LIBPATH="${CURRENTPATH}/lib" 37 | INCLUDEPATH="${CURRENTPATH}/include" 38 | SRCPATH="${CURRENTPATH}/src" 39 | LIBRTMP="librtmp.a" 40 | DEVELOPER=`xcode-select -print-path` 41 | 42 | if [ ! -d "$DEVELOPER" ]; then 43 | echo "xcode path is not set correctly $DEVELOPER does not exist (most likely because of xcode > 4.3)" 44 | echo "run" 45 | echo "sudo xcode-select -switch " 46 | echo "for default installation:" 47 | echo "sudo xcode-select -switch /Applications/Xcode.app/Contents/Developer" 48 | exit 1 49 | fi 50 | 51 | # Check whether openssl has already installed on the machine or not. 52 | # libcrypt.a / libssl.a 53 | 54 | set -e 55 | echo 'Check openssl installation' 56 | if [ -f "${LIBPATH}/libcrypto.a" ] && [ -f "${LIBPATH}/libssl.a" ] && [ -d "${INCLUDEPATH}/openssl" ]; then 57 | echo 'Openssl for iOS has already installed, no need to install openssl' 58 | else 59 | echo 'Openssl for iOS not found, will install openssl for iOS' 60 | # ./build-libssl.sh 61 | echo 'Succeeded to install openssl' 62 | fi 63 | 64 | # Download librtmp source code from git repository 65 | # We assuem the user already installed git client. 66 | echo 'Clone librtmp git repository' 67 | 68 | # Remove the directory if already exist 69 | rm -rf "${SRCPATH}/rtmpdump" 70 | 71 | git clone ${LIBRTMPREPO} src/rtmpdump 72 | 73 | cp rtmp.c ${SRCPATH}/rtmpdump/librtmp/rtmp.c 74 | cp rtmp.h ${SRCPATH}/rtmpdump/librtmp/rtmp.h 75 | cp Makefile ${SRCPATH}/rtmpdump/librtmp/Makefile 76 | cd "${SRCPATH}/rtmpdump/librtmp" 77 | 78 | LIBRTMP_REPO="" 79 | 80 | for ARCH in ${ARCHS} 81 | do 82 | if [ "${ARCH}" == "i386" ] || [ "${ARCH}" == "x86_64" ]; 83 | then 84 | PLATFORM="iPhoneSimulator" 85 | else 86 | PLATFORM="iPhoneOS" 87 | fi 88 | 89 | export CROSS_TOP="${DEVELOPER}/Platforms/${PLATFORM}.platform/Developer" 90 | export CROSS_SDK="${PLATFORM}${SDKVERSION}.sdk" 91 | export BUILD_TOOLS="${DEVELOPER}" 92 | 93 | echo "Building librtmp for ${PLATFORM} ${SDKVERSION} ${ARCH}" 94 | echo "Please wait..." 95 | 96 | # add arch to CC= 97 | sed -ie "s!AR=\$(CROSS_COMPILE)ar!AR=/usr/bin/ar!" "Makefile" 98 | sed -ie "/CC=\$(CROSS_COMPILE)gcc/d" "Makefile" 99 | echo "CC=\$(CROSS_COMPILE)gcc -arch ${ARCH}" >> "Makefile" 100 | 101 | export CROSS_COMPILE="${DEVELOPER}/usr/bin/" 102 | export XCFLAGS="-isysroot ${CROSS_TOP}/SDKs/${CROSS_SDK} -miphoneos-version-min=7.0 -I${INCLUDEPATH} -arch ${ARCH}" 103 | 104 | if [ "${ARCH}" == "i386" ] || [ "${ARCH}" == "x86_64" ]; 105 | then 106 | export XLDFLAGS="-L${LIBPATH} -arch ${ARCH}" 107 | else 108 | export XLDFLAGS="-isysroot ${CROSS_TOP}/SDKs/${CROSS_SDK} -miphoneos-version-min=7.0 -L${LIBPATH} -arch ${ARCH}" 109 | fi 110 | 111 | OUTPATH="${BUILDPATH}/librtmp-${PLATFORM}${SDKVERSION}-${ARCH}.sdk" 112 | mkdir -p "${OUTPATH}" 113 | LOG="${OUTPATH}/build-librtmp.log" 114 | 115 | make SYS=darwin >> "${LOG}" 2>&1 116 | make SYS=darwin prefix="${OUTPATH}" install >> "${LOG}" 2>&1 117 | make clean >> "${LOG}" 2>&1 118 | 119 | LIBRTMP_REPO+="${OUTPATH}/lib/${LIBRTMP} " 120 | done 121 | 122 | echo "Build universal library..." 123 | lipo -create ${LIBRTMP_REPO}-output ${LIBPATH}/${LIBRTMP} 124 | 125 | mkdir -p ${INCLUDEPATH} 126 | cp -R ${BUILDPATH}/librtmp-iPhoneSimulator${SDKVERSION}-i386.sdk/include/ ${INCLUDEPATH}/ 127 | 128 | echo "Building done." 129 | echo "Cleaning up..." 130 | 131 | rm -rf ${SRCPATH}/rtmpdump 132 | echo "Done." 133 | -------------------------------------------------------------------------------- /lib/sn.txt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/zhenwu1981/librtmp-for-ipv6/1f0feeacbdd72560a83c253755c1fcc715a906c3/lib/sn.txt -------------------------------------------------------------------------------- /rtmp.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2005-2008 Team XBMC 3 | * http://www.xbmc.org 4 | * Copyright (C) 2008-2009 Andrej Stepanchuk 5 | * Copyright (C) 2009-2010 Howard Chu 6 | * 7 | * This file is part of librtmp. 8 | * 9 | * librtmp is free software; you can redistribute it and/or modify 10 | * it under the terms of the GNU Lesser General Public License as 11 | * published by the Free Software Foundation; either version 2.1, 12 | * or (at your option) any later version. 13 | * 14 | * librtmp is distributed in the hope that it will be useful, 15 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 16 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 17 | * GNU General Public License for more details. 18 | * 19 | * You should have received a copy of the GNU Lesser General Public License 20 | * along with librtmp see the file COPYING. If not, write to 21 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 22 | * Boston, MA 02110-1301, USA. 23 | * http://www.gnu.org/copyleft/lgpl.html 24 | */ 25 | 26 | #include 27 | #include 28 | #include 29 | #include 30 | #include 31 | 32 | #include "rtmp_sys.h" 33 | #include "log.h" 34 | 35 | #ifdef CRYPTO 36 | #ifdef USE_POLARSSL 37 | #include 38 | #include 39 | #include 40 | #define MD5_DIGEST_LENGTH 16 41 | 42 | static const char *my_dhm_P = 43 | "E4004C1F94182000103D883A448B3F80" \ 44 | "2CE4B44A83301270002C20D0321CFD00" \ 45 | "11CCEF784C26A400F43DFB901BCA7538" \ 46 | "F2C6B176001CF5A0FD16D2C48B1D0C1C" \ 47 | "F6AC8E1DA6BCC3B4E1F96B0564965300" \ 48 | "FFA1D0B601EB2800F489AA512C4B248C" \ 49 | "01F76949A60BB7F00A40B1EAB64BDD48" \ 50 | "E8A700D60B7F1200FA8E77B0A979DABF"; 51 | 52 | static const char *my_dhm_G = "4"; 53 | 54 | #elif defined(USE_GNUTLS) 55 | #include 56 | #define MD5_DIGEST_LENGTH 16 57 | #include 58 | #include 59 | #else /* USE_OPENSSL */ 60 | #include 61 | #include 62 | #include 63 | #include 64 | #include 65 | #endif 66 | TLS_CTX RTMP_TLS_ctx; 67 | #endif 68 | 69 | #define RTMP_SIG_SIZE 1536 70 | #define RTMP_LARGE_HEADER_SIZE 12 71 | 72 | static const int packetSize[] = { 12, 8, 4, 1 }; 73 | 74 | int RTMP_ctrlC; 75 | 76 | const char RTMPProtocolStrings[][7] = { 77 | "RTMP", 78 | "RTMPT", 79 | "RTMPE", 80 | "RTMPTE", 81 | "RTMPS", 82 | "RTMPTS", 83 | "", 84 | "", 85 | "RTMFP" 86 | }; 87 | 88 | const char RTMPProtocolStringsLower[][7] = { 89 | "rtmp", 90 | "rtmpt", 91 | "rtmpe", 92 | "rtmpte", 93 | "rtmps", 94 | "rtmpts", 95 | "", 96 | "", 97 | "rtmfp" 98 | }; 99 | 100 | static const char *RTMPT_cmds[] = { 101 | "open", 102 | "send", 103 | "idle", 104 | "close" 105 | }; 106 | 107 | typedef enum { 108 | RTMPT_OPEN=0, RTMPT_SEND, RTMPT_IDLE, RTMPT_CLOSE 109 | } RTMPTCmd; 110 | 111 | static int DumpMetaData(AMFObject *obj); 112 | static int HandShake(RTMP *r, int FP9HandShake); 113 | static int SocksNegotiate(RTMP *r); 114 | 115 | static int SendConnectPacket(RTMP *r, RTMPPacket *cp); 116 | static int SendCheckBW(RTMP *r); 117 | static int SendCheckBWResult(RTMP *r, double txn); 118 | static int SendDeleteStream(RTMP *r, double dStreamId); 119 | static int SendFCSubscribe(RTMP *r, AVal *subscribepath); 120 | static int SendPlay(RTMP *r); 121 | static int SendBytesReceived(RTMP *r); 122 | static int SendUsherToken(RTMP *r, AVal *usherToken); 123 | 124 | #if 0 /* unused */ 125 | static int SendBGHasStream(RTMP *r, double dId, AVal *playpath); 126 | #endif 127 | 128 | static int HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize); 129 | static int HandleMetadata(RTMP *r, char *body, unsigned int len); 130 | static void HandleChangeChunkSize(RTMP *r, const RTMPPacket *packet); 131 | static void HandleAudio(RTMP *r, const RTMPPacket *packet); 132 | static void HandleVideo(RTMP *r, const RTMPPacket *packet); 133 | static void HandleCtrl(RTMP *r, const RTMPPacket *packet); 134 | static void HandleServerBW(RTMP *r, const RTMPPacket *packet); 135 | static void HandleClientBW(RTMP *r, const RTMPPacket *packet); 136 | 137 | static int ReadN(RTMP *r, char *buffer, int n); 138 | static int WriteN(RTMP *r, const char *buffer, int n); 139 | 140 | static void DecodeTEA(AVal *key, AVal *text); 141 | 142 | static int HTTP_Post(RTMP *r, RTMPTCmd cmd, const char *buf, int len); 143 | static int HTTP_read(RTMP *r, int fill); 144 | 145 | static void CloseInternal(RTMP *r, int reconnect); 146 | 147 | #ifndef _WIN32 148 | static int clk_tck; 149 | #endif 150 | 151 | #ifdef CRYPTO 152 | #include "handshake.h" 153 | #endif 154 | 155 | uint32_t 156 | RTMP_GetTime() 157 | { 158 | #ifdef _DEBUG 159 | return 0; 160 | #elif defined(_WIN32) 161 | return timeGetTime(); 162 | #else 163 | struct tms t; 164 | if (!clk_tck) clk_tck = sysconf(_SC_CLK_TCK); 165 | return times(&t) * 1000 / clk_tck; 166 | #endif 167 | } 168 | 169 | void 170 | RTMP_UserInterrupt() 171 | { 172 | RTMP_ctrlC = TRUE; 173 | } 174 | 175 | void 176 | RTMPPacket_Reset(RTMPPacket *p) 177 | { 178 | p->m_headerType = 0; 179 | p->m_packetType = 0; 180 | p->m_nChannel = 0; 181 | p->m_nTimeStamp = 0; 182 | p->m_nInfoField2 = 0; 183 | p->m_hasAbsTimestamp = FALSE; 184 | p->m_nBodySize = 0; 185 | p->m_nBytesRead = 0; 186 | } 187 | 188 | int 189 | RTMPPacket_Alloc(RTMPPacket *p, uint32_t nSize) 190 | { 191 | char *ptr; 192 | if (nSize > SIZE_MAX - RTMP_MAX_HEADER_SIZE) 193 | return FALSE; 194 | ptr = calloc(1, nSize + RTMP_MAX_HEADER_SIZE); 195 | if (!ptr) 196 | return FALSE; 197 | p->m_body = ptr + RTMP_MAX_HEADER_SIZE; 198 | p->m_nBytesRead = 0; 199 | return TRUE; 200 | } 201 | 202 | void 203 | RTMPPacket_Free(RTMPPacket *p) 204 | { 205 | if (p->m_body) 206 | { 207 | free(p->m_body - RTMP_MAX_HEADER_SIZE); 208 | p->m_body = NULL; 209 | } 210 | } 211 | 212 | void 213 | RTMPPacket_Dump(RTMPPacket *p) 214 | { 215 | RTMP_Log(RTMP_LOGDEBUG, 216 | "RTMP PACKET: packet type: 0x%02x. channel: 0x%02x. info 1: %d info 2: %d. Body size: %u. body: 0x%02x", 217 | p->m_packetType, p->m_nChannel, p->m_nTimeStamp, p->m_nInfoField2, 218 | p->m_nBodySize, p->m_body ? (unsigned char)p->m_body[0] : 0); 219 | } 220 | 221 | int 222 | RTMP_LibVersion() 223 | { 224 | return RTMP_LIB_VERSION; 225 | } 226 | 227 | void 228 | RTMP_TLS_Init() 229 | { 230 | #ifdef CRYPTO 231 | #ifdef USE_POLARSSL 232 | /* Do this regardless of NO_SSL, we use havege for rtmpe too */ 233 | RTMP_TLS_ctx = calloc(1,sizeof(struct tls_ctx)); 234 | havege_init(&RTMP_TLS_ctx->hs); 235 | #elif defined(USE_GNUTLS) && !defined(NO_SSL) 236 | /* Technically we need to initialize libgcrypt ourselves if 237 | * we're not going to call gnutls_global_init(). Ignoring this 238 | * for now. 239 | */ 240 | gnutls_global_init(); 241 | RTMP_TLS_ctx = malloc(sizeof(struct tls_ctx)); 242 | gnutls_certificate_allocate_credentials(&RTMP_TLS_ctx->cred); 243 | gnutls_priority_init(&RTMP_TLS_ctx->prios, "NORMAL", NULL); 244 | gnutls_certificate_set_x509_trust_file(RTMP_TLS_ctx->cred, 245 | "ca.pem", GNUTLS_X509_FMT_PEM); 246 | #elif !defined(NO_SSL) /* USE_OPENSSL */ 247 | /* libcrypto doesn't need anything special */ 248 | SSL_load_error_strings(); 249 | SSL_library_init(); 250 | OpenSSL_add_all_digests(); 251 | RTMP_TLS_ctx = SSL_CTX_new(SSLv23_method()); 252 | SSL_CTX_set_options(RTMP_TLS_ctx, SSL_OP_ALL); 253 | SSL_CTX_set_default_verify_paths(RTMP_TLS_ctx); 254 | #endif 255 | #endif 256 | } 257 | 258 | void * 259 | RTMP_TLS_AllocServerContext(const char* cert, const char* key) 260 | { 261 | void *ctx = NULL; 262 | #ifdef CRYPTO 263 | if (!RTMP_TLS_ctx) 264 | RTMP_TLS_Init(); 265 | #ifdef USE_POLARSSL 266 | tls_server_ctx *tc = ctx = calloc(1, sizeof(struct tls_server_ctx)); 267 | tc->dhm_P = my_dhm_P; 268 | tc->dhm_G = my_dhm_G; 269 | tc->hs = &RTMP_TLS_ctx->hs; 270 | if (x509parse_crtfile(&tc->cert, cert)) { 271 | free(tc); 272 | return NULL; 273 | } 274 | if (x509parse_keyfile(&tc->key, key, NULL)) { 275 | x509_free(&tc->cert); 276 | free(tc); 277 | return NULL; 278 | } 279 | #elif defined(USE_GNUTLS) && !defined(NO_SSL) 280 | gnutls_certificate_allocate_credentials((gnutls_certificate_credentials*) &ctx); 281 | if (gnutls_certificate_set_x509_key_file(ctx, cert, key, GNUTLS_X509_FMT_PEM) != 0) { 282 | gnutls_certificate_free_credentials(ctx); 283 | return NULL; 284 | } 285 | #elif !defined(NO_SSL) /* USE_OPENSSL */ 286 | ctx = SSL_CTX_new(SSLv23_server_method()); 287 | if (!SSL_CTX_use_certificate_chain_file(ctx, cert)) { 288 | SSL_CTX_free(ctx); 289 | return NULL; 290 | } 291 | if (!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) { 292 | SSL_CTX_free(ctx); 293 | return NULL; 294 | } 295 | #endif 296 | #endif 297 | return ctx; 298 | } 299 | 300 | void 301 | RTMP_TLS_FreeServerContext(void *ctx) 302 | { 303 | #ifdef CRYPTO 304 | #ifdef USE_POLARSSL 305 | x509_free(&((tls_server_ctx*)ctx)->cert); 306 | rsa_free(&((tls_server_ctx*)ctx)->key); 307 | free(ctx); 308 | #elif defined(USE_GNUTLS) && !defined(NO_SSL) 309 | gnutls_certificate_free_credentials(ctx); 310 | #elif !defined(NO_SSL) /* USE_OPENSSL */ 311 | SSL_CTX_free(ctx); 312 | #endif 313 | #endif 314 | } 315 | 316 | RTMP * 317 | RTMP_Alloc() 318 | { 319 | return calloc(1, sizeof(RTMP)); 320 | } 321 | 322 | void 323 | RTMP_Free(RTMP *r) 324 | { 325 | free(r); 326 | } 327 | 328 | void 329 | RTMP_Init(RTMP *r) 330 | { 331 | #ifdef CRYPTO 332 | if (!RTMP_TLS_ctx) 333 | RTMP_TLS_Init(); 334 | #endif 335 | 336 | memset(r, 0, sizeof(RTMP)); 337 | r->m_sb.sb_socket = -1; 338 | r->m_inChunkSize = RTMP_DEFAULT_CHUNKSIZE; 339 | r->m_outChunkSize = RTMP_DEFAULT_CHUNKSIZE; 340 | r->m_nBufferMS = 30000; 341 | r->m_nClientBW = 2500000; 342 | r->m_nClientBW2 = 2; 343 | r->m_nServerBW = 2500000; 344 | r->m_fAudioCodecs = 3191.0; 345 | r->m_fVideoCodecs = 252.0; 346 | r->Link.timeout = 30; 347 | r->Link.swfAge = 30; 348 | } 349 | 350 | void 351 | RTMP_EnableWrite(RTMP *r) 352 | { 353 | r->Link.protocol |= RTMP_FEATURE_WRITE; 354 | } 355 | 356 | double 357 | RTMP_GetDuration(RTMP *r) 358 | { 359 | return r->m_fDuration; 360 | } 361 | 362 | int 363 | RTMP_IsConnected(RTMP *r) 364 | { 365 | return r->m_sb.sb_socket != -1; 366 | } 367 | 368 | int 369 | RTMP_Socket(RTMP *r) 370 | { 371 | return r->m_sb.sb_socket; 372 | } 373 | 374 | int 375 | RTMP_IsTimedout(RTMP *r) 376 | { 377 | return r->m_sb.sb_timedout; 378 | } 379 | 380 | void 381 | RTMP_SetBufferMS(RTMP *r, int size) 382 | { 383 | r->m_nBufferMS = size; 384 | } 385 | 386 | void 387 | RTMP_UpdateBufferMS(RTMP *r) 388 | { 389 | RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS); 390 | } 391 | 392 | #undef OSS 393 | #ifdef _WIN32 394 | #define OSS "WIN" 395 | #elif defined(__sun__) 396 | #define OSS "SOL" 397 | #elif defined(__APPLE__) 398 | #define OSS "MAC" 399 | #elif defined(__linux__) 400 | #define OSS "LNX" 401 | #else 402 | #define OSS "GNU" 403 | #endif 404 | #define DEF_VERSTR OSS " 10,0,32,18" 405 | static const char DEFAULT_FLASH_VER[] = DEF_VERSTR; 406 | const AVal RTMP_DefaultFlashVer = 407 | { (char *)DEFAULT_FLASH_VER, sizeof(DEFAULT_FLASH_VER) - 1 }; 408 | 409 | static void 410 | SocksSetup(RTMP *r, AVal *sockshost) 411 | { 412 | printf("sockshost->av_len : %d \n",sockshost->av_len); 413 | if (sockshost->av_len) 414 | { 415 | const char *socksport = strchr(sockshost->av_val, ':'); 416 | char *hostname = strdup(sockshost->av_val); 417 | 418 | if (socksport) 419 | hostname[socksport - sockshost->av_val] = '\0'; 420 | r->Link.sockshost.av_val = hostname; 421 | r->Link.sockshost.av_len = strlen(hostname); 422 | printf("hostname : %s , av_len : %d \n",hostname,strlen(hostname)); 423 | 424 | r->Link.socksport = socksport ? atoi(socksport + 1) : 1080; 425 | RTMP_Log(RTMP_LOGDEBUG, "Connecting via SOCKS proxy: %s:%d", r->Link.sockshost.av_val, 426 | r->Link.socksport); 427 | } 428 | else 429 | { 430 | r->Link.sockshost.av_val = NULL; 431 | r->Link.sockshost.av_len = 0; 432 | r->Link.socksport = 0; 433 | } 434 | } 435 | 436 | void 437 | RTMP_SetupStream(RTMP *r, 438 | int protocol, 439 | AVal *host, 440 | unsigned int port, 441 | AVal *sockshost, 442 | AVal *playpath, 443 | AVal *tcUrl, 444 | AVal *swfUrl, 445 | AVal *pageUrl, 446 | AVal *app, 447 | AVal *auth, 448 | AVal *swfSHA256Hash, 449 | uint32_t swfSize, 450 | AVal *flashVer, 451 | AVal *subscribepath, 452 | AVal *usherToken, 453 | int dStart, 454 | int dStop, int bLiveStream, long int timeout) 455 | { 456 | RTMP_Log(RTMP_LOGDEBUG, "Protocol : %s", RTMPProtocolStrings[protocol&7]); 457 | RTMP_Log(RTMP_LOGDEBUG, "Hostname : %.*s", host->av_len, host->av_val); 458 | RTMP_Log(RTMP_LOGDEBUG, "Port : %d", port); 459 | RTMP_Log(RTMP_LOGDEBUG, "Playpath : %s", playpath->av_val); 460 | 461 | if (tcUrl && tcUrl->av_val) 462 | RTMP_Log(RTMP_LOGDEBUG, "tcUrl : %s", tcUrl->av_val); 463 | if (swfUrl && swfUrl->av_val) 464 | RTMP_Log(RTMP_LOGDEBUG, "swfUrl : %s", swfUrl->av_val); 465 | if (pageUrl && pageUrl->av_val) 466 | RTMP_Log(RTMP_LOGDEBUG, "pageUrl : %s", pageUrl->av_val); 467 | if (app && app->av_val) 468 | RTMP_Log(RTMP_LOGDEBUG, "app : %.*s", app->av_len, app->av_val); 469 | if (auth && auth->av_val) 470 | RTMP_Log(RTMP_LOGDEBUG, "auth : %s", auth->av_val); 471 | if (subscribepath && subscribepath->av_val) 472 | RTMP_Log(RTMP_LOGDEBUG, "subscribepath : %s", subscribepath->av_val); 473 | if (usherToken && usherToken->av_val) 474 | RTMP_Log(RTMP_LOGDEBUG, "NetStream.Authenticate.UsherToken : %s", usherToken->av_val); 475 | if (flashVer && flashVer->av_val) 476 | RTMP_Log(RTMP_LOGDEBUG, "flashVer : %s", flashVer->av_val); 477 | if (dStart > 0) 478 | RTMP_Log(RTMP_LOGDEBUG, "StartTime : %d msec", dStart); 479 | if (dStop > 0) 480 | RTMP_Log(RTMP_LOGDEBUG, "StopTime : %d msec", dStop); 481 | 482 | RTMP_Log(RTMP_LOGDEBUG, "live : %s", bLiveStream ? "yes" : "no"); 483 | RTMP_Log(RTMP_LOGDEBUG, "timeout : %ld sec", timeout); 484 | 485 | #ifdef CRYPTO 486 | if (swfSHA256Hash != NULL && swfSize > 0) 487 | { 488 | memcpy(r->Link.SWFHash, swfSHA256Hash->av_val, sizeof(r->Link.SWFHash)); 489 | r->Link.SWFSize = swfSize; 490 | RTMP_Log(RTMP_LOGDEBUG, "SWFSHA256:"); 491 | RTMP_LogHex(RTMP_LOGDEBUG, r->Link.SWFHash, sizeof(r->Link.SWFHash)); 492 | RTMP_Log(RTMP_LOGDEBUG, "SWFSize : %u", r->Link.SWFSize); 493 | } 494 | else 495 | { 496 | r->Link.SWFSize = 0; 497 | } 498 | #endif 499 | 500 | SocksSetup(r, sockshost); 501 | 502 | if (tcUrl && tcUrl->av_len) 503 | r->Link.tcUrl = *tcUrl; 504 | if (swfUrl && swfUrl->av_len) 505 | r->Link.swfUrl = *swfUrl; 506 | if (pageUrl && pageUrl->av_len) 507 | r->Link.pageUrl = *pageUrl; 508 | if (app && app->av_len) 509 | r->Link.app = *app; 510 | if (auth && auth->av_len) 511 | { 512 | r->Link.auth = *auth; 513 | r->Link.lFlags |= RTMP_LF_AUTH; 514 | } 515 | if (flashVer && flashVer->av_len) 516 | r->Link.flashVer = *flashVer; 517 | else 518 | r->Link.flashVer = RTMP_DefaultFlashVer; 519 | if (subscribepath && subscribepath->av_len) 520 | r->Link.subscribepath = *subscribepath; 521 | if (usherToken && usherToken->av_len) 522 | r->Link.usherToken = *usherToken; 523 | r->Link.seekTime = dStart; 524 | r->Link.stopTime = dStop; 525 | if (bLiveStream) 526 | r->Link.lFlags |= RTMP_LF_LIVE; 527 | r->Link.timeout = timeout; 528 | 529 | r->Link.protocol = protocol; 530 | r->Link.hostname = *host; 531 | r->Link.port = port; 532 | r->Link.playpath = *playpath; 533 | 534 | if (r->Link.port == 0) 535 | { 536 | if (protocol & RTMP_FEATURE_SSL) 537 | r->Link.port = 443; 538 | else if (protocol & RTMP_FEATURE_HTTP) 539 | r->Link.port = 80; 540 | else 541 | r->Link.port = 1935; 542 | } 543 | } 544 | 545 | enum { OPT_STR=0, OPT_INT, OPT_BOOL, OPT_CONN }; 546 | static const char *optinfo[] = { 547 | "string", "integer", "boolean", "AMF" }; 548 | 549 | #define OFF(x) offsetof(struct RTMP,x) 550 | 551 | static struct urlopt { 552 | AVal name; 553 | off_t off; 554 | int otype; 555 | int omisc; 556 | char *use; 557 | } options[] = { 558 | { AVC("socks"), OFF(Link.sockshost), OPT_STR, 0, 559 | "Use the specified SOCKS proxy" }, 560 | { AVC("app"), OFF(Link.app), OPT_STR, 0, 561 | "Name of target app on server" }, 562 | { AVC("tcUrl"), OFF(Link.tcUrl), OPT_STR, 0, 563 | "URL to played stream" }, 564 | { AVC("pageUrl"), OFF(Link.pageUrl), OPT_STR, 0, 565 | "URL of played media's web page" }, 566 | { AVC("swfUrl"), OFF(Link.swfUrl), OPT_STR, 0, 567 | "URL to player SWF file" }, 568 | { AVC("flashver"), OFF(Link.flashVer), OPT_STR, 0, 569 | "Flash version string (default " DEF_VERSTR ")" }, 570 | { AVC("conn"), OFF(Link.extras), OPT_CONN, 0, 571 | "Append arbitrary AMF data to Connect message" }, 572 | { AVC("playpath"), OFF(Link.playpath), OPT_STR, 0, 573 | "Path to target media on server" }, 574 | { AVC("playlist"), OFF(Link.lFlags), OPT_BOOL, RTMP_LF_PLST, 575 | "Set playlist before play command" }, 576 | { AVC("live"), OFF(Link.lFlags), OPT_BOOL, RTMP_LF_LIVE, 577 | "Stream is live, no seeking possible" }, 578 | { AVC("subscribe"), OFF(Link.subscribepath), OPT_STR, 0, 579 | "Stream to subscribe to" }, 580 | { AVC("jtv"), OFF(Link.usherToken), OPT_STR, 0, 581 | "Justin.tv authentication token" }, 582 | { AVC("token"), OFF(Link.token), OPT_STR, 0, 583 | "Key for SecureToken response" }, 584 | { AVC("swfVfy"), OFF(Link.lFlags), OPT_BOOL, RTMP_LF_SWFV, 585 | "Perform SWF Verification" }, 586 | { AVC("swfAge"), OFF(Link.swfAge), OPT_INT, 0, 587 | "Number of days to use cached SWF hash" }, 588 | { AVC("start"), OFF(Link.seekTime), OPT_INT, 0, 589 | "Stream start position in milliseconds" }, 590 | { AVC("stop"), OFF(Link.stopTime), OPT_INT, 0, 591 | "Stream stop position in milliseconds" }, 592 | { AVC("buffer"), OFF(m_nBufferMS), OPT_INT, 0, 593 | "Buffer time in milliseconds" }, 594 | { AVC("timeout"), OFF(Link.timeout), OPT_INT, 0, 595 | "Session timeout in seconds" }, 596 | { AVC("pubUser"), OFF(Link.pubUser), OPT_STR, 0, 597 | "Publisher username" }, 598 | { AVC("pubPasswd"), OFF(Link.pubPasswd), OPT_STR, 0, 599 | "Publisher password" }, 600 | { {NULL,0}, 0, 0} 601 | }; 602 | 603 | static const AVal truth[] = { 604 | AVC("1"), 605 | AVC("on"), 606 | AVC("yes"), 607 | AVC("true"), 608 | {0,0} 609 | }; 610 | 611 | static void RTMP_OptUsage() 612 | { 613 | int i; 614 | 615 | RTMP_Log(RTMP_LOGERROR, "Valid RTMP options are:\n"); 616 | for (i=0; options[i].name.av_len; i++) { 617 | RTMP_Log(RTMP_LOGERROR, "%10s %-7s %s\n", options[i].name.av_val, 618 | optinfo[options[i].otype], options[i].use); 619 | } 620 | } 621 | 622 | static int 623 | parseAMF(AMFObject *obj, AVal *av, int *depth) 624 | { 625 | AMFObjectProperty prop = {{0,0}}; 626 | int i; 627 | char *p, *arg = av->av_val; 628 | 629 | if (arg[1] == ':') 630 | { 631 | p = (char *)arg+2; 632 | switch(arg[0]) 633 | { 634 | case 'B': 635 | prop.p_type = AMF_BOOLEAN; 636 | prop.p_vu.p_number = atoi(p); 637 | break; 638 | case 'S': 639 | prop.p_type = AMF_STRING; 640 | prop.p_vu.p_aval.av_val = p; 641 | prop.p_vu.p_aval.av_len = av->av_len - (p-arg); 642 | break; 643 | case 'N': 644 | prop.p_type = AMF_NUMBER; 645 | prop.p_vu.p_number = strtod(p, NULL); 646 | break; 647 | case 'Z': 648 | prop.p_type = AMF_NULL; 649 | break; 650 | case 'O': 651 | i = atoi(p); 652 | if (i) 653 | { 654 | prop.p_type = AMF_OBJECT; 655 | } 656 | else 657 | { 658 | (*depth)--; 659 | return 0; 660 | } 661 | break; 662 | default: 663 | return -1; 664 | } 665 | } 666 | else if (arg[2] == ':' && arg[0] == 'N') 667 | { 668 | p = strchr(arg+3, ':'); 669 | if (!p || !*depth) 670 | return -1; 671 | prop.p_name.av_val = (char *)arg+3; 672 | prop.p_name.av_len = p - (arg+3); 673 | 674 | p++; 675 | switch(arg[1]) 676 | { 677 | case 'B': 678 | prop.p_type = AMF_BOOLEAN; 679 | prop.p_vu.p_number = atoi(p); 680 | break; 681 | case 'S': 682 | prop.p_type = AMF_STRING; 683 | prop.p_vu.p_aval.av_val = p; 684 | prop.p_vu.p_aval.av_len = av->av_len - (p-arg); 685 | break; 686 | case 'N': 687 | prop.p_type = AMF_NUMBER; 688 | prop.p_vu.p_number = strtod(p, NULL); 689 | break; 690 | case 'O': 691 | prop.p_type = AMF_OBJECT; 692 | break; 693 | default: 694 | return -1; 695 | } 696 | } 697 | else 698 | return -1; 699 | 700 | if (*depth) 701 | { 702 | AMFObject *o2; 703 | for (i=0; i<*depth; i++) 704 | { 705 | o2 = &obj->o_props[obj->o_num-1].p_vu.p_object; 706 | obj = o2; 707 | } 708 | } 709 | AMF_AddProp(obj, &prop); 710 | if (prop.p_type == AMF_OBJECT) 711 | (*depth)++; 712 | return 0; 713 | } 714 | 715 | int RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg) 716 | { 717 | int i; 718 | void *v; 719 | 720 | for (i=0; options[i].name.av_len; i++) { 721 | if (opt->av_len != options[i].name.av_len) continue; 722 | if (strcasecmp(opt->av_val, options[i].name.av_val)) continue; 723 | v = (char *)r + options[i].off; 724 | switch(options[i].otype) { 725 | case OPT_STR: { 726 | AVal *aptr = v; 727 | *aptr = *arg; } 728 | break; 729 | case OPT_INT: { 730 | long l = strtol(arg->av_val, NULL, 0); 731 | *(int *)v = l; } 732 | break; 733 | case OPT_BOOL: { 734 | int j, fl; 735 | fl = *(int *)v; 736 | for (j=0; truth[j].av_len; j++) { 737 | if (arg->av_len != truth[j].av_len) continue; 738 | if (strcasecmp(arg->av_val, truth[j].av_val)) continue; 739 | fl |= options[i].omisc; break; } 740 | *(int *)v = fl; 741 | } 742 | break; 743 | case OPT_CONN: 744 | if (parseAMF(&r->Link.extras, arg, &r->Link.edepth)) 745 | return FALSE; 746 | break; 747 | } 748 | break; 749 | } 750 | if (!options[i].name.av_len) { 751 | RTMP_Log(RTMP_LOGERROR, "Unknown option %s", opt->av_val); 752 | RTMP_OptUsage(); 753 | return FALSE; 754 | } 755 | 756 | return TRUE; 757 | } 758 | 759 | int RTMP_SetupURL(RTMP *r, char *url) 760 | { 761 | AVal opt, arg; 762 | char *p1, *p2, *ptr = strchr(url, ' '); 763 | int ret, len; 764 | unsigned int port = 0; 765 | 766 | if (ptr) 767 | *ptr = '\0'; 768 | 769 | len = strlen(url); 770 | ret = RTMP_ParseURL(url, &r->Link.protocol, &r->Link.hostname, 771 | &port, &r->Link.playpath0, &r->Link.app); 772 | printf("hostname : %s , port : %d \n",r->Link.hostname.av_val,port); 773 | if (!ret) 774 | return ret; 775 | r->Link.port = port; 776 | r->Link.playpath = r->Link.playpath0; 777 | 778 | while (ptr) { 779 | *ptr++ = '\0'; 780 | p1 = ptr; 781 | p2 = strchr(p1, '='); 782 | if (!p2) 783 | break; 784 | opt.av_val = p1; 785 | opt.av_len = p2 - p1; 786 | *p2++ = '\0'; 787 | arg.av_val = p2; 788 | ptr = strchr(p2, ' '); 789 | if (ptr) { 790 | *ptr = '\0'; 791 | arg.av_len = ptr - p2; 792 | /* skip repeated spaces */ 793 | while(ptr[1] == ' ') 794 | *ptr++ = '\0'; 795 | } else { 796 | arg.av_len = strlen(p2); 797 | } 798 | 799 | /* unescape */ 800 | port = arg.av_len; 801 | for (p1=p2; port >0;) { 802 | if (*p1 == '\\') { 803 | unsigned int c; 804 | if (port < 3) 805 | return FALSE; 806 | sscanf(p1+1, "%02x", &c); 807 | *p2++ = c; 808 | port -= 3; 809 | p1 += 3; 810 | } else { 811 | *p2++ = *p1++; 812 | port--; 813 | } 814 | } 815 | arg.av_len = p2 - arg.av_val; 816 | 817 | ret = RTMP_SetOpt(r, &opt, &arg); 818 | if (!ret) 819 | return ret; 820 | } 821 | 822 | if (!r->Link.tcUrl.av_len) 823 | { 824 | r->Link.tcUrl.av_val = url; 825 | if (r->Link.app.av_len) 826 | { 827 | if (r->Link.app.av_val < url + len) 828 | { 829 | /* if app is part of original url, just use it */ 830 | r->Link.tcUrl.av_len = r->Link.app.av_len + (r->Link.app.av_val - url); 831 | } 832 | else 833 | { 834 | len = r->Link.hostname.av_len + r->Link.app.av_len + 835 | sizeof("rtmpte://:65535/"); 836 | r->Link.tcUrl.av_val = malloc(len); 837 | r->Link.tcUrl.av_len = snprintf(r->Link.tcUrl.av_val, len, 838 | "%s://%.*s:%d/%.*s", 839 | RTMPProtocolStringsLower[r->Link.protocol], 840 | r->Link.hostname.av_len, r->Link.hostname.av_val, 841 | r->Link.port, 842 | r->Link.app.av_len, r->Link.app.av_val); 843 | r->Link.lFlags |= RTMP_LF_FTCU; 844 | } 845 | } 846 | else 847 | { 848 | r->Link.tcUrl.av_len = strlen(url); 849 | } 850 | } 851 | 852 | #ifdef CRYPTO 853 | if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len) 854 | RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize, 855 | (unsigned char *)r->Link.SWFHash, r->Link.swfAge); 856 | #endif 857 | 858 | SocksSetup(r, &r->Link.sockshost); 859 | 860 | if (r->Link.port == 0) 861 | { 862 | if (r->Link.protocol & RTMP_FEATURE_SSL) 863 | r->Link.port = 443; 864 | else if (r->Link.protocol & RTMP_FEATURE_HTTP) 865 | r->Link.port = 80; 866 | else 867 | r->Link.port = 1935; 868 | } 869 | return TRUE; 870 | } 871 | 872 | static int 873 | add_addr_info(struct sockaddr_in *service, AVal *host, int port) 874 | { 875 | char *hostname; 876 | int ret = TRUE; 877 | printf("host : %s , len : %d \n",host->av_val,host->av_len); 878 | if (host->av_val[host->av_len]) 879 | { 880 | printf("1 \n"); 881 | hostname = malloc(host->av_len+1); 882 | memcpy(hostname, host->av_val, host->av_len); 883 | hostname[host->av_len] = '\0'; 884 | } 885 | else 886 | { 887 | printf("2 \n"); 888 | hostname = host->av_val; 889 | } 890 | 891 | printf("hostname : %s \n",hostname); 892 | 893 | service->sin_addr.s_addr = inet_addr(hostname); 894 | printf("1 -- service->sin_addr.s_addr : %lu \n",service->sin_addr.s_addr); 895 | if (service->sin_addr.s_addr == INADDR_NONE) 896 | { 897 | struct hostent *host = gethostbyname(hostname); 898 | if (host == NULL || host->h_addr == NULL) 899 | { 900 | RTMP_Log(RTMP_LOGERROR, "Problem accessing the DNS. (addr: %s)", hostname); 901 | ret = FALSE; 902 | goto finish; 903 | } 904 | printf("2 \n"); 905 | service->sin_addr = *(struct in_addr *)host->h_addr; 906 | } 907 | 908 | printf("2 -- service->sin_addr.s_addr : %lu \n",service->sin_addr.s_addr); 909 | 910 | service->sin_port = htons(port); 911 | printf("sin_port : %d \n",service->sin_port); 912 | finish: 913 | if (hostname != host->av_val) 914 | free(hostname); 915 | return ret; 916 | } 917 | 918 | static int 919 | add_addr_info_ipv6(struct sockaddr_in6 *service, AVal *host, int port) 920 | { 921 | char *hostname; 922 | int ret = TRUE; 923 | if (host->av_val[host->av_len]) 924 | { 925 | hostname = malloc(host->av_len+1); 926 | memcpy(hostname, host->av_val, host->av_len); 927 | hostname[host->av_len] = '\0'; 928 | } 929 | else 930 | { 931 | hostname = host->av_val; 932 | } 933 | 934 | printf("hostname : %s \n",hostname); 935 | 936 | if (inet_pton(AF_INET6, hostname, &service->sin6_addr) <= 0) 937 | { 938 | printf("1"); 939 | struct hostent *host = gethostbyname2(hostname,AF_INET6); 940 | if (host == NULL || host->h_addr == NULL) 941 | { 942 | RTMP_Log(RTMP_LOGERROR, "Problem accessing the DNS. (addr: %s)", hostname); 943 | ret = FALSE; 944 | goto finish; 945 | } 946 | 947 | memcpy(service->sin6_addr.__u6_addr.__u6_addr8, host->h_addr, host->h_length ); 948 | } 949 | 950 | printf("inet_pton: 0x%x\n", service->sin6_addr); 951 | 952 | service->sin6_port = htons(port); 953 | 954 | printf("sin6_port : %d \n",service->sin6_port); 955 | finish: 956 | if (hostname != host->av_val) 957 | free(hostname); 958 | return ret; 959 | } 960 | 961 | 962 | int 963 | RTMP_Connect0(RTMP *r, struct sockaddr * service) 964 | { 965 | int on = 1; 966 | r->m_sb.sb_timedout = FALSE; 967 | r->m_pausing = 0; 968 | r->m_fDuration = 0.0; 969 | 970 | r->m_sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); 971 | if (r->m_sb.sb_socket != -1) 972 | { 973 | if (connect(r->m_sb.sb_socket, service, sizeof(struct sockaddr)) < 0) 974 | { 975 | int err = GetSockError(); 976 | RTMP_Log(RTMP_LOGERROR, "%s, failed to connect socket. %d (%s)", 977 | __FUNCTION__, err, strerror(err)); 978 | RTMP_Close(r); 979 | return FALSE; 980 | } 981 | 982 | if (r->Link.socksport) 983 | { 984 | RTMP_Log(RTMP_LOGDEBUG, "%s ... SOCKS negotiation", __FUNCTION__); 985 | if (!SocksNegotiate(r)) 986 | { 987 | RTMP_Log(RTMP_LOGERROR, "%s, SOCKS negotiation failed.", __FUNCTION__); 988 | RTMP_Close(r); 989 | return FALSE; 990 | } 991 | } 992 | } 993 | else 994 | { 995 | RTMP_Log(RTMP_LOGERROR, "%s, failed to create socket. Error: %d", __FUNCTION__, 996 | GetSockError()); 997 | return FALSE; 998 | } 999 | 1000 | /* set timeout */ 1001 | { 1002 | SET_RCVTIMEO(tv, r->Link.timeout); 1003 | if (setsockopt 1004 | (r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv))) 1005 | { 1006 | RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!", 1007 | __FUNCTION__, r->Link.timeout); 1008 | } 1009 | } 1010 | 1011 | setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)); 1012 | 1013 | return TRUE; 1014 | } 1015 | 1016 | int 1017 | RTMP_Connect0_IPV6(RTMP *r, struct sockaddr_in6 * service) 1018 | { 1019 | int on = 1; 1020 | r->m_sb.sb_timedout = FALSE; 1021 | r->m_pausing = 0; 1022 | r->m_fDuration = 0.0; 1023 | 1024 | r->m_sb.sb_socket = socket(PF_INET6, SOCK_STREAM, IPPROTO_TCP); 1025 | if (r->m_sb.sb_socket != -1) 1026 | { 1027 | printf("port : %d , RTMP_Connect0_IPV6: 0x%x \n",service->sin6_port, service->sin6_addr); 1028 | if (connect(r->m_sb.sb_socket, service, sizeof(struct sockaddr_in6)) < 0) 1029 | { 1030 | int err = GetSockError(); 1031 | RTMP_Log(RTMP_LOGERROR, "%s, failed to connect socket. %d (%s)", 1032 | __FUNCTION__, err, strerror(err)); 1033 | RTMP_Close(r); 1034 | return FALSE; 1035 | } 1036 | 1037 | if (r->Link.socksport) 1038 | { 1039 | RTMP_Log(RTMP_LOGDEBUG, "%s ... SOCKS negotiation", __FUNCTION__); 1040 | if (!SocksNegotiate(r)) 1041 | { 1042 | RTMP_Log(RTMP_LOGERROR, "%s, SOCKS negotiation failed.", __FUNCTION__); 1043 | RTMP_Close(r); 1044 | return FALSE; 1045 | } 1046 | } 1047 | } 1048 | else 1049 | { 1050 | RTMP_Log(RTMP_LOGERROR, "%s, failed to create socket. Error: %d", __FUNCTION__, 1051 | GetSockError()); 1052 | return FALSE; 1053 | } 1054 | 1055 | /* set timeout */ 1056 | { 1057 | SET_RCVTIMEO(tv, r->Link.timeout); 1058 | if (setsockopt 1059 | (r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *)&tv, sizeof(tv))) 1060 | { 1061 | RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %ds failed!", 1062 | __FUNCTION__, r->Link.timeout); 1063 | } 1064 | } 1065 | 1066 | setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on)); 1067 | 1068 | return TRUE; 1069 | } 1070 | 1071 | int 1072 | RTMP_TLS_Accept(RTMP *r, void *ctx) 1073 | { 1074 | #if defined(CRYPTO) && !defined(NO_SSL) 1075 | TLS_server(ctx, r->m_sb.sb_ssl); 1076 | TLS_setfd(r->m_sb.sb_ssl, r->m_sb.sb_socket); 1077 | if (TLS_accept(r->m_sb.sb_ssl) < 0) 1078 | { 1079 | RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__); 1080 | return FALSE; 1081 | } 1082 | return TRUE; 1083 | #else 1084 | return FALSE; 1085 | #endif 1086 | } 1087 | 1088 | int 1089 | RTMP_Connect1(RTMP *r, RTMPPacket *cp) 1090 | { 1091 | if (r->Link.protocol & RTMP_FEATURE_SSL) 1092 | { 1093 | #if defined(CRYPTO) && !defined(NO_SSL) 1094 | TLS_client(RTMP_TLS_ctx, r->m_sb.sb_ssl); 1095 | TLS_setfd(r->m_sb.sb_ssl, r->m_sb.sb_socket); 1096 | if (TLS_connect(r->m_sb.sb_ssl) < 0) 1097 | { 1098 | RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__); 1099 | RTMP_Close(r); 1100 | return FALSE; 1101 | } 1102 | #else 1103 | RTMP_Log(RTMP_LOGERROR, "%s, no SSL/TLS support", __FUNCTION__); 1104 | RTMP_Close(r); 1105 | return FALSE; 1106 | 1107 | #endif 1108 | } 1109 | if (r->Link.protocol & RTMP_FEATURE_HTTP) 1110 | { 1111 | r->m_msgCounter = 1; 1112 | r->m_clientID.av_val = NULL; 1113 | r->m_clientID.av_len = 0; 1114 | HTTP_Post(r, RTMPT_OPEN, "", 1); 1115 | if (HTTP_read(r, 1) != 0) 1116 | { 1117 | r->m_msgCounter = 0; 1118 | RTMP_Log(RTMP_LOGDEBUG, "%s, Could not connect for handshake", __FUNCTION__); 1119 | RTMP_Close(r); 1120 | return 0; 1121 | } 1122 | r->m_msgCounter = 0; 1123 | } 1124 | RTMP_Log(RTMP_LOGDEBUG, "%s, ... connected, handshaking", __FUNCTION__); 1125 | if (!HandShake(r, TRUE)) 1126 | { 1127 | RTMP_Log(RTMP_LOGERROR, "%s, handshake failed.", __FUNCTION__); 1128 | RTMP_Close(r); 1129 | return FALSE; 1130 | } 1131 | RTMP_Log(RTMP_LOGDEBUG, "%s, handshaked", __FUNCTION__); 1132 | 1133 | if (!SendConnectPacket(r, cp)) 1134 | { 1135 | RTMP_Log(RTMP_LOGERROR, "%s, RTMP connect failed.", __FUNCTION__); 1136 | RTMP_Close(r); 1137 | return FALSE; 1138 | } 1139 | return TRUE; 1140 | } 1141 | 1142 | int 1143 | RTMP_Connect(RTMP *r, RTMPPacket *cp) 1144 | { 1145 | struct addrinfo *answer,hints; 1146 | char *hostname; 1147 | AVal *host = NULL; 1148 | 1149 | if (r->Link.socksport) 1150 | { 1151 | // printf("sockshost : %s , len : %d , port : %d \n",r->Link.sockshost.av_val,r->Link.sockshost.av_len,r->Link.socksport); 1152 | host = &r->Link.sockshost; 1153 | } 1154 | else 1155 | { 1156 | // printf("sockshost : %s , len : %d , port : %d \n",r->Link.hostname.av_val,r->Link.hostname.av_len,r->Link.socksport); 1157 | host = &r->Link.hostname; 1158 | } 1159 | 1160 | if (host->av_val[host->av_len]) 1161 | { 1162 | hostname = malloc(host->av_len+1); 1163 | memcpy(hostname, host->av_val, host->av_len); 1164 | hostname[host->av_len] = '\0'; 1165 | } 1166 | else 1167 | { 1168 | hostname = host->av_val; 1169 | } 1170 | 1171 | // printf("1 -- hostname : %s \n",hostname); 1172 | int ret = getaddrinfo(hostname, NULL,NULL, &answer); 1173 | if (ret != 0) 1174 | { 1175 | printf("error -- %s \n",gai_strerror(ret)); 1176 | return FALSE; 1177 | } 1178 | 1179 | switch (answer->ai_family){ 1180 | case AF_UNSPEC: 1181 | printf("AF_UNSPEC \n"); 1182 | return FALSE; 1183 | case AF_INET: 1184 | printf("AF_INET \n"); 1185 | return RTMP_Connect_Ipv4(r,cp); 1186 | case AF_INET6: 1187 | printf("AF_INET6 \n"); 1188 | return RTMP_Connect_Ipv6(r,cp); 1189 | } 1190 | } 1191 | 1192 | int 1193 | RTMP_Connect_Ipv4(RTMP *r, RTMPPacket *cp) 1194 | { 1195 | struct sockaddr_in service; 1196 | if (!r->Link.hostname.av_len) 1197 | return FALSE; 1198 | 1199 | memset(&service, 0, sizeof(struct sockaddr_in)); 1200 | service.sin_family = AF_INET; 1201 | 1202 | // printf("sockshost : %s , len : %d , port : %d \n",r->Link.hostname.av_val,r->Link.hostname.av_len,r->Link.socksport); 1203 | 1204 | if (r->Link.socksport) 1205 | { 1206 | /* Connect via SOCKS */ 1207 | if (!add_addr_info(&service, &r->Link.sockshost, r->Link.socksport)) 1208 | return FALSE; 1209 | } 1210 | else 1211 | { 1212 | /* Connect directly */ 1213 | if (!add_addr_info(&service, &r->Link.hostname, r->Link.port)) 1214 | return FALSE; 1215 | } 1216 | 1217 | if (!RTMP_Connect0(r, (struct sockaddr *)&service)) 1218 | return FALSE; 1219 | 1220 | r->m_bSendCounter = TRUE; 1221 | 1222 | return RTMP_Connect1(r, cp); 1223 | } 1224 | 1225 | int RTMP_Connect_Ipv6(RTMP *r, RTMPPacket *cp) 1226 | { 1227 | struct sockaddr_in6 service; 1228 | if (!r->Link.hostname.av_len) 1229 | return FALSE; 1230 | 1231 | memset(&service, 0, sizeof(struct sockaddr_in6)); 1232 | service.sin6_family = AF_INET6; 1233 | 1234 | if (r->Link.socksport) 1235 | { 1236 | /* Connect via SOCKS */ 1237 | if (!add_addr_info_ipv6(&service, &r->Link.sockshost, r->Link.socksport)) 1238 | return FALSE; 1239 | } 1240 | else 1241 | { 1242 | /* Connect directly */ 1243 | if (!add_addr_info_ipv6(&service, &r->Link.hostname, r->Link.port)) 1244 | return FALSE; 1245 | } 1246 | 1247 | if (!RTMP_Connect0_IPV6(r, (struct sockaddr_in6 *)&service)) 1248 | return FALSE; 1249 | 1250 | r->m_bSendCounter = TRUE; 1251 | 1252 | return RTMP_Connect1(r, cp); 1253 | } 1254 | 1255 | static int 1256 | SocksNegotiate(RTMP *r) 1257 | { 1258 | unsigned long addr; 1259 | struct sockaddr_in service; 1260 | memset(&service, 0, sizeof(struct sockaddr_in)); 1261 | 1262 | add_addr_info(&service, &r->Link.hostname, r->Link.port); 1263 | addr = htonl(service.sin_addr.s_addr); 1264 | 1265 | { 1266 | char packet[] = { 1267 | 4, 1, /* SOCKS 4, connect */ 1268 | (r->Link.port >> 8) & 0xFF, 1269 | (r->Link.port) & 0xFF, 1270 | (char)(addr >> 24) & 0xFF, (char)(addr >> 16) & 0xFF, 1271 | (char)(addr >> 8) & 0xFF, (char)addr & 0xFF, 1272 | 0 1273 | }; /* NULL terminate */ 1274 | 1275 | WriteN(r, packet, sizeof packet); 1276 | 1277 | if (ReadN(r, packet, 8) != 8) 1278 | return FALSE; 1279 | 1280 | if (packet[0] == 0 && packet[1] == 90) 1281 | { 1282 | return TRUE; 1283 | } 1284 | else 1285 | { 1286 | RTMP_Log(RTMP_LOGERROR, "%s, SOCKS returned error code %d", __FUNCTION__, packet[1]); 1287 | return FALSE; 1288 | } 1289 | } 1290 | } 1291 | 1292 | int 1293 | RTMP_ConnectStream(RTMP *r, int seekTime) 1294 | { 1295 | RTMPPacket packet = { 0 }; 1296 | 1297 | /* seekTime was already set by SetupStream / SetupURL. 1298 | * This is only needed by ReconnectStream. 1299 | */ 1300 | if (seekTime > 0) 1301 | r->Link.seekTime = seekTime; 1302 | 1303 | r->m_mediaChannel = 0; 1304 | 1305 | while (!r->m_bPlaying && RTMP_IsConnected(r) && RTMP_ReadPacket(r, &packet)) 1306 | { 1307 | if (RTMPPacket_IsReady(&packet)) 1308 | { 1309 | if (!packet.m_nBodySize) 1310 | continue; 1311 | if ((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO) || 1312 | (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO) || 1313 | (packet.m_packetType == RTMP_PACKET_TYPE_INFO)) 1314 | { 1315 | RTMP_Log(RTMP_LOGWARNING, "Received FLV packet before play()! Ignoring."); 1316 | RTMPPacket_Free(&packet); 1317 | continue; 1318 | } 1319 | 1320 | RTMP_ClientPacket(r, &packet); 1321 | RTMPPacket_Free(&packet); 1322 | } 1323 | } 1324 | 1325 | return r->m_bPlaying; 1326 | } 1327 | 1328 | int 1329 | RTMP_ReconnectStream(RTMP *r, int seekTime) 1330 | { 1331 | RTMP_DeleteStream(r); 1332 | 1333 | RTMP_SendCreateStream(r); 1334 | 1335 | return RTMP_ConnectStream(r, seekTime); 1336 | } 1337 | 1338 | int 1339 | RTMP_ToggleStream(RTMP *r) 1340 | { 1341 | int res; 1342 | 1343 | if (!r->m_pausing) 1344 | { 1345 | if (RTMP_IsTimedout(r) && r->m_read.status == RTMP_READ_EOF) 1346 | r->m_read.status = 0; 1347 | 1348 | res = RTMP_SendPause(r, TRUE, r->m_pauseStamp); 1349 | if (!res) 1350 | return res; 1351 | 1352 | r->m_pausing = 1; 1353 | sleep(1); 1354 | } 1355 | res = RTMP_SendPause(r, FALSE, r->m_pauseStamp); 1356 | r->m_pausing = 3; 1357 | return res; 1358 | } 1359 | 1360 | void 1361 | RTMP_DeleteStream(RTMP *r) 1362 | { 1363 | if (r->m_stream_id < 0) 1364 | return; 1365 | 1366 | r->m_bPlaying = FALSE; 1367 | 1368 | SendDeleteStream(r, r->m_stream_id); 1369 | r->m_stream_id = -1; 1370 | } 1371 | 1372 | int 1373 | RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet) 1374 | { 1375 | int bHasMediaPacket = 0; 1376 | 1377 | while (!bHasMediaPacket && RTMP_IsConnected(r) 1378 | && RTMP_ReadPacket(r, packet)) 1379 | { 1380 | if (!RTMPPacket_IsReady(packet) || !packet->m_nBodySize) 1381 | { 1382 | continue; 1383 | } 1384 | 1385 | bHasMediaPacket = RTMP_ClientPacket(r, packet); 1386 | 1387 | if (!bHasMediaPacket) 1388 | { 1389 | RTMPPacket_Free(packet); 1390 | } 1391 | else if (r->m_pausing == 3) 1392 | { 1393 | if (packet->m_nTimeStamp <= r->m_mediaStamp) 1394 | { 1395 | bHasMediaPacket = 0; 1396 | #ifdef _DEBUG 1397 | RTMP_Log(RTMP_LOGDEBUG, 1398 | "Skipped type: %02X, size: %d, TS: %d ms, abs TS: %d, pause: %d ms", 1399 | packet->m_packetType, packet->m_nBodySize, 1400 | packet->m_nTimeStamp, packet->m_hasAbsTimestamp, 1401 | r->m_mediaStamp); 1402 | #endif 1403 | RTMPPacket_Free(packet); 1404 | continue; 1405 | } 1406 | r->m_pausing = 0; 1407 | } 1408 | } 1409 | 1410 | if (bHasMediaPacket) 1411 | r->m_bPlaying = TRUE; 1412 | else if (r->m_sb.sb_timedout && !r->m_pausing) 1413 | r->m_pauseStamp = r->m_mediaChannel < r->m_channelsAllocatedIn ? 1414 | r->m_channelTimestamp[r->m_mediaChannel] : 0; 1415 | 1416 | return bHasMediaPacket; 1417 | } 1418 | 1419 | int 1420 | RTMP_ClientPacket(RTMP *r, RTMPPacket *packet) 1421 | { 1422 | int bHasMediaPacket = 0; 1423 | switch (packet->m_packetType) 1424 | { 1425 | case RTMP_PACKET_TYPE_CHUNK_SIZE: 1426 | /* chunk size */ 1427 | HandleChangeChunkSize(r, packet); 1428 | break; 1429 | 1430 | case RTMP_PACKET_TYPE_BYTES_READ_REPORT: 1431 | /* bytes read report */ 1432 | RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__); 1433 | break; 1434 | 1435 | case RTMP_PACKET_TYPE_CONTROL: 1436 | /* ctrl */ 1437 | HandleCtrl(r, packet); 1438 | break; 1439 | 1440 | case RTMP_PACKET_TYPE_SERVER_BW: 1441 | /* server bw */ 1442 | HandleServerBW(r, packet); 1443 | break; 1444 | 1445 | case RTMP_PACKET_TYPE_CLIENT_BW: 1446 | /* client bw */ 1447 | HandleClientBW(r, packet); 1448 | break; 1449 | 1450 | case RTMP_PACKET_TYPE_AUDIO: 1451 | /* audio data */ 1452 | /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */ 1453 | HandleAudio(r, packet); 1454 | bHasMediaPacket = 1; 1455 | if (!r->m_mediaChannel) 1456 | r->m_mediaChannel = packet->m_nChannel; 1457 | if (!r->m_pausing) 1458 | r->m_mediaStamp = packet->m_nTimeStamp; 1459 | break; 1460 | 1461 | case RTMP_PACKET_TYPE_VIDEO: 1462 | /* video data */ 1463 | /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */ 1464 | HandleVideo(r, packet); 1465 | bHasMediaPacket = 1; 1466 | if (!r->m_mediaChannel) 1467 | r->m_mediaChannel = packet->m_nChannel; 1468 | if (!r->m_pausing) 1469 | r->m_mediaStamp = packet->m_nTimeStamp; 1470 | break; 1471 | 1472 | case RTMP_PACKET_TYPE_FLEX_STREAM_SEND: 1473 | /* flex stream send */ 1474 | RTMP_Log(RTMP_LOGDEBUG, 1475 | "%s, flex stream send, size %u bytes, not supported, ignoring", 1476 | __FUNCTION__, packet->m_nBodySize); 1477 | break; 1478 | 1479 | case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT: 1480 | /* flex shared object */ 1481 | RTMP_Log(RTMP_LOGDEBUG, 1482 | "%s, flex shared object, size %u bytes, not supported, ignoring", 1483 | __FUNCTION__, packet->m_nBodySize); 1484 | break; 1485 | 1486 | case RTMP_PACKET_TYPE_FLEX_MESSAGE: 1487 | /* flex message */ 1488 | { 1489 | RTMP_Log(RTMP_LOGDEBUG, 1490 | "%s, flex message, size %u bytes, not fully supported", 1491 | __FUNCTION__, packet->m_nBodySize); 1492 | /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */ 1493 | 1494 | /* some DEBUG code */ 1495 | #if 0 1496 | RTMP_LIB_AMFObject obj; 1497 | int nRes = obj.Decode(packet.m_body+1, packet.m_nBodySize-1); 1498 | if(nRes < 0) { 1499 | RTMP_Log(RTMP_LOGERROR, "%s, error decoding AMF3 packet", __FUNCTION__); 1500 | /*return; */ 1501 | } 1502 | 1503 | obj.Dump(); 1504 | #endif 1505 | 1506 | if (HandleInvoke(r, packet->m_body + 1, packet->m_nBodySize - 1) == 1) 1507 | bHasMediaPacket = 2; 1508 | break; 1509 | } 1510 | case RTMP_PACKET_TYPE_INFO: 1511 | /* metadata (notify) */ 1512 | RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %u bytes", __FUNCTION__, 1513 | packet->m_nBodySize); 1514 | if (HandleMetadata(r, packet->m_body, packet->m_nBodySize)) 1515 | bHasMediaPacket = 1; 1516 | break; 1517 | 1518 | case RTMP_PACKET_TYPE_SHARED_OBJECT: 1519 | RTMP_Log(RTMP_LOGDEBUG, "%s, shared object, not supported, ignoring", 1520 | __FUNCTION__); 1521 | break; 1522 | 1523 | case RTMP_PACKET_TYPE_INVOKE: 1524 | /* invoke */ 1525 | RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__, 1526 | packet->m_nBodySize); 1527 | /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */ 1528 | 1529 | if (HandleInvoke(r, packet->m_body, packet->m_nBodySize) == 1) 1530 | bHasMediaPacket = 2; 1531 | break; 1532 | 1533 | case RTMP_PACKET_TYPE_FLASH_VIDEO: 1534 | { 1535 | /* go through FLV packets and handle metadata packets */ 1536 | unsigned int pos = 0; 1537 | uint32_t nTimeStamp = packet->m_nTimeStamp; 1538 | 1539 | while (pos + 11 < packet->m_nBodySize) 1540 | { 1541 | uint32_t dataSize = AMF_DecodeInt24(packet->m_body + pos + 1); /* size without header (11) and prevTagSize (4) */ 1542 | 1543 | if (pos + 11 + dataSize + 4 > packet->m_nBodySize) 1544 | { 1545 | RTMP_Log(RTMP_LOGWARNING, "Stream corrupt?!"); 1546 | break; 1547 | } 1548 | if (packet->m_body[pos] == 0x12) 1549 | { 1550 | HandleMetadata(r, packet->m_body + pos + 11, dataSize); 1551 | } 1552 | else if (packet->m_body[pos] == 8 || packet->m_body[pos] == 9) 1553 | { 1554 | nTimeStamp = AMF_DecodeInt24(packet->m_body + pos + 4); 1555 | nTimeStamp |= (packet->m_body[pos + 7] << 24); 1556 | } 1557 | pos += (11 + dataSize + 4); 1558 | } 1559 | if (!r->m_pausing) 1560 | r->m_mediaStamp = nTimeStamp; 1561 | 1562 | /* FLV tag(s) */ 1563 | /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize); */ 1564 | bHasMediaPacket = 1; 1565 | break; 1566 | } 1567 | default: 1568 | RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__, 1569 | packet->m_packetType); 1570 | #ifdef _DEBUG 1571 | RTMP_LogHex(RTMP_LOGDEBUG, packet->m_body, packet->m_nBodySize); 1572 | #endif 1573 | } 1574 | 1575 | return bHasMediaPacket; 1576 | } 1577 | 1578 | #ifdef _DEBUG 1579 | extern FILE *netstackdump; 1580 | extern FILE *netstackdump_read; 1581 | #endif 1582 | 1583 | static int 1584 | ReadN(RTMP *r, char *buffer, int n) 1585 | { 1586 | int nOriginalSize = n; 1587 | int avail; 1588 | char *ptr; 1589 | 1590 | r->m_sb.sb_timedout = FALSE; 1591 | 1592 | #ifdef _DEBUG 1593 | memset(buffer, 0, n); 1594 | #endif 1595 | 1596 | ptr = buffer; 1597 | while (n > 0) 1598 | { 1599 | int nBytes = 0, nRead; 1600 | if (r->Link.protocol & RTMP_FEATURE_HTTP) 1601 | { 1602 | int refill = 0; 1603 | while (!r->m_resplen) 1604 | { 1605 | int ret; 1606 | if (r->m_sb.sb_size < 13 || refill) 1607 | { 1608 | if (!r->m_unackd) 1609 | HTTP_Post(r, RTMPT_IDLE, "", 1); 1610 | if (RTMPSockBuf_Fill(&r->m_sb) < 1) 1611 | { 1612 | if (!r->m_sb.sb_timedout) 1613 | RTMP_Close(r); 1614 | return 0; 1615 | } 1616 | } 1617 | if ((ret = HTTP_read(r, 0)) == -1) 1618 | { 1619 | RTMP_Log(RTMP_LOGDEBUG, "%s, No valid HTTP response found", __FUNCTION__); 1620 | RTMP_Close(r); 1621 | return 0; 1622 | } 1623 | else if (ret == -2) 1624 | { 1625 | refill = 1; 1626 | } 1627 | else 1628 | { 1629 | refill = 0; 1630 | } 1631 | } 1632 | if (r->m_resplen && !r->m_sb.sb_size) 1633 | RTMPSockBuf_Fill(&r->m_sb); 1634 | avail = r->m_sb.sb_size; 1635 | if (avail > r->m_resplen) 1636 | avail = r->m_resplen; 1637 | } 1638 | else 1639 | { 1640 | avail = r->m_sb.sb_size; 1641 | if (avail == 0) 1642 | { 1643 | if (RTMPSockBuf_Fill(&r->m_sb) < 1) 1644 | { 1645 | if (!r->m_sb.sb_timedout) 1646 | RTMP_Close(r); 1647 | return 0; 1648 | } 1649 | avail = r->m_sb.sb_size; 1650 | } 1651 | } 1652 | nRead = ((n < avail) ? n : avail); 1653 | if (nRead > 0) 1654 | { 1655 | memcpy(ptr, r->m_sb.sb_start, nRead); 1656 | r->m_sb.sb_start += nRead; 1657 | r->m_sb.sb_size -= nRead; 1658 | nBytes = nRead; 1659 | r->m_nBytesIn += nRead; 1660 | if (r->m_bSendCounter 1661 | && r->m_nBytesIn > ( r->m_nBytesInSent + r->m_nClientBW / 10)) 1662 | if (!SendBytesReceived(r)) 1663 | return FALSE; 1664 | } 1665 | /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */ 1666 | #ifdef _DEBUG 1667 | fwrite(ptr, 1, nBytes, netstackdump_read); 1668 | #endif 1669 | 1670 | if (nBytes == 0) 1671 | { 1672 | RTMP_Log(RTMP_LOGDEBUG, "%s, RTMP socket closed by peer", __FUNCTION__); 1673 | /*goto again; */ 1674 | RTMP_Close(r); 1675 | break; 1676 | } 1677 | 1678 | if (r->Link.protocol & RTMP_FEATURE_HTTP) 1679 | r->m_resplen -= nBytes; 1680 | 1681 | #ifdef CRYPTO 1682 | if (r->Link.rc4keyIn) 1683 | { 1684 | RC4_encrypt(r->Link.rc4keyIn, nBytes, ptr); 1685 | } 1686 | #endif 1687 | 1688 | n -= nBytes; 1689 | ptr += nBytes; 1690 | } 1691 | 1692 | return nOriginalSize - n; 1693 | } 1694 | 1695 | static int 1696 | WriteN(RTMP *r, const char *buffer, int n) 1697 | { 1698 | const char *ptr = buffer; 1699 | #ifdef CRYPTO 1700 | char *encrypted = 0; 1701 | char buf[RTMP_BUFFER_CACHE_SIZE]; 1702 | 1703 | if (r->Link.rc4keyOut) 1704 | { 1705 | if (n > sizeof(buf)) 1706 | encrypted = (char *)malloc(n); 1707 | else 1708 | encrypted = (char *)buf; 1709 | ptr = encrypted; 1710 | RC4_encrypt2(r->Link.rc4keyOut, n, buffer, ptr); 1711 | } 1712 | #endif 1713 | 1714 | while (n > 0) 1715 | { 1716 | int nBytes; 1717 | 1718 | if (r->Link.protocol & RTMP_FEATURE_HTTP) 1719 | nBytes = HTTP_Post(r, RTMPT_SEND, ptr, n); 1720 | else 1721 | nBytes = RTMPSockBuf_Send(&r->m_sb, ptr, n); 1722 | /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d\n", __FUNCTION__, nBytes); */ 1723 | 1724 | if (nBytes < 0) 1725 | { 1726 | int sockerr = GetSockError(); 1727 | RTMP_Log(RTMP_LOGERROR, "%s, RTMP send error %d (%d bytes)", __FUNCTION__, 1728 | sockerr, n); 1729 | 1730 | if (sockerr == EINTR && !RTMP_ctrlC) 1731 | continue; 1732 | 1733 | RTMP_Close(r); 1734 | n = 1; 1735 | break; 1736 | } 1737 | 1738 | if (nBytes == 0) 1739 | break; 1740 | 1741 | n -= nBytes; 1742 | ptr += nBytes; 1743 | } 1744 | 1745 | #ifdef CRYPTO 1746 | if (encrypted && encrypted != buf) 1747 | free(encrypted); 1748 | #endif 1749 | 1750 | return n == 0; 1751 | } 1752 | 1753 | #define SAVC(x) static const AVal av_##x = AVC(#x) 1754 | 1755 | SAVC(app); 1756 | SAVC(connect); 1757 | SAVC(flashVer); 1758 | SAVC(swfUrl); 1759 | SAVC(pageUrl); 1760 | SAVC(tcUrl); 1761 | SAVC(fpad); 1762 | SAVC(capabilities); 1763 | SAVC(audioCodecs); 1764 | SAVC(videoCodecs); 1765 | SAVC(videoFunction); 1766 | SAVC(objectEncoding); 1767 | SAVC(secureToken); 1768 | SAVC(secureTokenResponse); 1769 | SAVC(type); 1770 | SAVC(nonprivate); 1771 | 1772 | static int 1773 | SendConnectPacket(RTMP *r, RTMPPacket *cp) 1774 | { 1775 | RTMPPacket packet; 1776 | char pbuf[4096], *pend = pbuf + sizeof(pbuf); 1777 | char *enc; 1778 | 1779 | if (cp) 1780 | return RTMP_SendPacket(r, cp, TRUE); 1781 | 1782 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 1783 | packet.m_headerType = RTMP_PACKET_SIZE_LARGE; 1784 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 1785 | packet.m_nTimeStamp = 0; 1786 | packet.m_nInfoField2 = 0; 1787 | packet.m_hasAbsTimestamp = 0; 1788 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 1789 | 1790 | enc = packet.m_body; 1791 | enc = AMF_EncodeString(enc, pend, &av_connect); 1792 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 1793 | *enc++ = AMF_OBJECT; 1794 | 1795 | enc = AMF_EncodeNamedString(enc, pend, &av_app, &r->Link.app); 1796 | if (!enc) 1797 | return FALSE; 1798 | if (r->Link.protocol & RTMP_FEATURE_WRITE) 1799 | { 1800 | enc = AMF_EncodeNamedString(enc, pend, &av_type, &av_nonprivate); 1801 | if (!enc) 1802 | return FALSE; 1803 | } 1804 | if (r->Link.flashVer.av_len) 1805 | { 1806 | enc = AMF_EncodeNamedString(enc, pend, &av_flashVer, &r->Link.flashVer); 1807 | if (!enc) 1808 | return FALSE; 1809 | } 1810 | if (r->Link.swfUrl.av_len) 1811 | { 1812 | enc = AMF_EncodeNamedString(enc, pend, &av_swfUrl, &r->Link.swfUrl); 1813 | if (!enc) 1814 | return FALSE; 1815 | } 1816 | if (r->Link.tcUrl.av_len) 1817 | { 1818 | enc = AMF_EncodeNamedString(enc, pend, &av_tcUrl, &r->Link.tcUrl); 1819 | if (!enc) 1820 | return FALSE; 1821 | } 1822 | if (!(r->Link.protocol & RTMP_FEATURE_WRITE)) 1823 | { 1824 | enc = AMF_EncodeNamedBoolean(enc, pend, &av_fpad, FALSE); 1825 | if (!enc) 1826 | return FALSE; 1827 | enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 15.0); 1828 | if (!enc) 1829 | return FALSE; 1830 | enc = AMF_EncodeNamedNumber(enc, pend, &av_audioCodecs, r->m_fAudioCodecs); 1831 | if (!enc) 1832 | return FALSE; 1833 | enc = AMF_EncodeNamedNumber(enc, pend, &av_videoCodecs, r->m_fVideoCodecs); 1834 | if (!enc) 1835 | return FALSE; 1836 | enc = AMF_EncodeNamedNumber(enc, pend, &av_videoFunction, 1.0); 1837 | if (!enc) 1838 | return FALSE; 1839 | if (r->Link.pageUrl.av_len) 1840 | { 1841 | enc = AMF_EncodeNamedString(enc, pend, &av_pageUrl, &r->Link.pageUrl); 1842 | if (!enc) 1843 | return FALSE; 1844 | } 1845 | } 1846 | if (r->m_fEncoding != 0.0 || r->m_bSendEncoding) 1847 | { /* AMF0, AMF3 not fully supported yet */ 1848 | enc = AMF_EncodeNamedNumber(enc, pend, &av_objectEncoding, r->m_fEncoding); 1849 | if (!enc) 1850 | return FALSE; 1851 | } 1852 | if (enc + 3 >= pend) 1853 | return FALSE; 1854 | *enc++ = 0; 1855 | *enc++ = 0; /* end of object - 0x00 0x00 0x09 */ 1856 | *enc++ = AMF_OBJECT_END; 1857 | 1858 | /* add auth string */ 1859 | if (r->Link.auth.av_len) 1860 | { 1861 | enc = AMF_EncodeBoolean(enc, pend, r->Link.lFlags & RTMP_LF_AUTH); 1862 | if (!enc) 1863 | return FALSE; 1864 | enc = AMF_EncodeString(enc, pend, &r->Link.auth); 1865 | if (!enc) 1866 | return FALSE; 1867 | } 1868 | if (r->Link.extras.o_num) 1869 | { 1870 | int i; 1871 | for (i = 0; i < r->Link.extras.o_num; i++) 1872 | { 1873 | enc = AMFProp_Encode(&r->Link.extras.o_props[i], enc, pend); 1874 | if (!enc) 1875 | return FALSE; 1876 | } 1877 | } 1878 | packet.m_nBodySize = enc - packet.m_body; 1879 | 1880 | return RTMP_SendPacket(r, &packet, TRUE); 1881 | } 1882 | 1883 | #if 0 /* unused */ 1884 | SAVC(bgHasStream); 1885 | 1886 | static int 1887 | SendBGHasStream(RTMP *r, double dId, AVal *playpath) 1888 | { 1889 | RTMPPacket packet; 1890 | char pbuf[1024], *pend = pbuf + sizeof(pbuf); 1891 | char *enc; 1892 | 1893 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 1894 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 1895 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 1896 | packet.m_nTimeStamp = 0; 1897 | packet.m_nInfoField2 = 0; 1898 | packet.m_hasAbsTimestamp = 0; 1899 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 1900 | 1901 | enc = packet.m_body; 1902 | enc = AMF_EncodeString(enc, pend, &av_bgHasStream); 1903 | enc = AMF_EncodeNumber(enc, pend, dId); 1904 | *enc++ = AMF_NULL; 1905 | 1906 | enc = AMF_EncodeString(enc, pend, playpath); 1907 | if (enc == NULL) 1908 | return FALSE; 1909 | 1910 | packet.m_nBodySize = enc - packet.m_body; 1911 | 1912 | return RTMP_SendPacket(r, &packet, TRUE); 1913 | } 1914 | #endif 1915 | 1916 | SAVC(createStream); 1917 | 1918 | int 1919 | RTMP_SendCreateStream(RTMP *r) 1920 | { 1921 | RTMPPacket packet; 1922 | char pbuf[256], *pend = pbuf + sizeof(pbuf); 1923 | char *enc; 1924 | 1925 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 1926 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 1927 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 1928 | packet.m_nTimeStamp = 0; 1929 | packet.m_nInfoField2 = 0; 1930 | packet.m_hasAbsTimestamp = 0; 1931 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 1932 | 1933 | enc = packet.m_body; 1934 | enc = AMF_EncodeString(enc, pend, &av_createStream); 1935 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 1936 | *enc++ = AMF_NULL; /* NULL */ 1937 | 1938 | packet.m_nBodySize = enc - packet.m_body; 1939 | 1940 | return RTMP_SendPacket(r, &packet, TRUE); 1941 | } 1942 | 1943 | SAVC(FCSubscribe); 1944 | 1945 | static int 1946 | SendFCSubscribe(RTMP *r, AVal *subscribepath) 1947 | { 1948 | RTMPPacket packet; 1949 | char pbuf[512], *pend = pbuf + sizeof(pbuf); 1950 | char *enc; 1951 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 1952 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 1953 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 1954 | packet.m_nTimeStamp = 0; 1955 | packet.m_nInfoField2 = 0; 1956 | packet.m_hasAbsTimestamp = 0; 1957 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 1958 | 1959 | RTMP_Log(RTMP_LOGDEBUG, "FCSubscribe: %s", subscribepath->av_val); 1960 | enc = packet.m_body; 1961 | enc = AMF_EncodeString(enc, pend, &av_FCSubscribe); 1962 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 1963 | *enc++ = AMF_NULL; 1964 | enc = AMF_EncodeString(enc, pend, subscribepath); 1965 | 1966 | if (!enc) 1967 | return FALSE; 1968 | 1969 | packet.m_nBodySize = enc - packet.m_body; 1970 | 1971 | return RTMP_SendPacket(r, &packet, TRUE); 1972 | } 1973 | 1974 | /* Justin.tv specific authentication */ 1975 | static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken"); 1976 | 1977 | static int 1978 | SendUsherToken(RTMP *r, AVal *usherToken) 1979 | { 1980 | RTMPPacket packet; 1981 | char pbuf[1024], *pend = pbuf + sizeof(pbuf); 1982 | char *enc; 1983 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 1984 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 1985 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 1986 | packet.m_nTimeStamp = 0; 1987 | packet.m_nInfoField2 = 0; 1988 | packet.m_hasAbsTimestamp = 0; 1989 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 1990 | 1991 | RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %s", usherToken->av_val); 1992 | enc = packet.m_body; 1993 | enc = AMF_EncodeString(enc, pend, &av_NetStream_Authenticate_UsherToken); 1994 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 1995 | *enc++ = AMF_NULL; 1996 | enc = AMF_EncodeString(enc, pend, usherToken); 1997 | 1998 | if (!enc) 1999 | return FALSE; 2000 | 2001 | packet.m_nBodySize = enc - packet.m_body; 2002 | 2003 | return RTMP_SendPacket(r, &packet, FALSE); 2004 | } 2005 | /******************************************/ 2006 | 2007 | SAVC(releaseStream); 2008 | 2009 | static int 2010 | SendReleaseStream(RTMP *r) 2011 | { 2012 | RTMPPacket packet; 2013 | char pbuf[1024], *pend = pbuf + sizeof(pbuf); 2014 | char *enc; 2015 | 2016 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 2017 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 2018 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2019 | packet.m_nTimeStamp = 0; 2020 | packet.m_nInfoField2 = 0; 2021 | packet.m_hasAbsTimestamp = 0; 2022 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2023 | 2024 | enc = packet.m_body; 2025 | enc = AMF_EncodeString(enc, pend, &av_releaseStream); 2026 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 2027 | *enc++ = AMF_NULL; 2028 | enc = AMF_EncodeString(enc, pend, &r->Link.playpath); 2029 | if (!enc) 2030 | return FALSE; 2031 | 2032 | packet.m_nBodySize = enc - packet.m_body; 2033 | 2034 | return RTMP_SendPacket(r, &packet, FALSE); 2035 | } 2036 | 2037 | SAVC(FCPublish); 2038 | 2039 | static int 2040 | SendFCPublish(RTMP *r) 2041 | { 2042 | RTMPPacket packet; 2043 | char pbuf[1024], *pend = pbuf + sizeof(pbuf); 2044 | char *enc; 2045 | 2046 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 2047 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 2048 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2049 | packet.m_nTimeStamp = 0; 2050 | packet.m_nInfoField2 = 0; 2051 | packet.m_hasAbsTimestamp = 0; 2052 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2053 | 2054 | enc = packet.m_body; 2055 | enc = AMF_EncodeString(enc, pend, &av_FCPublish); 2056 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 2057 | *enc++ = AMF_NULL; 2058 | enc = AMF_EncodeString(enc, pend, &r->Link.playpath); 2059 | if (!enc) 2060 | return FALSE; 2061 | 2062 | packet.m_nBodySize = enc - packet.m_body; 2063 | 2064 | return RTMP_SendPacket(r, &packet, FALSE); 2065 | } 2066 | 2067 | SAVC(FCUnpublish); 2068 | 2069 | static int 2070 | SendFCUnpublish(RTMP *r) 2071 | { 2072 | RTMPPacket packet; 2073 | char pbuf[1024], *pend = pbuf + sizeof(pbuf); 2074 | char *enc; 2075 | 2076 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 2077 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 2078 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2079 | packet.m_nTimeStamp = 0; 2080 | packet.m_nInfoField2 = 0; 2081 | packet.m_hasAbsTimestamp = 0; 2082 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2083 | 2084 | enc = packet.m_body; 2085 | enc = AMF_EncodeString(enc, pend, &av_FCUnpublish); 2086 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 2087 | *enc++ = AMF_NULL; 2088 | enc = AMF_EncodeString(enc, pend, &r->Link.playpath); 2089 | if (!enc) 2090 | return FALSE; 2091 | 2092 | packet.m_nBodySize = enc - packet.m_body; 2093 | 2094 | return RTMP_SendPacket(r, &packet, FALSE); 2095 | } 2096 | 2097 | SAVC(publish); 2098 | SAVC(live); 2099 | SAVC(record); 2100 | 2101 | static int 2102 | SendPublish(RTMP *r) 2103 | { 2104 | RTMPPacket packet; 2105 | char pbuf[1024], *pend = pbuf + sizeof(pbuf); 2106 | char *enc; 2107 | 2108 | packet.m_nChannel = 0x04; /* source channel (invoke) */ 2109 | packet.m_headerType = RTMP_PACKET_SIZE_LARGE; 2110 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2111 | packet.m_nTimeStamp = 0; 2112 | packet.m_nInfoField2 = r->m_stream_id; 2113 | packet.m_hasAbsTimestamp = 0; 2114 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2115 | 2116 | enc = packet.m_body; 2117 | enc = AMF_EncodeString(enc, pend, &av_publish); 2118 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 2119 | *enc++ = AMF_NULL; 2120 | enc = AMF_EncodeString(enc, pend, &r->Link.playpath); 2121 | if (!enc) 2122 | return FALSE; 2123 | 2124 | /* FIXME: should we choose live based on Link.lFlags & RTMP_LF_LIVE? */ 2125 | enc = AMF_EncodeString(enc, pend, &av_live); 2126 | if (!enc) 2127 | return FALSE; 2128 | 2129 | packet.m_nBodySize = enc - packet.m_body; 2130 | 2131 | return RTMP_SendPacket(r, &packet, TRUE); 2132 | } 2133 | 2134 | SAVC(deleteStream); 2135 | 2136 | static int 2137 | SendDeleteStream(RTMP *r, double dStreamId) 2138 | { 2139 | RTMPPacket packet; 2140 | char pbuf[256], *pend = pbuf + sizeof(pbuf); 2141 | char *enc; 2142 | 2143 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 2144 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 2145 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2146 | packet.m_nTimeStamp = 0; 2147 | packet.m_nInfoField2 = 0; 2148 | packet.m_hasAbsTimestamp = 0; 2149 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2150 | 2151 | enc = packet.m_body; 2152 | enc = AMF_EncodeString(enc, pend, &av_deleteStream); 2153 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 2154 | *enc++ = AMF_NULL; 2155 | enc = AMF_EncodeNumber(enc, pend, dStreamId); 2156 | 2157 | packet.m_nBodySize = enc - packet.m_body; 2158 | 2159 | /* no response expected */ 2160 | return RTMP_SendPacket(r, &packet, FALSE); 2161 | } 2162 | 2163 | SAVC(pause); 2164 | 2165 | int 2166 | RTMP_SendPause(RTMP *r, int DoPause, int iTime) 2167 | { 2168 | RTMPPacket packet; 2169 | char pbuf[256], *pend = pbuf + sizeof(pbuf); 2170 | char *enc; 2171 | 2172 | packet.m_nChannel = 0x08; /* video channel */ 2173 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 2174 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2175 | packet.m_nTimeStamp = 0; 2176 | packet.m_nInfoField2 = 0; 2177 | packet.m_hasAbsTimestamp = 0; 2178 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2179 | 2180 | enc = packet.m_body; 2181 | enc = AMF_EncodeString(enc, pend, &av_pause); 2182 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 2183 | *enc++ = AMF_NULL; 2184 | enc = AMF_EncodeBoolean(enc, pend, DoPause); 2185 | enc = AMF_EncodeNumber(enc, pend, (double)iTime); 2186 | 2187 | packet.m_nBodySize = enc - packet.m_body; 2188 | 2189 | RTMP_Log(RTMP_LOGDEBUG, "%s, %d, pauseTime=%d", __FUNCTION__, DoPause, iTime); 2190 | return RTMP_SendPacket(r, &packet, TRUE); 2191 | } 2192 | 2193 | int RTMP_Pause(RTMP *r, int DoPause) 2194 | { 2195 | if (DoPause) 2196 | r->m_pauseStamp = r->m_mediaChannel < r->m_channelsAllocatedIn ? 2197 | r->m_channelTimestamp[r->m_mediaChannel] : 0; 2198 | return RTMP_SendPause(r, DoPause, r->m_pauseStamp); 2199 | } 2200 | 2201 | SAVC(seek); 2202 | 2203 | int 2204 | RTMP_SendSeek(RTMP *r, int iTime) 2205 | { 2206 | RTMPPacket packet; 2207 | char pbuf[256], *pend = pbuf + sizeof(pbuf); 2208 | char *enc; 2209 | 2210 | packet.m_nChannel = 0x08; /* video channel */ 2211 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 2212 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2213 | packet.m_nTimeStamp = 0; 2214 | packet.m_nInfoField2 = 0; 2215 | packet.m_hasAbsTimestamp = 0; 2216 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2217 | 2218 | enc = packet.m_body; 2219 | enc = AMF_EncodeString(enc, pend, &av_seek); 2220 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 2221 | *enc++ = AMF_NULL; 2222 | enc = AMF_EncodeNumber(enc, pend, (double)iTime); 2223 | 2224 | packet.m_nBodySize = enc - packet.m_body; 2225 | 2226 | r->m_read.flags |= RTMP_READ_SEEKING; 2227 | r->m_read.nResumeTS = 0; 2228 | 2229 | return RTMP_SendPacket(r, &packet, TRUE); 2230 | } 2231 | 2232 | int 2233 | RTMP_SendServerBW(RTMP *r) 2234 | { 2235 | RTMPPacket packet; 2236 | char pbuf[256], *pend = pbuf + sizeof(pbuf); 2237 | 2238 | packet.m_nChannel = 0x02; /* control channel (invoke) */ 2239 | packet.m_headerType = RTMP_PACKET_SIZE_LARGE; 2240 | packet.m_packetType = RTMP_PACKET_TYPE_SERVER_BW; 2241 | packet.m_nTimeStamp = 0; 2242 | packet.m_nInfoField2 = 0; 2243 | packet.m_hasAbsTimestamp = 0; 2244 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2245 | 2246 | packet.m_nBodySize = 4; 2247 | 2248 | AMF_EncodeInt32(packet.m_body, pend, r->m_nServerBW); 2249 | return RTMP_SendPacket(r, &packet, FALSE); 2250 | } 2251 | 2252 | int 2253 | RTMP_SendClientBW(RTMP *r) 2254 | { 2255 | RTMPPacket packet; 2256 | char pbuf[256], *pend = pbuf + sizeof(pbuf); 2257 | 2258 | packet.m_nChannel = 0x02; /* control channel (invoke) */ 2259 | packet.m_headerType = RTMP_PACKET_SIZE_LARGE; 2260 | packet.m_packetType = RTMP_PACKET_TYPE_CLIENT_BW; 2261 | packet.m_nTimeStamp = 0; 2262 | packet.m_nInfoField2 = 0; 2263 | packet.m_hasAbsTimestamp = 0; 2264 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2265 | 2266 | packet.m_nBodySize = 5; 2267 | 2268 | AMF_EncodeInt32(packet.m_body, pend, r->m_nClientBW); 2269 | packet.m_body[4] = r->m_nClientBW2; 2270 | return RTMP_SendPacket(r, &packet, FALSE); 2271 | } 2272 | 2273 | static int 2274 | SendBytesReceived(RTMP *r) 2275 | { 2276 | RTMPPacket packet; 2277 | char pbuf[256], *pend = pbuf + sizeof(pbuf); 2278 | 2279 | packet.m_nChannel = 0x02; /* control channel (invoke) */ 2280 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 2281 | packet.m_packetType = RTMP_PACKET_TYPE_BYTES_READ_REPORT; 2282 | packet.m_nTimeStamp = 0; 2283 | packet.m_nInfoField2 = 0; 2284 | packet.m_hasAbsTimestamp = 0; 2285 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2286 | 2287 | packet.m_nBodySize = 4; 2288 | 2289 | AMF_EncodeInt32(packet.m_body, pend, r->m_nBytesIn); /* hard coded for now */ 2290 | r->m_nBytesInSent = r->m_nBytesIn; 2291 | 2292 | /*RTMP_Log(RTMP_LOGDEBUG, "Send bytes report. 0x%x (%d bytes)", (unsigned int)m_nBytesIn, m_nBytesIn); */ 2293 | return RTMP_SendPacket(r, &packet, FALSE); 2294 | } 2295 | 2296 | SAVC(_checkbw); 2297 | 2298 | static int 2299 | SendCheckBW(RTMP *r) 2300 | { 2301 | RTMPPacket packet; 2302 | char pbuf[256], *pend = pbuf + sizeof(pbuf); 2303 | char *enc; 2304 | 2305 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 2306 | packet.m_headerType = RTMP_PACKET_SIZE_LARGE; 2307 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2308 | packet.m_nTimeStamp = 0; /* RTMP_GetTime(); */ 2309 | packet.m_nInfoField2 = 0; 2310 | packet.m_hasAbsTimestamp = 0; 2311 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2312 | 2313 | enc = packet.m_body; 2314 | enc = AMF_EncodeString(enc, pend, &av__checkbw); 2315 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 2316 | *enc++ = AMF_NULL; 2317 | 2318 | packet.m_nBodySize = enc - packet.m_body; 2319 | 2320 | /* triggers _onbwcheck and eventually results in _onbwdone */ 2321 | return RTMP_SendPacket(r, &packet, FALSE); 2322 | } 2323 | 2324 | SAVC(_result); 2325 | 2326 | static int 2327 | SendCheckBWResult(RTMP *r, double txn) 2328 | { 2329 | RTMPPacket packet; 2330 | char pbuf[256], *pend = pbuf + sizeof(pbuf); 2331 | char *enc; 2332 | 2333 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 2334 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 2335 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2336 | packet.m_nTimeStamp = 0x16 * r->m_nBWCheckCounter; /* temp inc value. till we figure it out. */ 2337 | packet.m_nInfoField2 = 0; 2338 | packet.m_hasAbsTimestamp = 0; 2339 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2340 | 2341 | enc = packet.m_body; 2342 | enc = AMF_EncodeString(enc, pend, &av__result); 2343 | enc = AMF_EncodeNumber(enc, pend, txn); 2344 | *enc++ = AMF_NULL; 2345 | enc = AMF_EncodeNumber(enc, pend, (double)r->m_nBWCheckCounter++); 2346 | 2347 | packet.m_nBodySize = enc - packet.m_body; 2348 | 2349 | return RTMP_SendPacket(r, &packet, FALSE); 2350 | } 2351 | 2352 | SAVC(ping); 2353 | SAVC(pong); 2354 | 2355 | static int 2356 | SendPong(RTMP *r, double txn) 2357 | { 2358 | RTMPPacket packet; 2359 | char pbuf[256], *pend = pbuf + sizeof(pbuf); 2360 | char *enc; 2361 | 2362 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 2363 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 2364 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2365 | packet.m_nTimeStamp = 0x16 * r->m_nBWCheckCounter; /* temp inc value. till we figure it out. */ 2366 | packet.m_nInfoField2 = 0; 2367 | packet.m_hasAbsTimestamp = 0; 2368 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2369 | 2370 | enc = packet.m_body; 2371 | enc = AMF_EncodeString(enc, pend, &av_pong); 2372 | enc = AMF_EncodeNumber(enc, pend, txn); 2373 | *enc++ = AMF_NULL; 2374 | 2375 | packet.m_nBodySize = enc - packet.m_body; 2376 | 2377 | return RTMP_SendPacket(r, &packet, FALSE); 2378 | } 2379 | 2380 | SAVC(play); 2381 | 2382 | static int 2383 | SendPlay(RTMP *r) 2384 | { 2385 | RTMPPacket packet; 2386 | char pbuf[1024], *pend = pbuf + sizeof(pbuf); 2387 | char *enc; 2388 | 2389 | packet.m_nChannel = 0x08; /* we make 8 our stream channel */ 2390 | packet.m_headerType = RTMP_PACKET_SIZE_LARGE; 2391 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2392 | packet.m_nTimeStamp = 0; 2393 | packet.m_nInfoField2 = r->m_stream_id; /*0x01000000; */ 2394 | packet.m_hasAbsTimestamp = 0; 2395 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2396 | 2397 | enc = packet.m_body; 2398 | enc = AMF_EncodeString(enc, pend, &av_play); 2399 | enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); 2400 | *enc++ = AMF_NULL; 2401 | 2402 | RTMP_Log(RTMP_LOGDEBUG, "%s, seekTime=%d, stopTime=%d, sending play: %s", 2403 | __FUNCTION__, r->Link.seekTime, r->Link.stopTime, 2404 | r->Link.playpath.av_val); 2405 | enc = AMF_EncodeString(enc, pend, &r->Link.playpath); 2406 | if (!enc) 2407 | return FALSE; 2408 | 2409 | /* Optional parameters start and len. 2410 | * 2411 | * start: -2, -1, 0, positive number 2412 | * -2: looks for a live stream, then a recorded stream, 2413 | * if not found any open a live stream 2414 | * -1: plays a live stream 2415 | * >=0: plays a recorded streams from 'start' milliseconds 2416 | */ 2417 | if (r->Link.lFlags & RTMP_LF_LIVE) 2418 | enc = AMF_EncodeNumber(enc, pend, -1000.0); 2419 | else 2420 | { 2421 | if (r->Link.seekTime > 0.0) 2422 | enc = AMF_EncodeNumber(enc, pend, r->Link.seekTime); /* resume from here */ 2423 | else 2424 | enc = AMF_EncodeNumber(enc, pend, 0.0); /*-2000.0);*/ /* recorded as default, -2000.0 is not reliable since that freezes the player if the stream is not found */ 2425 | } 2426 | if (!enc) 2427 | return FALSE; 2428 | 2429 | /* len: -1, 0, positive number 2430 | * -1: plays live or recorded stream to the end (default) 2431 | * 0: plays a frame 'start' ms away from the beginning 2432 | * >0: plays a live or recoded stream for 'len' milliseconds 2433 | */ 2434 | /*enc += EncodeNumber(enc, -1.0); */ /* len */ 2435 | if (r->Link.stopTime) 2436 | { 2437 | enc = AMF_EncodeNumber(enc, pend, r->Link.stopTime - r->Link.seekTime); 2438 | if (!enc) 2439 | return FALSE; 2440 | } 2441 | 2442 | packet.m_nBodySize = enc - packet.m_body; 2443 | 2444 | return RTMP_SendPacket(r, &packet, TRUE); 2445 | } 2446 | 2447 | SAVC(set_playlist); 2448 | SAVC(0); 2449 | 2450 | static int 2451 | SendPlaylist(RTMP *r) 2452 | { 2453 | RTMPPacket packet; 2454 | char pbuf[1024], *pend = pbuf + sizeof(pbuf); 2455 | char *enc; 2456 | 2457 | packet.m_nChannel = 0x08; /* we make 8 our stream channel */ 2458 | packet.m_headerType = RTMP_PACKET_SIZE_LARGE; 2459 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2460 | packet.m_nTimeStamp = 0; 2461 | packet.m_nInfoField2 = r->m_stream_id; /*0x01000000; */ 2462 | packet.m_hasAbsTimestamp = 0; 2463 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2464 | 2465 | enc = packet.m_body; 2466 | enc = AMF_EncodeString(enc, pend, &av_set_playlist); 2467 | enc = AMF_EncodeNumber(enc, pend, 0); 2468 | *enc++ = AMF_NULL; 2469 | *enc++ = AMF_ECMA_ARRAY; 2470 | *enc++ = 0; 2471 | *enc++ = 0; 2472 | *enc++ = 0; 2473 | *enc++ = AMF_OBJECT; 2474 | enc = AMF_EncodeNamedString(enc, pend, &av_0, &r->Link.playpath); 2475 | if (!enc) 2476 | return FALSE; 2477 | if (enc + 3 >= pend) 2478 | return FALSE; 2479 | *enc++ = 0; 2480 | *enc++ = 0; 2481 | *enc++ = AMF_OBJECT_END; 2482 | 2483 | packet.m_nBodySize = enc - packet.m_body; 2484 | 2485 | return RTMP_SendPacket(r, &packet, TRUE); 2486 | } 2487 | 2488 | static int 2489 | SendSecureTokenResponse(RTMP *r, AVal *resp) 2490 | { 2491 | RTMPPacket packet; 2492 | char pbuf[1024], *pend = pbuf + sizeof(pbuf); 2493 | char *enc; 2494 | 2495 | packet.m_nChannel = 0x03; /* control channel (invoke) */ 2496 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 2497 | packet.m_packetType = RTMP_PACKET_TYPE_INVOKE; 2498 | packet.m_nTimeStamp = 0; 2499 | packet.m_nInfoField2 = 0; 2500 | packet.m_hasAbsTimestamp = 0; 2501 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2502 | 2503 | enc = packet.m_body; 2504 | enc = AMF_EncodeString(enc, pend, &av_secureTokenResponse); 2505 | enc = AMF_EncodeNumber(enc, pend, 0.0); 2506 | *enc++ = AMF_NULL; 2507 | enc = AMF_EncodeString(enc, pend, resp); 2508 | if (!enc) 2509 | return FALSE; 2510 | 2511 | packet.m_nBodySize = enc - packet.m_body; 2512 | 2513 | return RTMP_SendPacket(r, &packet, FALSE); 2514 | } 2515 | 2516 | /* 2517 | from http://jira.red5.org/confluence/display/docs/Ping: 2518 | 2519 | Ping is the most mysterious message in RTMP and till now we haven't fully interpreted it yet. In summary, Ping message is used as a special command that are exchanged between client and server. This page aims to document all known Ping messages. Expect the list to grow. 2520 | 2521 | The type of Ping packet is 0x4 and contains two mandatory parameters and two optional parameters. The first parameter is the type of Ping and in short integer. The second parameter is the target of the ping. As Ping is always sent in Channel 2 (control channel) and the target object in RTMP header is always 0 which means the Connection object, it's necessary to put an extra parameter to indicate the exact target object the Ping is sent to. The second parameter takes this responsibility. The value has the same meaning as the target object field in RTMP header. (The second value could also be used as other purposes, like RTT Ping/Pong. It is used as the timestamp.) The third and fourth parameters are optional and could be looked upon as the parameter of the Ping packet. Below is an unexhausted list of Ping messages. 2522 | 2523 | * type 0: Clear the stream. No third and fourth parameters. The second parameter could be 0. After the connection is established, a Ping 0,0 will be sent from server to client. The message will also be sent to client on the start of Play and in response of a Seek or Pause/Resume request. This Ping tells client to re-calibrate the clock with the timestamp of the next packet server sends. 2524 | * type 1: Tell the stream to clear the playing buffer. 2525 | * type 3: Buffer time of the client. The third parameter is the buffer time in millisecond. 2526 | * type 4: Reset a stream. Used together with type 0 in the case of VOD. Often sent before type 0. 2527 | * type 6: Ping the client from server. The second parameter is the current time. 2528 | * type 7: Pong reply from client. The second parameter is the time the server sent with his ping request. 2529 | * type 26: SWFVerification request 2530 | * type 27: SWFVerification response 2531 | */ 2532 | int 2533 | RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime) 2534 | { 2535 | RTMPPacket packet; 2536 | char pbuf[256], *pend = pbuf + sizeof(pbuf); 2537 | int nSize; 2538 | char *buf; 2539 | 2540 | RTMP_Log(RTMP_LOGDEBUG, "sending ctrl. type: 0x%04x", (unsigned short)nType); 2541 | 2542 | packet.m_nChannel = 0x02; /* control channel (ping) */ 2543 | packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM; 2544 | packet.m_packetType = RTMP_PACKET_TYPE_CONTROL; 2545 | packet.m_nTimeStamp = 0; /* RTMP_GetTime(); */ 2546 | packet.m_nInfoField2 = 0; 2547 | packet.m_hasAbsTimestamp = 0; 2548 | packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE; 2549 | 2550 | switch(nType) { 2551 | case 0x03: nSize = 10; break; /* buffer time */ 2552 | case 0x1A: nSize = 3; break; /* SWF verify request */ 2553 | case 0x1B: nSize = 44; break; /* SWF verify response */ 2554 | default: nSize = 6; break; 2555 | } 2556 | 2557 | packet.m_nBodySize = nSize; 2558 | 2559 | buf = packet.m_body; 2560 | buf = AMF_EncodeInt16(buf, pend, nType); 2561 | 2562 | if (nType == 0x1B) 2563 | { 2564 | #ifdef CRYPTO 2565 | memcpy(buf, r->Link.SWFVerificationResponse, 42); 2566 | RTMP_Log(RTMP_LOGDEBUG, "Sending SWFVerification response: "); 2567 | RTMP_LogHex(RTMP_LOGDEBUG, (uint8_t *)packet.m_body, packet.m_nBodySize); 2568 | #endif 2569 | } 2570 | else if (nType == 0x1A) 2571 | { 2572 | *buf = nObject & 0xff; 2573 | } 2574 | else 2575 | { 2576 | if (nSize > 2) 2577 | buf = AMF_EncodeInt32(buf, pend, nObject); 2578 | 2579 | if (nSize > 6) 2580 | buf = AMF_EncodeInt32(buf, pend, nTime); 2581 | } 2582 | 2583 | return RTMP_SendPacket(r, &packet, FALSE); 2584 | } 2585 | 2586 | static void 2587 | AV_erase(RTMP_METHOD *vals, int *num, int i, int freeit) 2588 | { 2589 | if (freeit) 2590 | free(vals[i].name.av_val); 2591 | (*num)--; 2592 | for (; i < *num; i++) 2593 | { 2594 | vals[i] = vals[i + 1]; 2595 | } 2596 | vals[i].name.av_val = NULL; 2597 | vals[i].name.av_len = 0; 2598 | vals[i].num = 0; 2599 | } 2600 | 2601 | void 2602 | RTMP_DropRequest(RTMP *r, int i, int freeit) 2603 | { 2604 | AV_erase(r->m_methodCalls, &r->m_numCalls, i, freeit); 2605 | } 2606 | 2607 | static void 2608 | AV_queue(RTMP_METHOD **vals, int *num, AVal *av, int txn) 2609 | { 2610 | char *tmp; 2611 | if (!(*num & 0x0f)) 2612 | *vals = realloc(*vals, (*num + 16) * sizeof(RTMP_METHOD)); 2613 | tmp = malloc(av->av_len + 1); 2614 | memcpy(tmp, av->av_val, av->av_len); 2615 | tmp[av->av_len] = '\0'; 2616 | (*vals)[*num].num = txn; 2617 | (*vals)[*num].name.av_len = av->av_len; 2618 | (*vals)[(*num)++].name.av_val = tmp; 2619 | } 2620 | 2621 | static void 2622 | AV_clear(RTMP_METHOD *vals, int num) 2623 | { 2624 | int i; 2625 | for (i = 0; i < num; i++) 2626 | free(vals[i].name.av_val); 2627 | free(vals); 2628 | } 2629 | 2630 | 2631 | #ifdef CRYPTO 2632 | static int 2633 | b64enc(const unsigned char *input, int length, char *output, int maxsize) 2634 | { 2635 | #ifdef USE_POLARSSL 2636 | size_t buf_size = maxsize; 2637 | if(base64_encode((unsigned char *) output, &buf_size, input, length) == 0) 2638 | { 2639 | output[buf_size] = '\0'; 2640 | return 1; 2641 | } 2642 | else 2643 | { 2644 | RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__); 2645 | return 0; 2646 | } 2647 | #elif defined(USE_GNUTLS) 2648 | if (BASE64_ENCODE_RAW_LENGTH(length) <= maxsize) 2649 | base64_encode_raw((uint8_t*) output, length, input); 2650 | else 2651 | { 2652 | RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__); 2653 | return 0; 2654 | } 2655 | #else /* USE_OPENSSL */ 2656 | BIO *bmem, *b64; 2657 | BUF_MEM *bptr; 2658 | 2659 | b64 = BIO_new(BIO_f_base64()); 2660 | bmem = BIO_new(BIO_s_mem()); 2661 | b64 = BIO_push(b64, bmem); 2662 | BIO_write(b64, input, length); 2663 | if (BIO_flush(b64) == 1) 2664 | { 2665 | BIO_get_mem_ptr(b64, &bptr); 2666 | memcpy(output, bptr->data, bptr->length-1); 2667 | output[bptr->length-1] = '\0'; 2668 | } 2669 | else 2670 | { 2671 | RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__); 2672 | return 0; 2673 | } 2674 | BIO_free_all(b64); 2675 | #endif 2676 | return 1; 2677 | } 2678 | 2679 | #ifdef USE_POLARSSL 2680 | #define MD5_CTX md5_context 2681 | #define MD5_Init(ctx) md5_starts(ctx) 2682 | #define MD5_Update(ctx,data,len) md5_update(ctx,(unsigned char *)data,len) 2683 | #define MD5_Final(dig,ctx) md5_finish(ctx,dig) 2684 | #elif defined(USE_GNUTLS) 2685 | typedef struct md5_ctx MD5_CTX; 2686 | #define MD5_Init(ctx) md5_init(ctx) 2687 | #define MD5_Update(ctx,data,len) md5_update(ctx,len,data) 2688 | #define MD5_Final(dig,ctx) md5_digest(ctx,MD5_DIGEST_LENGTH,dig) 2689 | #else 2690 | #endif 2691 | 2692 | static const AVal av_authmod_adobe = AVC("authmod=adobe"); 2693 | static const AVal av_authmod_llnw = AVC("authmod=llnw"); 2694 | 2695 | static void hexenc(unsigned char *inbuf, int len, char *dst) 2696 | { 2697 | char *ptr = dst; 2698 | while(len--) { 2699 | sprintf(ptr, "%02x", *inbuf++); 2700 | ptr += 2; 2701 | } 2702 | *ptr = '\0'; 2703 | } 2704 | 2705 | static char * 2706 | AValChr(AVal *av, char c) 2707 | { 2708 | int i; 2709 | for (i = 0; i < av->av_len; i++) 2710 | if (av->av_val[i] == c) 2711 | return &av->av_val[i]; 2712 | return NULL; 2713 | } 2714 | 2715 | static int 2716 | PublisherAuth(RTMP *r, AVal *description) 2717 | { 2718 | char *token_in = NULL; 2719 | char *ptr; 2720 | unsigned char md5sum_val[MD5_DIGEST_LENGTH+1]; 2721 | MD5_CTX md5ctx; 2722 | int challenge2_data; 2723 | #define RESPONSE_LEN 32 2724 | #define CHALLENGE2_LEN 16 2725 | #define SALTED2_LEN (32+8+8+8) 2726 | #define B64DIGEST_LEN 24 /* 16 byte digest => 22 b64 chars + 2 chars padding */ 2727 | #define B64INT_LEN 8 /* 4 byte int => 6 b64 chars + 2 chars padding */ 2728 | #define HEXHASH_LEN (2*MD5_DIGEST_LENGTH) 2729 | char response[RESPONSE_LEN]; 2730 | char challenge2[CHALLENGE2_LEN]; 2731 | char salted2[SALTED2_LEN]; 2732 | AVal pubToken; 2733 | 2734 | if (strstr(description->av_val, av_authmod_adobe.av_val) != NULL) 2735 | { 2736 | if(strstr(description->av_val, "code=403 need auth") != NULL) 2737 | { 2738 | if (strstr(r->Link.app.av_val, av_authmod_adobe.av_val) != NULL) { 2739 | RTMP_Log(RTMP_LOGERROR, "%s, wrong pubUser & pubPasswd for publisher auth", __FUNCTION__); 2740 | return 0; 2741 | } else if(r->Link.pubUser.av_len && r->Link.pubPasswd.av_len) { 2742 | pubToken.av_val = malloc(r->Link.pubUser.av_len + av_authmod_adobe.av_len + 8); 2743 | pubToken.av_len = sprintf(pubToken.av_val, "?%s&user=%s", 2744 | av_authmod_adobe.av_val, 2745 | r->Link.pubUser.av_val); 2746 | RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken1: %s", __FUNCTION__, pubToken.av_val); 2747 | } else { 2748 | RTMP_Log(RTMP_LOGERROR, "%s, need to set pubUser & pubPasswd for publisher auth", __FUNCTION__); 2749 | return 0; 2750 | } 2751 | } 2752 | else if((token_in = strstr(description->av_val, "?reason=needauth")) != NULL) 2753 | { 2754 | char *par, *val = NULL, *orig_ptr; 2755 | AVal user, salt, opaque, challenge, *aptr = NULL; 2756 | opaque.av_len = 0; 2757 | challenge.av_len = 0; 2758 | 2759 | ptr = orig_ptr = strdup(token_in); 2760 | while (ptr) 2761 | { 2762 | par = ptr; 2763 | ptr = strchr(par, '&'); 2764 | if(ptr) 2765 | *ptr++ = '\0'; 2766 | 2767 | val = strchr(par, '='); 2768 | if(val) 2769 | *val++ = '\0'; 2770 | 2771 | if (aptr) { 2772 | aptr->av_len = par - aptr->av_val - 1; 2773 | aptr = NULL; 2774 | } 2775 | if (strcmp(par, "user") == 0){ 2776 | user.av_val = val; 2777 | aptr = &user; 2778 | } else if (strcmp(par, "salt") == 0){ 2779 | salt.av_val = val; 2780 | aptr = &salt; 2781 | } else if (strcmp(par, "opaque") == 0){ 2782 | opaque.av_val = val; 2783 | aptr = &opaque; 2784 | } else if (strcmp(par, "challenge") == 0){ 2785 | challenge.av_val = val; 2786 | aptr = &challenge; 2787 | } 2788 | 2789 | RTMP_Log(RTMP_LOGDEBUG, "%s, par:\"%s\" = val:\"%s\"", __FUNCTION__, par, val); 2790 | } 2791 | if (aptr) 2792 | aptr->av_len = strlen(aptr->av_val); 2793 | 2794 | /* hash1 = base64enc(md5(user + _aodbeAuthSalt + password)) */ 2795 | MD5_Init(&md5ctx); 2796 | MD5_Update(&md5ctx, user.av_val, user.av_len); 2797 | MD5_Update(&md5ctx, salt.av_val, salt.av_len); 2798 | MD5_Update(&md5ctx, r->Link.pubPasswd.av_val, r->Link.pubPasswd.av_len); 2799 | MD5_Final(md5sum_val, &md5ctx); 2800 | RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s%s%s) =>", __FUNCTION__, 2801 | user.av_val, salt.av_val, r->Link.pubPasswd.av_val); 2802 | RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH); 2803 | 2804 | b64enc(md5sum_val, MD5_DIGEST_LENGTH, salted2, SALTED2_LEN); 2805 | RTMP_Log(RTMP_LOGDEBUG, "%s, b64(md5_1) = %s", __FUNCTION__, salted2); 2806 | 2807 | challenge2_data = rand(); 2808 | 2809 | b64enc((unsigned char *) &challenge2_data, sizeof(int), challenge2, CHALLENGE2_LEN); 2810 | RTMP_Log(RTMP_LOGDEBUG, "%s, b64(%d) = %s", __FUNCTION__, challenge2_data, challenge2); 2811 | 2812 | MD5_Init(&md5ctx); 2813 | MD5_Update(&md5ctx, salted2, B64DIGEST_LEN); 2814 | /* response = base64enc(md5(hash1 + opaque + challenge2)) */ 2815 | if (opaque.av_len) 2816 | MD5_Update(&md5ctx, opaque.av_val, opaque.av_len); 2817 | else if (challenge.av_len) 2818 | MD5_Update(&md5ctx, challenge.av_val, challenge.av_len); 2819 | MD5_Update(&md5ctx, challenge2, B64INT_LEN); 2820 | MD5_Final(md5sum_val, &md5ctx); 2821 | 2822 | RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s%s%s) =>", __FUNCTION__, 2823 | salted2, opaque.av_len ? opaque.av_val : "", challenge2); 2824 | RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH); 2825 | 2826 | b64enc(md5sum_val, MD5_DIGEST_LENGTH, response, RESPONSE_LEN); 2827 | RTMP_Log(RTMP_LOGDEBUG, "%s, b64(md5_2) = %s", __FUNCTION__, response); 2828 | 2829 | /* have all hashes, create auth token for the end of app */ 2830 | pubToken.av_val = malloc(32 + B64INT_LEN + B64DIGEST_LEN + opaque.av_len); 2831 | pubToken.av_len = sprintf(pubToken.av_val, 2832 | "&challenge=%s&response=%s&opaque=%s", 2833 | challenge2, 2834 | response, 2835 | opaque.av_len ? opaque.av_val : ""); 2836 | RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken2: %s", __FUNCTION__, pubToken.av_val); 2837 | free(orig_ptr); 2838 | } 2839 | else if(strstr(description->av_val, "?reason=authfailed") != NULL) 2840 | { 2841 | RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: wrong password", __FUNCTION__); 2842 | return 0; 2843 | } 2844 | else if(strstr(description->av_val, "?reason=nosuchuser") != NULL) 2845 | { 2846 | RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: no such user", __FUNCTION__); 2847 | return 0; 2848 | } 2849 | else 2850 | { 2851 | RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: unknown auth mode: %s", 2852 | __FUNCTION__, description->av_val); 2853 | return 0; 2854 | } 2855 | 2856 | ptr = malloc(r->Link.app.av_len + pubToken.av_len); 2857 | strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); 2858 | strncpy(ptr + r->Link.app.av_len, pubToken.av_val, pubToken.av_len); 2859 | r->Link.app.av_len += pubToken.av_len; 2860 | if(r->Link.lFlags & RTMP_LF_FAPU) 2861 | free(r->Link.app.av_val); 2862 | r->Link.app.av_val = ptr; 2863 | 2864 | ptr = malloc(r->Link.tcUrl.av_len + pubToken.av_len); 2865 | strncpy(ptr, r->Link.tcUrl.av_val, r->Link.tcUrl.av_len); 2866 | strncpy(ptr + r->Link.tcUrl.av_len, pubToken.av_val, pubToken.av_len); 2867 | r->Link.tcUrl.av_len += pubToken.av_len; 2868 | if(r->Link.lFlags & RTMP_LF_FTCU) 2869 | free(r->Link.tcUrl.av_val); 2870 | r->Link.tcUrl.av_val = ptr; 2871 | 2872 | free(pubToken.av_val); 2873 | r->Link.lFlags |= RTMP_LF_FTCU | RTMP_LF_FAPU; 2874 | 2875 | RTMP_Log(RTMP_LOGDEBUG, "%s, new app: %.*s tcUrl: %.*s playpath: %s", __FUNCTION__, 2876 | r->Link.app.av_len, r->Link.app.av_val, 2877 | r->Link.tcUrl.av_len, r->Link.tcUrl.av_val, 2878 | r->Link.playpath.av_val); 2879 | } 2880 | else if (strstr(description->av_val, av_authmod_llnw.av_val) != NULL) 2881 | { 2882 | if(strstr(description->av_val, "code=403 need auth") != NULL) 2883 | { 2884 | /* This part seems to be the same for llnw and adobe */ 2885 | 2886 | if (strstr(r->Link.app.av_val, av_authmod_llnw.av_val) != NULL) { 2887 | RTMP_Log(RTMP_LOGERROR, "%s, wrong pubUser & pubPasswd for publisher auth", __FUNCTION__); 2888 | return 0; 2889 | } else if(r->Link.pubUser.av_len && r->Link.pubPasswd.av_len) { 2890 | pubToken.av_val = malloc(r->Link.pubUser.av_len + av_authmod_llnw.av_len + 8); 2891 | pubToken.av_len = sprintf(pubToken.av_val, "?%s&user=%s", 2892 | av_authmod_llnw.av_val, 2893 | r->Link.pubUser.av_val); 2894 | RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken1: %s", __FUNCTION__, pubToken.av_val); 2895 | } else { 2896 | RTMP_Log(RTMP_LOGERROR, "%s, need to set pubUser & pubPasswd for publisher auth", __FUNCTION__); 2897 | return 0; 2898 | } 2899 | } 2900 | else if((token_in = strstr(description->av_val, "?reason=needauth")) != NULL) 2901 | { 2902 | char *orig_ptr; 2903 | char *par, *val = NULL; 2904 | char hash1[HEXHASH_LEN+1], hash2[HEXHASH_LEN+1], hash3[HEXHASH_LEN+1]; 2905 | AVal user, nonce, *aptr = NULL; 2906 | AVal apptmp; 2907 | 2908 | /* llnw auth method 2909 | * Seems to be closely based on HTTP Digest Auth: 2910 | * http://tools.ietf.org/html/rfc2617 2911 | * http://en.wikipedia.org/wiki/Digest_access_authentication 2912 | */ 2913 | 2914 | const char authmod[] = "llnw"; 2915 | const char realm[] = "live"; 2916 | const char method[] = "publish"; 2917 | const char qop[] = "auth"; 2918 | /* nc = 1..connection count (or rather, number of times cnonce has been reused) */ 2919 | int nc = 1; 2920 | /* nchex = hexenc(nc) (8 hex digits according to RFC 2617) */ 2921 | char nchex[9]; 2922 | /* cnonce = hexenc(4 random bytes) (initialized on first connection) */ 2923 | char cnonce[9]; 2924 | 2925 | ptr = orig_ptr = strdup(token_in); 2926 | /* Extract parameters (we need user and nonce) */ 2927 | while (ptr) 2928 | { 2929 | par = ptr; 2930 | ptr = strchr(par, '&'); 2931 | if(ptr) 2932 | *ptr++ = '\0'; 2933 | 2934 | val = strchr(par, '='); 2935 | if(val) 2936 | *val++ = '\0'; 2937 | 2938 | if (aptr) { 2939 | aptr->av_len = par - aptr->av_val - 1; 2940 | aptr = NULL; 2941 | } 2942 | if (strcmp(par, "user") == 0){ 2943 | user.av_val = val; 2944 | aptr = &user; 2945 | } else if (strcmp(par, "nonce") == 0){ 2946 | nonce.av_val = val; 2947 | aptr = &nonce; 2948 | } 2949 | 2950 | RTMP_Log(RTMP_LOGDEBUG, "%s, par:\"%s\" = val:\"%s\"", __FUNCTION__, par, val); 2951 | } 2952 | if (aptr) 2953 | aptr->av_len = strlen(aptr->av_val); 2954 | 2955 | /* FIXME: handle case where user==NULL or nonce==NULL */ 2956 | 2957 | sprintf(nchex, "%08x", nc); 2958 | sprintf(cnonce, "%08x", rand()); 2959 | 2960 | /* hash1 = hexenc(md5(user + ":" + realm + ":" + password)) */ 2961 | MD5_Init(&md5ctx); 2962 | MD5_Update(&md5ctx, user.av_val, user.av_len); 2963 | MD5_Update(&md5ctx, ":", 1); 2964 | MD5_Update(&md5ctx, realm, sizeof(realm)-1); 2965 | MD5_Update(&md5ctx, ":", 1); 2966 | MD5_Update(&md5ctx, r->Link.pubPasswd.av_val, r->Link.pubPasswd.av_len); 2967 | MD5_Final(md5sum_val, &md5ctx); 2968 | RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s:%s:%s) =>", __FUNCTION__, 2969 | user.av_val, realm, r->Link.pubPasswd.av_val); 2970 | RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH); 2971 | hexenc(md5sum_val, MD5_DIGEST_LENGTH, hash1); 2972 | 2973 | /* hash2 = hexenc(md5(method + ":/" + app + "/" + appInstance)) */ 2974 | /* Extract appname + appinstance without query parameters */ 2975 | apptmp = r->Link.app; 2976 | ptr = AValChr(&apptmp, '?'); 2977 | if (ptr) 2978 | apptmp.av_len = ptr - apptmp.av_val; 2979 | 2980 | MD5_Init(&md5ctx); 2981 | MD5_Update(&md5ctx, method, sizeof(method)-1); 2982 | MD5_Update(&md5ctx, ":/", 2); 2983 | MD5_Update(&md5ctx, apptmp.av_val, apptmp.av_len); 2984 | if (!AValChr(&apptmp, '/')) 2985 | MD5_Update(&md5ctx, "/_definst_", sizeof("/_definst_") - 1); 2986 | MD5_Final(md5sum_val, &md5ctx); 2987 | RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s:/%.*s) =>", __FUNCTION__, 2988 | method, apptmp.av_len, apptmp.av_val); 2989 | RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH); 2990 | hexenc(md5sum_val, MD5_DIGEST_LENGTH, hash2); 2991 | 2992 | /* hash3 = hexenc(md5(hash1 + ":" + nonce + ":" + nchex + ":" + cnonce + ":" + qop + ":" + hash2)) */ 2993 | MD5_Init(&md5ctx); 2994 | MD5_Update(&md5ctx, hash1, HEXHASH_LEN); 2995 | MD5_Update(&md5ctx, ":", 1); 2996 | MD5_Update(&md5ctx, nonce.av_val, nonce.av_len); 2997 | MD5_Update(&md5ctx, ":", 1); 2998 | MD5_Update(&md5ctx, nchex, sizeof(nchex)-1); 2999 | MD5_Update(&md5ctx, ":", 1); 3000 | MD5_Update(&md5ctx, cnonce, sizeof(cnonce)-1); 3001 | MD5_Update(&md5ctx, ":", 1); 3002 | MD5_Update(&md5ctx, qop, sizeof(qop)-1); 3003 | MD5_Update(&md5ctx, ":", 1); 3004 | MD5_Update(&md5ctx, hash2, HEXHASH_LEN); 3005 | MD5_Final(md5sum_val, &md5ctx); 3006 | RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s:%s:%s:%s:%s:%s) =>", __FUNCTION__, 3007 | hash1, nonce.av_val, nchex, cnonce, qop, hash2); 3008 | RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH); 3009 | hexenc(md5sum_val, MD5_DIGEST_LENGTH, hash3); 3010 | 3011 | /* pubToken = &authmod=&user=&nonce=&cnonce=&nc=&response= */ 3012 | /* Append nonces and response to query string which already contains 3013 | * user + authmod */ 3014 | pubToken.av_val = malloc(64 + sizeof(authmod)-1 + user.av_len + nonce.av_len + sizeof(cnonce)-1 + sizeof(nchex)-1 + HEXHASH_LEN); 3015 | sprintf(pubToken.av_val, 3016 | "&nonce=%s&cnonce=%s&nc=%s&response=%s", 3017 | nonce.av_val, cnonce, nchex, hash3); 3018 | pubToken.av_len = strlen(pubToken.av_val); 3019 | RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken2: %s", __FUNCTION__, pubToken.av_val); 3020 | 3021 | free(orig_ptr); 3022 | } 3023 | else if(strstr(description->av_val, "?reason=authfail") != NULL) 3024 | { 3025 | RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed", __FUNCTION__); 3026 | return 0; 3027 | } 3028 | else if(strstr(description->av_val, "?reason=nosuchuser") != NULL) 3029 | { 3030 | RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: no such user", __FUNCTION__); 3031 | return 0; 3032 | } 3033 | else 3034 | { 3035 | RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: unknown auth mode: %s", 3036 | __FUNCTION__, description->av_val); 3037 | return 0; 3038 | } 3039 | 3040 | ptr = malloc(r->Link.app.av_len + pubToken.av_len); 3041 | strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len); 3042 | strncpy(ptr + r->Link.app.av_len, pubToken.av_val, pubToken.av_len); 3043 | r->Link.app.av_len += pubToken.av_len; 3044 | if(r->Link.lFlags & RTMP_LF_FAPU) 3045 | free(r->Link.app.av_val); 3046 | r->Link.app.av_val = ptr; 3047 | 3048 | ptr = malloc(r->Link.tcUrl.av_len + pubToken.av_len); 3049 | strncpy(ptr, r->Link.tcUrl.av_val, r->Link.tcUrl.av_len); 3050 | strncpy(ptr + r->Link.tcUrl.av_len, pubToken.av_val, pubToken.av_len); 3051 | r->Link.tcUrl.av_len += pubToken.av_len; 3052 | if(r->Link.lFlags & RTMP_LF_FTCU) 3053 | free(r->Link.tcUrl.av_val); 3054 | r->Link.tcUrl.av_val = ptr; 3055 | 3056 | free(pubToken.av_val); 3057 | r->Link.lFlags |= RTMP_LF_FTCU | RTMP_LF_FAPU; 3058 | 3059 | RTMP_Log(RTMP_LOGDEBUG, "%s, new app: %.*s tcUrl: %.*s playpath: %s", __FUNCTION__, 3060 | r->Link.app.av_len, r->Link.app.av_val, 3061 | r->Link.tcUrl.av_len, r->Link.tcUrl.av_val, 3062 | r->Link.playpath.av_val); 3063 | } 3064 | else 3065 | { 3066 | return 0; 3067 | } 3068 | return 1; 3069 | } 3070 | #endif 3071 | 3072 | 3073 | SAVC(onBWDone); 3074 | SAVC(onFCSubscribe); 3075 | SAVC(onFCUnsubscribe); 3076 | SAVC(_onbwcheck); 3077 | SAVC(_onbwdone); 3078 | SAVC(_error); 3079 | SAVC(close); 3080 | SAVC(code); 3081 | SAVC(level); 3082 | SAVC(description); 3083 | SAVC(onStatus); 3084 | SAVC(playlist_ready); 3085 | static const AVal av_NetStream_Failed = AVC("NetStream.Failed"); 3086 | static const AVal av_NetStream_Play_Failed = AVC("NetStream.Play.Failed"); 3087 | static const AVal av_NetStream_Play_StreamNotFound = 3088 | AVC("NetStream.Play.StreamNotFound"); 3089 | static const AVal av_NetConnection_Connect_InvalidApp = 3090 | AVC("NetConnection.Connect.InvalidApp"); 3091 | static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start"); 3092 | static const AVal av_NetStream_Play_Complete = AVC("NetStream.Play.Complete"); 3093 | static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop"); 3094 | static const AVal av_NetStream_Seek_Notify = AVC("NetStream.Seek.Notify"); 3095 | static const AVal av_NetStream_Pause_Notify = AVC("NetStream.Pause.Notify"); 3096 | static const AVal av_NetStream_Play_PublishNotify = 3097 | AVC("NetStream.Play.PublishNotify"); 3098 | static const AVal av_NetStream_Play_UnpublishNotify = 3099 | AVC("NetStream.Play.UnpublishNotify"); 3100 | static const AVal av_NetStream_Publish_Start = AVC("NetStream.Publish.Start"); 3101 | static const AVal av_NetConnection_Connect_Rejected = 3102 | AVC("NetConnection.Connect.Rejected"); 3103 | 3104 | /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */ 3105 | static int 3106 | HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize) 3107 | { 3108 | AMFObject obj; 3109 | AVal method; 3110 | double txn; 3111 | int ret = 0, nRes; 3112 | if (body[0] != 0x02) /* make sure it is a string method name we start with */ 3113 | { 3114 | RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet", 3115 | __FUNCTION__); 3116 | return 0; 3117 | } 3118 | 3119 | nRes = AMF_Decode(&obj, body, nBodySize, FALSE); 3120 | if (nRes < 0) 3121 | { 3122 | RTMP_Log(RTMP_LOGERROR, "%s, error decoding invoke packet", __FUNCTION__); 3123 | return 0; 3124 | } 3125 | 3126 | AMF_Dump(&obj); 3127 | AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method); 3128 | txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1)); 3129 | RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val); 3130 | 3131 | if (AVMATCH(&method, &av__result)) 3132 | { 3133 | AVal methodInvoked = {0}; 3134 | int i; 3135 | 3136 | for (i=0; im_numCalls; i++) { 3137 | if (r->m_methodCalls[i].num == (int)txn) { 3138 | methodInvoked = r->m_methodCalls[i].name; 3139 | AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE); 3140 | break; 3141 | } 3142 | } 3143 | if (!methodInvoked.av_val) { 3144 | RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request", 3145 | __FUNCTION__, txn); 3146 | goto leave; 3147 | } 3148 | 3149 | RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__, 3150 | methodInvoked.av_val); 3151 | 3152 | if (AVMATCH(&methodInvoked, &av_connect)) 3153 | { 3154 | if (r->Link.token.av_len) 3155 | { 3156 | AMFObjectProperty p; 3157 | if (RTMP_FindFirstMatchingProperty(&obj, &av_secureToken, &p)) 3158 | { 3159 | DecodeTEA(&r->Link.token, &p.p_vu.p_aval); 3160 | SendSecureTokenResponse(r, &p.p_vu.p_aval); 3161 | } 3162 | } 3163 | if (r->Link.protocol & RTMP_FEATURE_WRITE) 3164 | { 3165 | SendReleaseStream(r); 3166 | SendFCPublish(r); 3167 | } 3168 | else 3169 | { 3170 | RTMP_SendServerBW(r); 3171 | RTMP_SendCtrl(r, 3, 0, 300); 3172 | } 3173 | RTMP_SendCreateStream(r); 3174 | 3175 | if (!(r->Link.protocol & RTMP_FEATURE_WRITE)) 3176 | { 3177 | /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */ 3178 | if (r->Link.usherToken.av_len) 3179 | SendUsherToken(r, &r->Link.usherToken); 3180 | /* Send the FCSubscribe if live stream or if subscribepath is set */ 3181 | if (r->Link.subscribepath.av_len) 3182 | SendFCSubscribe(r, &r->Link.subscribepath); 3183 | else if (r->Link.lFlags & RTMP_LF_LIVE) 3184 | SendFCSubscribe(r, &r->Link.playpath); 3185 | } 3186 | } 3187 | else if (AVMATCH(&methodInvoked, &av_createStream)) 3188 | { 3189 | r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3)); 3190 | 3191 | if (r->Link.protocol & RTMP_FEATURE_WRITE) 3192 | { 3193 | SendPublish(r); 3194 | } 3195 | else 3196 | { 3197 | if (r->Link.lFlags & RTMP_LF_PLST) 3198 | SendPlaylist(r); 3199 | SendPlay(r); 3200 | RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS); 3201 | } 3202 | } 3203 | else if (AVMATCH(&methodInvoked, &av_play) || 3204 | AVMATCH(&methodInvoked, &av_publish)) 3205 | { 3206 | r->m_bPlaying = TRUE; 3207 | } 3208 | free(methodInvoked.av_val); 3209 | } 3210 | else if (AVMATCH(&method, &av_onBWDone)) 3211 | { 3212 | if (!r->m_nBWCheckCounter) 3213 | SendCheckBW(r); 3214 | } 3215 | else if (AVMATCH(&method, &av_onFCSubscribe)) 3216 | { 3217 | /* SendOnFCSubscribe(); */ 3218 | } 3219 | else if (AVMATCH(&method, &av_onFCUnsubscribe)) 3220 | { 3221 | RTMP_Close(r); 3222 | ret = 1; 3223 | } 3224 | else if (AVMATCH(&method, &av_ping)) 3225 | { 3226 | SendPong(r, txn); 3227 | } 3228 | else if (AVMATCH(&method, &av__onbwcheck)) 3229 | { 3230 | SendCheckBWResult(r, txn); 3231 | } 3232 | else if (AVMATCH(&method, &av__onbwdone)) 3233 | { 3234 | int i; 3235 | for (i = 0; i < r->m_numCalls; i++) 3236 | if (AVMATCH(&r->m_methodCalls[i].name, &av__checkbw)) 3237 | { 3238 | AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); 3239 | break; 3240 | } 3241 | } 3242 | else if (AVMATCH(&method, &av__error)) 3243 | { 3244 | #ifdef CRYPTO 3245 | AVal methodInvoked = {0}; 3246 | int i; 3247 | 3248 | if (r->Link.protocol & RTMP_FEATURE_WRITE) 3249 | { 3250 | for (i=0; im_numCalls; i++) 3251 | { 3252 | if (r->m_methodCalls[i].num == txn) 3253 | { 3254 | methodInvoked = r->m_methodCalls[i].name; 3255 | AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE); 3256 | break; 3257 | } 3258 | } 3259 | if (!methodInvoked.av_val) 3260 | { 3261 | RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request", 3262 | __FUNCTION__, txn); 3263 | goto leave; 3264 | } 3265 | 3266 | RTMP_Log(RTMP_LOGDEBUG, "%s, received error for method call <%s>", __FUNCTION__, 3267 | methodInvoked.av_val); 3268 | 3269 | if (AVMATCH(&methodInvoked, &av_connect)) 3270 | { 3271 | AMFObject obj2; 3272 | AVal code, level, description; 3273 | AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2); 3274 | AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code); 3275 | AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level); 3276 | AMFProp_GetString(AMF_GetProp(&obj2, &av_description, -1), &description); 3277 | RTMP_Log(RTMP_LOGDEBUG, "%s, error description: %s", __FUNCTION__, description.av_val); 3278 | /* if PublisherAuth returns 1, then reconnect */ 3279 | if (PublisherAuth(r, &description) == 1) 3280 | { 3281 | CloseInternal(r, 1); 3282 | if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0)) 3283 | goto leave; 3284 | } 3285 | } 3286 | } 3287 | else 3288 | { 3289 | RTMP_Log(RTMP_LOGERROR, "rtmp server sent error"); 3290 | } 3291 | free(methodInvoked.av_val); 3292 | #else 3293 | RTMP_Log(RTMP_LOGERROR, "rtmp server sent error"); 3294 | #endif 3295 | } 3296 | else if (AVMATCH(&method, &av_close)) 3297 | { 3298 | RTMP_Log(RTMP_LOGERROR, "rtmp server requested close"); 3299 | RTMP_Close(r); 3300 | } 3301 | else if (AVMATCH(&method, &av_onStatus)) 3302 | { 3303 | AMFObject obj2; 3304 | AVal code, level; 3305 | AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2); 3306 | AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code); 3307 | AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level); 3308 | 3309 | RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val); 3310 | if (AVMATCH(&code, &av_NetStream_Failed) 3311 | || AVMATCH(&code, &av_NetStream_Play_Failed) 3312 | || AVMATCH(&code, &av_NetStream_Play_StreamNotFound) 3313 | || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp)) 3314 | { 3315 | r->m_stream_id = -1; 3316 | RTMP_Close(r); 3317 | RTMP_Log(RTMP_LOGERROR, "Closing connection: %s", code.av_val); 3318 | } 3319 | 3320 | else if (AVMATCH(&code, &av_NetStream_Play_Start) 3321 | || AVMATCH(&code, &av_NetStream_Play_PublishNotify)) 3322 | { 3323 | int i; 3324 | r->m_bPlaying = TRUE; 3325 | for (i = 0; i < r->m_numCalls; i++) 3326 | { 3327 | if (AVMATCH(&r->m_methodCalls[i].name, &av_play)) 3328 | { 3329 | AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); 3330 | break; 3331 | } 3332 | } 3333 | } 3334 | 3335 | else if (AVMATCH(&code, &av_NetStream_Publish_Start)) 3336 | { 3337 | int i; 3338 | r->m_bPlaying = TRUE; 3339 | for (i = 0; i < r->m_numCalls; i++) 3340 | { 3341 | if (AVMATCH(&r->m_methodCalls[i].name, &av_publish)) 3342 | { 3343 | AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); 3344 | break; 3345 | } 3346 | } 3347 | } 3348 | 3349 | /* Return 1 if this is a Play.Complete or Play.Stop */ 3350 | else if (AVMATCH(&code, &av_NetStream_Play_Complete) 3351 | || AVMATCH(&code, &av_NetStream_Play_Stop) 3352 | || AVMATCH(&code, &av_NetStream_Play_UnpublishNotify)) 3353 | { 3354 | RTMP_Close(r); 3355 | ret = 1; 3356 | } 3357 | 3358 | else if (AVMATCH(&code, &av_NetStream_Seek_Notify)) 3359 | { 3360 | r->m_read.flags &= ~RTMP_READ_SEEKING; 3361 | } 3362 | 3363 | else if (AVMATCH(&code, &av_NetStream_Pause_Notify)) 3364 | { 3365 | if (r->m_pausing == 1 || r->m_pausing == 2) 3366 | { 3367 | RTMP_SendPause(r, FALSE, r->m_pauseStamp); 3368 | r->m_pausing = 3; 3369 | } 3370 | } 3371 | } 3372 | else if (AVMATCH(&method, &av_playlist_ready)) 3373 | { 3374 | int i; 3375 | for (i = 0; i < r->m_numCalls; i++) 3376 | { 3377 | if (AVMATCH(&r->m_methodCalls[i].name, &av_set_playlist)) 3378 | { 3379 | AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE); 3380 | break; 3381 | } 3382 | } 3383 | } 3384 | else 3385 | { 3386 | 3387 | } 3388 | leave: 3389 | AMF_Reset(&obj); 3390 | return ret; 3391 | } 3392 | 3393 | int 3394 | RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name, 3395 | AMFObjectProperty * p) 3396 | { 3397 | int n; 3398 | /* this is a small object search to locate the "duration" property */ 3399 | for (n = 0; n < obj->o_num; n++) 3400 | { 3401 | AMFObjectProperty *prop = AMF_GetProp(obj, NULL, n); 3402 | 3403 | if (AVMATCH(&prop->p_name, name)) 3404 | { 3405 | memcpy(p, prop, sizeof(*prop)); 3406 | return TRUE; 3407 | } 3408 | 3409 | if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY) 3410 | { 3411 | if (RTMP_FindFirstMatchingProperty(&prop->p_vu.p_object, name, p)) 3412 | return TRUE; 3413 | } 3414 | } 3415 | return FALSE; 3416 | } 3417 | 3418 | /* Like above, but only check if name is a prefix of property */ 3419 | int 3420 | RTMP_FindPrefixProperty(AMFObject *obj, const AVal *name, 3421 | AMFObjectProperty * p) 3422 | { 3423 | int n; 3424 | for (n = 0; n < obj->o_num; n++) 3425 | { 3426 | AMFObjectProperty *prop = AMF_GetProp(obj, NULL, n); 3427 | 3428 | if (prop->p_name.av_len > name->av_len && 3429 | !memcmp(prop->p_name.av_val, name->av_val, name->av_len)) 3430 | { 3431 | memcpy(p, prop, sizeof(*prop)); 3432 | return TRUE; 3433 | } 3434 | 3435 | if (prop->p_type == AMF_OBJECT) 3436 | { 3437 | if (RTMP_FindPrefixProperty(&prop->p_vu.p_object, name, p)) 3438 | return TRUE; 3439 | } 3440 | } 3441 | return FALSE; 3442 | } 3443 | 3444 | static int 3445 | DumpMetaData(AMFObject *obj) 3446 | { 3447 | AMFObjectProperty *prop; 3448 | int n, len; 3449 | for (n = 0; n < obj->o_num; n++) 3450 | { 3451 | char str[256] = ""; 3452 | prop = AMF_GetProp(obj, NULL, n); 3453 | switch (prop->p_type) 3454 | { 3455 | case AMF_OBJECT: 3456 | case AMF_ECMA_ARRAY: 3457 | case AMF_STRICT_ARRAY: 3458 | if (prop->p_name.av_len) 3459 | RTMP_Log(RTMP_LOGINFO, "%.*s:", prop->p_name.av_len, prop->p_name.av_val); 3460 | DumpMetaData(&prop->p_vu.p_object); 3461 | break; 3462 | case AMF_NUMBER: 3463 | snprintf(str, 255, "%.2f", prop->p_vu.p_number); 3464 | break; 3465 | case AMF_BOOLEAN: 3466 | snprintf(str, 255, "%s", 3467 | prop->p_vu.p_number != 0. ? "TRUE" : "FALSE"); 3468 | break; 3469 | case AMF_STRING: 3470 | len = snprintf(str, 255, "%.*s", prop->p_vu.p_aval.av_len, 3471 | prop->p_vu.p_aval.av_val); 3472 | if (len >= 1 && str[len-1] == '\n') 3473 | str[len-1] = '\0'; 3474 | break; 3475 | case AMF_DATE: 3476 | snprintf(str, 255, "timestamp:%.2f", prop->p_vu.p_number); 3477 | break; 3478 | default: 3479 | snprintf(str, 255, "INVALID TYPE 0x%02x", 3480 | (unsigned char)prop->p_type); 3481 | } 3482 | if (str[0] && prop->p_name.av_len) 3483 | { 3484 | RTMP_Log(RTMP_LOGINFO, " %-22.*s%s", prop->p_name.av_len, 3485 | prop->p_name.av_val, str); 3486 | } 3487 | } 3488 | return FALSE; 3489 | } 3490 | 3491 | SAVC(onMetaData); 3492 | SAVC(duration); 3493 | SAVC(video); 3494 | SAVC(audio); 3495 | 3496 | static int 3497 | HandleMetadata(RTMP *r, char *body, unsigned int len) 3498 | { 3499 | /* allright we get some info here, so parse it and print it */ 3500 | /* also keep duration or filesize to make a nice progress bar */ 3501 | 3502 | AMFObject obj; 3503 | AVal metastring; 3504 | int ret = FALSE; 3505 | 3506 | int nRes = AMF_Decode(&obj, body, len, FALSE); 3507 | if (nRes < 0) 3508 | { 3509 | RTMP_Log(RTMP_LOGERROR, "%s, error decoding meta data packet", __FUNCTION__); 3510 | return FALSE; 3511 | } 3512 | 3513 | AMF_Dump(&obj); 3514 | AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &metastring); 3515 | 3516 | if (AVMATCH(&metastring, &av_onMetaData)) 3517 | { 3518 | AMFObjectProperty prop; 3519 | /* Show metadata */ 3520 | RTMP_Log(RTMP_LOGINFO, "Metadata:"); 3521 | DumpMetaData(&obj); 3522 | if (RTMP_FindFirstMatchingProperty(&obj, &av_duration, &prop)) 3523 | { 3524 | r->m_fDuration = prop.p_vu.p_number; 3525 | /*RTMP_Log(RTMP_LOGDEBUG, "Set duration: %.2f", m_fDuration); */ 3526 | } 3527 | /* Search for audio or video tags */ 3528 | if (RTMP_FindPrefixProperty(&obj, &av_video, &prop)) 3529 | r->m_read.dataType |= 1; 3530 | if (RTMP_FindPrefixProperty(&obj, &av_audio, &prop)) 3531 | r->m_read.dataType |= 4; 3532 | ret = TRUE; 3533 | } 3534 | AMF_Reset(&obj); 3535 | return ret; 3536 | } 3537 | 3538 | static void 3539 | HandleChangeChunkSize(RTMP *r, const RTMPPacket *packet) 3540 | { 3541 | if (packet->m_nBodySize >= 4) 3542 | { 3543 | r->m_inChunkSize = AMF_DecodeInt32(packet->m_body); 3544 | RTMP_Log(RTMP_LOGDEBUG, "%s, received: chunk size change to %d", __FUNCTION__, 3545 | r->m_inChunkSize); 3546 | } 3547 | } 3548 | 3549 | static void 3550 | HandleAudio(RTMP *r, const RTMPPacket *packet) 3551 | { 3552 | } 3553 | 3554 | static void 3555 | HandleVideo(RTMP *r, const RTMPPacket *packet) 3556 | { 3557 | } 3558 | 3559 | static void 3560 | HandleCtrl(RTMP *r, const RTMPPacket *packet) 3561 | { 3562 | short nType = -1; 3563 | unsigned int tmp; 3564 | if (packet->m_body && packet->m_nBodySize >= 2) 3565 | nType = AMF_DecodeInt16(packet->m_body); 3566 | RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl. type: %d, len: %d", __FUNCTION__, nType, 3567 | packet->m_nBodySize); 3568 | /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */ 3569 | 3570 | if (packet->m_nBodySize >= 6) 3571 | { 3572 | switch (nType) 3573 | { 3574 | case 0: 3575 | tmp = AMF_DecodeInt32(packet->m_body + 2); 3576 | RTMP_Log(RTMP_LOGDEBUG, "%s, Stream Begin %d", __FUNCTION__, tmp); 3577 | break; 3578 | 3579 | case 1: 3580 | tmp = AMF_DecodeInt32(packet->m_body + 2); 3581 | RTMP_Log(RTMP_LOGDEBUG, "%s, Stream EOF %d", __FUNCTION__, tmp); 3582 | if (r->m_pausing == 1) 3583 | r->m_pausing = 2; 3584 | break; 3585 | 3586 | case 2: 3587 | tmp = AMF_DecodeInt32(packet->m_body + 2); 3588 | RTMP_Log(RTMP_LOGDEBUG, "%s, Stream Dry %d", __FUNCTION__, tmp); 3589 | break; 3590 | 3591 | case 4: 3592 | tmp = AMF_DecodeInt32(packet->m_body + 2); 3593 | RTMP_Log(RTMP_LOGDEBUG, "%s, Stream IsRecorded %d", __FUNCTION__, tmp); 3594 | break; 3595 | 3596 | case 6: /* server ping. reply with pong. */ 3597 | tmp = AMF_DecodeInt32(packet->m_body + 2); 3598 | RTMP_Log(RTMP_LOGDEBUG, "%s, Ping %d", __FUNCTION__, tmp); 3599 | RTMP_SendCtrl(r, 0x07, tmp, 0); 3600 | break; 3601 | 3602 | /* FMS 3.5 servers send the following two controls to let the client 3603 | * know when the server has sent a complete buffer. I.e., when the 3604 | * server has sent an amount of data equal to m_nBufferMS in duration. 3605 | * The server meters its output so that data arrives at the client 3606 | * in realtime and no faster. 3607 | * 3608 | * The rtmpdump program tries to set m_nBufferMS as large as 3609 | * possible, to force the server to send data as fast as possible. 3610 | * In practice, the server appears to cap this at about 1 hour's 3611 | * worth of data. After the server has sent a complete buffer, and 3612 | * sends this BufferEmpty message, it will wait until the play 3613 | * duration of that buffer has passed before sending a new buffer. 3614 | * The BufferReady message will be sent when the new buffer starts. 3615 | * (There is no BufferReady message for the very first buffer; 3616 | * presumably the Stream Begin message is sufficient for that 3617 | * purpose.) 3618 | * 3619 | * If the network speed is much faster than the data bitrate, then 3620 | * there may be long delays between the end of one buffer and the 3621 | * start of the next. 3622 | * 3623 | * Since usually the network allows data to be sent at 3624 | * faster than realtime, and rtmpdump wants to download the data 3625 | * as fast as possible, we use this RTMP_LF_BUFX hack: when we 3626 | * get the BufferEmpty message, we send a Pause followed by an 3627 | * Unpause. This causes the server to send the next buffer immediately 3628 | * instead of waiting for the full duration to elapse. (That's 3629 | * also the purpose of the ToggleStream function, which rtmpdump 3630 | * calls if we get a read timeout.) 3631 | * 3632 | * Media player apps don't need this hack since they are just 3633 | * going to play the data in realtime anyway. It also doesn't work 3634 | * for live streams since they obviously can only be sent in 3635 | * realtime. And it's all moot if the network speed is actually 3636 | * slower than the media bitrate. 3637 | */ 3638 | case 31: 3639 | tmp = AMF_DecodeInt32(packet->m_body + 2); 3640 | RTMP_Log(RTMP_LOGDEBUG, "%s, Stream BufferEmpty %d", __FUNCTION__, tmp); 3641 | if (!(r->Link.lFlags & RTMP_LF_BUFX)) 3642 | break; 3643 | if (!r->m_pausing) 3644 | { 3645 | r->m_pauseStamp = r->m_mediaChannel < r->m_channelsAllocatedIn ? 3646 | r->m_channelTimestamp[r->m_mediaChannel] : 0; 3647 | RTMP_SendPause(r, TRUE, r->m_pauseStamp); 3648 | r->m_pausing = 1; 3649 | } 3650 | else if (r->m_pausing == 2) 3651 | { 3652 | RTMP_SendPause(r, FALSE, r->m_pauseStamp); 3653 | r->m_pausing = 3; 3654 | } 3655 | break; 3656 | 3657 | case 32: 3658 | tmp = AMF_DecodeInt32(packet->m_body + 2); 3659 | RTMP_Log(RTMP_LOGDEBUG, "%s, Stream BufferReady %d", __FUNCTION__, tmp); 3660 | break; 3661 | 3662 | default: 3663 | tmp = AMF_DecodeInt32(packet->m_body + 2); 3664 | RTMP_Log(RTMP_LOGDEBUG, "%s, Stream xx %d", __FUNCTION__, tmp); 3665 | break; 3666 | } 3667 | 3668 | } 3669 | 3670 | if (nType == 0x1A) 3671 | { 3672 | RTMP_Log(RTMP_LOGDEBUG, "%s, SWFVerification ping received: ", __FUNCTION__); 3673 | if (packet->m_nBodySize > 2 && packet->m_body[2] > 0x01) 3674 | { 3675 | RTMP_Log(RTMP_LOGERROR, 3676 | "%s: SWFVerification Type %d request not supported! Patches welcome...", 3677 | __FUNCTION__, packet->m_body[2]); 3678 | } 3679 | #ifdef CRYPTO 3680 | /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */ 3681 | 3682 | /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */ 3683 | else if (r->Link.SWFSize) 3684 | { 3685 | RTMP_SendCtrl(r, 0x1B, 0, 0); 3686 | } 3687 | else 3688 | { 3689 | RTMP_Log(RTMP_LOGERROR, 3690 | "%s: Ignoring SWFVerification request, use --swfVfy!", 3691 | __FUNCTION__); 3692 | } 3693 | #else 3694 | RTMP_Log(RTMP_LOGERROR, 3695 | "%s: Ignoring SWFVerification request, no CRYPTO support!", 3696 | __FUNCTION__); 3697 | #endif 3698 | } 3699 | } 3700 | 3701 | static void 3702 | HandleServerBW(RTMP *r, const RTMPPacket *packet) 3703 | { 3704 | r->m_nServerBW = AMF_DecodeInt32(packet->m_body); 3705 | RTMP_Log(RTMP_LOGDEBUG, "%s: server BW = %d", __FUNCTION__, r->m_nServerBW); 3706 | } 3707 | 3708 | static void 3709 | HandleClientBW(RTMP *r, const RTMPPacket *packet) 3710 | { 3711 | r->m_nClientBW = AMF_DecodeInt32(packet->m_body); 3712 | if (packet->m_nBodySize > 4) 3713 | r->m_nClientBW2 = packet->m_body[4]; 3714 | else 3715 | r->m_nClientBW2 = -1; 3716 | RTMP_Log(RTMP_LOGDEBUG, "%s: client BW = %d %d", __FUNCTION__, r->m_nClientBW, 3717 | r->m_nClientBW2); 3718 | } 3719 | 3720 | static int 3721 | DecodeInt32LE(const char *data) 3722 | { 3723 | unsigned char *c = (unsigned char *)data; 3724 | unsigned int val; 3725 | 3726 | val = (c[3] << 24) | (c[2] << 16) | (c[1] << 8) | c[0]; 3727 | return val; 3728 | } 3729 | 3730 | static int 3731 | EncodeInt32LE(char *output, int nVal) 3732 | { 3733 | output[0] = nVal; 3734 | nVal >>= 8; 3735 | output[1] = nVal; 3736 | nVal >>= 8; 3737 | output[2] = nVal; 3738 | nVal >>= 8; 3739 | output[3] = nVal; 3740 | return 4; 3741 | } 3742 | 3743 | int 3744 | RTMP_ReadPacket(RTMP *r, RTMPPacket *packet) 3745 | { 3746 | uint8_t hbuf[RTMP_MAX_HEADER_SIZE] = { 0 }; 3747 | char *header = (char *)hbuf; 3748 | int nSize, hSize, nToRead, nChunk; 3749 | int didAlloc = FALSE; 3750 | int extendedTimestamp; 3751 | 3752 | RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d", __FUNCTION__, r->m_sb.sb_socket); 3753 | 3754 | if (ReadN(r, (char *)hbuf, 1) == 0) 3755 | { 3756 | RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header", __FUNCTION__); 3757 | return FALSE; 3758 | } 3759 | 3760 | packet->m_headerType = (hbuf[0] & 0xc0) >> 6; 3761 | packet->m_nChannel = (hbuf[0] & 0x3f); 3762 | header++; 3763 | if (packet->m_nChannel == 0) 3764 | { 3765 | if (ReadN(r, (char *)&hbuf[1], 1) != 1) 3766 | { 3767 | RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header 2nd byte", 3768 | __FUNCTION__); 3769 | return FALSE; 3770 | } 3771 | packet->m_nChannel = hbuf[1]; 3772 | packet->m_nChannel += 64; 3773 | header++; 3774 | } 3775 | else if (packet->m_nChannel == 1) 3776 | { 3777 | int tmp; 3778 | if (ReadN(r, (char *)&hbuf[1], 2) != 2) 3779 | { 3780 | RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header 3nd byte", 3781 | __FUNCTION__); 3782 | return FALSE; 3783 | } 3784 | tmp = (hbuf[2] << 8) + hbuf[1]; 3785 | packet->m_nChannel = tmp + 64; 3786 | RTMP_Log(RTMP_LOGDEBUG, "%s, m_nChannel: %0x", __FUNCTION__, packet->m_nChannel); 3787 | header += 2; 3788 | } 3789 | 3790 | nSize = packetSize[packet->m_headerType]; 3791 | 3792 | if (packet->m_nChannel >= r->m_channelsAllocatedIn) 3793 | { 3794 | int n = packet->m_nChannel + 10; 3795 | int *timestamp = realloc(r->m_channelTimestamp, sizeof(int) * n); 3796 | RTMPPacket **packets = realloc(r->m_vecChannelsIn, sizeof(RTMPPacket*) * n); 3797 | if (!timestamp) 3798 | free(r->m_channelTimestamp); 3799 | if (!packets) 3800 | free(r->m_vecChannelsIn); 3801 | r->m_channelTimestamp = timestamp; 3802 | r->m_vecChannelsIn = packets; 3803 | if (!timestamp || !packets) { 3804 | r->m_channelsAllocatedIn = 0; 3805 | return FALSE; 3806 | } 3807 | memset(r->m_channelTimestamp + r->m_channelsAllocatedIn, 0, sizeof(int) * (n - r->m_channelsAllocatedIn)); 3808 | memset(r->m_vecChannelsIn + r->m_channelsAllocatedIn, 0, sizeof(RTMPPacket*) * (n - r->m_channelsAllocatedIn)); 3809 | r->m_channelsAllocatedIn = n; 3810 | } 3811 | 3812 | if (nSize == RTMP_LARGE_HEADER_SIZE) /* if we get a full header the timestamp is absolute */ 3813 | packet->m_hasAbsTimestamp = TRUE; 3814 | 3815 | else if (nSize < RTMP_LARGE_HEADER_SIZE) 3816 | { /* using values from the last message of this channel */ 3817 | if (r->m_vecChannelsIn[packet->m_nChannel]) 3818 | memcpy(packet, r->m_vecChannelsIn[packet->m_nChannel], 3819 | sizeof(RTMPPacket)); 3820 | } 3821 | 3822 | nSize--; 3823 | 3824 | if (nSize > 0 && ReadN(r, header, nSize) != nSize) 3825 | { 3826 | RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet header. type: %x", 3827 | __FUNCTION__, (unsigned int)hbuf[0]); 3828 | return FALSE; 3829 | } 3830 | 3831 | hSize = nSize + (header - (char *)hbuf); 3832 | 3833 | if (nSize >= 3) 3834 | { 3835 | packet->m_nTimeStamp = AMF_DecodeInt24(header); 3836 | 3837 | /*RTMP_Log(RTMP_LOGDEBUG, "%s, reading RTMP packet chunk on channel %x, headersz %i, timestamp %i, abs timestamp %i", __FUNCTION__, packet.m_nChannel, nSize, packet.m_nTimeStamp, packet.m_hasAbsTimestamp); */ 3838 | 3839 | if (nSize >= 6) 3840 | { 3841 | packet->m_nBodySize = AMF_DecodeInt24(header + 3); 3842 | packet->m_nBytesRead = 0; 3843 | 3844 | if (nSize > 6) 3845 | { 3846 | packet->m_packetType = header[6]; 3847 | 3848 | if (nSize == 11) 3849 | packet->m_nInfoField2 = DecodeInt32LE(header + 7); 3850 | } 3851 | } 3852 | } 3853 | 3854 | extendedTimestamp = packet->m_nTimeStamp == 0xffffff; 3855 | if (extendedTimestamp) 3856 | { 3857 | if (ReadN(r, header + nSize, 4) != 4) 3858 | { 3859 | RTMP_Log(RTMP_LOGERROR, "%s, failed to read extended timestamp", 3860 | __FUNCTION__); 3861 | return FALSE; 3862 | } 3863 | packet->m_nTimeStamp = AMF_DecodeInt32(header + nSize); 3864 | hSize += 4; 3865 | } 3866 | 3867 | RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)hbuf, hSize); 3868 | 3869 | if (packet->m_nBodySize > 0 && packet->m_body == NULL) 3870 | { 3871 | if (!RTMPPacket_Alloc(packet, packet->m_nBodySize)) 3872 | { 3873 | RTMP_Log(RTMP_LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__); 3874 | return FALSE; 3875 | } 3876 | didAlloc = TRUE; 3877 | packet->m_headerType = (hbuf[0] & 0xc0) >> 6; 3878 | } 3879 | 3880 | nToRead = packet->m_nBodySize - packet->m_nBytesRead; 3881 | nChunk = r->m_inChunkSize; 3882 | if (nToRead < nChunk) 3883 | nChunk = nToRead; 3884 | 3885 | /* Does the caller want the raw chunk? */ 3886 | if (packet->m_chunk) 3887 | { 3888 | packet->m_chunk->c_headerSize = hSize; 3889 | memcpy(packet->m_chunk->c_header, hbuf, hSize); 3890 | packet->m_chunk->c_chunk = packet->m_body + packet->m_nBytesRead; 3891 | packet->m_chunk->c_chunkSize = nChunk; 3892 | } 3893 | 3894 | if (ReadN(r, packet->m_body + packet->m_nBytesRead, nChunk) != nChunk) 3895 | { 3896 | RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet body. len: %u", 3897 | __FUNCTION__, packet->m_nBodySize); 3898 | return FALSE; 3899 | } 3900 | 3901 | RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)packet->m_body + packet->m_nBytesRead, nChunk); 3902 | 3903 | packet->m_nBytesRead += nChunk; 3904 | 3905 | /* keep the packet as ref for other packets on this channel */ 3906 | if (!r->m_vecChannelsIn[packet->m_nChannel]) 3907 | r->m_vecChannelsIn[packet->m_nChannel] = malloc(sizeof(RTMPPacket)); 3908 | memcpy(r->m_vecChannelsIn[packet->m_nChannel], packet, sizeof(RTMPPacket)); 3909 | if (extendedTimestamp) 3910 | { 3911 | r->m_vecChannelsIn[packet->m_nChannel]->m_nTimeStamp = 0xffffff; 3912 | } 3913 | 3914 | if (RTMPPacket_IsReady(packet)) 3915 | { 3916 | /* make packet's timestamp absolute */ 3917 | if (!packet->m_hasAbsTimestamp) 3918 | packet->m_nTimeStamp += r->m_channelTimestamp[packet->m_nChannel]; /* timestamps seem to be always relative!! */ 3919 | 3920 | r->m_channelTimestamp[packet->m_nChannel] = packet->m_nTimeStamp; 3921 | 3922 | /* reset the data from the stored packet. we keep the header since we may use it later if a new packet for this channel */ 3923 | /* arrives and requests to re-use some info (small packet header) */ 3924 | r->m_vecChannelsIn[packet->m_nChannel]->m_body = NULL; 3925 | r->m_vecChannelsIn[packet->m_nChannel]->m_nBytesRead = 0; 3926 | r->m_vecChannelsIn[packet->m_nChannel]->m_hasAbsTimestamp = FALSE; /* can only be false if we reuse header */ 3927 | } 3928 | else 3929 | { 3930 | packet->m_body = NULL; /* so it won't be erased on free */ 3931 | } 3932 | 3933 | return TRUE; 3934 | } 3935 | 3936 | #ifndef CRYPTO 3937 | static int 3938 | HandShake(RTMP *r, int FP9HandShake) 3939 | { 3940 | int i; 3941 | uint32_t uptime, suptime; 3942 | int bMatch; 3943 | char type; 3944 | char clientbuf[RTMP_SIG_SIZE + 1], *clientsig = clientbuf + 1; 3945 | char serversig[RTMP_SIG_SIZE]; 3946 | 3947 | clientbuf[0] = 0x03; /* not encrypted */ 3948 | 3949 | uptime = htonl(RTMP_GetTime()); 3950 | memcpy(clientsig, &uptime, 4); 3951 | 3952 | memset(&clientsig[4], 0, 4); 3953 | 3954 | #ifdef _DEBUG 3955 | for (i = 8; i < RTMP_SIG_SIZE; i++) 3956 | clientsig[i] = 0xff; 3957 | #else 3958 | for (i = 8; i < RTMP_SIG_SIZE; i++) 3959 | clientsig[i] = (char)(rand() % 256); 3960 | #endif 3961 | 3962 | if (!WriteN(r, clientbuf, RTMP_SIG_SIZE + 1)) 3963 | return FALSE; 3964 | 3965 | if (ReadN(r, &type, 1) != 1) /* 0x03 or 0x06 */ 3966 | return FALSE; 3967 | 3968 | RTMP_Log(RTMP_LOGDEBUG, "%s: Type Answer : %02X", __FUNCTION__, type); 3969 | 3970 | if (type != clientbuf[0]) 3971 | RTMP_Log(RTMP_LOGWARNING, "%s: Type mismatch: client sent %d, server answered %d", 3972 | __FUNCTION__, clientbuf[0], type); 3973 | 3974 | if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) 3975 | return FALSE; 3976 | 3977 | /* decode server response */ 3978 | 3979 | memcpy(&suptime, serversig, 4); 3980 | suptime = ntohl(suptime); 3981 | 3982 | RTMP_Log(RTMP_LOGDEBUG, "%s: Server Uptime : %d", __FUNCTION__, suptime); 3983 | RTMP_Log(RTMP_LOGDEBUG, "%s: FMS Version : %d.%d.%d.%d", __FUNCTION__, 3984 | serversig[4], serversig[5], serversig[6], serversig[7]); 3985 | 3986 | /* 2nd part of handshake */ 3987 | if (!WriteN(r, serversig, RTMP_SIG_SIZE)) 3988 | return FALSE; 3989 | 3990 | if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) 3991 | return FALSE; 3992 | 3993 | bMatch = (memcmp(serversig, clientsig, RTMP_SIG_SIZE) == 0); 3994 | if (!bMatch) 3995 | { 3996 | RTMP_Log(RTMP_LOGWARNING, "%s, client signature does not match!", __FUNCTION__); 3997 | } 3998 | return TRUE; 3999 | } 4000 | 4001 | static int 4002 | SHandShake(RTMP *r) 4003 | { 4004 | int i; 4005 | char serverbuf[RTMP_SIG_SIZE + 1], *serversig = serverbuf + 1; 4006 | char clientsig[RTMP_SIG_SIZE]; 4007 | uint32_t uptime; 4008 | int bMatch; 4009 | 4010 | if (ReadN(r, serverbuf, 1) != 1) /* 0x03 or 0x06 */ 4011 | return FALSE; 4012 | 4013 | RTMP_Log(RTMP_LOGDEBUG, "%s: Type Request : %02X", __FUNCTION__, serverbuf[0]); 4014 | 4015 | if (serverbuf[0] != 3) 4016 | { 4017 | RTMP_Log(RTMP_LOGERROR, "%s: Type unknown: client sent %02X", 4018 | __FUNCTION__, serverbuf[0]); 4019 | return FALSE; 4020 | } 4021 | 4022 | uptime = htonl(RTMP_GetTime()); 4023 | memcpy(serversig, &uptime, 4); 4024 | 4025 | memset(&serversig[4], 0, 4); 4026 | #ifdef _DEBUG 4027 | for (i = 8; i < RTMP_SIG_SIZE; i++) 4028 | serversig[i] = 0xff; 4029 | #else 4030 | for (i = 8; i < RTMP_SIG_SIZE; i++) 4031 | serversig[i] = (char)(rand() % 256); 4032 | #endif 4033 | 4034 | if (!WriteN(r, serverbuf, RTMP_SIG_SIZE + 1)) 4035 | return FALSE; 4036 | 4037 | if (ReadN(r, clientsig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) 4038 | return FALSE; 4039 | 4040 | /* decode client response */ 4041 | 4042 | memcpy(&uptime, clientsig, 4); 4043 | uptime = ntohl(uptime); 4044 | 4045 | RTMP_Log(RTMP_LOGDEBUG, "%s: Client Uptime : %d", __FUNCTION__, uptime); 4046 | RTMP_Log(RTMP_LOGDEBUG, "%s: Player Version: %d.%d.%d.%d", __FUNCTION__, 4047 | clientsig[4], clientsig[5], clientsig[6], clientsig[7]); 4048 | 4049 | /* 2nd part of handshake */ 4050 | if (!WriteN(r, clientsig, RTMP_SIG_SIZE)) 4051 | return FALSE; 4052 | 4053 | if (ReadN(r, clientsig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE) 4054 | return FALSE; 4055 | 4056 | bMatch = (memcmp(serversig, clientsig, RTMP_SIG_SIZE) == 0); 4057 | if (!bMatch) 4058 | { 4059 | RTMP_Log(RTMP_LOGWARNING, "%s, client signature does not match!", __FUNCTION__); 4060 | } 4061 | return TRUE; 4062 | } 4063 | #endif 4064 | 4065 | int 4066 | RTMP_SendChunk(RTMP *r, RTMPChunk *chunk) 4067 | { 4068 | int wrote; 4069 | char hbuf[RTMP_MAX_HEADER_SIZE]; 4070 | 4071 | RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d, size=%d", __FUNCTION__, r->m_sb.sb_socket, 4072 | chunk->c_chunkSize); 4073 | RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)chunk->c_header, chunk->c_headerSize); 4074 | if (chunk->c_chunkSize) 4075 | { 4076 | char *ptr = chunk->c_chunk - chunk->c_headerSize; 4077 | RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)chunk->c_chunk, chunk->c_chunkSize); 4078 | /* save header bytes we're about to overwrite */ 4079 | memcpy(hbuf, ptr, chunk->c_headerSize); 4080 | memcpy(ptr, chunk->c_header, chunk->c_headerSize); 4081 | wrote = WriteN(r, ptr, chunk->c_headerSize + chunk->c_chunkSize); 4082 | memcpy(ptr, hbuf, chunk->c_headerSize); 4083 | } 4084 | else 4085 | wrote = WriteN(r, chunk->c_header, chunk->c_headerSize); 4086 | return wrote; 4087 | } 4088 | 4089 | int 4090 | RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue) 4091 | { 4092 | const RTMPPacket *prevPacket; 4093 | uint32_t last = 0; 4094 | int nSize; 4095 | int hSize, cSize; 4096 | char *header, *hptr, *hend, hbuf[RTMP_MAX_HEADER_SIZE], c; 4097 | uint32_t t; 4098 | char *buffer, *tbuf = NULL, *toff = NULL; 4099 | int nChunkSize; 4100 | int tlen; 4101 | 4102 | if (packet->m_nChannel >= r->m_channelsAllocatedOut) 4103 | { 4104 | int n = packet->m_nChannel + 10; 4105 | RTMPPacket **packets = realloc(r->m_vecChannelsOut, sizeof(RTMPPacket*) * n); 4106 | if (!packets) { 4107 | free(r->m_vecChannelsOut); 4108 | r->m_vecChannelsOut = NULL; 4109 | r->m_channelsAllocatedOut = 0; 4110 | return FALSE; 4111 | } 4112 | r->m_vecChannelsOut = packets; 4113 | memset(r->m_vecChannelsOut + r->m_channelsAllocatedOut, 0, sizeof(RTMPPacket*) * (n - r->m_channelsAllocatedOut)); 4114 | r->m_channelsAllocatedOut = n; 4115 | } 4116 | 4117 | prevPacket = r->m_vecChannelsOut[packet->m_nChannel]; 4118 | if (prevPacket && packet->m_headerType != RTMP_PACKET_SIZE_LARGE) 4119 | { 4120 | /* compress a bit by using the prev packet's attributes */ 4121 | if (prevPacket->m_nBodySize == packet->m_nBodySize 4122 | && prevPacket->m_packetType == packet->m_packetType 4123 | && packet->m_headerType == RTMP_PACKET_SIZE_MEDIUM) 4124 | packet->m_headerType = RTMP_PACKET_SIZE_SMALL; 4125 | 4126 | if (prevPacket->m_nTimeStamp == packet->m_nTimeStamp 4127 | && packet->m_headerType == RTMP_PACKET_SIZE_SMALL) 4128 | packet->m_headerType = RTMP_PACKET_SIZE_MINIMUM; 4129 | last = prevPacket->m_nTimeStamp; 4130 | } 4131 | 4132 | if (packet->m_headerType > 3) /* sanity */ 4133 | { 4134 | RTMP_Log(RTMP_LOGERROR, "sanity failed!! trying to send header of type: 0x%02x.", 4135 | (unsigned char)packet->m_headerType); 4136 | return FALSE; 4137 | } 4138 | 4139 | nSize = packetSize[packet->m_headerType]; 4140 | hSize = nSize; cSize = 0; 4141 | t = packet->m_nTimeStamp - last; 4142 | 4143 | if (packet->m_body) 4144 | { 4145 | header = packet->m_body - nSize; 4146 | hend = packet->m_body; 4147 | } 4148 | else 4149 | { 4150 | header = hbuf + 6; 4151 | hend = hbuf + sizeof(hbuf); 4152 | } 4153 | 4154 | if (packet->m_nChannel > 319) 4155 | cSize = 2; 4156 | else if (packet->m_nChannel > 63) 4157 | cSize = 1; 4158 | if (cSize) 4159 | { 4160 | header -= cSize; 4161 | hSize += cSize; 4162 | } 4163 | 4164 | if (t >= 0xffffff) 4165 | { 4166 | header -= 4; 4167 | hSize += 4; 4168 | RTMP_Log(RTMP_LOGWARNING, "Larger timestamp than 24-bit: 0x%x", t); 4169 | } 4170 | 4171 | hptr = header; 4172 | c = packet->m_headerType << 6; 4173 | switch (cSize) 4174 | { 4175 | case 0: 4176 | c |= packet->m_nChannel; 4177 | break; 4178 | case 1: 4179 | break; 4180 | case 2: 4181 | c |= 1; 4182 | break; 4183 | } 4184 | *hptr++ = c; 4185 | if (cSize) 4186 | { 4187 | int tmp = packet->m_nChannel - 64; 4188 | *hptr++ = tmp & 0xff; 4189 | if (cSize == 2) 4190 | *hptr++ = tmp >> 8; 4191 | } 4192 | 4193 | if (nSize > 1) 4194 | { 4195 | hptr = AMF_EncodeInt24(hptr, hend, t > 0xffffff ? 0xffffff : t); 4196 | } 4197 | 4198 | if (nSize > 4) 4199 | { 4200 | hptr = AMF_EncodeInt24(hptr, hend, packet->m_nBodySize); 4201 | *hptr++ = packet->m_packetType; 4202 | } 4203 | 4204 | if (nSize > 8) 4205 | hptr += EncodeInt32LE(hptr, packet->m_nInfoField2); 4206 | 4207 | if (t >= 0xffffff) 4208 | hptr = AMF_EncodeInt32(hptr, hend, t); 4209 | 4210 | nSize = packet->m_nBodySize; 4211 | buffer = packet->m_body; 4212 | nChunkSize = r->m_outChunkSize; 4213 | 4214 | RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d, size=%d", __FUNCTION__, r->m_sb.sb_socket, 4215 | nSize); 4216 | /* send all chunks in one HTTP request */ 4217 | if (r->Link.protocol & RTMP_FEATURE_HTTP) 4218 | { 4219 | int chunks = (nSize+nChunkSize-1) / nChunkSize; 4220 | if (chunks > 1) 4221 | { 4222 | tlen = chunks * (cSize + 1) + nSize + hSize; 4223 | tbuf = malloc(tlen); 4224 | if (!tbuf) 4225 | return FALSE; 4226 | toff = tbuf; 4227 | } 4228 | } 4229 | while (nSize + hSize) 4230 | { 4231 | int wrote; 4232 | 4233 | if (nSize < nChunkSize) 4234 | nChunkSize = nSize; 4235 | 4236 | RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)header, hSize); 4237 | RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)buffer, nChunkSize); 4238 | if (tbuf) 4239 | { 4240 | memcpy(toff, header, nChunkSize + hSize); 4241 | toff += nChunkSize + hSize; 4242 | } 4243 | else 4244 | { 4245 | wrote = WriteN(r, header, nChunkSize + hSize); 4246 | if (!wrote) 4247 | return FALSE; 4248 | } 4249 | nSize -= nChunkSize; 4250 | buffer += nChunkSize; 4251 | hSize = 0; 4252 | 4253 | if (nSize > 0) 4254 | { 4255 | header = buffer - 1; 4256 | hSize = 1; 4257 | if (cSize) 4258 | { 4259 | header -= cSize; 4260 | hSize += cSize; 4261 | } 4262 | if (t >= 0xffffff) 4263 | { 4264 | header -= 4; 4265 | hSize += 4; 4266 | } 4267 | *header = (0xc0 | c); 4268 | if (cSize) 4269 | { 4270 | int tmp = packet->m_nChannel - 64; 4271 | header[1] = tmp & 0xff; 4272 | if (cSize == 2) 4273 | header[2] = tmp >> 8; 4274 | } 4275 | if (t >= 0xffffff) 4276 | { 4277 | char* extendedTimestamp = header + 1 + cSize; 4278 | AMF_EncodeInt32(extendedTimestamp, extendedTimestamp + 4, t); 4279 | } 4280 | } 4281 | } 4282 | if (tbuf) 4283 | { 4284 | int wrote = WriteN(r, tbuf, toff-tbuf); 4285 | free(tbuf); 4286 | tbuf = NULL; 4287 | if (!wrote) 4288 | return FALSE; 4289 | } 4290 | 4291 | /* we invoked a remote method */ 4292 | if (packet->m_packetType == RTMP_PACKET_TYPE_INVOKE) 4293 | { 4294 | AVal method; 4295 | char *ptr; 4296 | ptr = packet->m_body + 1; 4297 | AMF_DecodeString(ptr, &method); 4298 | RTMP_Log(RTMP_LOGDEBUG, "Invoking %s", method.av_val); 4299 | /* keep it in call queue till result arrives */ 4300 | if (queue) { 4301 | int txn; 4302 | ptr += 3 + method.av_len; 4303 | txn = (int)AMF_DecodeNumber(ptr); 4304 | AV_queue(&r->m_methodCalls, &r->m_numCalls, &method, txn); 4305 | } 4306 | } 4307 | 4308 | if (!r->m_vecChannelsOut[packet->m_nChannel]) 4309 | r->m_vecChannelsOut[packet->m_nChannel] = malloc(sizeof(RTMPPacket)); 4310 | memcpy(r->m_vecChannelsOut[packet->m_nChannel], packet, sizeof(RTMPPacket)); 4311 | return TRUE; 4312 | } 4313 | 4314 | int 4315 | RTMP_Serve(RTMP *r) 4316 | { 4317 | return SHandShake(r); 4318 | } 4319 | 4320 | void 4321 | RTMP_Close(RTMP *r) 4322 | { 4323 | CloseInternal(r, 0); 4324 | } 4325 | 4326 | static void 4327 | CloseInternal(RTMP *r, int reconnect) 4328 | { 4329 | int i; 4330 | 4331 | if (RTMP_IsConnected(r)) 4332 | { 4333 | if (r->m_stream_id > 0) 4334 | { 4335 | i = r->m_stream_id; 4336 | r->m_stream_id = 0; 4337 | if ((r->Link.protocol & RTMP_FEATURE_WRITE)) 4338 | SendFCUnpublish(r); 4339 | SendDeleteStream(r, i); 4340 | } 4341 | if (r->m_clientID.av_val) 4342 | { 4343 | HTTP_Post(r, RTMPT_CLOSE, "", 1); 4344 | free(r->m_clientID.av_val); 4345 | r->m_clientID.av_val = NULL; 4346 | r->m_clientID.av_len = 0; 4347 | } 4348 | RTMPSockBuf_Close(&r->m_sb); 4349 | } 4350 | 4351 | r->m_stream_id = -1; 4352 | r->m_sb.sb_socket = -1; 4353 | r->m_nBWCheckCounter = 0; 4354 | r->m_nBytesIn = 0; 4355 | r->m_nBytesInSent = 0; 4356 | 4357 | if (r->m_read.flags & RTMP_READ_HEADER) { 4358 | free(r->m_read.buf); 4359 | r->m_read.buf = NULL; 4360 | } 4361 | r->m_read.dataType = 0; 4362 | r->m_read.flags = 0; 4363 | r->m_read.status = 0; 4364 | r->m_read.nResumeTS = 0; 4365 | r->m_read.nIgnoredFrameCounter = 0; 4366 | r->m_read.nIgnoredFlvFrameCounter = 0; 4367 | 4368 | r->m_write.m_nBytesRead = 0; 4369 | RTMPPacket_Free(&r->m_write); 4370 | 4371 | for (i = 0; i < r->m_channelsAllocatedIn; i++) 4372 | { 4373 | if (r->m_vecChannelsIn[i]) 4374 | { 4375 | RTMPPacket_Free(r->m_vecChannelsIn[i]); 4376 | free(r->m_vecChannelsIn[i]); 4377 | r->m_vecChannelsIn[i] = NULL; 4378 | } 4379 | } 4380 | free(r->m_vecChannelsIn); 4381 | r->m_vecChannelsIn = NULL; 4382 | free(r->m_channelTimestamp); 4383 | r->m_channelTimestamp = NULL; 4384 | r->m_channelsAllocatedIn = 0; 4385 | for (i = 0; i < r->m_channelsAllocatedOut; i++) 4386 | { 4387 | if (r->m_vecChannelsOut[i]) 4388 | { 4389 | free(r->m_vecChannelsOut[i]); 4390 | r->m_vecChannelsOut[i] = NULL; 4391 | } 4392 | } 4393 | free(r->m_vecChannelsOut); 4394 | r->m_vecChannelsOut = NULL; 4395 | r->m_channelsAllocatedOut = 0; 4396 | AV_clear(r->m_methodCalls, r->m_numCalls); 4397 | r->m_methodCalls = NULL; 4398 | r->m_numCalls = 0; 4399 | r->m_numInvokes = 0; 4400 | 4401 | r->m_bPlaying = FALSE; 4402 | r->m_sb.sb_size = 0; 4403 | 4404 | r->m_msgCounter = 0; 4405 | r->m_resplen = 0; 4406 | r->m_unackd = 0; 4407 | 4408 | if (r->Link.lFlags & RTMP_LF_FTCU && !reconnect) 4409 | { 4410 | free(r->Link.tcUrl.av_val); 4411 | r->Link.tcUrl.av_val = NULL; 4412 | r->Link.lFlags ^= RTMP_LF_FTCU; 4413 | } 4414 | if (r->Link.lFlags & RTMP_LF_FAPU && !reconnect) 4415 | { 4416 | free(r->Link.app.av_val); 4417 | r->Link.app.av_val = NULL; 4418 | r->Link.lFlags ^= RTMP_LF_FAPU; 4419 | } 4420 | 4421 | if (!reconnect) 4422 | { 4423 | free(r->Link.playpath0.av_val); 4424 | r->Link.playpath0.av_val = NULL; 4425 | } 4426 | #ifdef CRYPTO 4427 | if (r->Link.dh) 4428 | { 4429 | MDH_free(r->Link.dh); 4430 | r->Link.dh = NULL; 4431 | } 4432 | if (r->Link.rc4keyIn) 4433 | { 4434 | RC4_free(r->Link.rc4keyIn); 4435 | r->Link.rc4keyIn = NULL; 4436 | } 4437 | if (r->Link.rc4keyOut) 4438 | { 4439 | RC4_free(r->Link.rc4keyOut); 4440 | r->Link.rc4keyOut = NULL; 4441 | } 4442 | #endif 4443 | } 4444 | 4445 | int 4446 | RTMPSockBuf_Fill(RTMPSockBuf *sb) 4447 | { 4448 | int nBytes; 4449 | 4450 | if (!sb->sb_size) 4451 | sb->sb_start = sb->sb_buf; 4452 | 4453 | while (1) 4454 | { 4455 | nBytes = sizeof(sb->sb_buf) - 1 - sb->sb_size - (sb->sb_start - sb->sb_buf); 4456 | #if defined(CRYPTO) && !defined(NO_SSL) 4457 | if (sb->sb_ssl) 4458 | { 4459 | nBytes = TLS_read(sb->sb_ssl, sb->sb_start + sb->sb_size, nBytes); 4460 | } 4461 | else 4462 | #endif 4463 | { 4464 | nBytes = recv(sb->sb_socket, sb->sb_start + sb->sb_size, nBytes, 0); 4465 | } 4466 | if (nBytes != -1) 4467 | { 4468 | sb->sb_size += nBytes; 4469 | } 4470 | else 4471 | { 4472 | int sockerr = GetSockError(); 4473 | RTMP_Log(RTMP_LOGDEBUG, "%s, recv returned %d. GetSockError(): %d (%s)", 4474 | __FUNCTION__, nBytes, sockerr, strerror(sockerr)); 4475 | if (sockerr == EINTR && !RTMP_ctrlC) 4476 | continue; 4477 | 4478 | if (sockerr == EWOULDBLOCK || sockerr == EAGAIN) 4479 | { 4480 | sb->sb_timedout = TRUE; 4481 | nBytes = 0; 4482 | } 4483 | } 4484 | break; 4485 | } 4486 | 4487 | return nBytes; 4488 | } 4489 | 4490 | int 4491 | RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len) 4492 | { 4493 | int rc; 4494 | 4495 | #ifdef _DEBUG 4496 | fwrite(buf, 1, len, netstackdump); 4497 | #endif 4498 | 4499 | #if defined(CRYPTO) && !defined(NO_SSL) 4500 | if (sb->sb_ssl) 4501 | { 4502 | rc = TLS_write(sb->sb_ssl, buf, len); 4503 | } 4504 | else 4505 | #endif 4506 | { 4507 | rc = send(sb->sb_socket, buf, len, 0); 4508 | } 4509 | return rc; 4510 | } 4511 | 4512 | int 4513 | RTMPSockBuf_Close(RTMPSockBuf *sb) 4514 | { 4515 | #if defined(CRYPTO) && !defined(NO_SSL) 4516 | if (sb->sb_ssl) 4517 | { 4518 | TLS_shutdown(sb->sb_ssl); 4519 | TLS_close(sb->sb_ssl); 4520 | sb->sb_ssl = NULL; 4521 | } 4522 | #endif 4523 | if (sb->sb_socket != -1) 4524 | return closesocket(sb->sb_socket); 4525 | return 0; 4526 | } 4527 | 4528 | #define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf)) 4529 | 4530 | static void 4531 | DecodeTEA(AVal *key, AVal *text) 4532 | { 4533 | uint32_t *v, k[4] = { 0 }, u; 4534 | uint32_t z, y, sum = 0, e, DELTA = 0x9e3779b9; 4535 | int32_t p, q; 4536 | int i, n; 4537 | unsigned char *ptr, *out; 4538 | 4539 | /* prep key: pack 1st 16 chars into 4 LittleEndian ints */ 4540 | ptr = (unsigned char *)key->av_val; 4541 | u = 0; 4542 | n = 0; 4543 | v = k; 4544 | p = key->av_len > 16 ? 16 : key->av_len; 4545 | for (i = 0; i < p; i++) 4546 | { 4547 | u |= ptr[i] << (n * 8); 4548 | if (n == 3) 4549 | { 4550 | *v++ = u; 4551 | u = 0; 4552 | n = 0; 4553 | } 4554 | else 4555 | { 4556 | n++; 4557 | } 4558 | } 4559 | /* any trailing chars */ 4560 | if (u) 4561 | *v = u; 4562 | 4563 | /* prep text: hex2bin, multiples of 4 */ 4564 | n = (text->av_len + 7) / 8; 4565 | out = malloc(n * 8); 4566 | ptr = (unsigned char *)text->av_val; 4567 | v = (uint32_t *) out; 4568 | for (i = 0; i < n; i++) 4569 | { 4570 | u = (HEX2BIN(ptr[0]) << 4) + HEX2BIN(ptr[1]); 4571 | u |= ((HEX2BIN(ptr[2]) << 4) + HEX2BIN(ptr[3])) << 8; 4572 | u |= ((HEX2BIN(ptr[4]) << 4) + HEX2BIN(ptr[5])) << 16; 4573 | u |= ((HEX2BIN(ptr[6]) << 4) + HEX2BIN(ptr[7])) << 24; 4574 | *v++ = u; 4575 | ptr += 8; 4576 | } 4577 | v = (uint32_t *) out; 4578 | 4579 | /* http://www.movable-type.co.uk/scripts/tea-block.html */ 4580 | #define MX (((z>>5)^(y<<2)) + ((y>>3)^(z<<4))) ^ ((sum^y) + (k[(p&3)^e]^z)); 4581 | z = v[n - 1]; 4582 | y = v[0]; 4583 | q = 6 + 52 / n; 4584 | sum = q * DELTA; 4585 | while (sum != 0) 4586 | { 4587 | e = sum >> 2 & 3; 4588 | for (p = n - 1; p > 0; p--) 4589 | z = v[p - 1], y = v[p] -= MX; 4590 | z = v[n - 1]; 4591 | y = v[0] -= MX; 4592 | sum -= DELTA; 4593 | } 4594 | 4595 | text->av_len /= 2; 4596 | memcpy(text->av_val, out, text->av_len); 4597 | free(out); 4598 | } 4599 | 4600 | static int 4601 | HTTP_Post(RTMP *r, RTMPTCmd cmd, const char *buf, int len) 4602 | { 4603 | char hbuf[512]; 4604 | int hlen = snprintf(hbuf, sizeof(hbuf), "POST /%s%s/%d HTTP/1.1\r\n" 4605 | "Host: %.*s:%d\r\n" 4606 | "Accept: */*\r\n" 4607 | "User-Agent: Shockwave Flash\r\n" 4608 | "Connection: Keep-Alive\r\n" 4609 | "Cache-Control: no-cache\r\n" 4610 | "Content-type: application/x-fcs\r\n" 4611 | "Content-length: %d\r\n\r\n", RTMPT_cmds[cmd], 4612 | r->m_clientID.av_val ? r->m_clientID.av_val : "", 4613 | r->m_msgCounter, r->Link.hostname.av_len, r->Link.hostname.av_val, 4614 | r->Link.port, len); 4615 | RTMPSockBuf_Send(&r->m_sb, hbuf, hlen); 4616 | hlen = RTMPSockBuf_Send(&r->m_sb, buf, len); 4617 | r->m_msgCounter++; 4618 | r->m_unackd++; 4619 | return hlen; 4620 | } 4621 | 4622 | static int 4623 | HTTP_read(RTMP *r, int fill) 4624 | { 4625 | char *ptr; 4626 | int hlen; 4627 | 4628 | restart: 4629 | if (fill) 4630 | RTMPSockBuf_Fill(&r->m_sb); 4631 | if (r->m_sb.sb_size < 13) { 4632 | if (fill) 4633 | goto restart; 4634 | return -2; 4635 | } 4636 | if (strncmp(r->m_sb.sb_start, "HTTP/1.1 200 ", 13)) 4637 | return -1; 4638 | r->m_sb.sb_start[r->m_sb.sb_size] = '\0'; 4639 | if (!strstr(r->m_sb.sb_start, "\r\n\r\n")) { 4640 | if (fill) 4641 | goto restart; 4642 | return -2; 4643 | } 4644 | 4645 | ptr = r->m_sb.sb_start + sizeof("HTTP/1.1 200"); 4646 | while ((ptr = strstr(ptr, "Content-"))) { 4647 | if (!strncasecmp(ptr+8, "length:", 7)) break; 4648 | ptr += 8; 4649 | } 4650 | if (!ptr) 4651 | return -1; 4652 | hlen = atoi(ptr+16); 4653 | ptr = strstr(ptr+16, "\r\n\r\n"); 4654 | if (!ptr) 4655 | return -1; 4656 | ptr += 4; 4657 | if (ptr + (r->m_clientID.av_val ? 1 : hlen) > r->m_sb.sb_start + r->m_sb.sb_size) 4658 | { 4659 | if (fill) 4660 | goto restart; 4661 | return -2; 4662 | } 4663 | r->m_sb.sb_size -= ptr - r->m_sb.sb_start; 4664 | r->m_sb.sb_start = ptr; 4665 | r->m_unackd--; 4666 | 4667 | if (!r->m_clientID.av_val) 4668 | { 4669 | r->m_clientID.av_len = hlen; 4670 | r->m_clientID.av_val = malloc(hlen+1); 4671 | if (!r->m_clientID.av_val) 4672 | return -1; 4673 | r->m_clientID.av_val[0] = '/'; 4674 | memcpy(r->m_clientID.av_val+1, ptr, hlen-1); 4675 | r->m_clientID.av_val[hlen] = 0; 4676 | r->m_sb.sb_size = 0; 4677 | } 4678 | else 4679 | { 4680 | r->m_polling = *ptr++; 4681 | r->m_resplen = hlen - 1; 4682 | r->m_sb.sb_start++; 4683 | r->m_sb.sb_size--; 4684 | } 4685 | return 0; 4686 | } 4687 | 4688 | #define MAX_IGNORED_FRAMES 50 4689 | 4690 | /* Read from the stream until we get a media packet. 4691 | * Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media 4692 | * packets, 0 if ignorable error, >0 if there is a media packet 4693 | */ 4694 | static int 4695 | Read_1_Packet(RTMP *r, char *buf, unsigned int buflen) 4696 | { 4697 | uint32_t prevTagSize = 0; 4698 | int rtnGetNextMediaPacket = 0, ret = RTMP_READ_EOF; 4699 | RTMPPacket packet = { 0 }; 4700 | int recopy = FALSE; 4701 | unsigned int size; 4702 | char *ptr, *pend; 4703 | uint32_t nTimeStamp = 0; 4704 | unsigned int len; 4705 | 4706 | rtnGetNextMediaPacket = RTMP_GetNextMediaPacket(r, &packet); 4707 | while (rtnGetNextMediaPacket) 4708 | { 4709 | char *packetBody = packet.m_body; 4710 | unsigned int nPacketLen = packet.m_nBodySize; 4711 | 4712 | /* Return RTMP_READ_COMPLETE if this was completed nicely with 4713 | * invoke message Play.Stop or Play.Complete 4714 | */ 4715 | if (rtnGetNextMediaPacket == 2) 4716 | { 4717 | RTMP_Log(RTMP_LOGDEBUG, 4718 | "Got Play.Complete or Play.Stop from server. " 4719 | "Assuming stream is complete"); 4720 | ret = RTMP_READ_COMPLETE; 4721 | break; 4722 | } 4723 | 4724 | r->m_read.dataType |= (((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO) << 2) | 4725 | (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO)); 4726 | 4727 | if (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO && nPacketLen <= 5) 4728 | { 4729 | RTMP_Log(RTMP_LOGDEBUG, "ignoring too small video packet: size: %d", 4730 | nPacketLen); 4731 | ret = RTMP_READ_IGNORE; 4732 | break; 4733 | } 4734 | if (packet.m_packetType == RTMP_PACKET_TYPE_AUDIO && nPacketLen <= 1) 4735 | { 4736 | RTMP_Log(RTMP_LOGDEBUG, "ignoring too small audio packet: size: %d", 4737 | nPacketLen); 4738 | ret = RTMP_READ_IGNORE; 4739 | break; 4740 | } 4741 | 4742 | if (r->m_read.flags & RTMP_READ_SEEKING) 4743 | { 4744 | ret = RTMP_READ_IGNORE; 4745 | break; 4746 | } 4747 | #ifdef _DEBUG 4748 | RTMP_Log(RTMP_LOGDEBUG, "type: %02X, size: %d, TS: %d ms, abs TS: %d", 4749 | packet.m_packetType, nPacketLen, packet.m_nTimeStamp, 4750 | packet.m_hasAbsTimestamp); 4751 | if (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO) 4752 | RTMP_Log(RTMP_LOGDEBUG, "frametype: %02X", (*packetBody & 0xf0)); 4753 | #endif 4754 | 4755 | if (r->m_read.flags & RTMP_READ_RESUME) 4756 | { 4757 | /* check the header if we get one */ 4758 | if (packet.m_nTimeStamp == 0) 4759 | { 4760 | if (r->m_read.nMetaHeaderSize > 0 4761 | && packet.m_packetType == RTMP_PACKET_TYPE_INFO) 4762 | { 4763 | AMFObject metaObj; 4764 | int nRes = 4765 | AMF_Decode(&metaObj, packetBody, nPacketLen, FALSE); 4766 | if (nRes >= 0) 4767 | { 4768 | AVal metastring; 4769 | AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0), 4770 | &metastring); 4771 | 4772 | if (AVMATCH(&metastring, &av_onMetaData)) 4773 | { 4774 | /* compare */ 4775 | if ((r->m_read.nMetaHeaderSize != nPacketLen) || 4776 | (memcmp 4777 | (r->m_read.metaHeader, packetBody, 4778 | r->m_read.nMetaHeaderSize) != 0)) 4779 | { 4780 | ret = RTMP_READ_ERROR; 4781 | } 4782 | } 4783 | AMF_Reset(&metaObj); 4784 | if (ret == RTMP_READ_ERROR) 4785 | break; 4786 | } 4787 | } 4788 | 4789 | /* check first keyframe to make sure we got the right position 4790 | * in the stream! (the first non ignored frame) 4791 | */ 4792 | if (r->m_read.nInitialFrameSize > 0) 4793 | { 4794 | /* video or audio data */ 4795 | if (packet.m_packetType == r->m_read.initialFrameType 4796 | && r->m_read.nInitialFrameSize == nPacketLen) 4797 | { 4798 | /* we don't compare the sizes since the packet can 4799 | * contain several FLV packets, just make sure the 4800 | * first frame is our keyframe (which we are going 4801 | * to rewrite) 4802 | */ 4803 | if (memcmp 4804 | (r->m_read.initialFrame, packetBody, 4805 | r->m_read.nInitialFrameSize) == 0) 4806 | { 4807 | RTMP_Log(RTMP_LOGDEBUG, "Checked keyframe successfully!"); 4808 | r->m_read.flags |= RTMP_READ_GOTKF; 4809 | /* ignore it! (what about audio data after it? it is 4810 | * handled by ignoring all 0ms frames, see below) 4811 | */ 4812 | ret = RTMP_READ_IGNORE; 4813 | break; 4814 | } 4815 | } 4816 | 4817 | /* hande FLV streams, even though the server resends the 4818 | * keyframe as an extra video packet it is also included 4819 | * in the first FLV stream chunk and we have to compare 4820 | * it and filter it out !! 4821 | */ 4822 | if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO) 4823 | { 4824 | /* basically we have to find the keyframe with the 4825 | * correct TS being nResumeTS 4826 | */ 4827 | unsigned int pos = 0; 4828 | uint32_t ts = 0; 4829 | 4830 | while (pos + 11 < nPacketLen) 4831 | { 4832 | /* size without header (11) and prevTagSize (4) */ 4833 | uint32_t dataSize = 4834 | AMF_DecodeInt24(packetBody + pos + 1); 4835 | ts = AMF_DecodeInt24(packetBody + pos + 4); 4836 | ts |= (packetBody[pos + 7] << 24); 4837 | 4838 | #ifdef _DEBUG 4839 | RTMP_Log(RTMP_LOGDEBUG, 4840 | "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms", 4841 | packetBody[pos], dataSize, ts); 4842 | #endif 4843 | /* ok, is it a keyframe?: 4844 | * well doesn't work for audio! 4845 | */ 4846 | if (packetBody[pos /*6928, test 0 */ ] == 4847 | r->m_read.initialFrameType 4848 | /* && (packetBody[11]&0xf0) == 0x10 */ ) 4849 | { 4850 | if (ts == r->m_read.nResumeTS) 4851 | { 4852 | RTMP_Log(RTMP_LOGDEBUG, 4853 | "Found keyframe with resume-keyframe timestamp!"); 4854 | if (r->m_read.nInitialFrameSize != dataSize 4855 | || memcmp(r->m_read.initialFrame, 4856 | packetBody + pos + 11, 4857 | r->m_read. 4858 | nInitialFrameSize) != 0) 4859 | { 4860 | RTMP_Log(RTMP_LOGERROR, 4861 | "FLV Stream: Keyframe doesn't match!"); 4862 | ret = RTMP_READ_ERROR; 4863 | break; 4864 | } 4865 | r->m_read.flags |= RTMP_READ_GOTFLVK; 4866 | 4867 | /* skip this packet? 4868 | * check whether skippable: 4869 | */ 4870 | if (pos + 11 + dataSize + 4 > nPacketLen) 4871 | { 4872 | RTMP_Log(RTMP_LOGWARNING, 4873 | "Non skipable packet since it doesn't end with chunk, stream corrupt!"); 4874 | ret = RTMP_READ_ERROR; 4875 | break; 4876 | } 4877 | packetBody += (pos + 11 + dataSize + 4); 4878 | nPacketLen -= (pos + 11 + dataSize + 4); 4879 | 4880 | goto stopKeyframeSearch; 4881 | 4882 | } 4883 | else if (r->m_read.nResumeTS < ts) 4884 | { 4885 | /* the timestamp ts will only increase with 4886 | * further packets, wait for seek 4887 | */ 4888 | goto stopKeyframeSearch; 4889 | } 4890 | } 4891 | pos += (11 + dataSize + 4); 4892 | } 4893 | if (ts < r->m_read.nResumeTS) 4894 | { 4895 | RTMP_Log(RTMP_LOGERROR, 4896 | "First packet does not contain keyframe, all " 4897 | "timestamps are smaller than the keyframe " 4898 | "timestamp; probably the resume seek failed?"); 4899 | } 4900 | stopKeyframeSearch: 4901 | ; 4902 | if (!(r->m_read.flags & RTMP_READ_GOTFLVK)) 4903 | { 4904 | RTMP_Log(RTMP_LOGERROR, 4905 | "Couldn't find the seeked keyframe in this chunk!"); 4906 | ret = RTMP_READ_IGNORE; 4907 | break; 4908 | } 4909 | } 4910 | } 4911 | } 4912 | 4913 | if (packet.m_nTimeStamp > 0 4914 | && (r->m_read.flags & (RTMP_READ_GOTKF|RTMP_READ_GOTFLVK))) 4915 | { 4916 | /* another problem is that the server can actually change from 4917 | * 09/08 video/audio packets to an FLV stream or vice versa and 4918 | * our keyframe check will prevent us from going along with the 4919 | * new stream if we resumed. 4920 | * 4921 | * in this case set the 'found keyframe' variables to true. 4922 | * We assume that if we found one keyframe somewhere and were 4923 | * already beyond TS > 0 we have written data to the output 4924 | * which means we can accept all forthcoming data including the 4925 | * change between 08/09 <-> FLV packets 4926 | */ 4927 | r->m_read.flags |= (RTMP_READ_GOTKF|RTMP_READ_GOTFLVK); 4928 | } 4929 | 4930 | /* skip till we find our keyframe 4931 | * (seeking might put us somewhere before it) 4932 | */ 4933 | if (!(r->m_read.flags & RTMP_READ_GOTKF) && 4934 | packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO) 4935 | { 4936 | RTMP_Log(RTMP_LOGWARNING, 4937 | "Stream does not start with requested frame, ignoring data... "); 4938 | r->m_read.nIgnoredFrameCounter++; 4939 | if (r->m_read.nIgnoredFrameCounter > MAX_IGNORED_FRAMES) 4940 | ret = RTMP_READ_ERROR; /* fatal error, couldn't continue stream */ 4941 | else 4942 | ret = RTMP_READ_IGNORE; 4943 | break; 4944 | } 4945 | /* ok, do the same for FLV streams */ 4946 | if (!(r->m_read.flags & RTMP_READ_GOTFLVK) && 4947 | packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO) 4948 | { 4949 | RTMP_Log(RTMP_LOGWARNING, 4950 | "Stream does not start with requested FLV frame, ignoring data... "); 4951 | r->m_read.nIgnoredFlvFrameCounter++; 4952 | if (r->m_read.nIgnoredFlvFrameCounter > MAX_IGNORED_FRAMES) 4953 | ret = RTMP_READ_ERROR; 4954 | else 4955 | ret = RTMP_READ_IGNORE; 4956 | break; 4957 | } 4958 | 4959 | /* we have to ignore the 0ms frames since these are the first 4960 | * keyframes; we've got these so don't mess around with multiple 4961 | * copies sent by the server to us! (if the keyframe is found at a 4962 | * later position there is only one copy and it will be ignored by 4963 | * the preceding if clause) 4964 | */ 4965 | if (!(r->m_read.flags & RTMP_READ_NO_IGNORE) && 4966 | packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO) 4967 | { 4968 | /* exclude type RTMP_PACKET_TYPE_FLASH_VIDEO since it can 4969 | * contain several FLV packets 4970 | */ 4971 | if (packet.m_nTimeStamp == 0) 4972 | { 4973 | ret = RTMP_READ_IGNORE; 4974 | break; 4975 | } 4976 | else 4977 | { 4978 | /* stop ignoring packets */ 4979 | r->m_read.flags |= RTMP_READ_NO_IGNORE; 4980 | } 4981 | } 4982 | } 4983 | 4984 | /* calculate packet size and allocate slop buffer if necessary */ 4985 | size = nPacketLen + 4986 | ((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO 4987 | || packet.m_packetType == RTMP_PACKET_TYPE_VIDEO 4988 | || packet.m_packetType == RTMP_PACKET_TYPE_INFO) ? 11 : 0) + 4989 | (packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO ? 4 : 0); 4990 | 4991 | if (size + 4 > buflen) 4992 | { 4993 | /* the extra 4 is for the case of an FLV stream without a last 4994 | * prevTagSize (we need extra 4 bytes to append it) */ 4995 | r->m_read.buf = malloc(size + 4); 4996 | if (r->m_read.buf == 0) 4997 | { 4998 | RTMP_Log(RTMP_LOGERROR, "Couldn't allocate memory!"); 4999 | ret = RTMP_READ_ERROR; /* fatal error */ 5000 | break; 5001 | } 5002 | recopy = TRUE; 5003 | ptr = r->m_read.buf; 5004 | } 5005 | else 5006 | { 5007 | ptr = buf; 5008 | } 5009 | pend = ptr + size + 4; 5010 | 5011 | /* use to return timestamp of last processed packet */ 5012 | 5013 | /* audio (0x08), video (0x09) or metadata (0x12) packets : 5014 | * construct 11 byte header then add rtmp packet's data */ 5015 | if (packet.m_packetType == RTMP_PACKET_TYPE_AUDIO 5016 | || packet.m_packetType == RTMP_PACKET_TYPE_VIDEO 5017 | || packet.m_packetType == RTMP_PACKET_TYPE_INFO) 5018 | { 5019 | nTimeStamp = r->m_read.nResumeTS + packet.m_nTimeStamp; 5020 | prevTagSize = 11 + nPacketLen; 5021 | 5022 | *ptr = packet.m_packetType; 5023 | ptr++; 5024 | ptr = AMF_EncodeInt24(ptr, pend, nPacketLen); 5025 | 5026 | #if 0 5027 | if(packet.m_packetType == RTMP_PACKET_TYPE_VIDEO) { 5028 | 5029 | /* H264 fix: */ 5030 | if((packetBody[0] & 0x0f) == 7) { /* CodecId = H264 */ 5031 | uint8_t packetType = *(packetBody+1); 5032 | 5033 | uint32_t ts = AMF_DecodeInt24(packetBody+2); /* composition time */ 5034 | int32_t cts = (ts+0xff800000)^0xff800000; 5035 | RTMP_Log(RTMP_LOGDEBUG, "cts : %d\n", cts); 5036 | 5037 | nTimeStamp -= cts; 5038 | /* get rid of the composition time */ 5039 | CRTMP::EncodeInt24(packetBody+2, 0); 5040 | } 5041 | RTMP_Log(RTMP_LOGDEBUG, "VIDEO: nTimeStamp: 0x%08X (%d)\n", nTimeStamp, nTimeStamp); 5042 | } 5043 | #endif 5044 | 5045 | ptr = AMF_EncodeInt24(ptr, pend, nTimeStamp); 5046 | *ptr = (char)((nTimeStamp & 0xFF000000) >> 24); 5047 | ptr++; 5048 | 5049 | /* stream id */ 5050 | ptr = AMF_EncodeInt24(ptr, pend, 0); 5051 | } 5052 | 5053 | memcpy(ptr, packetBody, nPacketLen); 5054 | len = nPacketLen; 5055 | 5056 | /* correct tagSize and obtain timestamp if we have an FLV stream */ 5057 | if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO) 5058 | { 5059 | unsigned int pos = 0; 5060 | int delta; 5061 | 5062 | /* grab first timestamp and see if it needs fixing */ 5063 | nTimeStamp = AMF_DecodeInt24(packetBody + 4); 5064 | nTimeStamp |= (packetBody[7] << 24); 5065 | delta = packet.m_nTimeStamp - nTimeStamp + r->m_read.nResumeTS; 5066 | 5067 | while (pos + 11 < nPacketLen) 5068 | { 5069 | /* size without header (11) and without prevTagSize (4) */ 5070 | uint32_t dataSize = AMF_DecodeInt24(packetBody + pos + 1); 5071 | nTimeStamp = AMF_DecodeInt24(packetBody + pos + 4); 5072 | nTimeStamp |= (packetBody[pos + 7] << 24); 5073 | 5074 | if (delta) 5075 | { 5076 | nTimeStamp += delta; 5077 | AMF_EncodeInt24(ptr+pos+4, pend, nTimeStamp); 5078 | ptr[pos+7] = nTimeStamp>>24; 5079 | } 5080 | 5081 | /* set data type */ 5082 | r->m_read.dataType |= (((*(packetBody + pos) == 0x08) << 2) | 5083 | (*(packetBody + pos) == 0x09)); 5084 | 5085 | if (pos + 11 + dataSize + 4 > nPacketLen) 5086 | { 5087 | if (pos + 11 + dataSize > nPacketLen) 5088 | { 5089 | RTMP_Log(RTMP_LOGERROR, 5090 | "Wrong data size (%u), stream corrupted, aborting!", 5091 | dataSize); 5092 | ret = RTMP_READ_ERROR; 5093 | break; 5094 | } 5095 | RTMP_Log(RTMP_LOGWARNING, "No tagSize found, appending!"); 5096 | 5097 | /* we have to append a last tagSize! */ 5098 | prevTagSize = dataSize + 11; 5099 | AMF_EncodeInt32(ptr + pos + 11 + dataSize, pend, 5100 | prevTagSize); 5101 | size += 4; 5102 | len += 4; 5103 | } 5104 | else 5105 | { 5106 | prevTagSize = 5107 | AMF_DecodeInt32(packetBody + pos + 11 + dataSize); 5108 | 5109 | #ifdef _DEBUG 5110 | RTMP_Log(RTMP_LOGDEBUG, 5111 | "FLV Packet: type %02X, dataSize: %lu, tagSize: %lu, timeStamp: %lu ms", 5112 | (unsigned char)packetBody[pos], dataSize, prevTagSize, 5113 | nTimeStamp); 5114 | #endif 5115 | 5116 | if (prevTagSize != (dataSize + 11)) 5117 | { 5118 | #ifdef _DEBUG 5119 | RTMP_Log(RTMP_LOGWARNING, 5120 | "Tag and data size are not consitent, writing tag size according to dataSize+11: %d", 5121 | dataSize + 11); 5122 | #endif 5123 | 5124 | prevTagSize = dataSize + 11; 5125 | AMF_EncodeInt32(ptr + pos + 11 + dataSize, pend, 5126 | prevTagSize); 5127 | } 5128 | } 5129 | 5130 | pos += prevTagSize + 4; /*(11+dataSize+4); */ 5131 | } 5132 | } 5133 | ptr += len; 5134 | 5135 | if (packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO) 5136 | { 5137 | /* FLV tag packets contain their own prevTagSize */ 5138 | AMF_EncodeInt32(ptr, pend, prevTagSize); 5139 | } 5140 | 5141 | /* In non-live this nTimeStamp can contain an absolute TS. 5142 | * Update ext timestamp with this absolute offset in non-live mode 5143 | * otherwise report the relative one 5144 | */ 5145 | /* RTMP_Log(RTMP_LOGDEBUG, "type: %02X, size: %d, pktTS: %dms, TS: %dms, bLiveStream: %d", packet.m_packetType, nPacketLen, packet.m_nTimeStamp, nTimeStamp, r->Link.lFlags & RTMP_LF_LIVE); */ 5146 | r->m_read.timestamp = (r->Link.lFlags & RTMP_LF_LIVE) ? packet.m_nTimeStamp : nTimeStamp; 5147 | 5148 | ret = size; 5149 | break; 5150 | } 5151 | 5152 | if (rtnGetNextMediaPacket) 5153 | RTMPPacket_Free(&packet); 5154 | 5155 | if (recopy) 5156 | { 5157 | len = ret > buflen ? buflen : ret; 5158 | memcpy(buf, r->m_read.buf, len); 5159 | r->m_read.bufpos = r->m_read.buf + len; 5160 | r->m_read.buflen = ret - len; 5161 | } 5162 | return ret; 5163 | } 5164 | 5165 | static const char flvHeader[] = { 'F', 'L', 'V', 0x01, 5166 | 0x00, /* 0x04 == audio, 0x01 == video */ 5167 | 0x00, 0x00, 0x00, 0x09, 5168 | 0x00, 0x00, 0x00, 0x00 5169 | }; 5170 | 5171 | #define HEADERBUF (128*1024) 5172 | int 5173 | RTMP_Read(RTMP *r, char *buf, int size) 5174 | { 5175 | int nRead = 0, total = 0; 5176 | 5177 | /* can't continue */ 5178 | fail: 5179 | switch (r->m_read.status) { 5180 | case RTMP_READ_EOF: 5181 | case RTMP_READ_COMPLETE: 5182 | return 0; 5183 | case RTMP_READ_ERROR: /* corrupted stream, resume failed */ 5184 | SetSockError(EINVAL); 5185 | return -1; 5186 | default: 5187 | break; 5188 | } 5189 | 5190 | /* first time thru */ 5191 | if (!(r->m_read.flags & RTMP_READ_HEADER)) 5192 | { 5193 | if (!(r->m_read.flags & RTMP_READ_RESUME)) 5194 | { 5195 | char *mybuf = malloc(HEADERBUF), *end = mybuf + HEADERBUF; 5196 | int cnt = 0; 5197 | r->m_read.buf = mybuf; 5198 | r->m_read.buflen = HEADERBUF; 5199 | 5200 | memcpy(mybuf, flvHeader, sizeof(flvHeader)); 5201 | r->m_read.buf += sizeof(flvHeader); 5202 | r->m_read.buflen -= sizeof(flvHeader); 5203 | cnt += sizeof(flvHeader); 5204 | 5205 | while (r->m_read.timestamp == 0) 5206 | { 5207 | nRead = Read_1_Packet(r, r->m_read.buf, r->m_read.buflen); 5208 | if (nRead < 0) 5209 | { 5210 | free(mybuf); 5211 | r->m_read.buf = NULL; 5212 | r->m_read.buflen = 0; 5213 | r->m_read.status = nRead; 5214 | goto fail; 5215 | } 5216 | /* buffer overflow, fix buffer and give up */ 5217 | if (r->m_read.buf < mybuf || r->m_read.buf > end) { 5218 | mybuf = realloc(mybuf, cnt + nRead); 5219 | memcpy(mybuf+cnt, r->m_read.buf, nRead); 5220 | free(r->m_read.buf); 5221 | r->m_read.buf = mybuf+cnt+nRead; 5222 | break; 5223 | } 5224 | cnt += nRead; 5225 | r->m_read.buf += nRead; 5226 | r->m_read.buflen -= nRead; 5227 | if (r->m_read.dataType == 5) 5228 | break; 5229 | } 5230 | mybuf[4] = r->m_read.dataType; 5231 | r->m_read.buflen = r->m_read.buf - mybuf; 5232 | r->m_read.buf = mybuf; 5233 | r->m_read.bufpos = mybuf; 5234 | } 5235 | r->m_read.flags |= RTMP_READ_HEADER; 5236 | } 5237 | 5238 | if ((r->m_read.flags & RTMP_READ_SEEKING) && r->m_read.buf) 5239 | { 5240 | /* drop whatever's here */ 5241 | free(r->m_read.buf); 5242 | r->m_read.buf = NULL; 5243 | r->m_read.bufpos = NULL; 5244 | r->m_read.buflen = 0; 5245 | } 5246 | 5247 | /* If there's leftover data buffered, use it up */ 5248 | if (r->m_read.buf) 5249 | { 5250 | nRead = r->m_read.buflen; 5251 | if (nRead > size) 5252 | nRead = size; 5253 | memcpy(buf, r->m_read.bufpos, nRead); 5254 | r->m_read.buflen -= nRead; 5255 | if (!r->m_read.buflen) 5256 | { 5257 | free(r->m_read.buf); 5258 | r->m_read.buf = NULL; 5259 | r->m_read.bufpos = NULL; 5260 | } 5261 | else 5262 | { 5263 | r->m_read.bufpos += nRead; 5264 | } 5265 | buf += nRead; 5266 | total += nRead; 5267 | size -= nRead; 5268 | } 5269 | 5270 | while (size > 0 && (nRead = Read_1_Packet(r, buf, size)) >= 0) 5271 | { 5272 | if (!nRead) continue; 5273 | buf += nRead; 5274 | total += nRead; 5275 | size -= nRead; 5276 | break; 5277 | } 5278 | if (nRead < 0) 5279 | r->m_read.status = nRead; 5280 | 5281 | if (size < 0) 5282 | total += size; 5283 | return total; 5284 | } 5285 | 5286 | static const AVal av_setDataFrame = AVC("@setDataFrame"); 5287 | 5288 | int 5289 | RTMP_Write(RTMP *r, const char *buf, int size) 5290 | { 5291 | RTMPPacket *pkt = &r->m_write; 5292 | char *pend, *enc; 5293 | int s2 = size, ret, num; 5294 | 5295 | pkt->m_nChannel = 0x04; /* source channel */ 5296 | pkt->m_nInfoField2 = r->m_stream_id; 5297 | 5298 | while (s2) 5299 | { 5300 | if (!pkt->m_nBytesRead) 5301 | { 5302 | if (size < 11) { 5303 | /* FLV pkt too small */ 5304 | return 0; 5305 | } 5306 | 5307 | if (buf[0] == 'F' && buf[1] == 'L' && buf[2] == 'V') 5308 | { 5309 | buf += 13; 5310 | s2 -= 13; 5311 | } 5312 | 5313 | pkt->m_packetType = *buf++; 5314 | pkt->m_nBodySize = AMF_DecodeInt24(buf); 5315 | buf += 3; 5316 | pkt->m_nTimeStamp = AMF_DecodeInt24(buf); 5317 | buf += 3; 5318 | pkt->m_nTimeStamp |= *buf++ << 24; 5319 | buf += 3; 5320 | s2 -= 11; 5321 | 5322 | if (((pkt->m_packetType == RTMP_PACKET_TYPE_AUDIO 5323 | || pkt->m_packetType == RTMP_PACKET_TYPE_VIDEO) && 5324 | !pkt->m_nTimeStamp) || pkt->m_packetType == RTMP_PACKET_TYPE_INFO) 5325 | { 5326 | pkt->m_headerType = RTMP_PACKET_SIZE_LARGE; 5327 | if (pkt->m_packetType == RTMP_PACKET_TYPE_INFO) 5328 | pkt->m_nBodySize += 16; 5329 | } 5330 | else 5331 | { 5332 | pkt->m_headerType = RTMP_PACKET_SIZE_MEDIUM; 5333 | } 5334 | 5335 | if (!RTMPPacket_Alloc(pkt, pkt->m_nBodySize)) 5336 | { 5337 | RTMP_Log(RTMP_LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__); 5338 | return FALSE; 5339 | } 5340 | enc = pkt->m_body; 5341 | pend = enc + pkt->m_nBodySize; 5342 | if (pkt->m_packetType == RTMP_PACKET_TYPE_INFO) 5343 | { 5344 | enc = AMF_EncodeString(enc, pend, &av_setDataFrame); 5345 | pkt->m_nBytesRead = enc - pkt->m_body; 5346 | } 5347 | } 5348 | else 5349 | { 5350 | enc = pkt->m_body + pkt->m_nBytesRead; 5351 | } 5352 | num = pkt->m_nBodySize - pkt->m_nBytesRead; 5353 | if (num > s2) 5354 | num = s2; 5355 | memcpy(enc, buf, num); 5356 | pkt->m_nBytesRead += num; 5357 | s2 -= num; 5358 | buf += num; 5359 | if (pkt->m_nBytesRead == pkt->m_nBodySize) 5360 | { 5361 | ret = RTMP_SendPacket(r, pkt, FALSE); 5362 | RTMPPacket_Free(pkt); 5363 | pkt->m_nBytesRead = 0; 5364 | if (!ret) 5365 | return -1; 5366 | buf += 4; 5367 | s2 -= 4; 5368 | if (s2 < 0) 5369 | break; 5370 | } 5371 | } 5372 | return size+s2; 5373 | } 5374 | -------------------------------------------------------------------------------- /rtmp.h: -------------------------------------------------------------------------------- 1 | #ifndef __RTMP_H__ 2 | #define __RTMP_H__ 3 | /* 4 | * Copyright (C) 2005-2008 Team XBMC 5 | * http://www.xbmc.org 6 | * Copyright (C) 2008-2009 Andrej Stepanchuk 7 | * Copyright (C) 2009-2010 Howard Chu 8 | * 9 | * This file is part of librtmp. 10 | * 11 | * librtmp is free software; you can redistribute it and/or modify 12 | * it under the terms of the GNU Lesser General Public License as 13 | * published by the Free Software Foundation; either version 2.1, 14 | * or (at your option) any later version. 15 | * 16 | * librtmp is distributed in the hope that it will be useful, 17 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 18 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 19 | * GNU General Public License for more details. 20 | * 21 | * You should have received a copy of the GNU Lesser General Public License 22 | * along with librtmp see the file COPYING. If not, write to 23 | * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 24 | * Boston, MA 02110-1301, USA. 25 | * http://www.gnu.org/copyleft/lgpl.html 26 | */ 27 | 28 | #if !defined(NO_CRYPTO) && !defined(CRYPTO) 29 | #define CRYPTO 30 | #endif 31 | 32 | #include 33 | #include 34 | #include 35 | 36 | #include "amf.h" 37 | 38 | #ifdef __cplusplus 39 | extern "C" 40 | { 41 | #endif 42 | 43 | #define RTMP_LIB_VERSION 0x020300 /* 2.3 */ 44 | 45 | #define RTMP_FEATURE_HTTP 0x01 46 | #define RTMP_FEATURE_ENC 0x02 47 | #define RTMP_FEATURE_SSL 0x04 48 | #define RTMP_FEATURE_MFP 0x08 /* not yet supported */ 49 | #define RTMP_FEATURE_WRITE 0x10 /* publish, not play */ 50 | #define RTMP_FEATURE_HTTP2 0x20 /* server-side rtmpt */ 51 | 52 | #define RTMP_PROTOCOL_UNDEFINED -1 53 | #define RTMP_PROTOCOL_RTMP 0 54 | #define RTMP_PROTOCOL_RTMPE RTMP_FEATURE_ENC 55 | #define RTMP_PROTOCOL_RTMPT RTMP_FEATURE_HTTP 56 | #define RTMP_PROTOCOL_RTMPS RTMP_FEATURE_SSL 57 | #define RTMP_PROTOCOL_RTMPTE (RTMP_FEATURE_HTTP|RTMP_FEATURE_ENC) 58 | #define RTMP_PROTOCOL_RTMPTS (RTMP_FEATURE_HTTP|RTMP_FEATURE_SSL) 59 | #define RTMP_PROTOCOL_RTMFP RTMP_FEATURE_MFP 60 | 61 | #define RTMP_DEFAULT_CHUNKSIZE 128 62 | 63 | /* needs to fit largest number of bytes recv() may return */ 64 | #define RTMP_BUFFER_CACHE_SIZE (16*1024) 65 | 66 | #define RTMP_CHANNELS 65600 67 | 68 | extern const char RTMPProtocolStringsLower[][7]; 69 | extern const AVal RTMP_DefaultFlashVer; 70 | extern int RTMP_ctrlC; 71 | 72 | uint32_t RTMP_GetTime(void); 73 | 74 | /* RTMP_PACKET_TYPE_... 0x00 */ 75 | #define RTMP_PACKET_TYPE_CHUNK_SIZE 0x01 76 | /* RTMP_PACKET_TYPE_... 0x02 */ 77 | #define RTMP_PACKET_TYPE_BYTES_READ_REPORT 0x03 78 | #define RTMP_PACKET_TYPE_CONTROL 0x04 79 | #define RTMP_PACKET_TYPE_SERVER_BW 0x05 80 | #define RTMP_PACKET_TYPE_CLIENT_BW 0x06 81 | /* RTMP_PACKET_TYPE_... 0x07 */ 82 | #define RTMP_PACKET_TYPE_AUDIO 0x08 83 | #define RTMP_PACKET_TYPE_VIDEO 0x09 84 | /* RTMP_PACKET_TYPE_... 0x0A */ 85 | /* RTMP_PACKET_TYPE_... 0x0B */ 86 | /* RTMP_PACKET_TYPE_... 0x0C */ 87 | /* RTMP_PACKET_TYPE_... 0x0D */ 88 | /* RTMP_PACKET_TYPE_... 0x0E */ 89 | #define RTMP_PACKET_TYPE_FLEX_STREAM_SEND 0x0F 90 | #define RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT 0x10 91 | #define RTMP_PACKET_TYPE_FLEX_MESSAGE 0x11 92 | #define RTMP_PACKET_TYPE_INFO 0x12 93 | #define RTMP_PACKET_TYPE_SHARED_OBJECT 0x13 94 | #define RTMP_PACKET_TYPE_INVOKE 0x14 95 | /* RTMP_PACKET_TYPE_... 0x15 */ 96 | #define RTMP_PACKET_TYPE_FLASH_VIDEO 0x16 97 | 98 | #define RTMP_MAX_HEADER_SIZE 18 99 | 100 | #define RTMP_PACKET_SIZE_LARGE 0 101 | #define RTMP_PACKET_SIZE_MEDIUM 1 102 | #define RTMP_PACKET_SIZE_SMALL 2 103 | #define RTMP_PACKET_SIZE_MINIMUM 3 104 | 105 | typedef struct RTMPChunk 106 | { 107 | int c_headerSize; 108 | int c_chunkSize; 109 | char *c_chunk; 110 | char c_header[RTMP_MAX_HEADER_SIZE]; 111 | } RTMPChunk; 112 | 113 | typedef struct RTMPPacket 114 | { 115 | uint8_t m_headerType; 116 | uint8_t m_packetType; 117 | uint8_t m_hasAbsTimestamp; /* timestamp absolute or relative? */ 118 | int m_nChannel; 119 | uint32_t m_nTimeStamp; /* timestamp */ 120 | int32_t m_nInfoField2; /* last 4 bytes in a long header */ 121 | uint32_t m_nBodySize; 122 | uint32_t m_nBytesRead; 123 | RTMPChunk *m_chunk; 124 | char *m_body; 125 | } RTMPPacket; 126 | 127 | typedef struct RTMPSockBuf 128 | { 129 | int sb_socket; 130 | int sb_size; /* number of unprocessed bytes in buffer */ 131 | char *sb_start; /* pointer into sb_pBuffer of next byte to process */ 132 | char sb_buf[RTMP_BUFFER_CACHE_SIZE]; /* data read from socket */ 133 | int sb_timedout; 134 | void *sb_ssl; 135 | } RTMPSockBuf; 136 | 137 | void RTMPPacket_Reset(RTMPPacket *p); 138 | void RTMPPacket_Dump(RTMPPacket *p); 139 | int RTMPPacket_Alloc(RTMPPacket *p, uint32_t nSize); 140 | void RTMPPacket_Free(RTMPPacket *p); 141 | 142 | #define RTMPPacket_IsReady(a) ((a)->m_nBytesRead == (a)->m_nBodySize) 143 | 144 | typedef struct RTMP_LNK 145 | { 146 | AVal hostname; 147 | AVal sockshost; 148 | 149 | AVal playpath0; /* parsed from URL */ 150 | AVal playpath; /* passed in explicitly */ 151 | AVal tcUrl; 152 | AVal swfUrl; 153 | AVal pageUrl; 154 | AVal app; 155 | AVal auth; 156 | AVal flashVer; 157 | AVal subscribepath; 158 | AVal usherToken; 159 | AVal token; 160 | AVal pubUser; 161 | AVal pubPasswd; 162 | AMFObject extras; 163 | int edepth; 164 | 165 | int seekTime; 166 | int stopTime; 167 | 168 | #define RTMP_LF_AUTH 0x0001 /* using auth param */ 169 | #define RTMP_LF_LIVE 0x0002 /* stream is live */ 170 | #define RTMP_LF_SWFV 0x0004 /* do SWF verification */ 171 | #define RTMP_LF_PLST 0x0008 /* send playlist before play */ 172 | #define RTMP_LF_BUFX 0x0010 /* toggle stream on BufferEmpty msg */ 173 | #define RTMP_LF_FTCU 0x0020 /* free tcUrl on close */ 174 | #define RTMP_LF_FAPU 0x0040 /* free app on close */ 175 | int lFlags; 176 | 177 | int swfAge; 178 | 179 | int protocol; 180 | int timeout; /* connection timeout in seconds */ 181 | 182 | int pFlags; /* unused, but kept to avoid breaking ABI */ 183 | 184 | unsigned short socksport; 185 | unsigned short port; 186 | 187 | #ifdef CRYPTO 188 | #define RTMP_SWF_HASHLEN 32 189 | void *dh; /* for encryption */ 190 | void *rc4keyIn; 191 | void *rc4keyOut; 192 | 193 | uint32_t SWFSize; 194 | uint8_t SWFHash[RTMP_SWF_HASHLEN]; 195 | char SWFVerificationResponse[RTMP_SWF_HASHLEN+10]; 196 | #endif 197 | } RTMP_LNK; 198 | 199 | /* state for read() wrapper */ 200 | typedef struct RTMP_READ 201 | { 202 | char *buf; 203 | char *bufpos; 204 | unsigned int buflen; 205 | uint32_t timestamp; 206 | uint8_t dataType; 207 | uint8_t flags; 208 | #define RTMP_READ_HEADER 0x01 209 | #define RTMP_READ_RESUME 0x02 210 | #define RTMP_READ_NO_IGNORE 0x04 211 | #define RTMP_READ_GOTKF 0x08 212 | #define RTMP_READ_GOTFLVK 0x10 213 | #define RTMP_READ_SEEKING 0x20 214 | int8_t status; 215 | #define RTMP_READ_COMPLETE -3 216 | #define RTMP_READ_ERROR -2 217 | #define RTMP_READ_EOF -1 218 | #define RTMP_READ_IGNORE 0 219 | 220 | /* if bResume == TRUE */ 221 | uint8_t initialFrameType; 222 | uint32_t nResumeTS; 223 | char *metaHeader; 224 | char *initialFrame; 225 | uint32_t nMetaHeaderSize; 226 | uint32_t nInitialFrameSize; 227 | uint32_t nIgnoredFrameCounter; 228 | uint32_t nIgnoredFlvFrameCounter; 229 | } RTMP_READ; 230 | 231 | typedef struct RTMP_METHOD 232 | { 233 | AVal name; 234 | int num; 235 | } RTMP_METHOD; 236 | 237 | typedef struct RTMP 238 | { 239 | int m_inChunkSize; 240 | int m_outChunkSize; 241 | int m_nBWCheckCounter; 242 | int m_nBytesIn; 243 | int m_nBytesInSent; 244 | int m_nBufferMS; 245 | int m_stream_id; /* returned in _result from createStream */ 246 | int m_mediaChannel; 247 | uint32_t m_mediaStamp; 248 | uint32_t m_pauseStamp; 249 | int m_pausing; 250 | int m_nServerBW; 251 | int m_nClientBW; 252 | uint8_t m_nClientBW2; 253 | uint8_t m_bPlaying; 254 | uint8_t m_bSendEncoding; 255 | uint8_t m_bSendCounter; 256 | 257 | int m_numInvokes; 258 | int m_numCalls; 259 | RTMP_METHOD *m_methodCalls; /* remote method calls queue */ 260 | 261 | int m_channelsAllocatedIn; 262 | int m_channelsAllocatedOut; 263 | RTMPPacket **m_vecChannelsIn; 264 | RTMPPacket **m_vecChannelsOut; 265 | int *m_channelTimestamp; /* abs timestamp of last packet */ 266 | 267 | double m_fAudioCodecs; /* audioCodecs for the connect packet */ 268 | double m_fVideoCodecs; /* videoCodecs for the connect packet */ 269 | double m_fEncoding; /* AMF0 or AMF3 */ 270 | 271 | double m_fDuration; /* duration of stream in seconds */ 272 | 273 | int m_msgCounter; /* RTMPT stuff */ 274 | int m_polling; 275 | int m_resplen; 276 | int m_unackd; 277 | AVal m_clientID; 278 | 279 | RTMP_READ m_read; 280 | RTMPPacket m_write; 281 | RTMPSockBuf m_sb; 282 | RTMP_LNK Link; 283 | } RTMP; 284 | 285 | int RTMP_ParseURL(const char *url, int *protocol, AVal *host, 286 | unsigned int *port, AVal *playpath, AVal *app); 287 | 288 | void RTMP_ParsePlaypath(AVal *in, AVal *out); 289 | void RTMP_SetBufferMS(RTMP *r, int size); 290 | void RTMP_UpdateBufferMS(RTMP *r); 291 | 292 | int RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg); 293 | int RTMP_SetupURL(RTMP *r, char *url); 294 | void RTMP_SetupStream(RTMP *r, int protocol, 295 | AVal *hostname, 296 | unsigned int port, 297 | AVal *sockshost, 298 | AVal *playpath, 299 | AVal *tcUrl, 300 | AVal *swfUrl, 301 | AVal *pageUrl, 302 | AVal *app, 303 | AVal *auth, 304 | AVal *swfSHA256Hash, 305 | uint32_t swfSize, 306 | AVal *flashVer, 307 | AVal *subscribepath, 308 | AVal *usherToken, 309 | int dStart, 310 | int dStop, int bLiveStream, long int timeout); 311 | 312 | int RTMP_Connect(RTMP *r, RTMPPacket *cp); 313 | int RTMP_Connect_Ipv4(RTMP *r, RTMPPacket *cp); 314 | int RTMP_Connect_Ipv6(RTMP *r, RTMPPacket *cp); 315 | struct sockaddr; 316 | struct sockaddr_in6; 317 | int RTMP_Connect0(RTMP *r, struct sockaddr *svc); 318 | int RTMP_Connect0_IPV6(RTMP *r, struct sockaddr_in6 *svc); 319 | int RTMP_Connect1(RTMP *r, RTMPPacket *cp); 320 | int RTMP_Serve(RTMP *r); 321 | int RTMP_TLS_Accept(RTMP *r, void *ctx); 322 | 323 | int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet); 324 | int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue); 325 | int RTMP_SendChunk(RTMP *r, RTMPChunk *chunk); 326 | int RTMP_IsConnected(RTMP *r); 327 | int RTMP_Socket(RTMP *r); 328 | int RTMP_IsTimedout(RTMP *r); 329 | double RTMP_GetDuration(RTMP *r); 330 | int RTMP_ToggleStream(RTMP *r); 331 | 332 | int RTMP_ConnectStream(RTMP *r, int seekTime); 333 | int RTMP_ReconnectStream(RTMP *r, int seekTime); 334 | void RTMP_DeleteStream(RTMP *r); 335 | int RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet); 336 | int RTMP_ClientPacket(RTMP *r, RTMPPacket *packet); 337 | 338 | void RTMP_Init(RTMP *r); 339 | void RTMP_Close(RTMP *r); 340 | RTMP *RTMP_Alloc(void); 341 | void RTMP_Free(RTMP *r); 342 | void RTMP_EnableWrite(RTMP *r); 343 | 344 | void *RTMP_TLS_AllocServerContext(const char* cert, const char* key); 345 | void RTMP_TLS_FreeServerContext(void *ctx); 346 | 347 | int RTMP_LibVersion(void); 348 | void RTMP_UserInterrupt(void); /* user typed Ctrl-C */ 349 | 350 | int RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, 351 | unsigned int nTime); 352 | 353 | /* caller probably doesn't know current timestamp, should 354 | * just use RTMP_Pause instead 355 | */ 356 | int RTMP_SendPause(RTMP *r, int DoPause, int dTime); 357 | int RTMP_Pause(RTMP *r, int DoPause); 358 | 359 | int RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name, 360 | AMFObjectProperty * p); 361 | 362 | int RTMPSockBuf_Fill(RTMPSockBuf *sb); 363 | int RTMPSockBuf_Send(RTMPSockBuf *sb, const char *buf, int len); 364 | int RTMPSockBuf_Close(RTMPSockBuf *sb); 365 | 366 | int RTMP_SendCreateStream(RTMP *r); 367 | int RTMP_SendSeek(RTMP *r, int dTime); 368 | int RTMP_SendServerBW(RTMP *r); 369 | int RTMP_SendClientBW(RTMP *r); 370 | void RTMP_DropRequest(RTMP *r, int i, int freeit); 371 | int RTMP_Read(RTMP *r, char *buf, int size); 372 | int RTMP_Write(RTMP *r, const char *buf, int size); 373 | 374 | /* hashswf.c */ 375 | int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash, 376 | int age); 377 | 378 | #ifdef __cplusplus 379 | }; 380 | #endif 381 | 382 | #endif 383 | --------------------------------------------------------------------------------