Skip to content
This repository
Browse code

[droid] bump librtmp from 2.3 to 2.4

  • Loading branch information...
commit f4c48575915a3ee99a45ce5d0377e5a053bb65bc 1 parent 87b1736
authored January 11, 2013
29  tools/android/depends/librtmp/Makefile
@@ -3,9 +3,13 @@ DEPS= ../Makefile.include Makefile prefix.patch
3 3
 
4 4
 # lib name, version
5 5
 LIBNAME=rtmpdump
6  
-VERSION=2.3
  6
+VERSION=e0056c51cc1710c9a44d2a2c4e2f344fa9cabcf4
  7
+GIT_DIR=$(TARBALLS_LOCATION)/$(LIBNAME).git
  8
+BASE_URL=git://git.ffmpeg.org/$(LIBNAME).git
7 9
 SOURCE=$(LIBNAME)-$(VERSION)
8  
-ARCHIVE=$(SOURCE).tgz
  10
+#tell git to use the addons repo rather than xbmc's repo
  11
+export GIT_DIR
  12
+export GIT_WORK_TREE=$(PLATFORM)
9 13
 
10 14
 # configuration settings
11 15
 
@@ -15,16 +19,25 @@ CLEAN_FILES=$(ARCHIVE) $(PLATFORM)
15 19
 
16 20
 all: .installed-$(PLATFORM)
17 21
 
18  
-$(TARBALLS_LOCATION)/$(ARCHIVE):
19  
-	$(RETRIEVE_TOOL) $(RETRIEVE_TOOL_FLAGS) $(BASE_URL)/$(ARCHIVE)
  22
+$(GIT_DIR)/HEAD:
  23
+	cd $(TARBALLS_LOCATION); git clone --bare $(BASE_URL)
20 24
 
21  
-$(PLATFORM): $(TARBALLS_LOCATION)/$(ARCHIVE) $(DEPS)
22  
-	rm -rf $(PLATFORM)/*; mkdir -p $(PLATFORM)
23  
-	$(ARCHIVE_TOOL) $(ARCHIVE_TOOL_FLAGS) $(TARBALLS_LOCATION)/$(ARCHIVE)
  25
+$(GIT_DIR)/current/$(VERSION): $(GIT_DIR)/HEAD $(DEPS)
  26
+	git rev-list -1 $(VERSION) >/dev/null || git fetch origin "+refs/heads/*:refs/remotes/origin/*"
  27
+	git rev-list -1 $(VERSION) >/dev/null
  28
+	rm -rf $(GIT_DIR)/current; mkdir -p $(GIT_DIR)/current
  29
+	touch $@
  30
+
  31
+$(PLATFORM)/bootstrap: $(GIT_DIR)/current/$(VERSION)
  32
+	rm -rf $(PLATFORM); mkdir -p $(PLATFORM)
  33
+	git checkout $(VERSION) -- .
  34
+
  35
+$(PLATFORM): $(PLATFORM)/bootstrap
  36
+	cd $(PLATFORM); patch -p1 < ../librtmp-60-second-fix.patch
24 37
 	cd $(PLATFORM); patch -p0 < ../prefix.patch
25 38
 
26 39
 $(LIBDYLIB): $(PLATFORM)
27  
-	$(MAKE) -C $(PLATFORM)/librtmp CROSS_COMPILE=$(CROSSTOOLS) PREFIX=$(PREFIX)
  40
+	$(MAKE) -C $(PLATFORM)/librtmp CROSS_COMPILE=$(CROSSTOOLS) PREFIX=$(PREFIX) XCFLAGS="$(CFLAGS)" XLDFLAGS="$(LDFLAGS) -lm"
28 41
 
29 42
 .installed-$(PLATFORM): $(LIBDYLIB)
30 43
 	$(MAKE) -C $(PLATFORM)/librtmp install PREFIX=$(PREFIX)
1,913  tools/android/depends/librtmp/librtmp-60-second-fix.patch
... ...
@@ -0,0 +1,1913 @@
  1
+diff --git a/librtmp/amf.c b/librtmp/amf.c
  2
+index ce84f81..a25bc04 100644
  3
+--- a/librtmp/amf.c
  4
++++ b/librtmp/amf.c
  5
+@@ -610,6 +610,9 @@ AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
  6
+       return -1;
  7
+     }
  8
+ 
  9
++  if (*pBuffer == AMF_NULL)
  10
++    bDecodeName = 0;
  11
++
  12
+   if (bDecodeName && nSize < 4)
  13
+     {				/* at least name (length + at least 1 byte) and 1 byte of data */
  14
+       RTMP_Log(RTMP_LOGDEBUG,
  15
+@@ -801,8 +804,8 @@ AMFProp_Dump(AMFObjectProperty *prop)
  16
+     }
  17
+   else
  18
+     {
  19
+-      name.av_val = "no-name.";
  20
+-      name.av_len = sizeof("no-name.") - 1;
  21
++      name.av_val = "no-name";
  22
++      name.av_len = sizeof("no-name") - 1;
  23
+     }
  24
+   if (name.av_len > 18)
  25
+     name.av_len = 18;
  26
+diff --git a/librtmp/dh.h b/librtmp/dh.h
  27
+index 9959532..e29587b 100644
  28
+--- a/librtmp/dh.h
  29
++++ b/librtmp/dh.h
  30
+@@ -61,7 +61,7 @@ static int MDH_generate_key(MDH *dh)
  31
+   MP_set(&dh->ctx.P, dh->p);
  32
+   MP_set(&dh->ctx.G, dh->g);
  33
+   dh->ctx.len = 128;
  34
+-  dhm_make_public(&dh->ctx, 1024, out, 1, havege_rand, &RTMP_TLS_ctx->hs);
  35
++  dhm_make_public(&dh->ctx, 1024, out, 1, havege_random, &RTMP_TLS_ctx->hs);
  36
+   MP_new(dh->pub_key);
  37
+   MP_new(dh->priv_key);
  38
+   MP_set(dh->pub_key, &dh->ctx.GX);
  39
+diff --git a/librtmp/handshake.h b/librtmp/handshake.h
  40
+index 0438486..102ba82 100644
  41
+--- a/librtmp/handshake.h
  42
++++ b/librtmp/handshake.h
  43
+@@ -965,8 +965,18 @@ HandShake(RTMP * r, int FP9HandShake)
  44
+     __FUNCTION__);
  45
+   RTMP_LogHex(RTMP_LOGDEBUG, reply, RTMP_SIG_SIZE);
  46
+ #endif
  47
+-  if (!WriteN(r, (char *)reply, RTMP_SIG_SIZE))
  48
+-    return FALSE;
  49
++  if (r->Link.CombineConnectPacket)
  50
++    {
  51
++      char *HandshakeResponse = malloc(RTMP_SIG_SIZE);
  52
++      memcpy(HandshakeResponse, (char *) reply, RTMP_SIG_SIZE);
  53
++      r->Link.HandshakeResponse.av_val = HandshakeResponse;
  54
++      r->Link.HandshakeResponse.av_len = RTMP_SIG_SIZE;
  55
++    }
  56
++  else
  57
++    {
  58
++      if (!WriteN(r, (char *) reply, RTMP_SIG_SIZE))
  59
++        return FALSE;
  60
++    }
  61
+ 
  62
+   /* 2nd part of handshake */
  63
+   if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
  64
+diff --git a/librtmp/hashswf.c b/librtmp/hashswf.c
  65
+index 9f4e2c0..eeed34c 100644
  66
+--- a/librtmp/hashswf.c
  67
++++ b/librtmp/hashswf.c
  68
+@@ -70,7 +70,7 @@ extern TLS_CTX RTMP_TLS_ctx;
  69
+ 
  70
+ #endif /* CRYPTO */
  71
+ 
  72
+-#define	AGENT	"Mozilla/5.0"
  73
++#define	AGENT	"Mozilla/5.0 (Windows NT 5.1; rv:8.0) Gecko/20100101 Firefox/8.0"
  74
+ 
  75
+ HTTPResult
  76
+ HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
  77
+@@ -528,7 +528,7 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
  78
+ 
  79
+ 	  if (strncmp(buf, "url: ", 5))
  80
+ 	    continue;
  81
+-	  if (strncmp(buf + 5, url, hlen))
  82
++	  if (strncmp(buf + 5, url, strlen(buf + 5) - 1))
  83
+ 	    continue;
  84
+ 	  r1 = strrchr(buf, '/');
  85
+ 	  i = strlen(r1);
  86
+diff --git a/librtmp/log.c b/librtmp/log.c
  87
+index 0012985..856e3e4 100644
  88
+--- a/librtmp/log.c
  89
++++ b/librtmp/log.c
  90
+@@ -52,8 +52,8 @@ static void rtmp_log_default(int level, const char *format, va_list vl)
  91
+ 	vsnprintf(str, MAX_PRINT_LEN-1, format, vl);
  92
+ 
  93
+ 	/* Filter out 'no-name' */
  94
+-	if ( RTMP_debuglevel<RTMP_LOGALL && strstr(str, "no-name" ) != NULL )
  95
+-		return;
  96
++	if (RTMP_debuglevel < RTMP_LOGDEBUG && strstr(str, "no-name") != NULL)
  97
++	  return;
  98
+ 
  99
+ 	if ( !fmsg ) fmsg = stderr;
  100
+ 
  101
+diff --git a/librtmp/rtmp.c b/librtmp/rtmp.c
  102
+index 52d0254..bef37aa 100644
  103
+--- a/librtmp/rtmp.c
  104
++++ b/librtmp/rtmp.c
  105
+@@ -27,6 +27,7 @@
  106
+ #include <stdlib.h>
  107
+ #include <string.h>
  108
+ #include <assert.h>
  109
++#include <math.h>
  110
+ 
  111
+ #include "rtmp_sys.h"
  112
+ #include "log.h"
  113
+@@ -45,6 +46,7 @@ TLS_CTX RTMP_TLS_ctx;
  114
+ 
  115
+ #define RTMP_SIG_SIZE 1536
  116
+ #define RTMP_LARGE_HEADER_SIZE 12
  117
++#define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
  118
+ 
  119
+ static const int packetSize[] = { 12, 8, 4, 1 };
  120
+ 
  121
+@@ -97,6 +99,9 @@ static int SendFCSubscribe(RTMP *r, AVal *subscribepath);
  122
+ static int SendPlay(RTMP *r);
  123
+ static int SendBytesReceived(RTMP *r);
  124
+ static int SendUsherToken(RTMP *r, AVal *usherToken);
  125
++static int SendInvoke(RTMP *r, AVal *Command, int queue);
  126
++static int SendGetStreamLength(RTMP *r);
  127
++static int strsplit(char *src, int srclen, char delim, char ***params);
  128
+ 
  129
+ #if 0				/* unused */
  130
+ static int SendBGHasStream(RTMP *r, double dId, AVal *playpath);
  131
+@@ -259,6 +264,8 @@ RTMP_Init(RTMP *r)
  132
+   r->m_fVideoCodecs = 252.0;
  133
+   r->Link.timeout = 30;
  134
+   r->Link.swfAge = 30;
  135
++  r->Link.CombineConnectPacket = TRUE;
  136
++  r->Link.ConnectPacket = FALSE;
  137
+ }
  138
+ 
  139
+ void
  140
+@@ -337,6 +344,7 @@ RTMP_SetupStream(RTMP *r,
  141
+ 		 AVal *flashVer,
  142
+ 		 AVal *subscribepath,
  143
+ 		 AVal *usherToken,
  144
++		 AVal *WeebToken,
  145
+ 		 int dStart,
  146
+ 		 int dStop, int bLiveStream, long int timeout)
  147
+ {
  148
+@@ -359,6 +367,8 @@ RTMP_SetupStream(RTMP *r,
  149
+     RTMP_Log(RTMP_LOGDEBUG, "subscribepath : %s", subscribepath->av_val);
  150
+   if (usherToken && usherToken->av_val)
  151
+     RTMP_Log(RTMP_LOGDEBUG, "NetStream.Authenticate.UsherToken : %s", usherToken->av_val);
  152
++  if (WeebToken && WeebToken->av_val)
  153
++    RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", WeebToken->av_val);
  154
+   if (flashVer && flashVer->av_val)
  155
+     RTMP_Log(RTMP_LOGDEBUG, "flashVer : %s", flashVer->av_val);
  156
+   if (dStart > 0)
  157
+@@ -426,6 +436,8 @@ RTMP_SetupStream(RTMP *r,
  158
+     r->Link.subscribepath = *subscribepath;
  159
+   if (usherToken && usherToken->av_len)
  160
+     r->Link.usherToken = *usherToken;
  161
++  if (WeebToken && WeebToken->av_len)
  162
++    r->Link.WeebToken = *WeebToken;
  163
+   r->Link.seekTime = dStart;
  164
+   r->Link.stopTime = dStop;
  165
+   if (bLiveStream)
  166
+@@ -483,14 +495,22 @@ static struct urlopt {
  167
+   	"Stream is live, no seeking possible" },
  168
+   { AVC("subscribe"), OFF(Link.subscribepath), OPT_STR, 0,
  169
+   	"Stream to subscribe to" },
  170
+-  { AVC("jtv"), OFF(Link.usherToken),          OPT_STR, 0,
  171
+-	"Justin.tv authentication token" },
  172
+-  { AVC("token"),     OFF(Link.token),	       OPT_STR, 0,
  173
++  { AVC("jtv"),       OFF(Link.usherToken),    OPT_STR, 0,
  174
++        "Justin.tv authentication token"},
  175
++  { AVC("weeb"),      OFF(Link.WeebToken),     OPT_STR, 0,
  176
++        "Weeb.tv authentication token"},
  177
++  { AVC("token"),     OFF(Link.token),         OPT_STR, 0,
  178
+   	"Key for SecureToken response" },
  179
+   { AVC("swfVfy"),    OFF(Link.lFlags),        OPT_BOOL, RTMP_LF_SWFV,
  180
+   	"Perform SWF Verification" },
  181
+   { AVC("swfAge"),    OFF(Link.swfAge),        OPT_INT, 0,
  182
+   	"Number of days to use cached SWF hash" },
  183
++#ifdef CRYPTO
  184
++  { AVC("swfsize"),   OFF(Link.swfSize),       OPT_INT, 0,
  185
++        "Size of the decompressed SWF file"},
  186
++  { AVC("swfhash"),   OFF(Link.swfHash),       OPT_STR, 0,
  187
++        "SHA256 hash of the decompressed SWF file"},
  188
++#endif
  189
+   { AVC("start"),     OFF(Link.seekTime),      OPT_INT, 0,
  190
+   	"Stream start position in milliseconds" },
  191
+   { AVC("stop"),      OFF(Link.stopTime),      OPT_INT, 0,
  192
+@@ -751,9 +771,16 @@ int RTMP_SetupURL(RTMP *r, char *url)
  193
+     }
  194
+ 
  195
+ #ifdef CRYPTO
  196
+-  if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len)
  197
+-    RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize,
  198
+-	  (unsigned char *)r->Link.SWFHash, r->Link.swfAge);
  199
++  RTMP_Log(RTMP_LOGDEBUG, "Khalsa: %d %d %s\n", r->Link.swfSize, r->Link.swfHash.av_len, r->Link.swfHash.av_val);
  200
++  if (r->Link.swfSize && r->Link.swfHash.av_len)
  201
++    {
  202
++      int i, j = 0;
  203
++      for (i = 0; i < r->Link.swfHash.av_len; i += 2)
  204
++        r->Link.SWFHash[j++] = (HEX2BIN(r->Link.swfHash.av_val[i]) << 4) | HEX2BIN(r->Link.swfHash.av_val[i + 1]);
  205
++      r->Link.SWFSize = (uint32_t) r->Link.swfSize;
  206
++    }
  207
++  else if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len)
  208
++    RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize, (unsigned char *) r->Link.SWFHash, r->Link.swfAge);
  209
+ #endif
  210
+ 
  211
+   if (r->Link.port == 0)
  212
+@@ -854,6 +881,8 @@ RTMP_Connect0(RTMP *r, struct sockaddr * service)
  213
+   }
  214
+ 
  215
+   setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on));
  216
++  if (r->Link.protocol & RTMP_FEATURE_HTTP)
  217
++    setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof (on));
  218
+ 
  219
+   return TRUE;
  220
+ }
  221
+@@ -1308,8 +1337,24 @@ ReadN(RTMP *r, char *buffer, int n)
  222
+ 		  return 0;
  223
+ 		}
  224
+ 	    }
  225
+-	  if (r->m_resplen && !r->m_sb.sb_size)
  226
+-	    RTMPSockBuf_Fill(&r->m_sb);
  227
++
  228
++          // Try to fill the whole buffer. previous buffer needs to be consumed
  229
++          // completely before receiving new data.
  230
++          if (r->m_resplen && (r->m_sb.sb_size <= 0))
  231
++            {
  232
++              do
  233
++                {
  234
++                  nBytes = RTMPSockBuf_Fill(&r->m_sb);
  235
++                  if (nBytes == -1)
  236
++                    {
  237
++                      if (!r->m_sb.sb_timedout)
  238
++                        RTMP_Close(r);
  239
++                      return 0;
  240
++                    }
  241
++                }
  242
++              while (r->m_resplen && (r->m_sb.sb_size < r->m_resplen) && (nBytes > 0));
  243
++            }
  244
++
  245
+           avail = r->m_sb.sb_size;
  246
+ 	  if (avail > r->m_resplen)
  247
+ 	    avail = r->m_resplen;
  248
+@@ -1336,10 +1381,9 @@ ReadN(RTMP *r, char *buffer, int n)
  249
+ 	  r->m_sb.sb_size -= nRead;
  250
+ 	  nBytes = nRead;
  251
+ 	  r->m_nBytesIn += nRead;
  252
+-	  if (r->m_bSendCounter
  253
+-	      && r->m_nBytesIn > ( r->m_nBytesInSent + r->m_nClientBW / 10))
  254
+-	    if (!SendBytesReceived(r))
  255
+-	        return FALSE;
  256
++          if (r->m_bSendCounter && r->m_nBytesIn > (r->m_nBytesInSent + r->m_nClientBW / 10))
  257
++            if (!SendBytesReceived(r))
  258
++              return FALSE;
  259
+ 	}
  260
+       /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */
  261
+ #ifdef _DEBUG
  262
+@@ -1390,6 +1434,16 @@ WriteN(RTMP *r, const char *buffer, int n)
  263
+     }
  264
+ #endif
  265
+ 
  266
++  if (r->Link.ConnectPacket)
  267
++    {
  268
++      char *ConnectPacket = malloc(r->Link.HandshakeResponse.av_len + n);
  269
++      memcpy(ConnectPacket, r->Link.HandshakeResponse.av_val, r->Link.HandshakeResponse.av_len);
  270
++      memcpy(ConnectPacket + r->Link.HandshakeResponse.av_len, ptr, n);
  271
++      ptr = ConnectPacket;
  272
++      n += r->Link.HandshakeResponse.av_len;
  273
++      r->Link.ConnectPacket = FALSE;
  274
++    }
  275
++
  276
+   while (n > 0)
  277
+     {
  278
+       int nBytes;
  279
+@@ -1455,6 +1509,9 @@ SendConnectPacket(RTMP *r, RTMPPacket *cp)
  280
+   char pbuf[4096], *pend = pbuf + sizeof(pbuf);
  281
+   char *enc;
  282
+ 
  283
++  if (r->Link.CombineConnectPacket)
  284
++    r->Link.ConnectPacket = TRUE;
  285
++
  286
+   if (cp)
  287
+     return RTMP_SendPacket(r, cp, TRUE);
  288
+ 
  289
+@@ -1667,7 +1724,7 @@ SendUsherToken(RTMP *r, AVal *usherToken)
  290
+   packet.m_hasAbsTimestamp = 0;
  291
+   packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
  292
+ 
  293
+-  RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %s", usherToken->av_val);
  294
++  RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %.*s", usherToken->av_len, usherToken->av_val);
  295
+   enc = packet.m_body;
  296
+   enc = AMF_EncodeString(enc, pend, &av_NetStream_Authenticate_UsherToken);
  297
+   enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
  298
+@@ -2096,10 +2153,8 @@ SendPlay(RTMP *r)
  299
+     enc = AMF_EncodeNumber(enc, pend, -1000.0);
  300
+   else
  301
+     {
  302
+-      if (r->Link.seekTime > 0.0)
  303
+-	enc = AMF_EncodeNumber(enc, pend, r->Link.seekTime);	/* resume from here */
  304
+-      else
  305
+-	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 */
  306
++      if (r->Link.seekTime > 0.0 || r->Link.stopTime)
  307
++        enc = AMF_EncodeNumber(enc, pend, r->Link.seekTime); /* resume from here */
  308
+     }
  309
+   if (!enc)
  310
+     return FALSE;
  311
+@@ -2215,7 +2270,7 @@ RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime)
  312
+   int nSize;
  313
+   char *buf;
  314
+ 
  315
+-  RTMP_Log(RTMP_LOGDEBUG, "sending ctrl. type: 0x%04x", (unsigned short)nType);
  316
++  RTMP_Log(RTMP_LOGDEBUG, "sending ctrl, type: 0x%04x", (unsigned short)nType);
  317
+ 
  318
+   packet.m_nChannel = 0x02;	/* control channel (ping) */
  319
+   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
  320
+@@ -2247,8 +2302,8 @@ RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime)
  321
+     }
  322
+   else if (nType == 0x1A)
  323
+     {
  324
+-	  *buf = nObject & 0xff;
  325
+-	}
  326
++      *buf = nObject & 0xff;
  327
++    }
  328
+   else
  329
+     {
  330
+       if (nSize > 2)
  331
+@@ -2305,6 +2360,7 @@ AV_clear(RTMP_METHOD *vals, int num)
  332
+   free(vals);
  333
+ }
  334
+ 
  335
++SAVC(onBWCheck);
  336
+ SAVC(onBWDone);
  337
+ SAVC(onFCSubscribe);
  338
+ SAVC(onFCUnsubscribe);
  339
+@@ -2314,24 +2370,26 @@ SAVC(_error);
  340
+ SAVC(close);
  341
+ SAVC(code);
  342
+ SAVC(level);
  343
++SAVC(description);
  344
+ SAVC(onStatus);
  345
+ SAVC(playlist_ready);
  346
+ static const AVal av_NetStream_Failed = AVC("NetStream.Failed");
  347
+ static const AVal av_NetStream_Play_Failed = AVC("NetStream.Play.Failed");
  348
+-static const AVal av_NetStream_Play_StreamNotFound =
  349
+-AVC("NetStream.Play.StreamNotFound");
  350
+-static const AVal av_NetConnection_Connect_InvalidApp =
  351
+-AVC("NetConnection.Connect.InvalidApp");
  352
++static const AVal av_NetStream_Play_StreamNotFound = AVC("NetStream.Play.StreamNotFound");
  353
++static const AVal av_NetConnection_Connect_InvalidApp = AVC("NetConnection.Connect.InvalidApp");
  354
+ static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
  355
+ static const AVal av_NetStream_Play_Complete = AVC("NetStream.Play.Complete");
  356
+ static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
  357
+ static const AVal av_NetStream_Seek_Notify = AVC("NetStream.Seek.Notify");
  358
+ static const AVal av_NetStream_Pause_Notify = AVC("NetStream.Pause.Notify");
  359
+-static const AVal av_NetStream_Play_PublishNotify =
  360
+-AVC("NetStream.Play.PublishNotify");
  361
+-static const AVal av_NetStream_Play_UnpublishNotify =
  362
+-AVC("NetStream.Play.UnpublishNotify");
  363
++static const AVal av_NetStream_Play_PublishNotify = AVC("NetStream.Play.PublishNotify");
  364
++static const AVal av_NetStream_Play_UnpublishNotify = AVC("NetStream.Play.UnpublishNotify");
  365
+ static const AVal av_NetStream_Publish_Start = AVC("NetStream.Publish.Start");
  366
++static const AVal av_NetConnection_confStream = AVC("NetConnection.confStream");
  367
++static const AVal av_verifyClient = AVC("verifyClient");
  368
++static const AVal av_sendStatus = AVC("sendStatus");
  369
++static const AVal av_getStreamLength = AVC("getStreamLength");
  370
++static const AVal av_ReceiveCheckPublicStatus = AVC("ReceiveCheckPublicStatus");
  371
+ 
  372
+ /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
  373
+ static int
  374
+@@ -2341,6 +2399,11 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
  375
+   AVal method;
  376
+   double txn;
  377
+   int ret = 0, nRes;
  378
++  char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc, **params = NULL;
  379
++  char *host = r->Link.hostname.av_len ? r->Link.hostname.av_val : "";
  380
++  char *pageUrl = r->Link.pageUrl.av_len ? r->Link.pageUrl.av_val : "";
  381
++  int param_count;
  382
++  AVal av_Command, av_Response;
  383
+   if (body[0] != 0x02)		/* make sure it is a string method name we start with */
  384
+     {
  385
+       RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
  386
+@@ -2402,23 +2465,137 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
  387
+ 	      RTMP_SendServerBW(r);
  388
+ 	      RTMP_SendCtrl(r, 3, 0, 300);
  389
+ 	    }
  390
+-	  RTMP_SendCreateStream(r);
  391
+-
  392
+-	  if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
  393
+-	    {
  394
+-	      /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
  395
+-	      if (r->Link.usherToken.av_len)
  396
+-	        SendUsherToken(r, &r->Link.usherToken);
  397
+-	      /* Send the FCSubscribe if live stream or if subscribepath is set */
  398
+-	      if (r->Link.subscribepath.av_len)
  399
+-	        SendFCSubscribe(r, &r->Link.subscribepath);
  400
+-	      else if (r->Link.lFlags & RTMP_LF_LIVE)
  401
+-	        SendFCSubscribe(r, &r->Link.playpath);
  402
+-	    }
  403
+-	}
  404
++          if (strstr(host, "tv-stream.to") || strstr(pageUrl, "tv-stream.to"))
  405
++            {
  406
++              static char auth[] = {'h', 0xC2, 0xA7, '4', 'j', 'h', 'H', '4', '3', 'd'};
  407
++              AVal av_auth;
  408
++              SAVC(requestAccess);
  409
++              av_auth.av_val = auth;
  410
++              av_auth.av_len = sizeof (auth);
  411
++
  412
++              enc = pbuf;
  413
++              enc = AMF_EncodeString(enc, pend, &av_requestAccess);
  414
++              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
  415
++              *enc++ = AMF_NULL;
  416
++              enc = AMF_EncodeString(enc, pend, &av_auth);
  417
++              av_Command.av_val = pbuf;
  418
++              av_Command.av_len = enc - pbuf;
  419
++              SendInvoke(r, &av_Command, FALSE);
  420
++
  421
++              SAVC(getConnectionCount);
  422
++              enc = pbuf;
  423
++              enc = AMF_EncodeString(enc, pend, &av_getConnectionCount);
  424
++              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
  425
++              *enc++ = AMF_NULL;
  426
++              av_Command.av_val = pbuf;
  427
++              av_Command.av_len = enc - pbuf;
  428
++              SendInvoke(r, &av_Command, FALSE);
  429
++
  430
++              SendGetStreamLength(r);
  431
++            }
  432
++          else if (strstr(host, "jampo.com.ua") || strstr(pageUrl, "jampo.com.ua"))
  433
++            {
  434
++              SendGetStreamLength(r);
  435
++            }
  436
++          else if (strstr(host, "streamscene.cc") || strstr(pageUrl, "streamscene.cc")
  437
++                   || strstr(host, "tsboard.tv") || strstr(pageUrl, "teamstream.in"))
  438
++            {
  439
++              SAVC(r);
  440
++              enc = pbuf;
  441
++              enc = AMF_EncodeString(enc, pend, &av_r);
  442
++              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
  443
++              *enc++ = AMF_NULL;
  444
++              av_Command.av_val = pbuf;
  445
++              av_Command.av_len = enc - pbuf;
  446
++              SendInvoke(r, &av_Command, FALSE);
  447
++
  448
++              SendGetStreamLength(r);
  449
++            }
  450
++          else if (strstr(host, "chaturbate.com") || strstr(pageUrl, "chaturbate.com"))
  451
++            {
  452
++              AVal av_ModelName;
  453
++              SAVC(CheckPublicStatus);
  454
++
  455
++              if (strlen(pageUrl) > 7)
  456
++                {
  457
++                  strsplit(pageUrl + 7, FALSE, '/', &params);
  458
++                  av_ModelName.av_val = params[1];
  459
++                  av_ModelName.av_len = strlen(params[1]);
  460
++
  461
++                  enc = pbuf;
  462
++                  enc = AMF_EncodeString(enc, pend, &av_CheckPublicStatus);
  463
++                  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
  464
++                  *enc++ = AMF_NULL;
  465
++                  enc = AMF_EncodeString(enc, pend, &av_ModelName);
  466
++                  av_Command.av_val = pbuf;
  467
++                  av_Command.av_len = enc - pbuf;
  468
++
  469
++                  SendInvoke(r, &av_Command, FALSE);
  470
++                }
  471
++              else
  472
++                {
  473
++                  RTMP_Log(RTMP_LOGERROR, "you must specify the pageUrl");
  474
++                  RTMP_Close(r);
  475
++                }
  476
++            }
  477
++          /* Weeb.tv specific authentication */
  478
++          else if (r->Link.WeebToken.av_len)
  479
++            {
  480
++              AVal av_Token, av_Username, av_Password;
  481
++              SAVC(determineAccess);
  482
++
  483
++              param_count = strsplit(r->Link.WeebToken.av_val, FALSE, ';', &params);
  484
++              if (param_count >= 1)
  485
++                {
  486
++                  av_Token.av_val = params[0];
  487
++                  av_Token.av_len = strlen(params[0]);
  488
++                }
  489
++              if (param_count >= 2)
  490
++                {
  491
++                  av_Username.av_val = params[1];
  492
++                  av_Username.av_len = strlen(params[1]);
  493
++                }
  494
++              if (param_count >= 3)
  495
++                {
  496
++                  av_Password.av_val = params[2];
  497
++                  av_Password.av_len = strlen(params[2]);
  498
++                }
  499
++
  500
++              enc = pbuf;
  501
++              enc = AMF_EncodeString(enc, pend, &av_determineAccess);
  502
++              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
  503
++              *enc++ = AMF_NULL;
  504
++              enc = AMF_EncodeString(enc, pend, &av_Token);
  505
++              enc = AMF_EncodeString(enc, pend, &av_Username);
  506
++              enc = AMF_EncodeString(enc, pend, &av_Password);
  507
++              av_Command.av_val = pbuf;
  508
++              av_Command.av_len = enc - pbuf;
  509
++
  510
++              RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", r->Link.WeebToken.av_val);
  511
++              SendInvoke(r, &av_Command, FALSE);
  512
++            }
  513
++          else
  514
++            RTMP_SendCreateStream(r);
  515
++        }
  516
++      else if (AVMATCH(&methodInvoked, &av_getStreamLength))
  517
++        {
  518
++          RTMP_SendCreateStream(r);
  519
++        }
  520
+       else if (AVMATCH(&methodInvoked, &av_createStream))
  521
+-	{
  522
+-	  r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
  523
++        {
  524
++          r->m_stream_id = (int) AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
  525
++
  526
++          if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
  527
++            {
  528
++              /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
  529
++              if (r->Link.usherToken.av_len)
  530
++                SendUsherToken(r, &r->Link.usherToken);
  531
++              /* Send the FCSubscribe if live stream or if subscribepath is set */
  532
++              if (r->Link.subscribepath.av_len)
  533
++                SendFCSubscribe(r, &r->Link.subscribepath);
  534
++              else if ((r->Link.lFlags & RTMP_LF_LIVE) && (!r->Link.WeebToken.av_len))
  535
++                SendFCSubscribe(r, &r->Link.playpath);
  536
++            }
  537
+ 
  538
+ 	  if (r->Link.protocol & RTMP_FEATURE_WRITE)
  539
+ 	    {
  540
+@@ -2441,7 +2618,7 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
  541
+     }
  542
+   else if (AVMATCH(&method, &av_onBWDone))
  543
+     {
  544
+-	  if (!r->m_nBWCheckCounter)
  545
++      if (!r->m_nBWCheckCounter)
  546
+         SendCheckBW(r);
  547
+     }
  548
+   else if (AVMATCH(&method, &av_onFCSubscribe))
  549
+@@ -2457,7 +2634,7 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
  550
+     {
  551
+       SendPong(r, txn);
  552
+     }
  553
+-  else if (AVMATCH(&method, &av__onbwcheck))
  554
++  else if (AVMATCH(&method, &av__onbwcheck) || AVMATCH(&method, &av_onBWCheck))
  555
+     {
  556
+       SendCheckBWResult(r, txn);
  557
+     }
  558
+@@ -2473,20 +2650,63 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
  559
+     }
  560
+   else if (AVMATCH(&method, &av__error))
  561
+     {
  562
+-      RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
  563
++      double code = 0;
  564
++      unsigned int parsedPort;
  565
++      AMFObject obj2;
  566
++      AMFObjectProperty p;
  567
++      AVal redirect;
  568
++      SAVC(ex);
  569
++      SAVC(redirect);
  570
++
  571
++      AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
  572
++      if (RTMP_FindFirstMatchingProperty(&obj2, &av_ex, &p))
  573
++        {
  574
++          AMFProp_GetObject(&p, &obj2);
  575
++          if (RTMP_FindFirstMatchingProperty(&obj2, &av_code, &p))
  576
++            code = AMFProp_GetNumber(&p);
  577
++          if (code == 302 && RTMP_FindFirstMatchingProperty(&obj2, &av_redirect, &p))
  578
++            {
  579
++              AMFProp_GetString(&p, &redirect);
  580
++              r->Link.redirected = TRUE;
  581
++
  582
++              char *url = malloc(redirect.av_len + sizeof ("/playpath"));
  583
++              strncpy(url, redirect.av_val, redirect.av_len);
  584
++              url[redirect.av_len] = '\0';
  585
++              r->Link.tcUrl.av_val = url;
  586
++              r->Link.tcUrl.av_len = redirect.av_len;
  587
++              strcat(url, "/playpath");
  588
++              RTMP_ParseURL(url, &r->Link.protocol, &r->Link.hostname, &parsedPort, &r->Link.playpath0, &r->Link.app);
  589
++              r->Link.port = parsedPort;
  590
++            }
  591
++        }
  592
++      if (r->Link.redirected)
  593
++        RTMP_Log(RTMP_LOGINFO, "rtmp server sent redirect");
  594
++      else
  595
++        RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
  596
+     }
  597
+   else if (AVMATCH(&method, &av_close))
  598
+     {
  599
+-      RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
  600
+-      RTMP_Close(r);
  601
++      if (r->Link.redirected)
  602
++        {
  603
++          RTMP_Log(RTMP_LOGINFO, "trying to connect with redirected url");
  604
++          RTMP_Close(r);
  605
++          r->Link.redirected = FALSE;
  606
++          RTMP_Connect(r, NULL);
  607
++        }
  608
++      else
  609
++        {
  610
++          RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
  611
++          RTMP_Close(r);
  612
++        }
  613
+     }
  614
+   else if (AVMATCH(&method, &av_onStatus))
  615
+     {
  616
+       AMFObject obj2;
  617
+-      AVal code, level;
  618
++      AVal code, level, description;
  619
+       AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
  620
+       AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code);
  621
+       AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level);
  622
++      AMFProp_GetString(AMF_GetProp(&obj2, &av_description, -1), &description);
  623
+ 
  624
+       RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val);
  625
+       if (AVMATCH(&code, &av_NetStream_Failed)
  626
+@@ -2550,6 +2770,45 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
  627
+ 	    r->m_pausing = 3;
  628
+ 	  }
  629
+ 	}
  630
++
  631
++      else if (AVMATCH(&code, &av_NetConnection_confStream))
  632
++        {
  633
++#ifdef CRYPTO
  634
++          static const char hexdig[] = "0123456789abcdef";
  635
++          SAVC(cf_stream);
  636
++          int i;
  637
++          char hash_hex[33] = {0};
  638
++          unsigned char hash[16];
  639
++          AVal auth;
  640
++          param_count = strsplit(description.av_val, description.av_len, ':', &params);
  641
++          if (param_count >= 3)
  642
++            {
  643
++              char *buf = malloc(strlen(params[0]) + r->Link.playpath.av_len + 1);
  644
++              strcpy(buf, params[0]);
  645
++              strncat(buf, r->Link.playpath.av_val, r->Link.playpath.av_len);
  646
++              md5_hash((unsigned char *) buf, strlen(buf), hash);
  647
++              for (i = 0; i < 16; i++)
  648
++                {
  649
++                  hash_hex[i * 2] = hexdig[0x0f & (hash[i] >> 4)];
  650
++                  hash_hex[i * 2 + 1] = hexdig[0x0f & (hash[i])];
  651
++                }
  652
++              auth.av_val = &hash_hex[atoi(params[1]) - 1];
  653
++              auth.av_len = atoi(params[2]);
  654
++              RTMP_Log(RTMP_LOGDEBUG, "Khalsa: %.*s", auth.av_len, auth.av_val);
  655
++
  656
++              enc = pbuf;
  657
++              enc = AMF_EncodeString(enc, pend, &av_cf_stream);
  658
++              enc = AMF_EncodeNumber(enc, pend, txn);
  659
++              *enc++ = AMF_NULL;
  660
++              enc = AMF_EncodeString(enc, pend, &auth);
  661
++              av_Command.av_val = pbuf;
  662
++              av_Command.av_len = enc - pbuf;
  663
++
  664
++              SendInvoke(r, &av_Command, FALSE);
  665
++              free(buf);
  666
++            }
  667
++#endif
  668
++        }
  669
+     }
  670
+   else if (AVMATCH(&method, &av_playlist_ready))
  671
+     {
  672
+@@ -2563,6 +2822,74 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
  673
+ 	    }
  674
+         }
  675
+     }
  676
++  else if (AVMATCH(&method, &av_verifyClient))
  677
++    {
  678
++      double VerificationNumber = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
  679
++      RTMP_Log(RTMP_LOGDEBUG, "VerificationNumber: %.2f", VerificationNumber);
  680
++
  681
++      enc = pbuf;
  682
++      enc = AMF_EncodeString(enc, pend, &av__result);
  683
++      enc = AMF_EncodeNumber(enc, pend, txn);
  684
++      *enc++ = AMF_NULL;
  685
++      enc = AMF_EncodeNumber(enc, pend, exp(atan(sqrt(VerificationNumber))) + 1);
  686
++      av_Response.av_val = pbuf;
  687
++      av_Response.av_len = enc - pbuf;
  688
++
  689
++      AMF_Decode(&obj, av_Response.av_val, av_Response.av_len, FALSE);
  690
++      AMF_Dump(&obj);
  691
++      SendInvoke(r, &av_Response, FALSE);
  692
++    }
  693
++  else if (AVMATCH(&method, &av_sendStatus))
  694
++    {
  695
++      if (r->Link.WeebToken.av_len)
  696
++        {
  697
++          AVal av_Authorized = AVC("User.hasAccess");
  698
++          AVal av_TransferLimit = AVC("User.noPremium.limited");
  699
++          AVal av_UserLimit = AVC("User.noPremium.tooManyUsers");
  700
++          AVal av_TimeLeft = AVC("timeLeft");
  701
++          AVal av_Status, av_ReconnectionTime;
  702
++
  703
++          AMFObject Status;
  704
++          AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &Status);
  705
++          AMFProp_GetString(AMF_GetProp(&Status, &av_code, -1), &av_Status);
  706
++          RTMP_Log(RTMP_LOGINFO, "%.*s", av_Status.av_len, av_Status.av_val);
  707
++          if (AVMATCH(&av_Status, &av_Authorized))
  708
++            {
  709
++              RTMP_Log(RTMP_LOGINFO, "Weeb.tv authentication successful");
  710
++              RTMP_SendCreateStream(r);
  711
++            }
  712
++          else if (AVMATCH(&av_Status, &av_UserLimit))
  713
++            {
  714
++              RTMP_Log(RTMP_LOGINFO, "No free slots available");
  715
++              RTMP_Close(r);
  716
++            }
  717
++          else if (AVMATCH(&av_Status, &av_TransferLimit))
  718
++            {
  719
++              AMFProp_GetString(AMF_GetProp(&Status, &av_TimeLeft, -1), &av_ReconnectionTime);
  720
++              RTMP_Log(RTMP_LOGINFO, "Viewing limit exceeded. try again in %.*s minutes.", av_ReconnectionTime.av_len, av_ReconnectionTime.av_val);
  721
++              RTMP_Close(r);
  722
++            }
  723
++        }
  724
++    }
  725
++  else if (AVMATCH(&method, &av_ReceiveCheckPublicStatus))
  726
++    {
  727
++      AVal Status;
  728
++      AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &Status);
  729
++      strsplit(Status.av_val, Status.av_len, ',', &params);
  730
++      if (strcmp(params[0], "0") == 0)
  731
++        {
  732
++          RTMP_Log(RTMP_LOGINFO, "Model status is %s", params[1]);
  733
++          RTMP_Close(r);
  734
++        }
  735
++      else
  736
++        {
  737
++          AVal Playpath;
  738
++          Playpath.av_val = params[1];
  739
++          Playpath.av_len = strlen(params[1]);
  740
++          RTMP_ParsePlaypath(&Playpath, &r->Link.playpath);
  741
++          RTMP_SendCreateStream(r);
  742
++        }
  743
++    }
  744
+   else
  745
+     {
  746
+ 
  747
+@@ -2748,7 +3075,7 @@ HandleCtrl(RTMP *r, const RTMPPacket *packet)
  748
+   unsigned int tmp;
  749
+   if (packet->m_body && packet->m_nBodySize >= 2)
  750
+     nType = AMF_DecodeInt16(packet->m_body);
  751
+-  RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl. type: %d, len: %d", __FUNCTION__, nType,
  752
++  RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl, type: %d, len: %d", __FUNCTION__, nType,
  753
+       packet->m_nBodySize);
  754
+   /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
  755
+ 
  756
+@@ -2856,15 +3183,15 @@ HandleCtrl(RTMP *r, const RTMPPacket *packet)
  757
+       RTMP_Log(RTMP_LOGDEBUG, "%s, SWFVerification ping received: ", __FUNCTION__);
  758
+       if (packet->m_nBodySize > 2 && packet->m_body[2] > 0x01)
  759
+ 	{
  760
+-	  RTMP_Log(RTMP_LOGERROR,
  761
+-            "%s: SWFVerification Type %d request not supported! Patches welcome...",
  762
+-	    __FUNCTION__, packet->m_body[2]);
  763
++          RTMP_Log(RTMP_LOGERROR,
  764
++                   "%s: SWFVerification Type %d request not supported, attempting to use SWFVerification Type 1! Patches welcome...",
  765
++                   __FUNCTION__, packet->m_body[2]);
  766
+ 	}
  767
+ #ifdef CRYPTO
  768
+       /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
  769
+ 
  770
+       /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */
  771
+-      else if (r->Link.SWFSize)
  772
++      if (r->Link.SWFSize)
  773
+ 	{
  774
+ 	  RTMP_SendCtrl(r, 0x1B, 0, 0);
  775
+ 	}
  776
+@@ -3142,8 +3469,18 @@ HandShake(RTMP *r, int FP9HandShake)
  777
+       serversig[4], serversig[5], serversig[6], serversig[7]);
  778
+ 
  779
+   /* 2nd part of handshake */
  780
+-  if (!WriteN(r, serversig, RTMP_SIG_SIZE))
  781
+-    return FALSE;
  782
++  if (r->Link.CombineConnectPacket)
  783
++    {
  784
++      char *HandshakeResponse = malloc(RTMP_SIG_SIZE);
  785
++      memcpy(HandshakeResponse, (char *) serversig, RTMP_SIG_SIZE);
  786
++      r->Link.HandshakeResponse.av_val = HandshakeResponse;
  787
++      r->Link.HandshakeResponse.av_len = RTMP_SIG_SIZE;
  788
++    }
  789
++  else
  790
++    {
  791
++      if (!WriteN(r, (char *) serversig, RTMP_SIG_SIZE))
  792
++        return FALSE;
  793
++    }
  794
+ 
  795
+   if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
  796
+     return FALSE;
  797
+@@ -3709,12 +4046,11 @@ HTTP_Post(RTMP *r, RTMPTCmd cmd, const char *buf, int len)
  798
+   char hbuf[512];
  799
+   int hlen = snprintf(hbuf, sizeof(hbuf), "POST /%s%s/%d HTTP/1.1\r\n"
  800
+     "Host: %.*s:%d\r\n"
  801
+-    "Accept: */*\r\n"
  802
+-    "User-Agent: Shockwave Flash\n"
  803
+-    "Connection: Keep-Alive\n"
  804
++    "User-Agent: Shockwave Flash\r\n"
  805
++    "Connection: Keep-Alive\r\n"
  806
+     "Cache-Control: no-cache\r\n"
  807
+-    "Content-type: application/x-fcs\r\n"
  808
+-    "Content-length: %d\r\n\r\n", RTMPT_cmds[cmd],
  809
++    "Content-Type: application/x-fcs\r\n"
  810
++    "Content-Length: %d\r\n\r\n", RTMPT_cmds[cmd],
  811
+     r->m_clientID.av_val ? r->m_clientID.av_val : "",
  812
+     r->m_msgCounter, r->Link.hostname.av_len, r->Link.hostname.av_val,
  813
+     r->Link.port, len);
  814
+@@ -3749,6 +4085,14 @@ HTTP_read(RTMP *r, int fill)
  815
+   if (!ptr)
  816
+     return -1;
  817
+   ptr += 4;
  818
++  int resplen = r->m_sb.sb_size - (ptr - r->m_sb.sb_start);
  819
++  if (hlen < 4096)
  820
++    while (resplen < hlen)
  821
++      {
  822
++        if (RTMPSockBuf_Fill(&r->m_sb) == -1)
  823
++          return -1;
  824
++        resplen = r->m_sb.sb_size - (ptr - r->m_sb.sb_start);
  825
++      }
  826
+   r->m_sb.sb_size -= ptr - r->m_sb.sb_start;
  827
+   r->m_sb.sb_start = ptr;
  828
+   r->m_unackd--;
  829
+@@ -4301,13 +4645,21 @@ fail:
  830
+ 		  r->m_read.status = nRead;
  831
+ 		  goto fail;
  832
+ 		}
  833
+-	      /* buffer overflow, fix buffer and give up */
  834
+-	      if (r->m_read.buf < mybuf || r->m_read.buf > end) {
  835
+-	      	mybuf = realloc(mybuf, cnt + nRead);
  836
+-		memcpy(mybuf+cnt, r->m_read.buf, nRead);
  837
+-		r->m_read.buf = mybuf+cnt+nRead;
  838
+-	        break;
  839
+-	      }
  840
++              /* buffer overflow, fix buffer and give up */
  841
++              if (r->m_read.buf < mybuf || r->m_read.buf > end)
  842
++                {
  843
++                  if (!cnt)
  844
++                    {
  845
++                      mybuf = realloc(mybuf, sizeof (flvHeader) + cnt + nRead);
  846
++                      memcpy(mybuf, flvHeader, sizeof (flvHeader));
  847
++                      cnt += sizeof (flvHeader);
  848
++                    }
  849
++                  else
  850
++                    mybuf = realloc(mybuf, cnt + nRead);
  851
++                  memcpy(mybuf + cnt, r->m_read.buf, nRead);
  852
++                  r->m_read.buf = mybuf + cnt + nRead;
  853
++                  break;
  854
++                }
  855
+ 	      cnt += nRead;
  856
+ 	      r->m_read.buf += nRead;
  857
+ 	      r->m_read.buflen -= nRead;
  858
+@@ -4458,3 +4810,90 @@ RTMP_Write(RTMP *r, const char *buf, int size)
  859
+     }
  860
+   return size+s2;
  861
+ }
  862
++
  863
++static int
  864
++SendInvoke(RTMP *r, AVal *Command, int queue)
  865
++{
  866
++  RTMPPacket packet;
  867
++  char pbuf[512], *enc;
  868
++
  869
++  packet.m_nChannel = 0x03; /* control channel (invoke) */
  870
++  packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
  871
++  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
  872
++  packet.m_nTimeStamp = 0;
  873
++  packet.m_nInfoField2 = 0;
  874
++  packet.m_hasAbsTimestamp = 0;
  875
++  packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
  876
++
  877
++  enc = packet.m_body;
  878
++  if (Command->av_len)
  879
++    {
  880
++      memcpy(enc, Command->av_val, Command->av_len);
  881
++      enc += Command->av_len;
  882
++    }
  883
++  else
  884
++    return FALSE;
  885
++  packet.m_nBodySize = enc - packet.m_body;
  886
++
  887
++  return RTMP_SendPacket(r, &packet, queue);
  888
++}
  889
++
  890
++static int
  891
++strsplit(char *src, int srclen, char delim, char ***params)
  892
++{
  893
++  char *sptr, *srcbeg, *srcend, *dstr;
  894
++  int count = 1, i = 0, len = 0;
  895
++
  896
++  if (src == NULL)
  897
++    return 0;
  898
++  if (!srclen)
  899
++    srclen = strlen(src);
  900
++  srcbeg = src;
  901
++  srcend = srcbeg + srclen;
  902
++  sptr = srcbeg;
  903
++
  904
++  /* count the delimiters */
  905
++  while (sptr < srcend)
  906
++    {
  907
++      if (*sptr++ == delim)
  908
++        count++;
  909
++    }
  910
++  sptr = srcbeg;
  911
++  *params = calloc(count, sizeof (size_t));
  912
++  char **param = *params;
  913
++
  914
++  for (i = 0; i < (count - 1); i++)
  915
++    {
  916
++      dstr = strchr(sptr, delim);
  917
++      len = dstr - sptr;
  918
++      param[i] = calloc(len + 1, sizeof (char));
  919
++      strncpy(param[i], sptr, len);
  920
++      sptr += len + 1;
  921
++    }
  922
++
  923
++  /* copy the last string */
  924
++  if (sptr <= srcend)
  925
++    {
  926
++      len = srclen - (sptr - srcbeg);
  927
++      param[i] = calloc(len + 1, sizeof (char));
  928
++      strncpy(param[i], sptr, len);
  929
++    }
  930
++  return count;
  931
++}
  932
++
  933
++static int
  934
++SendGetStreamLength(RTMP *r)
  935
++{
  936
++  char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc;
  937
++  AVal av_Command;
  938
++
  939
++  enc = pbuf;
  940
++  enc = AMF_EncodeString(enc, pend, &av_getStreamLength);
  941
++  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
  942
++  *enc++ = AMF_NULL;
  943
++  enc = AMF_EncodeString(enc, pend, &r->Link.playpath);
  944
++  av_Command.av_val = pbuf;
  945
++  av_Command.av_len = enc - pbuf;
  946
++
  947
++  return SendInvoke(r, &av_Command, TRUE);
  948
++}
  949
+diff --git a/librtmp/rtmp.h b/librtmp/rtmp.h
  950
+index 6b2ae5b..411b488 100644
  951
+--- a/librtmp/rtmp.h
  952
++++ b/librtmp/rtmp.h
  953
+@@ -150,12 +150,14 @@ extern "C"
  954
+     AVal playpath;	/* passed in explicitly */
  955
+     AVal tcUrl;
  956
+     AVal swfUrl;
  957
++    AVal swfHash;
  958
+     AVal pageUrl;
  959
+     AVal app;
  960
+     AVal auth;
  961
+     AVal flashVer;
  962
+     AVal subscribepath;
  963
+     AVal usherToken;
  964
++    AVal WeebToken;
  965
+     AVal token;
  966
+     AMFObject extras;
  967
+     int edepth;
  968
+@@ -172,9 +174,15 @@ extern "C"