diff --git Makefile Makefile
index a1595a8..9fe7584 100644
--- Makefile
+++ Makefile
@@ -32,7 +32,7 @@ BINDIR=$(DESTDIR)$(bindir)
 SBINDIR=$(DESTDIR)$(sbindir)
 MANDIR=$(DESTDIR)$(mandir)
 
-LIBS_posix=
+LIBS_posix=-lm
 LIBS_darwin=
 LIBS_mingw=-lws2_32 -lwinmm -lgdi32
 LIB_RTMP=-Llibrtmp -lrtmp
diff --git librtmp/Makefile librtmp/Makefile
index 2c1c790..e367535 100644
--- librtmp/Makefile
+++ librtmp/Makefile
@@ -26,7 +26,7 @@ REQ_GNUTLS=gnutls,hogweed,nettle
 REQ_OPENSSL=libssl,libcrypto
 PUB_GNUTLS=-lgmp
 LIBZ=-lz
-LIBS_posix=
+LIBS_posix=-lm
 LIBS_darwin=
 LIBS_mingw=-lws2_32 -lwinmm -lgdi32
 LIB_GNUTLS=-lgnutls -lhogweed -lnettle -lgmp $(LIBZ)
diff --git librtmp/amf.c librtmp/amf.c
index 1c5f99f..1310cbe 100644
--- librtmp/amf.c
+++ librtmp/amf.c
@@ -319,6 +319,13 @@ AMFProp_SetName(AMFObjectProperty *prop, AVal *name)
   prop->p_name = *name;
 }
 
+void
+AMFProp_SetString(AMFObjectProperty *prop, AVal *str)
+{
+  prop->p_type = AMF_STRING;
+  prop->p_vu.p_aval = *str;
+}
+
 AMFDataType
 AMFProp_GetType(AMFObjectProperty *prop)
 {
@@ -503,6 +510,9 @@ AMF3Prop_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
       return -1;
     }
 
+  if (*pBuffer == AMF3_NULL)
+    bDecodeName = FALSE;
+
   /* decode name */
   if (bDecodeName)
     {
@@ -586,7 +596,7 @@ AMF3Prop_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
       }
     case AMF3_OBJECT:
       {
-	int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, TRUE);
+        int nRes = AMF3_Decode(&prop->p_vu.p_object, pBuffer, nSize, FALSE);
 	if (nRes == -1)
 	  return -1;
 	nSize -= nRes;
@@ -620,6 +630,9 @@ AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
       return -1;
     }
 
+  if (*pBuffer == AMF_NULL)
+    bDecodeName = FALSE;
+
   if (bDecodeName && nSize < 4)
     {				/* at least name (length + at least 1 byte) and 1 byte of data */
       RTMP_Log(RTMP_LOGDEBUG,
@@ -649,9 +662,8 @@ AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
       return -1;
     }
 
-  nSize--;
-
   prop->p_type = *pBuffer++;
+  nSize--;
   switch (prop->p_type)
     {
     case AMF_NUMBER:
@@ -697,9 +709,13 @@ AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
       break;
     case AMF_REFERENCE:
       {
-	RTMP_Log(RTMP_LOGERROR, "AMF_REFERENCE not supported!");
-	return -1;
-	break;
+        RTMP_Log(RTMP_LOGDEBUG, "AMF_REFERENCE is not fully supported!");
+        if (nSize < 2)
+          return -1;
+        prop->p_type = AMF_NUMBER;
+        prop->p_vu.p_number = AMF_DecodeInt16(pBuffer);
+        nSize -= 2;
+        break;
       }
     case AMF_ECMA_ARRAY:
       {
@@ -731,13 +747,13 @@ AMFProp_Decode(AMFObjectProperty *prop, const char *pBuffer, int nSize,
       }
     case AMF_DATE:
       {
-	RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE");
-
 	if (nSize < 10)
 	  return -1;
 
 	prop->p_vu.p_number = AMF_DecodeNumber(pBuffer);
 	prop->p_UTCoffset = AMF_DecodeInt16(pBuffer + 8);
+        RTMP_Log(RTMP_LOGDEBUG, "AMF_DATE: %f, UTC offset: %d", prop->p_vu.p_number,
+                 prop->p_UTCoffset);
 
 	nSize -= 10;
 	break;
@@ -809,8 +825,8 @@ AMFProp_Dump(AMFObjectProperty *prop)
     }
   else
     {
-      name.av_val = "no-name.";
-      name.av_len = sizeof("no-name.") - 1;
+      name.av_val = "no-name";
+      name.av_len = sizeof ("no-name") - 1;
     }
   if (name.av_len > 18)
     name.av_len = 18;
@@ -1021,11 +1037,18 @@ AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bAMFData)
   obj->o_props = NULL;
   if (bAMFData)
     {
-      if (*pBuffer != AMF3_OBJECT)
-	RTMP_Log(RTMP_LOGERROR,
-	    "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!");
-      pBuffer++;
-      nSize--;
+      // Decode only if it's an AMF3 object
+      if (*pBuffer == AMF3_OBJECT)
+        {
+          pBuffer++;
+          nSize--;
+        }
+      else
+        {
+          RTMP_Log(RTMP_LOGERROR, "AMF3 Object encapsulated in AMF stream does not start with AMF3_OBJECT!");
+          pBuffer += nOriginalSize;
+          return nOriginalSize;
+        }
     }
 
   ref = 0;
@@ -1043,8 +1066,12 @@ AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bAMFData)
     {
       int32_t classRef = (ref >> 1);
 
-      AMF3ClassDef cd = { {0, 0}
-      };
+      AMF3ClassDef cd;
+      cd.cd_name.av_len = 0;
+      cd.cd_name.av_val = 0;
+      cd.cd_externalizable = FALSE;
+      cd.cd_dynamic = TRUE;
+      cd.cd_num = 0;
       AMFObjectProperty prop;
 
       if ((classRef & 0x1) == 0)
@@ -1061,6 +1088,7 @@ AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bAMFData)
 	  cd.cd_dynamic = ((classExtRef >> 1) & 0x1) == 1;
 
 	  cdnum = classExtRef >> 2;
+          cd.cd_num = cdnum;
 
 	  /* class name */
 
@@ -1070,24 +1098,25 @@ AMF3_Decode(AMFObject *obj, const char *pBuffer, int nSize, int bAMFData)
 
 	  /*std::string str = className; */
 
-	  RTMP_Log(RTMP_LOGDEBUG,
-	      "Class name: %s, externalizable: %d, dynamic: %d, classMembers: %d",
-	      cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic,
-	      cd.cd_num);
+          RTMP_Log(RTMP_LOGDEBUG, "Class name: %.*s, externalizable: %d, dynamic: %d, classMembers: %d",
+                   cd.cd_name.av_len, cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic, cd.cd_num);
 
 	  for (i = 0; i < cdnum; i++)
-	    {
-	      AVal memberName;
-	      if (nSize <=0)
+            {
+              AVal memberName = {NULL, 0};
+              if (nSize <= 0)
 		{
 invalid:
 		  RTMP_Log(RTMP_LOGDEBUG, "%s, invalid class encoding!",
 		    __FUNCTION__);
 		  return nOriginalSize;
-		}
-	      len = AMF3ReadString(pBuffer, &memberName);
-	      RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val);
-	      AMF3CD_AddProp(&cd, &memberName);
+                }
+              len = AMF3ReadString(pBuffer, &memberName);
+              if (memberName.av_val)
+                {
+                  RTMP_Log(RTMP_LOGDEBUG, "Member: %.*s", memberName.av_len, memberName.av_val);
+                  AMF3CD_AddProp(&cd, &memberName);
+                }
 	      nSize -= len;
 	      pBuffer += len;
 	    }
@@ -1118,10 +1147,10 @@ invalid:
       else
 	{
 	  int nRes, i;
-	  for (i = 0; i < cd.cd_num; i++)	/* non-dynamic */
-	    {
-	      if (nSize <=0)
-	        goto invalid;
+          for (i = 0; i < cd.cd_num; i++) /* non-dynamic */
+            {
+              if (nSize <= 0)
+                goto invalid;
 	      nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
 	      if (nRes == -1)
 		RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
@@ -1138,9 +1167,9 @@ invalid:
 	      int len = 0;
 
 	      do
-		{
-		  if (nSize <=0)
-		    goto invalid;
+                {
+                  if (nSize <= 0)
+                    goto invalid;
 		  nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, TRUE);
 		  AMF_AddProp(obj, &prop);
 
@@ -1154,7 +1183,15 @@ invalid:
 	}
       RTMP_Log(RTMP_LOGDEBUG, "class object!");
     }
-  return nOriginalSize - nSize;
+
+  /**
+   * In case of switch to AMF3 serialization consume rest of the unprocessed
+   * packet data to make sure it's not later processed as AMF0 data.
+   */
+  if (bAMFData)
+    return nOriginalSize;
+  else
+    return nOriginalSize - nSize;
 }
 
 int
@@ -1272,7 +1309,8 @@ AMF3CD_AddProp(AMF3ClassDef *cd, AVal *prop)
 {
   if (!(cd->cd_num & 0x0f))
     cd->cd_props = realloc(cd->cd_props, (cd->cd_num + 16) * sizeof(AVal));
-  cd->cd_props[cd->cd_num++] = *prop;
+  if (cd->cd_props)
+    cd->cd_props[cd->cd_num++] = *prop;
 }
 
 AVal *
diff --git librtmp/handshake.h librtmp/handshake.h
index 0438486..104af28 100644
--- librtmp/handshake.h
+++ librtmp/handshake.h
@@ -707,7 +707,7 @@ HandShake(RTMP * r, int FP9HandShake)
   uint32_t uptime;
 
   uint8_t clientbuf[RTMP_SIG_SIZE + 4], *clientsig=clientbuf+4;
-  uint8_t serversig[RTMP_SIG_SIZE], client2[RTMP_SIG_SIZE], *reply;
+  uint8_t serversig[RTMP_SIG_SIZE], serversig1[RTMP_SIG_SIZE], client2[RTMP_SIG_SIZE], *reply;
   uint8_t type;
   getoff *getdh = NULL, *getdig = NULL;
 
@@ -760,7 +760,7 @@ HandShake(RTMP * r, int FP9HandShake)
 #else
   ip = (int32_t *)(clientsig+8);
   for (i = 2; i < RTMP_SIG_SIZE/4; i++)
-    *ip++ = rand();
+    *ip++ = ((rand() & 0xFFFF) << 16) | (rand() & 0xFFFF);
 #endif
 
   /* set handshake digest */
@@ -825,6 +825,8 @@ HandShake(RTMP * r, int FP9HandShake)
 
   if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
     return FALSE;
+  if (ReadN(r, (char *) serversig1, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
+    return FALSE;
 
   /* decode server response */
   memcpy(&uptime, serversig, 4);
@@ -834,7 +836,7 @@ HandShake(RTMP * r, int FP9HandShake)
   RTMP_Log(RTMP_LOGDEBUG, "%s: FMS Version   : %d.%d.%d.%d", __FUNCTION__, serversig[4],
       serversig[5], serversig[6], serversig[7]);
 
-  if (FP9HandShake && type == 3 && !serversig[4])
+  if (FP9HandShake && type == 3 && (!serversig[4] || !serversig1[4]))
     FP9HandShake = FALSE;
 
 #ifdef _DEBUG
@@ -914,7 +916,7 @@ HandShake(RTMP * r, int FP9HandShake)
 #else
       ip = (int32_t *)reply;
       for (i = 0; i < RTMP_SIG_SIZE/4; i++)
-        *ip++ = rand();
+        *ip++ = ((rand() & 0xFFFF) << 16) | (rand() & 0xFFFF);
 #endif
       /* calculate response now */
       signatureResp = reply+RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH;
@@ -965,16 +967,22 @@ HandShake(RTMP * r, int FP9HandShake)
     __FUNCTION__);
   RTMP_LogHex(RTMP_LOGDEBUG, reply, RTMP_SIG_SIZE);
 #endif
-  if (!WriteN(r, (char *)reply, RTMP_SIG_SIZE))
-    return FALSE;
-
-  /* 2nd part of handshake */
-  if (ReadN(r, (char *)serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
-    return FALSE;
+  if (r->Link.CombineConnectPacket)
+    {
+      char *HandshakeResponse = malloc(RTMP_SIG_SIZE);
+      memcpy(HandshakeResponse, (char *) reply, RTMP_SIG_SIZE);
+      r->Link.HandshakeResponse.av_val = HandshakeResponse;
+      r->Link.HandshakeResponse.av_len = RTMP_SIG_SIZE;
+    }
+  else
+    {
+      if (!WriteN(r, (char *) reply, RTMP_SIG_SIZE))
+        return FALSE;
+    }
 
 #ifdef _DEBUG
   RTMP_Log(RTMP_LOGDEBUG, "%s: 2nd handshake: ", __FUNCTION__);
-  RTMP_LogHex(RTMP_LOGDEBUG, serversig, RTMP_SIG_SIZE);
+  RTMP_LogHex(RTMP_LOGDEBUG, serversig1, RTMP_SIG_SIZE);
 #endif
 
   if (FP9HandShake)
@@ -982,21 +990,21 @@ HandShake(RTMP * r, int FP9HandShake)
       uint8_t signature[SHA256_DIGEST_LENGTH];
       uint8_t digest[SHA256_DIGEST_LENGTH];
 
-      if (serversig[4] == 0 && serversig[5] == 0 && serversig[6] == 0
-	  && serversig[7] == 0)
+      if (serversig1[4] == 0 && serversig1[5] == 0 && serversig1[6] == 0
+	  && serversig1[7] == 0)
 	{
 	  RTMP_Log(RTMP_LOGDEBUG,
 	      "%s: Wait, did the server just refuse signed authentication?",
 	      __FUNCTION__);
 	}
       RTMP_Log(RTMP_LOGDEBUG, "%s: Server sent signature:", __FUNCTION__);
-      RTMP_LogHex(RTMP_LOGDEBUG, &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH],
+      RTMP_LogHex(RTMP_LOGDEBUG, &serversig1[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH],
 	     SHA256_DIGEST_LENGTH);
 
       /* verify server response */
       HMACsha256(&clientsig[digestPosClient], SHA256_DIGEST_LENGTH,
 		 GenuineFMSKey, sizeof(GenuineFMSKey), digest);
-      HMACsha256(serversig, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digest,
+      HMACsha256(serversig1, RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH, digest,
 		 SHA256_DIGEST_LENGTH, signature);
 
       /* show some information */
@@ -1024,7 +1032,7 @@ HandShake(RTMP * r, int FP9HandShake)
       RTMP_Log(RTMP_LOGDEBUG, "%s: Signature calculated:", __FUNCTION__);
       RTMP_LogHex(RTMP_LOGDEBUG, signature, SHA256_DIGEST_LENGTH);
       if (memcmp
-	  (signature, &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH],
+	  (signature, &serversig1[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH],
 	   SHA256_DIGEST_LENGTH) != 0)
 	{
 	  RTMP_Log(RTMP_LOGWARNING, "%s: Server not genuine Adobe!", __FUNCTION__);
@@ -1057,7 +1065,7 @@ HandShake(RTMP * r, int FP9HandShake)
     }
   else
     {
-      if (memcmp(serversig, clientsig, RTMP_SIG_SIZE) != 0)
+      if (memcmp(serversig1, clientsig, RTMP_SIG_SIZE) != 0)
 	{
 	  RTMP_Log(RTMP_LOGWARNING, "%s: client signature does not match!",
 	      __FUNCTION__);
@@ -1099,7 +1107,7 @@ SHandShake(RTMP * r)
     {
       encrypted = FALSE;
     }
-  else if (type == 6 || type == 8)
+  else if (type == 6 || type == 8 || type == 9)
     {
       offalg = 1;
       encrypted = TRUE;
@@ -1148,7 +1156,7 @@ SHandShake(RTMP * r)
 #else
   ip = (int32_t *)(serversig+8);
   for (i = 2; i < RTMP_SIG_SIZE/4; i++)
-    *ip++ = rand();
+    *ip++ = ((rand() & 0xFFFF) << 16) | (rand() & 0xFFFF);
 #endif
 
   /* set handshake digest */
diff --git librtmp/hashswf.c librtmp/hashswf.c
index 9f4e2c0..01b97e2 100644
--- librtmp/hashswf.c
+++ librtmp/hashswf.c
@@ -70,7 +70,7 @@ extern TLS_CTX RTMP_TLS_ctx;
 
 #endif /* CRYPTO */
 
-#define	AGENT	"Mozilla/5.0"
+#define	AGENT	"Mozilla/5.0 (Windows NT 5.1; rv:21.0) Gecko/20100101 Firefox/21.0"
 
 HTTPResult
 HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
@@ -116,6 +116,8 @@ HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
 
   host = p1 + 3;
   path = strchr(host, '/');
+  if (!path)
+    return HTTPRES_BAD_REQUEST;
   hlen = path - host;
   strncpy(hbuf, host, hlen);
   hbuf[hlen] = '\0';
@@ -200,7 +202,7 @@ HTTP_get(struct HTTP_ctx *http, const char *url, HTTP_read_callback *cb)
     }
 
   p1 = strchr(sb.sb_buf, ' ');
-  rc = atoi(p1 + 1);
+  rc = p1 ? atoi(p1 + 1) : 400;
   http->status = rc;
 
   if (rc >= 300)
@@ -379,13 +381,13 @@ make_unix_time(char *s)
   if (fmt)
     {
       /* Day, DD-MMM-YYYY HH:MM:SS GMT */
-      time.tm_mday = strtol(n + 1, &n, 0);
+      time.tm_mday = strtol(n + 1, &n, 10);
       month = n + 1;
       n = strchr(month, ' ');
-      time.tm_year = strtol(n + 1, &n, 0);
-      time.tm_hour = strtol(n + 1, &n, 0);
-      time.tm_min = strtol(n + 1, &n, 0);
-      time.tm_sec = strtol(n + 1, NULL, 0);
+      time.tm_year = strtol(n + 1, &n, 10);
+      time.tm_hour = strtol(n + 1, &n, 10);
+      time.tm_min = strtol(n + 1, &n, 10);
+      time.tm_sec = strtol(n + 1, NULL, 10);
     }
   else
     {
@@ -395,11 +397,11 @@ make_unix_time(char *s)
       n = strchr(month, ' ');
       while (isspace(*n))
 	n++;
-      time.tm_mday = strtol(n, &n, 0);
-      time.tm_hour = strtol(n + 1, &n, 0);
-      time.tm_min = strtol(n + 1, &n, 0);
-      time.tm_sec = strtol(n + 1, &n, 0);
-      time.tm_year = strtol(n + 1, NULL, 0);
+      time.tm_mday = strtol(n, &n, 10);
+      time.tm_hour = strtol(n + 1, &n, 10);
+      time.tm_min = strtol(n + 1, &n, 10);
+      time.tm_sec = strtol(n + 1, &n, 10);
+      time.tm_year = strtol(n + 1, NULL, 10);
     }
   if (time.tm_year > 100)
     time.tm_year -= ysub;
@@ -528,9 +530,11 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
 
 	  if (strncmp(buf, "url: ", 5))
 	    continue;
-	  if (strncmp(buf + 5, url, hlen))
+	  if (strncmp(buf + 5, url, strlen(buf + 5) - 1))
 	    continue;
 	  r1 = strrchr(buf, '/');
+          if (!r1)
+            continue;
 	  i = strlen(r1);
 	  r1[--i] = '\0';
 	  if (strncmp(r1, file, i))
@@ -640,7 +644,7 @@ RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
 	  HMAC_finish(in.ctx, hash, hlen);
 	  *size = in.size;
 
-	  fprintf(f, "date: %s\n", date);
+          fprintf(f, "date: %s\n", date[0] ? date : cctim);
 	  fprintf(f, "size: %08x\n", in.size);
 	  fprintf(f, "hash: ");
 	  for (i = 0; i < SHA256_DIGEST_LENGTH; i++)
diff --git librtmp/log.c librtmp/log.c
index 1b52000..7564a15 100644
--- librtmp/log.c
+++ librtmp/log.c
@@ -52,8 +52,8 @@ static void rtmp_log_default(int level, const char *format, va_list vl)
 	vsnprintf(str, MAX_PRINT_LEN-1, format, vl);
 
 	/* Filter out 'no-name' */
-	if ( RTMP_debuglevel<RTMP_LOGALL && strstr(str, "no-name" ) != NULL )
-		return;
+	if (RTMP_debuglevel < RTMP_LOGDEBUG && strstr(str, "no-name") != NULL)
+	  return;
 
 	if ( !fmsg ) fmsg = stderr;
 
diff --git librtmp/parseurl.c librtmp/parseurl.c
index 646c70c..a0a83e6 100644
--- librtmp/parseurl.c
+++ librtmp/parseurl.c
@@ -34,6 +34,7 @@ int RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port
 	AVal *playpath, AVal *app)
 {
 	char *p, *end, *col, *ques, *slash;
+	int doubleSlash = FALSE;
 
 	RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
 
@@ -140,11 +141,19 @@ parsehost:
 	char *slash2, *slash3 = NULL, *slash4 = NULL;
 	int applen, appnamelen;
 
-	slash2 = strchr(p, '/');
-	if(slash2)
-		slash3 = strchr(slash2+1, '/');
-	if(slash3)
-		slash4 = strchr(slash3+1, '/');
+    if ((slash2 = strstr(p, "//")))
+      {
+        doubleSlash = TRUE;
+        slash2 += 1;
+      }
+    else
+      {
+        slash2 = strchr(p, '/');
+        if (slash2)
+          slash3 = strchr(slash2 + 1, '/');
+        if (slash3)
+          slash4 = strchr(slash3 + 1, '/');
+      }
 
 	applen = end-p; /* ondemand, pass all parameters as app */
 	appnamelen = applen; /* ondemand length */
@@ -168,6 +177,8 @@ parsehost:
 		applen = appnamelen;
 	}
 
+	if (doubleSlash)
+	  applen -= 1;
 	app->av_val = p;
 	app->av_len = applen;
 	RTMP_Log(RTMP_LOGDEBUG, "Parsed app     : %.*s", applen, p);
diff --git librtmp/rtmp.c librtmp/rtmp.c
index ca7db6a..c652cff 100644
--- librtmp/rtmp.c
+++ librtmp/rtmp.c
@@ -28,6 +28,7 @@
 #include <string.h>
 #include <assert.h>
 #include <time.h>
+#include <math.h>
 
 #include "rtmp_sys.h"
 #include "log.h"
@@ -68,6 +69,7 @@ TLS_CTX RTMP_TLS_ctx;
 
 #define RTMP_SIG_SIZE 1536
 #define RTMP_LARGE_HEADER_SIZE 12
+#define HEX2BIN(a) (((a)&0x40)?((a)&0xf)+9:((a)&0xf))
 
 static const int packetSize[] = { 12, 8, 4, 1 };
 
@@ -108,18 +110,25 @@ typedef enum {
   RTMPT_OPEN=0, RTMPT_SEND, RTMPT_IDLE, RTMPT_CLOSE
 } RTMPTCmd;
 
+static int ConnectSocket(RTMP *r);
 static int DumpMetaData(AMFObject *obj);
 static int HandShake(RTMP *r, int FP9HandShake);
 static int SocksNegotiate(RTMP *r);
 
+static int SendBytesReceived(RTMP *r);
+static int SendCommand(RTMP *r, char *method, int queue);
 static int SendConnectPacket(RTMP *r, RTMPPacket *cp);
 static int SendCheckBW(RTMP *r);
 static int SendCheckBWResult(RTMP *r, double txn);
 static int SendDeleteStream(RTMP *r, double dStreamId);
 static int SendFCSubscribe(RTMP *r, AVal *subscribepath);
+static int SendGetStreamLength(RTMP *r);
+static int SendInvoke(RTMP *r, AVal *command, int queue);
 static int SendPlay(RTMP *r);
-static int SendBytesReceived(RTMP *r);
 static int SendUsherToken(RTMP *r, AVal *usherToken);
+static void TransformRot13(AMFObject *obj, AVal *rindex, AVal *r);
+static void __TeaCrypt(uint32_t *block, uint32_t len, uint32_t *key);
+static AVal TeaEncrypt(AVal *srcData, AVal *srcKey);
 
 #if 0				/* unused */
 static int SendBGHasStream(RTMP *r, double dId, AVal *playpath);
@@ -338,10 +347,15 @@ RTMP_Init(RTMP *r)
   r->m_nClientBW = 2500000;
   r->m_nClientBW2 = 2;
   r->m_nServerBW = 2500000;
-  r->m_fAudioCodecs = 3191.0;
+  r->m_fAudioCodecs = 3575.0;
   r->m_fVideoCodecs = 252.0;
+  r->m_fEncoding = 3.0;
   r->Link.timeout = 30;
   r->Link.swfAge = 30;
+  r->Link.CombineConnectPacket = TRUE;
+  r->Link.ConnectPacket = FALSE;
+  r->Link.publishId = 0;
+  r->Link.dynamicPublish = FALSE;
 }
 
 void
@@ -359,6 +373,8 @@ RTMP_GetDuration(RTMP *r)
 int
 RTMP_IsConnected(RTMP *r)
 {
+  if (r->m_sb.sb_size > 0)
+    return TRUE;
   return r->m_sb.sb_socket != -1;
 }
 
@@ -445,6 +461,8 @@ RTMP_SetupStream(RTMP *r,
 		 AVal *flashVer,
 		 AVal *subscribepath,
 		 AVal *usherToken,
+		 AVal *WeebToken,
+		 AVal *ccomm,
 		 int dStart,
 		 int dStop, int bLiveStream, long int timeout)
 {
@@ -467,6 +485,8 @@ RTMP_SetupStream(RTMP *r,
     RTMP_Log(RTMP_LOGDEBUG, "subscribepath : %s", subscribepath->av_val);
   if (usherToken && usherToken->av_val)
     RTMP_Log(RTMP_LOGDEBUG, "NetStream.Authenticate.UsherToken : %s", usherToken->av_val);
+  if (WeebToken && WeebToken->av_val)
+    RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", WeebToken->av_val);
   if (flashVer && flashVer->av_val)
     RTMP_Log(RTMP_LOGDEBUG, "flashVer : %s", flashVer->av_val);
   if (dStart > 0)
@@ -515,6 +535,10 @@ RTMP_SetupStream(RTMP *r,
     r->Link.subscribepath = *subscribepath;
   if (usherToken && usherToken->av_len)
     r->Link.usherToken = *usherToken;
+  if (WeebToken && WeebToken->av_len)
+    r->Link.WeebToken = *WeebToken;
+  if (ccomm && ccomm->av_len)
+    r->Link.ccomm = *ccomm;
   r->Link.seekTime = dStart;
   r->Link.stopTime = dStop;
   if (bLiveStream)
@@ -572,14 +596,24 @@ static struct urlopt {
   	"Stream is live, no seeking possible" },
   { AVC("subscribe"), OFF(Link.subscribepath), OPT_STR, 0,
   	"Stream to subscribe to" },
-  { AVC("jtv"), OFF(Link.usherToken),          OPT_STR, 0,
-	"Justin.tv authentication token" },
-  { AVC("token"),     OFF(Link.token),	       OPT_STR, 0,
+  { AVC("jtv"),       OFF(Link.usherToken),    OPT_STR, 0,
+        "Justin.tv authentication token"},
+  { AVC("weeb"),      OFF(Link.WeebToken),     OPT_STR, 0,
+        "Weeb.tv authentication token"},
+  { AVC("token"),     OFF(Link.token),         OPT_STR, 0,
   	"Key for SecureToken response" },
+  { AVC("ccommand"),  OFF(Link.ccomm),         OPT_STR, 0,
+  	"Send custom command before play" },
   { AVC("swfVfy"),    OFF(Link.lFlags),        OPT_BOOL, RTMP_LF_SWFV,
   	"Perform SWF Verification" },
   { AVC("swfAge"),    OFF(Link.swfAge),        OPT_INT, 0,
   	"Number of days to use cached SWF hash" },
+#ifdef CRYPTO
+  { AVC("swfsize"),   OFF(Link.swfSize),       OPT_INT, 0,
+        "Size of the decompressed SWF file"},
+  { AVC("swfhash"),   OFF(Link.swfHash),       OPT_STR, 0,
+        "SHA256 hash of the decompressed SWF file"},
+#endif
   { AVC("start"),     OFF(Link.seekTime),      OPT_INT, 0,
   	"Stream start position in milliseconds" },
   { AVC("stop"),      OFF(Link.stopTime),      OPT_INT, 0,
@@ -685,6 +719,9 @@ parseAMF(AMFObject *obj, AVal *av, int *depth)
 	case 'O':
 	  prop.p_type = AMF_OBJECT;
 	  break;
+        case 'Z':
+          prop.p_type = AMF_NULL;
+          break;
 	default:
 	  return -1;
 	}
@@ -722,7 +759,7 @@ int RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg)
       *aptr = *arg; }
       break;
     case OPT_INT: {
-      long l = strtol(arg->av_val, NULL, 0);
+      long l = strtol(arg->av_val, NULL, 10);
       *(int *)v = l; }
       break;
     case OPT_BOOL: {
@@ -767,7 +804,7 @@ int RTMP_SetupURL(RTMP *r, char *url)
   if (!ret)
     return ret;
   r->Link.port = port;
-  r->Link.playpath = r->Link.playpath0;
+  r->Link.playpath = AVcopy(r->Link.playpath0);
 
   while (ptr) {
     *ptr++ = '\0';
@@ -844,9 +881,16 @@ int RTMP_SetupURL(RTMP *r, char *url)
     }
 
 #ifdef CRYPTO
-  if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len)
-    RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize,
-	  (unsigned char *)r->Link.SWFHash, r->Link.swfAge);
+  RTMP_Log(RTMP_LOGDEBUG, "Khalsa: %d %d %s", r->Link.swfSize, r->Link.swfHash.av_len, r->Link.swfHash.av_val);
+  if (r->Link.swfSize && r->Link.swfHash.av_len)
+    {
+      int i, j = 0;
+      for (i = 0; i < r->Link.swfHash.av_len; i += 2)
+        r->Link.SWFHash[j++] = (HEX2BIN(r->Link.swfHash.av_val[i]) << 4) | HEX2BIN(r->Link.swfHash.av_val[i + 1]);
+      r->Link.SWFSize = (uint32_t) r->Link.swfSize;
+    }
+  else if ((r->Link.lFlags & RTMP_LF_SWFV) && r->Link.swfUrl.av_len)
+    RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize, (unsigned char *) r->Link.SWFHash, r->Link.swfAge);
 #endif
 
   SocksSetup(r, &r->Link.sockshost);
@@ -949,6 +993,8 @@ RTMP_Connect0(RTMP *r, struct sockaddr * service)
   }
 
   setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof(on));
+  if (r->Link.protocol & RTMP_FEATURE_HTTP)
+    setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof (on));
 
   return TRUE;
 }
@@ -1399,41 +1445,96 @@ ReadN(RTMP *r, char *buffer, int n)
   ptr = buffer;
   while (n > 0)
     {
-      int nBytes = 0, nRead;
+      int nBytes = 0, nRead, status = 0, retries = 0;
       if (r->Link.protocol & RTMP_FEATURE_HTTP)
         {
-	  int refill = 0;
-	  while (!r->m_resplen)
-	    {
-	      int ret;
-	      if (r->m_sb.sb_size < 13 || refill)
-	        {
-		  if (!r->m_unackd)
-		    HTTP_Post(r, RTMPT_IDLE, "", 1);
-		  if (RTMPSockBuf_Fill(&r->m_sb) < 1)
-		    {
-		      if (!r->m_sb.sb_timedout)
-		        RTMP_Close(r);
-		      return 0;
-		    }
-		}
-	      if ((ret = HTTP_read(r, 0)) == -1)
-		{
-		  RTMP_Log(RTMP_LOGDEBUG, "%s, No valid HTTP response found", __FUNCTION__);
-		  RTMP_Close(r);
-		  return 0;
-		}
-              else if (ret == -2)
+          while (!r->m_resplen)
+            {
+              /* Refill if socket buffer is empty */
+              if (!r->m_sb.sb_size)
                 {
-                  refill = 1;
+                  if (retries > 30)
+                    {
+                      RTMP_Close(r);
+                      return 0;
+                    }
+
+                  if (!r->m_unackd)
+                    {
+                      if (retries > 0)
+                        {
+                          HTTP_Post(r, RTMPT_IDLE, "", 1);
+                          r->m_unackd = TRUE;
+                        }
+                      retries++;
+
+                      if (!r->m_bPlaying)
+                        sleep(.25);
+                    }
+
+                  RTMP_Log(RTMP_LOGDEBUG, "Trying to fill HTTP buffer, Retries: %d", retries);
+                  status = RTMPSockBuf_Fill(&r->m_sb);
+                  /* Reconnect socket when closed by some moronic servers after
+                   * every HTTP data packet */
+                  if (status < 1)
+                    {
+                      /* Close connection on connection reset */
+                      if (status == -1)
+                        {
+                          RTMP_Close(r);
+                          return 0;
+                        }
+
+                      RTMP_Log(RTMP_LOGDEBUG, "Reconnecting socket, Status: %d", status);
+                      if (ConnectSocket(r))
+                        {
+                          HTTP_Post(r, RTMPT_IDLE, "", 1);
+                          r->m_unackd = TRUE;
+                          retries++;
+                        }
+                      else
+                        {
+                          RTMP_Close(r);
+                          return 0;
+                        }
+                    }
                 }
-              else
+
+              RTMP_Log(RTMP_LOGDEBUG, "Trying to read HTTP response, Bytes Available: %d", r->m_sb.sb_size);
+              status = HTTP_read(r, 0);
+              if (status == -1)
                 {
-                  refill = 0;
+                  RTMP_Log(RTMP_LOGDEBUG, "%s, No valid HTTP response found", __FUNCTION__);
+                  RTMP_Close(r);
+                  return 0;
                 }
-	    }
-	  if (r->m_resplen && !r->m_sb.sb_size)
-	    RTMPSockBuf_Fill(&r->m_sb);
+              else if (status == -2)
+                {
+                  if (RTMPSockBuf_Fill(&r->m_sb) < 1)
+                    if (!r->m_sb.sb_timedout)
+                      {
+                        RTMP_Close(r);
+                        return 0;
+                      }
+                }
+              else if (status == -3)
+                {
+                  RTMP_Close(r);
+                  return 0;
+                }
+              else
+                r->m_unackd = FALSE;
+            }
+
+          /* Refill when there is still some data to be read and socket buffer
+           * is empty */
+          if (r->m_resplen && (!r->m_sb.sb_size))
+            {
+              if (RTMPSockBuf_Fill(&r->m_sb) < 1)
+                if (!r->m_sb.sb_timedout)
+                  RTMP_Close(r);
+            }
+
           avail = r->m_sb.sb_size;
 	  if (avail > r->m_resplen)
 	    avail = r->m_resplen;
@@ -1460,10 +1561,11 @@ ReadN(RTMP *r, char *buffer, int n)
 	  r->m_sb.sb_size -= nRead;
 	  nBytes = nRead;
 	  r->m_nBytesIn += nRead;
-	  if (r->m_bSendCounter
-	      && r->m_nBytesIn > ( r->m_nBytesInSent + r->m_nClientBW / 10))
-	    if (!SendBytesReceived(r))
-	        return FALSE;
+          if (r->m_nBytesIn > 0xF0000000)
+            r->m_nBytesIn -= 0xF0000000;
+          if (r->m_bSendCounter && (r->m_nBytesIn > (r->m_nBytesInSent + r->m_nClientBW / 10)))
+            if (!SendBytesReceived(r))
+              return FALSE;
 	}
       /*RTMP_Log(RTMP_LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes); */
 #ifdef _DEBUG
@@ -1474,7 +1576,8 @@ ReadN(RTMP *r, char *buffer, int n)
 	{
 	  RTMP_Log(RTMP_LOGDEBUG, "%s, RTMP socket closed by peer", __FUNCTION__);
 	  /*goto again; */
-	  RTMP_Close(r);
+          if (!r->m_sb.sb_timedout)
+            RTMP_Close(r);
 	  break;
 	}
 
@@ -1499,6 +1602,7 @@ static int
 WriteN(RTMP *r, const char *buffer, int n)
 {
   const char *ptr = buffer;
+  char *ConnectPacket = 0;
 #ifdef CRYPTO
   char *encrypted = 0;
   char buf[RTMP_BUFFER_CACHE_SIZE];
@@ -1514,6 +1618,15 @@ WriteN(RTMP *r, const char *buffer, int n)
     }
 #endif
 
+  if (r->Link.ConnectPacket)
+    {
+      char *ConnectPacket = malloc(r->Link.HandshakeResponse.av_len + n);
+      memcpy(ConnectPacket, r->Link.HandshakeResponse.av_val, r->Link.HandshakeResponse.av_len);
+      memcpy(ConnectPacket + r->Link.HandshakeResponse.av_len, ptr, n);
+      ptr = ConnectPacket;
+      n += r->Link.HandshakeResponse.av_len;
+    }
+
   while (n > 0)
     {
       int nBytes;
@@ -1550,6 +1663,14 @@ WriteN(RTMP *r, const char *buffer, int n)
     free(encrypted);
 #endif
 
+  if (r->Link.ConnectPacket)
+    {
+      if (r->Link.HandshakeResponse.av_val)
+        free(r->Link.HandshakeResponse.av_val);
+      free(ConnectPacket);
+      r->Link.ConnectPacket = FALSE;
+    }
+
   return n == 0;
 }
 
@@ -1579,6 +1700,9 @@ SendConnectPacket(RTMP *r, RTMPPacket *cp)
   char pbuf[4096], *pend = pbuf + sizeof(pbuf);
   char *enc;
 
+  if (r->Link.CombineConnectPacket)
+    r->Link.ConnectPacket = TRUE;
+
   if (cp)
     return RTMP_SendPacket(r, cp, TRUE);
 
@@ -1627,7 +1751,7 @@ SendConnectPacket(RTMP *r, RTMPPacket *cp)
       enc = AMF_EncodeNamedBoolean(enc, pend, &av_fpad, FALSE);
       if (!enc)
 	return FALSE;
-      enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 15.0);
+      enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 239.0);
       if (!enc)
 	return FALSE;
       enc = AMF_EncodeNamedNumber(enc, pend, &av_audioCodecs, r->m_fAudioCodecs);
@@ -1791,7 +1915,7 @@ SendUsherToken(RTMP *r, AVal *usherToken)
   packet.m_hasAbsTimestamp = 0;
   packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
 
-  RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %s", usherToken->av_val);
+  RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %.*s", usherToken->av_len, usherToken->av_val);
   enc = packet.m_body;
   enc = AMF_EncodeString(enc, pend, &av_NetStream_Authenticate_UsherToken);
   enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
@@ -1934,6 +2058,26 @@ SendPublish(RTMP *r)
   return RTMP_SendPacket(r, &packet, TRUE);
 }
 
+static int
+SendDynamicPublish(RTMP *r, double publishId)
+{
+  char pbuf[1024], *pend = pbuf + sizeof (pbuf), *enc;
+  AVal av_command, av_publishId;
+
+  enc = pbuf;
+  enc = AMF_EncodeString(enc, pend, &av_publish);
+  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+  *enc++ = AMF_NULL;
+  av_publishId.av_val = malloc(128 * sizeof (char));
+  av_publishId.av_len = sprintf(av_publishId.av_val, "%.0f", publishId);
+  enc = AMF_EncodeString(enc, pend, &av_publishId);
+  enc = AMF_EncodeString(enc, pend, &av_live);
+  av_command.av_val = pbuf;
+  av_command.av_len = enc - pbuf;
+
+  return SendInvoke(r, &av_command, FALSE);
+}
+
 SAVC(deleteStream);
 
 static int
@@ -2097,6 +2241,7 @@ SendBytesReceived(RTMP *r)
 }
 
 SAVC(_checkbw);
+SAVC(checkBandwidth);
 
 static int
 SendCheckBW(RTMP *r)
@@ -2114,7 +2259,7 @@ SendCheckBW(RTMP *r)
   packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
 
   enc = packet.m_body;
-  enc = AMF_EncodeString(enc, pend, &av__checkbw);
+  enc = AMF_EncodeString(enc, pend, &av_checkBandwidth);
   enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
   *enc++ = AMF_NULL;
 
@@ -2221,10 +2366,8 @@ SendPlay(RTMP *r)
     enc = AMF_EncodeNumber(enc, pend, -1000.0);
   else
     {
-      if (r->Link.seekTime > 0.0)
-	enc = AMF_EncodeNumber(enc, pend, r->Link.seekTime);	/* resume from here */
-      else
-	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 */
+      if (r->Link.seekTime > 0.0 || r->Link.stopTime)
+        enc = AMF_EncodeNumber(enc, pend, r->Link.seekTime); /* resume from here */
     }
   if (!enc)
     return FALSE;
@@ -2340,7 +2483,7 @@ RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime)
   int nSize;
   char *buf;
 
-  RTMP_Log(RTMP_LOGDEBUG, "sending ctrl. type: 0x%04x", (unsigned short)nType);
+  RTMP_Log(RTMP_LOGDEBUG, "sending ctrl, type: 0x%04x", (unsigned short)nType);
 
   packet.m_nChannel = 0x02;	/* control channel (ping) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
@@ -2372,8 +2515,8 @@ RTMP_SendCtrl(RTMP *r, short nType, unsigned int nObject, unsigned int nTime)
     }
   else if (nType == 0x1A)
     {
-	  *buf = nObject & 0xff;
-	}
+      *buf = nObject & 0xff;
+    }
   else
     {
       if (nSize > 2)
@@ -2873,6 +3016,7 @@ PublisherAuth(RTMP *r, AVal *description)
 #endif
 
 
+SAVC(onBWCheck);
 SAVC(onBWDone);
 SAVC(onFCSubscribe);
 SAVC(onFCUnsubscribe);
@@ -2885,24 +3029,25 @@ SAVC(level);
 SAVC(description);
 SAVC(onStatus);
 SAVC(playlist_ready);
+SAVC(cps);
+SAVC(disneyToken);
+SAVC(getStreamLength);
+SAVC(sendStatus);
+SAVC(verifyClient);
 static const AVal av_NetStream_Failed = AVC("NetStream.Failed");
 static const AVal av_NetStream_Play_Failed = AVC("NetStream.Play.Failed");
-static const AVal av_NetStream_Play_StreamNotFound =
-AVC("NetStream.Play.StreamNotFound");
-static const AVal av_NetConnection_Connect_InvalidApp =
-AVC("NetConnection.Connect.InvalidApp");
+static const AVal av_NetStream_Play_StreamNotFound = AVC("NetStream.Play.StreamNotFound");
+static const AVal av_NetConnection_Connect_InvalidApp = AVC("NetConnection.Connect.InvalidApp");
 static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
 static const AVal av_NetStream_Play_Complete = AVC("NetStream.Play.Complete");
 static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
 static const AVal av_NetStream_Seek_Notify = AVC("NetStream.Seek.Notify");
 static const AVal av_NetStream_Pause_Notify = AVC("NetStream.Pause.Notify");
-static const AVal av_NetStream_Play_PublishNotify =
-AVC("NetStream.Play.PublishNotify");
-static const AVal av_NetStream_Play_UnpublishNotify =
-AVC("NetStream.Play.UnpublishNotify");
+static const AVal av_NetStream_Play_PublishNotify = AVC("NetStream.Play.PublishNotify");
+static const AVal av_NetStream_Play_UnpublishNotify = AVC("NetStream.Play.UnpublishNotify");
 static const AVal av_NetStream_Publish_Start = AVC("NetStream.Publish.Start");
-static const AVal av_NetConnection_Connect_Rejected =
-AVC("NetConnection.Connect.Rejected");
+static const AVal av_NetConnection_Connect_Rejected = AVC("NetConnection.Connect.Rejected");
+static const AVal av_NetConnection_confStream = AVC("NetConnection.confStream");
 
 /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
 static int
@@ -2912,6 +3057,11 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
   AVal method;
   double txn;
   int ret = 0, nRes;
+  char pbuf[512], *pend = pbuf + sizeof (pbuf), *enc, **params = NULL;
+  char *host = r->Link.hostname.av_len ? r->Link.hostname.av_val : "";
+  char *pageUrl = r->Link.pageUrl.av_len ? r->Link.pageUrl.av_val : "";
+  int param_count;
+  AVal av_Command, av_Response;
   if (body[0] != 0x02)		/* make sure it is a string method name we start with */
     {
       RTMP_Log(RTMP_LOGWARNING, "%s, Sanity failed. no string method in invoke packet",
@@ -2952,7 +3102,14 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
       RTMP_Log(RTMP_LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__,
 	  methodInvoked.av_val);
 
-      if (AVMATCH(&methodInvoked, &av_connect))
+      if ((r->Link.dynamicPublish == TRUE) && AVMATCH(&methodInvoked, &r->Link.dynamicCommand))
+        {
+          r->Link.dynamicPublish = FALSE;
+          r->Link.publishId = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
+          RTMP_Log(RTMP_LOGDEBUG, "server returned dynamic publish id: %.0f", r->Link.publishId);
+          RTMP_SendCreateStream(r);
+        }
+      else if (AVMATCH(&methodInvoked, &av_connect))
 	{
 	  if (r->Link.token.av_len)
 	    {
@@ -2973,46 +3130,360 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
 	      RTMP_SendServerBW(r);
 	      RTMP_SendCtrl(r, 3, 0, 300);
 	    }
-	  RTMP_SendCreateStream(r);
+          if (r->Link.ccomm.av_len)
+            {
+              param_count = strsplit(r->Link.ccomm.av_val, FALSE, ';', &params);
+              if ((param_count > 1) && (strcasecmp(params[1], "TRUE") == 0))
+                SendCommand(r, params[0], TRUE);
+              else
+                SendCommand(r, params[0], FALSE);
+              if ((param_count > 2) && (strcasecmp(params[2], "TRUE") == 0))
+                {
+                  RTMP_Log(RTMP_LOGDEBUG, "overriding inbuilt dynamic publish command with -K (ccommand) switch");
+                  r->Link.dynamicPublish = TRUE;
+                  r->Link.dynamicCommand.av_val = params[0];
+                  r->Link.dynamicCommand.av_len = strlen(params[0]);
+                }
+              else
+                {
+                  RTMP_Log(RTMP_LOGDEBUG, "overriding inbuilt site specific authentication with -K (ccommand) switch");
+                  r->Link.dynamicPublish = FALSE;
+                  RTMP_SendCreateStream(r);
+                }
+            }
+          else if (strstr(host, "3dbuzz.com") || strstr(pageUrl, "3dbuzz.com"))
+            {
+              AVal r1, r3;
+              AVal av_r1 = AVC("r1");
+              AVal av_r3 = AVC("r3");
+              AVal r1_key = AVC("4V?c6k7Y`(6~rMjp6S6!xT04]8m$g2");
+              AVal r3_key = AVC("aB`d^+8?9;36]Lw2#rg?PDMcX?lCw2");
+              TransformRot13(&obj, &av_r1, &r1);
+              TransformRot13(&obj, &av_r3, &r3);
+              if (r1.av_val && r3.av_val)
+                {
+                  AVal av_qq = AVC("qq");
+                  AVal av_tos = AVC("http://www.3dbuzz.com/home/tos");
+                  AVal av_warning = AVC("Stream capturing is a violation of our terms, and may result in immediate cancellation of your account without refund");
+                  AVal r1_response;
+
+                  RTMP_Log(RTMP_LOGDEBUG, "3DBuzz SecureToken r1 request - %.*s", r1.av_len, r1.av_val);
+                  RTMP_Log(RTMP_LOGDEBUG, "3DBuzz SecureToken r3 request - %.*s", r3.av_len, r3.av_val);
+                  DecodeTEA(&r1_key, &r1);
+                  DecodeTEA(&r3_key, &r3);
+                  r1_response = TeaEncrypt(&av_tos, &r1);
+                  RTMP_Log(RTMP_LOGDEBUG, "3DBuzz SecureToken r1 response - %.*s", r1_response.av_len, r1_response.av_val);
+                  RTMP_Log(RTMP_LOGDEBUG, "3DBuzz SecureToken r3 response - %.*s", r3.av_len, r3.av_val);
+
+                  enc = pbuf;
+                  enc = AMF_EncodeString(enc, pend, &av_qq);
+                  enc = AMF_EncodeNumber(enc, pend, 0);
+                  *enc++ = AMF_NULL;
+                  enc = AMF_EncodeString(enc, pend, &r3);
+                  enc = AMF_EncodeString(enc, pend, &av_tos);
+                  enc = AMF_EncodeString(enc, pend, &r1_response);
+                  enc = AMF_EncodeString(enc, pend, &av_warning);
+                  av_Command.av_val = pbuf;
+                  av_Command.av_len = enc - pbuf;
+                  SendInvoke(r, &av_Command, FALSE);
+                }
 
-	  if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
-	    {
-	      /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
-	      if (r->Link.usherToken.av_len)
-	        SendUsherToken(r, &r->Link.usherToken);
-	      /* Send the FCSubscribe if live stream or if subscribepath is set */
-	      if (r->Link.subscribepath.av_len)
-	        SendFCSubscribe(r, &r->Link.subscribepath);
-	      else if (r->Link.lFlags & RTMP_LF_LIVE)
-	        SendFCSubscribe(r, &r->Link.playpath);
-	    }
-	}
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(pageUrl, "cam4"))
+            {
+              AMFObject obj2, response;
+              AMFObjectProperty p;
+              AVal Host, ID, IP, av_ChallengeResponse;
+              AVal av_receiveRTMPResponse = AVC("receiveRTMPResponse");
+              AVal av_client = AVC("client");
+              AVal av_result = AVC("result");
+              char ChallengeResponse[16] = {0};
+              SAVC(application);
+              SAVC(Host);
+              SAVC(ID);
+              SAVC(IP);
+
+              AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
+              if (RTMP_FindFirstMatchingProperty(&obj2, &av_application, &p))
+                {
+                  RTMP_Log(RTMP_LOGDEBUG, "sending cam4 authentication");
+                  AMFProp_GetObject(&p, &obj2);
+                  RTMP_FindFirstMatchingProperty(&obj2, &av_Host, &p);
+                  AMFProp_GetString(&p, &Host);
+                  RTMP_FindFirstMatchingProperty(&obj2, &av_ID, &p);
+                  AMFProp_GetString(&p, &ID);
+                  RTMP_FindFirstMatchingProperty(&obj2, &av_IP, &p);
+                  AMFProp_GetString(&p, &IP);
+                  RTMP_Log(RTMP_LOGDEBUG, "Cam4 Host: %.*s", Host.av_len, Host.av_val);
+                  RTMP_Log(RTMP_LOGDEBUG, "Cam4 ID  : %.*s", ID.av_len, ID.av_val);
+                  RTMP_Log(RTMP_LOGDEBUG, "Cam4 IP  : %.*s", IP.av_len, IP.av_val);
+                  snprintf(ChallengeResponse, 15, "%d", Host.av_len + ID.av_len + IP.av_len);
+                  av_ChallengeResponse.av_val = ChallengeResponse;
+                  av_ChallengeResponse.av_len = strlen(av_ChallengeResponse.av_val);
+                  AMFProp_SetName(&p, &av_client);
+                  AMFProp_SetString(&p, &ID);
+                  AMF_AddProp(&response, &p);
+                  AMFProp_SetName(&p, &av_result);
+                  AMFProp_SetString(&p, &av_ChallengeResponse);
+                  AMF_AddProp(&response, &p);
+
+                  enc = pbuf;
+                  enc = AMF_EncodeString(enc, pend, &av_receiveRTMPResponse);
+                  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+                  *enc++ = AMF_NULL;
+                  enc = AMF_Encode(&response, enc, pend);
+                  enc = AMF_EncodeBoolean(enc, pend, TRUE);
+                  av_Response.av_val = pbuf;
+                  av_Response.av_len = enc - pbuf;
+
+                  AMF_Decode(&obj, av_Response.av_val, av_Response.av_len, FALSE);
+                  AMF_Dump(&obj);
+                  SendInvoke(r, &av_Response, TRUE);
+                }
+
+              RTMP_SendCreateStream(r);
+            }
+          else if ((strstr(host, "highwebmedia.com") || strstr(pageUrl, "chaturbate.com"))
+                   && (!strstr(host, "origin")))
+            {
+              AVal av_ModelName;
+              SAVC(CheckPublicStatus);
+
+              if (strlen(pageUrl) > 7)
+                {
+                  strsplit(pageUrl + 7, FALSE, '/', &params);
+                  av_ModelName.av_val = params[1];
+                  av_ModelName.av_len = strlen(params[1]);
+
+                  enc = pbuf;
+                  enc = AMF_EncodeString(enc, pend, &av_CheckPublicStatus);
+                  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+                  *enc++ = AMF_NULL;
+                  enc = AMF_EncodeString(enc, pend, &av_ModelName);
+                  av_Command.av_val = pbuf;
+                  av_Command.av_len = enc - pbuf;
+
+                  SendInvoke(r, &av_Command, FALSE);
+                }
+              else
+                {
+                  RTMP_Log(RTMP_LOGERROR, "you must specify the pageUrl");
+                  RTMP_Close(r);
+                }
+            }
+          else if (strstr(host, "featve.com") || strstr(pageUrl, "featve.com"))
+            {
+              AVal av_auth = AVC("yes");
+              SAVC(youCannotPlayMe);
+
+              enc = pbuf;
+              enc = AMF_EncodeString(enc, pend, &av_youCannotPlayMe);
+              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+              *enc++ = AMF_NULL;
+              enc = AMF_EncodeString(enc, pend, &av_auth);
+              av_Command.av_val = pbuf;
+              av_Command.av_len = enc - pbuf;
+              SendInvoke(r, &av_Command, FALSE);
+
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(host, "tv-stream.to") || strstr(pageUrl, "tv-stream.to"))
+            {
+              static char auth[] = {'h', 0xC2, 0xA7, '4', 'j', 'h', 'H', '4', '3', 'd'};
+              AVal av_auth;
+              SAVC(requestAccess);
+              av_auth.av_val = auth;
+              av_auth.av_len = sizeof (auth);
+
+              enc = pbuf;
+              enc = AMF_EncodeString(enc, pend, &av_requestAccess);
+              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+              *enc++ = AMF_NULL;
+              enc = AMF_EncodeString(enc, pend, &av_auth);
+              av_Command.av_val = pbuf;
+              av_Command.av_len = enc - pbuf;
+              SendInvoke(r, &av_Command, FALSE);
+
+              SendCommand(r, "getConnectionCount", FALSE);
+              SendGetStreamLength(r);
+              RTMP_SendCreateStream(r);
+            }
+          else if (r->Link.WeebToken.av_len)
+            {
+              AVal av_Token, av_Username, av_Password;
+              SAVC(determineAccess);
+
+              param_count = strsplit(r->Link.WeebToken.av_val, FALSE, ';', &params);
+              if (param_count >= 1)
+                {
+                  av_Token.av_val = params[0];
+                  av_Token.av_len = strlen(params[0]);
+                }
+              if (param_count >= 2)
+                {
+                  av_Username.av_val = params[1];
+                  av_Username.av_len = strlen(params[1]);
+                }
+              if (param_count >= 3)
+                {
+                  av_Password.av_val = params[2];
+                  av_Password.av_len = strlen(params[2]);
+                }
+
+              enc = pbuf;
+              enc = AMF_EncodeString(enc, pend, &av_determineAccess);
+              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+              *enc++ = AMF_NULL;
+              enc = AMF_EncodeString(enc, pend, &av_Token);
+              enc = AMF_EncodeString(enc, pend, &av_Username);
+              enc = AMF_EncodeString(enc, pend, &av_Password);
+              av_Command.av_val = pbuf;
+              av_Command.av_len = enc - pbuf;
+
+              RTMP_Log(RTMP_LOGDEBUG, "WeebToken: %s", r->Link.WeebToken.av_val);
+              SendInvoke(r, &av_Command, FALSE);
+            }
+          else if (strstr(host, "wfctv.com") || strstr(pageUrl, "wfctv.com"))
+            {
+              AVal av_auth1 = AVC("zoivid");
+              AVal av_auth2 = AVC("yePi4jee");
+              SAVC(stream_login);
+
+              enc = pbuf;
+              enc = AMF_EncodeString(enc, pend, &av_stream_login);
+              enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+              *enc++ = AMF_NULL;
+              enc = AMF_EncodeString(enc, pend, &av_auth1);
+              enc = AMF_EncodeString(enc, pend, &av_auth2);
+              av_Command.av_val = pbuf;
+              av_Command.av_len = enc - pbuf;
+              SendInvoke(r, &av_Command, FALSE);
+
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(host, "pc3oot.us.to"))
+            {
+              SendCommand(r, "UIUIUINASOWAS", TRUE);
+              SendGetStreamLength(r);
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(host, "streamscene.cc") || strstr(pageUrl, "streamscene.cc")
+                   || strstr(host, "tsboard.tv") || strstr(pageUrl, "teamstream.in")
+                   || strstr(host, "hdstreams.tv") || strstr(pageUrl, "teamstream.to")
+                   || strstr(pageUrl, "istreams.to"))
+            {
+              SendCommand(r, "r", FALSE);
+              SendGetStreamLength(r);
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(pageUrl, "axcast.com"))
+            {
+              SendCommand(r, "requestData", FALSE);
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(pageUrl, "dhmediahosting.com"))
+            {
+              SendCommand(r, "netStreamEnable", FALSE);
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(pageUrl, "ezcast.tv"))
+            {
+              SendCommand(r, "iUsteJaSakamCarevataKerka", TRUE);
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(pageUrl, "janjua.tv"))
+            {
+              SendCommand(r, "soLagaDaSeStoriAga", TRUE);
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(pageUrl, "liveflash.tv"))
+            {
+              char *command = "kaskatijaEkonomista";
+              r->Link.dynamicPublish = TRUE;
+              r->Link.dynamicCommand.av_val = command;
+              r->Link.dynamicCommand.av_len = strlen(command);
+              SendCommand(r, command, TRUE);
+            }
+          else if (strstr(pageUrl, "mips.tv") || strstr(pageUrl, "mipsplayer.com"))
+            {
+              char *command = "gaolVanusPobeleVoKosata";
+              r->Link.dynamicPublish = TRUE;
+              r->Link.dynamicCommand.av_val = command;
+              r->Link.dynamicCommand.av_len = strlen(command);
+              SendCommand(r, command, TRUE);
+            }
+          else if (strstr(pageUrl, "streamify.tv"))
+            {
+              SendCommand(r, "keGoVidishStambolSoseBardovci", TRUE);
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(pageUrl, "ucaster.eu"))
+            {
+              SendCommand(r, "vujkoMiLazarBarakovOdMonospitovo", TRUE);
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(pageUrl, "yukons.net"))
+            {
+              SendCommand(r, "trxuwaaLahRKnaechb", TRUE);
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(pageUrl, "yycast.com"))
+            {
+              SendCommand(r, "trajkoProkopiev", TRUE);
+              RTMP_SendCreateStream(r);
+            }
+          else if (strstr(pageUrl, "zenex.tv"))
+            {
+              SendCommand(r, "goVideStambolSoseBardovci", TRUE);
+              RTMP_SendCreateStream(r);
+            }
+          else
+            RTMP_SendCreateStream(r);
+        }
       else if (AVMATCH(&methodInvoked, &av_createStream))
-	{
-	  r->m_stream_id = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
+        {
+          r->m_stream_id = (int) AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
 
-	  if (r->Link.protocol & RTMP_FEATURE_WRITE)
-	    {
-	      SendPublish(r);
-	    }
-	  else
-	    {
-	      if (r->Link.lFlags & RTMP_LF_PLST)
-	        SendPlaylist(r);
-	      SendPlay(r);
-	      RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS);
-	    }
-	}
+          if (!(r->Link.protocol & RTMP_FEATURE_WRITE))
+            {
+              /* Authenticate on Justin.tv legacy servers before sending FCSubscribe */
+              if (r->Link.usherToken.av_len)
+                SendUsherToken(r, &r->Link.usherToken);
+              if (r->Link.publishId > 0)
+                {
+                  RTMP_Log(RTMP_LOGDEBUG, "sending dynamic publish id: %.0f", r->Link.publishId);
+                  SendDynamicPublish(r, r->Link.publishId);
+                }
+              /* Send the FCSubscribe if live stream or if subscribepath is set */
+              if (r->Link.subscribepath.av_len)
+                SendFCSubscribe(r, &r->Link.subscribepath);
+              else if ((r->Link.lFlags & RTMP_LF_LIVE) && (!r->Link.WeebToken.av_len))
+                SendFCSubscribe(r, &r->Link.playpath);
+            }
+
+          if (r->Link.protocol & RTMP_FEATURE_WRITE)
+            {
+              SendPublish(r);
+            }
+          else
+            {
+              if (r->Link.lFlags & RTMP_LF_PLST)
+                SendPlaylist(r);
+              SendPlay(r);
+              RTMP_SendCtrl(r, 3, r->m_stream_id, r->m_nBufferMS);
+            }
+        }
       else if (AVMATCH(&methodInvoked, &av_play) ||
-      	AVMATCH(&methodInvoked, &av_publish))
-	{
-	  r->m_bPlaying = TRUE;
-	}
+               AVMATCH(&methodInvoked, &av_publish))
+        {
+          r->m_bPlaying = TRUE;
+        }
       free(methodInvoked.av_val);
     }
   else if (AVMATCH(&method, &av_onBWDone))
     {
-	  if (!r->m_nBWCheckCounter)
+      if (!r->m_nBWCheckCounter)
         SendCheckBW(r);
     }
   else if (AVMATCH(&method, &av_onFCSubscribe))
@@ -3036,21 +3507,22 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
     {
       int i;
       for (i = 0; i < r->m_numCalls; i++)
-	if (AVMATCH(&r->m_methodCalls[i].name, &av__checkbw))
-	  {
-	    AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);
-	    break;
-	  }
+        if (AVMATCH(&r->m_methodCalls[i].name, &av__checkbw))
+          {
+            AV_erase(r->m_methodCalls, &r->m_numCalls, i, TRUE);
+            break;
+          }
     }
   else if (AVMATCH(&method, &av__error))
     {
+      int handled = FALSE;
 #ifdef CRYPTO
       AVal methodInvoked = {0};
       int i;
 
       if (r->Link.protocol & RTMP_FEATURE_WRITE)
         {
-          for (i=0; i<r->m_numCalls; i++)
+          for (i = 0; i < r->m_numCalls; i++)
             {
               if (r->m_methodCalls[i].num == txn)
                 {
@@ -3062,12 +3534,12 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
           if (!methodInvoked.av_val)
             {
               RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request",
-                    __FUNCTION__, txn);
+                       __FUNCTION__, txn);
               goto leave;
             }
 
           RTMP_Log(RTMP_LOGDEBUG, "%s, received error for method call <%s>", __FUNCTION__,
-          methodInvoked.av_val);
+                   methodInvoked.av_val);
 
           if (AVMATCH(&methodInvoked, &av_connect))
             {
@@ -3086,34 +3558,96 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
                   goto leave;
               }
             }
-        }
-      else
-        {
-          RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
+          handled = TRUE;
         }
       free(methodInvoked.av_val);
-#else
-      RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
 #endif
+      double code = 0.0;
+      unsigned int parsedPort = 0;
+      AMFObject obj2;
+      AMFObjectProperty p;
+      AVal redirect;
+      SAVC(ex);
+      SAVC(redirect);
+
+      AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
+      if (RTMP_FindFirstMatchingProperty(&obj2, &av_ex, &p))
+        {
+          AMFProp_GetObject(&p, &obj2);
+          if (RTMP_FindFirstMatchingProperty(&obj2, &av_code, &p))
+            code = AMFProp_GetNumber(&p);
+          if (code == 302 && RTMP_FindFirstMatchingProperty(&obj2, &av_redirect, &p))
+            {
+              AMFProp_GetString(&p, &redirect);
+              r->Link.redirected = TRUE;
+
+              char *playpath = "//playpath";
+              int len = redirect.av_len + strlen(playpath);
+              char *url = malloc(len + 1);
+              memcpy(url, redirect.av_val, redirect.av_len);
+              memcpy(url + redirect.av_len, playpath, strlen(playpath));
+              url[len] = '\0';
+              r->Link.tcUrl.av_val = url;
+              r->Link.tcUrl.av_len = redirect.av_len;
+              if (r->Link.lFlags & RTMP_LF_FTCU)
+                r->Link.lFlags ^= RTMP_LF_FTCU;
+              RTMP_ParseURL(url, &r->Link.protocol, &r->Link.hostname, &parsedPort, &r->Link.playpath0, &r->Link.app);
+              if (parsedPort)
+                r->Link.port = parsedPort;
+            }
+        }
+      if (r->Link.redirected)
+        {
+          handled = TRUE;
+          RTMP_Log(RTMP_LOGINFO, "rtmp server sent redirect");
+        }
+
+      if (!handled)
+        RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
     }
   else if (AVMATCH(&method, &av_close))
     {
-      RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
-      RTMP_Close(r);
+      if (r->Link.redirected)
+        {
+          r->Link.redirected = FALSE;
+          RTMP_Close(r);
+          RTMP_Log(RTMP_LOGINFO, "trying to connect with redirected url");
+          if (r->Link.port == 0)
+            {
+              if (r->Link.protocol & RTMP_FEATURE_SSL)
+                r->Link.port = 443;
+              else if (r->Link.protocol & RTMP_FEATURE_HTTP)
+                r->Link.port = 80;
+              else
+                r->Link.port = 1935;
+            }
+          RTMP_Connect(r, NULL);
+        }
+      else
+        {
+
+          RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
+          if (r->m_bPlaying && (strstr(pageUrl, "streamlive.to") || strstr(pageUrl, "uk-iptv.co.uk")))
+            RTMP_Log(RTMP_LOGINFO, "ignoring close request");
+          else
+            RTMP_Close(r);
+        }
     }
   else if (AVMATCH(&method, &av_onStatus))
     {
       AMFObject obj2;
-      AVal code, level;
+      AVal code, level, description;
       AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &obj2);
       AMFProp_GetString(AMF_GetProp(&obj2, &av_code, -1), &code);
       AMFProp_GetString(AMF_GetProp(&obj2, &av_level, -1), &level);
+      AMFProp_GetString(AMF_GetProp(&obj2, &av_description, -1), &description);
 
       RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val);
       if (AVMATCH(&code, &av_NetStream_Failed)
-	  || AVMATCH(&code, &av_NetStream_Play_Failed)
-	  || AVMATCH(&code, &av_NetStream_Play_StreamNotFound)
-	  || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp))
+          || AVMATCH(&code, &av_NetStream_Play_Failed)
+          || AVMATCH(&code, &av_NetStream_Play_StreamNotFound)
+          || AVMATCH(&code, &av_NetConnection_Connect_Rejected)
+          || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp))
 	{
 	  r->m_stream_id = -1;
 	  RTMP_Close(r);
@@ -3171,6 +3705,46 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
 	    r->m_pausing = 3;
 	  }
 	}
+
+      else if (AVMATCH(&code, &av_NetConnection_confStream))
+        {
+#ifdef CRYPTO
+          static const char hexdig[] = "0123456789abcdef";
+          AVal auth;
+          SAVC(cf_stream);
+          int i;
+          char hash_hex[33] = {0};
+          unsigned char hash[16];
+
+          param_count = strsplit(description.av_val, description.av_len, ':', &params);
+          if (param_count >= 3)
+            {
+              char *buf = malloc(strlen(params[0]) + r->Link.playpath.av_len + 1);
+              strcpy(buf, params[0]);
+              strncat(buf, r->Link.playpath.av_val, r->Link.playpath.av_len);
+              md5_hash((unsigned char *) buf, strlen(buf), hash);
+              for (i = 0; i < 16; i++)
+                {
+                  hash_hex[i * 2] = hexdig[0x0f & (hash[i] >> 4)];
+                  hash_hex[i * 2 + 1] = hexdig[0x0f & (hash[i])];
+                }
+              auth.av_val = &hash_hex[atoi(params[1]) - 1];
+              auth.av_len = atoi(params[2]);
+              RTMP_Log(RTMP_LOGDEBUG, "Khalsa: %.*s", auth.av_len, auth.av_val);
+
+              enc = pbuf;
+              enc = AMF_EncodeString(enc, pend, &av_cf_stream);
+              enc = AMF_EncodeNumber(enc, pend, txn);
+              *enc++ = AMF_NULL;
+              enc = AMF_EncodeString(enc, pend, &auth);
+              av_Command.av_val = pbuf;
+              av_Command.av_len = enc - pbuf;
+
+              SendInvoke(r, &av_Command, FALSE);
+              free(buf);
+            }
+#endif
+        }
     }
   else if (AVMATCH(&method, &av_playlist_ready))
     {
@@ -3184,6 +3758,109 @@ HandleInvoke(RTMP *r, const char *body, unsigned int nBodySize)
 	    }
         }
     }
+  else if (AVMATCH(&method, &av_cps))
+    {
+      if (obj.o_num >= 4)
+        {
+          int Status = AMFProp_GetBoolean(AMF_GetProp(&obj, NULL, 3));
+          if (Status == FALSE)
+            {
+              AVal Message;
+              AMFProp_GetString(AMF_GetProp(&obj, NULL, 4), &Message);
+              RTMP_Log(RTMP_LOGINFO, "Model status is %.*s", Message.av_len, Message.av_val);
+              RTMP_Close(r);
+            }
+          else
+            {
+              if (obj.o_num >= 7)
+                {
+                  AVal Playpath, Server;
+                  AMFProp_GetString(AMF_GetProp(&obj, NULL, 5), &Playpath);
+                  AMFProp_GetString(AMF_GetProp(&obj, NULL, 6), &Server);
+                  if (strncasecmp(&Playpath.av_val[Playpath.av_len - 4], ".mp4", 4) != 0)
+                    {
+                      char *playpath = calloc(Server.av_len + Playpath.av_len + 25, sizeof (char));
+                      strcat(playpath, "rtmp://");
+                      strncat(playpath, Server.av_val, Server.av_len);
+                      strcat(playpath, "/live-origin/");
+                      strncat(playpath, Playpath.av_val, Playpath.av_len);
+                      strcat(playpath, ".mp4");
+                      Playpath.av_val = playpath;
+                      Playpath.av_len = strlen(playpath);
+                    }
+                  RTMP_ParsePlaypath(&Playpath, &r->Link.playpath);
+                  RTMP_SendCreateStream(r);
+                }
+            }
+        }
+    }
+  else if (AVMATCH(&method, &av_disneyToken))
+    {
+      double FirstNumber = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
+      double SecondNumber = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
+      RTMP_Log(RTMP_LOGDEBUG, "FirstNumber: %.2f, SecondNumber: %.2f", FirstNumber, SecondNumber);
+
+      enc = pbuf;
+      enc = AMF_EncodeString(enc, pend, &av__result);
+      enc = AMF_EncodeNumber(enc, pend, txn);
+      *enc++ = AMF_NULL;
+      enc = AMF_EncodeNumber(enc, pend, FirstNumber * SecondNumber);
+      av_Response.av_val = pbuf;
+      av_Response.av_len = enc - pbuf;
+
+      AMF_Decode(&obj, av_Response.av_val, av_Response.av_len, FALSE);
+      AMF_Dump(&obj);
+      SendInvoke(r, &av_Response, FALSE);
+    }
+  else if (AVMATCH(&method, &av_verifyClient))
+    {
+      double VerificationNumber = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 3));
+      RTMP_Log(RTMP_LOGDEBUG, "VerificationNumber: %.2f", VerificationNumber);
+
+      enc = pbuf;
+      enc = AMF_EncodeString(enc, pend, &av__result);
+      enc = AMF_EncodeNumber(enc, pend, txn);
+      *enc++ = AMF_NULL;
+      enc = AMF_EncodeNumber(enc, pend, exp(atan(sqrt(VerificationNumber))) + 1);
+      av_Response.av_val = pbuf;
+      av_Response.av_len = enc - pbuf;
+
+      AMF_Decode(&obj, av_Response.av_val, av_Response.av_len, FALSE);
+      AMF_Dump(&obj);
+      SendInvoke(r, &av_Response, FALSE);
+    }
+  else if (AVMATCH(&method, &av_sendStatus))
+    {
+      if (r->Link.WeebToken.av_len)
+        {
+          AVal av_Authorized = AVC("User.hasAccess");
+          AVal av_TransferLimit = AVC("User.noPremium.limited");
+          AVal av_UserLimit = AVC("User.noPremium.tooManyUsers");
+          AVal av_TimeLeft = AVC("timeLeft");
+          AVal av_Status, av_ReconnectionTime;
+
+          AMFObject Status;
+          AMFProp_GetObject(AMF_GetProp(&obj, NULL, 3), &Status);
+          AMFProp_GetString(AMF_GetProp(&Status, &av_code, -1), &av_Status);
+          RTMP_Log(RTMP_LOGINFO, "%.*s", av_Status.av_len, av_Status.av_val);
+          if (AVMATCH(&av_Status, &av_Authorized))
+            {
+              RTMP_Log(RTMP_LOGINFO, "Weeb.tv authentication successful");
+              RTMP_SendCreateStream(r);
+            }
+          else if (AVMATCH(&av_Status, &av_UserLimit))
+            {
+              RTMP_Log(RTMP_LOGINFO, "No free slots available");
+              RTMP_Close(r);
+            }
+          else if (AVMATCH(&av_Status, &av_TransferLimit))
+            {
+              AMFProp_GetString(AMF_GetProp(&Status, &av_TimeLeft, -1), &av_ReconnectionTime);
+              RTMP_Log(RTMP_LOGINFO, "Viewing limit exceeded. try again in %.*s minutes.", av_ReconnectionTime.av_len, av_ReconnectionTime.av_val);
+              RTMP_Close(r);
+            }
+        }
+    }
   else
     {
 
@@ -3209,7 +3886,8 @@ RTMP_FindFirstMatchingProperty(AMFObject *obj, const AVal *name,
 	  return TRUE;
 	}
 
-      if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY)
+      if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY
+          || prop->p_type == AMF_STRICT_ARRAY)
 	{
 	  if (RTMP_FindFirstMatchingProperty(&prop->p_vu.p_object, name, p))
 	    return TRUE;
@@ -3235,7 +3913,8 @@ RTMP_FindPrefixProperty(AMFObject *obj, const AVal *name,
 	  return TRUE;
 	}
 
-      if (prop->p_type == AMF_OBJECT)
+      if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY
+          || prop->p_type == AMF_STRICT_ARRAY)
 	{
 	  if (RTMP_FindPrefixProperty(&prop->p_vu.p_object, name, p))
 	    return TRUE;
@@ -3269,6 +3948,7 @@ DumpMetaData(AMFObject *obj)
 	  snprintf(str, 255, "%s",
 		   prop->p_vu.p_number != 0. ? "TRUE" : "FALSE");
 	  break;
+        case AMF_NULL:
 	case AMF_STRING:
 	  len = snprintf(str, 255, "%.*s", prop->p_vu.p_aval.av_len,
 		   prop->p_vu.p_aval.av_val);
@@ -3284,7 +3964,7 @@ DumpMetaData(AMFObject *obj)
 	}
       if (str[0] && prop->p_name.av_len)
 	{
-	  RTMP_Log(RTMP_LOGINFO, "  %-22.*s%s", prop->p_name.av_len,
+          RTMP_Log(RTMP_LOGINFO, "  %-24.*s%s", prop->p_name.av_len,
 		    prop->p_name.av_val, str);
 	}
     }
@@ -3366,7 +4046,7 @@ HandleCtrl(RTMP *r, const RTMPPacket *packet)
   unsigned int tmp;
   if (packet->m_body && packet->m_nBodySize >= 2)
     nType = AMF_DecodeInt16(packet->m_body);
-  RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl. type: %d, len: %d", __FUNCTION__, nType,
+  RTMP_Log(RTMP_LOGDEBUG, "%s, received ctrl, type: %d, len: %d", __FUNCTION__, nType,
       packet->m_nBodySize);
   /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
 
@@ -3475,15 +4155,15 @@ HandleCtrl(RTMP *r, const RTMPPacket *packet)
       RTMP_Log(RTMP_LOGDEBUG, "%s, SWFVerification ping received: ", __FUNCTION__);
       if (packet->m_nBodySize > 2 && packet->m_body[2] > 0x01)
 	{
-	  RTMP_Log(RTMP_LOGERROR,
-            "%s: SWFVerification Type %d request not supported! Patches welcome...",
-	    __FUNCTION__, packet->m_body[2]);
+          RTMP_Log(RTMP_LOGERROR,
+                   "%s: SWFVerification Type %d request not supported, attempting to use SWFVerification Type 1! Patches welcome...",
+                   __FUNCTION__, packet->m_body[2]);
 	}
 #ifdef CRYPTO
       /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
 
       /* respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied */
-      else if (r->Link.SWFSize)
+      if (r->Link.SWFSize)
 	{
 	  RTMP_SendCtrl(r, 0x1B, 0, 0);
 	}
@@ -3788,8 +4468,18 @@ HandShake(RTMP *r, int FP9HandShake)
       serversig[4], serversig[5], serversig[6], serversig[7]);
 
   /* 2nd part of handshake */
-  if (!WriteN(r, serversig, RTMP_SIG_SIZE))
-    return FALSE;
+  if (r->Link.CombineConnectPacket)
+    {
+      char *HandshakeResponse = malloc(RTMP_SIG_SIZE);
+      memcpy(HandshakeResponse, (char *) serversig, RTMP_SIG_SIZE);
+      r->Link.HandshakeResponse.av_val = HandshakeResponse;
+      r->Link.HandshakeResponse.av_len = RTMP_SIG_SIZE;
+    }
+  else
+    {
+      if (!WriteN(r, (char *) serversig, RTMP_SIG_SIZE))
+        return FALSE;
+    }
 
   if (ReadN(r, serversig, RTMP_SIG_SIZE) != RTMP_SIG_SIZE)
     return FALSE;
@@ -3942,7 +4632,7 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue)
 
   nSize = packetSize[packet->m_headerType];
   hSize = nSize; cSize = 0;
-  t = packet->m_nTimeStamp - last;
+  t = packet->m_nTimeStamp ? packet->m_nTimeStamp - last : 0;
 
   if (packet->m_body)
     {
@@ -4251,8 +4941,13 @@ RTMPSockBuf_Fill(RTMPSockBuf *sb)
 {
   int nBytes;
 
-  if (!sb->sb_size)
-    sb->sb_start = sb->sb_buf;
+  /* Copy unprocessed bytes to the start of buffer to make optimum use of
+   * available buffer */
+  if (sb->sb_start != sb->sb_buf)
+    {
+      memcpy(sb->sb_buf, sb->sb_start, sb->sb_size);
+      sb->sb_start = sb->sb_buf;
+    }
 
   while (1)
     {
@@ -4266,8 +4961,10 @@ RTMPSockBuf_Fill(RTMPSockBuf *sb)
 #endif
 	{
 	  nBytes = recv(sb->sb_socket, sb->sb_start + sb->sb_size, nBytes, 0);
-	}
-      if (nBytes != -1)
+          if (!nBytes)
+            RTMP_Log(RTMP_LOGDEBUG, "Socket closed by server, nBytes: %d", nBytes);
+        }
+      if (nBytes >= 0)
 	{
 	  sb->sb_size += nBytes;
 	}
@@ -4405,21 +5102,19 @@ static int
 HTTP_Post(RTMP *r, RTMPTCmd cmd, const char *buf, int len)
 {
   char hbuf[512];
-  int hlen = snprintf(hbuf, sizeof(hbuf), "POST /%s%s/%d HTTP/1.1\r\n"
-    "Host: %.*s:%d\r\n"
-    "Accept: */*\r\n"
-    "User-Agent: Shockwave Flash\r\n"
-    "Connection: Keep-Alive\r\n"
-    "Cache-Control: no-cache\r\n"
-    "Content-type: application/x-fcs\r\n"
-    "Content-length: %d\r\n\r\n", RTMPT_cmds[cmd],
-    r->m_clientID.av_val ? r->m_clientID.av_val : "",
-    r->m_msgCounter, r->Link.hostname.av_len, r->Link.hostname.av_val,
-    r->Link.port, len);
+  int hlen = snprintf(hbuf, sizeof (hbuf), "POST /%s%s/%d HTTP/1.1\r\n"
+                      "Content-Type: application/x-fcs\r\n"
+                      "User-Agent: Shockwave Flash\r\n"
+                      "Host: %.*s:%d\r\n"
+                      "Content-Length: %d\r\n"
+                      "Connection: Keep-Alive\r\n"
+                      "Cache-Control: no-cache\r\n\r\n", RTMPT_cmds[cmd],
+                      r->m_clientID.av_val ? r->m_clientID.av_val : "",
+                      r->m_msgCounter, r->Link.hostname.av_len, r->Link.hostname.av_val,
+                      r->Link.port, len);
   RTMPSockBuf_Send(&r->m_sb, hbuf, hlen);
   hlen = RTMPSockBuf_Send(&r->m_sb, buf, len);
   r->m_msgCounter++;
-  r->m_unackd++;
   return hlen;
 }
 
@@ -4429,22 +5124,17 @@ HTTP_read(RTMP *r, int fill)
   char *ptr;
   int hlen;
 
-restart:
   if (fill)
     RTMPSockBuf_Fill(&r->m_sb);
-  if (r->m_sb.sb_size < 13) {
-    if (fill)
-      goto restart;
+
+  /* Check if socket buffer is empty or HTTP header isn't completely received */
+  memset(r->m_sb.sb_start + r->m_sb.sb_size, '\0', 1);
+  if ((!r->m_sb.sb_size) || (!strstr(r->m_sb.sb_start, "\r\n\r\n")))
     return -2;
-  }
+
   if (strncmp(r->m_sb.sb_start, "HTTP/1.1 200 ", 13))
     return -1;
   r->m_sb.sb_start[r->m_sb.sb_size] = '\0';
-  if (!strstr(r->m_sb.sb_start, "\r\n\r\n")) {
-    if (fill)
-      goto restart;
-    return -2;
-  }
 
   ptr = r->m_sb.sb_start + sizeof("HTTP/1.1 200");
   while ((ptr = strstr(ptr, "Content-"))) {
@@ -4452,21 +5142,31 @@ restart:
     ptr += 8;
   }
   if (!ptr)
-    return -1;
-  hlen = atoi(ptr+16);
+    {
+      ptr = r->m_sb.sb_start + sizeof ("HTTP/1.1 200");
+      RTMP_Log(RTMP_LOGDEBUG, "No Content-Length header found, assuming continuous stream");
+      hlen = 2147483648UL; // 2 GB
+    }
+  else
+    hlen = atoi(ptr + 16);
   ptr = strstr(ptr+16, "\r\n\r\n");
   if (!ptr)
     return -1;
   ptr += 4;
-  if (ptr + (r->m_clientID.av_val ? 1 : hlen) > r->m_sb.sb_start + r->m_sb.sb_size)
-    {
-      if (fill)
-        goto restart;
-      return -2;
-    }
   r->m_sb.sb_size -= ptr - r->m_sb.sb_start;
   r->m_sb.sb_start = ptr;
-  r->m_unackd--;
+
+  /* Stop processing if content length is 0 */
+  if (!hlen)
+    return -3;
+
+  /* Refill buffer if no payload is received */
+  if (hlen && (!r->m_sb.sb_size))
+    {
+      RTMPSockBuf_Fill(&r->m_sb);
+      ptr = r->m_sb.sb_buf;
+      r->m_sb.sb_start = ptr;
+    }
 
   if (!r->m_clientID.av_val)
     {
@@ -4486,10 +5186,17 @@ restart:
       r->m_sb.sb_start++;
       r->m_sb.sb_size--;
     }
+
+  /* Following values shouldn't be negative in any case */
+  if (r->m_resplen < 0)
+    r->m_resplen = 0;
+  if (r->m_sb.sb_size < 0)
+    r->m_sb.sb_size = 0;
+
   return 0;
 }
 
-#define MAX_IGNORED_FRAMES	50
+#define MAX_IGNORED_FRAMES	100
 
 /* Read from the stream until we get a media packet.
  * Returns -3 if Play.Close/Stop, -2 if fatal error, -1 if no more media
@@ -4557,162 +5264,156 @@ Read_1_Packet(RTMP *r, char *buf, unsigned int buflen)
 #endif
 
       if (r->m_read.flags & RTMP_READ_RESUME)
-	{
-	  /* check the header if we get one */
-	  if (packet.m_nTimeStamp == 0)
-	    {
-	      if (r->m_read.nMetaHeaderSize > 0
-		  && packet.m_packetType == RTMP_PACKET_TYPE_INFO)
-		{
-		  AMFObject metaObj;
-		  int nRes =
-		    AMF_Decode(&metaObj, packetBody, nPacketLen, FALSE);
-		  if (nRes >= 0)
-		    {
-		      AVal metastring;
-		      AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0),
-					&metastring);
-
-		      if (AVMATCH(&metastring, &av_onMetaData))
-			{
-			  /* compare */
-			  if ((r->m_read.nMetaHeaderSize != nPacketLen) ||
-			      (memcmp
-			       (r->m_read.metaHeader, packetBody,
-				r->m_read.nMetaHeaderSize) != 0))
-			    {
-			      ret = RTMP_READ_ERROR;
-			    }
-			}
-		      AMF_Reset(&metaObj);
-		      if (ret == RTMP_READ_ERROR)
-			break;
-		    }
-		}
+        {
+          RTMP_Log(RTMP_LOGDEBUG2, "Received timestamp: %d, type %d",
+                   packet.m_nTimeStamp, packet.m_packetType);
+          if (packet.m_nTimeStamp > 0 && r->m_read.nResumeDriftTS > 0)
+            packet.m_nTimeStamp -= r->m_read.nResumeDriftTS;
+          RTMP_Log(RTMP_LOGDEBUG2, "Adjusted timestamp: %d", packet.m_nTimeStamp);
+
+          /* check the header if we get one */
+          if (r->m_read.nMetaHeaderSize > 0
+              && packet.m_packetType == RTMP_PACKET_TYPE_INFO)
+            {
+              AMFObject metaObj;
+              int nRes = AMF_Decode(&metaObj, packetBody, nPacketLen, FALSE);
+              if (nRes >= 0)
+                {
+                  AVal metastring;
+                  AMFProp_GetString(AMF_GetProp(&metaObj, NULL, 0), &metastring);
+
+                  if (AVMATCH(&metastring, &av_onMetaData))
+                    {
+                      /* compare */
+                      if ((r->m_read.nMetaHeaderSize != nPacketLen) ||
+                          (memcmp(r->m_read.metaHeader, packetBody, r->m_read.nMetaHeaderSize) != 0))
+                        {
+                          ret = RTMP_READ_ERROR;
+                        }
+                    }
+                  AMF_Reset(&metaObj);
+                  if (ret == RTMP_READ_ERROR)
+                    break;
+                }
+            }
 
-	      /* check first keyframe to make sure we got the right position
-	       * in the stream! (the first non ignored frame)
-	       */
-	      if (r->m_read.nInitialFrameSize > 0)
-		{
-		  /* video or audio data */
-		  if (packet.m_packetType == r->m_read.initialFrameType
-		      && r->m_read.nInitialFrameSize == nPacketLen)
-		    {
-		      /* we don't compare the sizes since the packet can
-		       * contain several FLV packets, just make sure the
-		       * first frame is our keyframe (which we are going
-		       * to rewrite)
-		       */
-		      if (memcmp
-			  (r->m_read.initialFrame, packetBody,
-			   r->m_read.nInitialFrameSize) == 0)
-			{
-			  RTMP_Log(RTMP_LOGDEBUG, "Checked keyframe successfully!");
-			  r->m_read.flags |= RTMP_READ_GOTKF;
-			  /* ignore it! (what about audio data after it? it is
-			   * handled by ignoring all 0ms frames, see below)
-			   */
-			  ret = RTMP_READ_IGNORE;
-			  break;
-			}
-		    }
+          /* check first keyframe to make sure we got the right position
+           * in the stream! (the first non ignored frame)
+           */
+          RTMP_Log(RTMP_LOGDEBUG2, "Required packet length: %d, Packet length: %d",
+                   r->m_read.nInitialFrameSize, nPacketLen);
+          if (r->m_read.nInitialFrameSize > 0)
+            {
+              /* video or audio data */
+              if (packet.m_packetType == r->m_read.initialFrameType
+                  && r->m_read.nInitialFrameSize == nPacketLen)
+                {
+                  /* we don't compare the sizes since the packet can
+                   * contain several FLV packets, just make sure the
+                   * first frame is our keyframe (which we are going
+                   * to rewrite)
+                   */
+                  RTMP_Log(RTMP_LOGDEBUG2, "Comparing keyframe data");
+                  if (memcmp(r->m_read.initialFrame, packetBody,
+                             r->m_read.nInitialFrameSize) == 0)
+                    {
+                      RTMP_Log(RTMP_LOGDEBUG, "Checked keyframe successfully!");
+                      r->m_read.flags |= RTMP_READ_GOTKF;
+                      r->m_read.nResumeDriftTS = packet.m_nTimeStamp;
+                      /* ignore it! (what about audio data after it? it is
+                       * handled by ignoring all 0ms frames, see below)
+                       */
+                      ret = RTMP_READ_IGNORE;
+                      break;
+                    }
+                }
 
-		  /* hande FLV streams, even though the server resends the
-		   * keyframe as an extra video packet it is also included
-		   * in the first FLV stream chunk and we have to compare
-		   * it and filter it out !!
-		   */
-		  if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO)
-		    {
-		      /* basically we have to find the keyframe with the
-		       * correct TS being nResumeTS
-		       */
-		      unsigned int pos = 0;
-		      uint32_t ts = 0;
-
-		      while (pos + 11 < nPacketLen)
-			{
-			  /* size without header (11) and prevTagSize (4) */
-			  uint32_t dataSize =
-			    AMF_DecodeInt24(packetBody + pos + 1);
-			  ts = AMF_DecodeInt24(packetBody + pos + 4);
-			  ts |= (packetBody[pos + 7] << 24);
+              /* hande FLV streams, even though the server resends the
+               * keyframe as an extra video packet it is also included
+               * in the first FLV stream chunk and we have to compare
+               * it and filter it out !!
+               */
+              if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO)
+                {
+                  /* basically we have to find the keyframe with the
+                   * correct TS being nResumeTS
+                   */
+                  unsigned int pos = 0;
+                  uint32_t ts = 0;
+
+                  while (pos + 11 < nPacketLen)
+                    {
+                      /* size without header (11) and prevTagSize (4) */
+                      uint32_t dataSize = AMF_DecodeInt24(packetBody + pos + 1);
+                      ts = AMF_DecodeInt24(packetBody + pos + 4);
+                      ts |= (packetBody[pos + 7] << 24);
 
 #ifdef _DEBUG
-			  RTMP_Log(RTMP_LOGDEBUG,
-			      "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
-			      packetBody[pos], dataSize, ts);
+                      RTMP_Log(RTMP_LOGDEBUG,
+                               "keyframe search: FLV Packet: type %02X, dataSize: %d, timeStamp: %d ms",
+                               packetBody[pos], dataSize, ts);
 #endif
-			  /* ok, is it a keyframe?:
-			   * well doesn't work for audio!
-			   */
-			  if (packetBody[pos /*6928, test 0 */ ] ==
-			      r->m_read.initialFrameType
-			      /* && (packetBody[11]&0xf0) == 0x10 */ )
-			    {
-			      if (ts == r->m_read.nResumeTS)
-				{
-				  RTMP_Log(RTMP_LOGDEBUG,
-				      "Found keyframe with resume-keyframe timestamp!");
-				  if (r->m_read.nInitialFrameSize != dataSize
-				      || memcmp(r->m_read.initialFrame,
-						packetBody + pos + 11,
-						r->m_read.
-						nInitialFrameSize) != 0)
-				    {
-				      RTMP_Log(RTMP_LOGERROR,
-					  "FLV Stream: Keyframe doesn't match!");
-				      ret = RTMP_READ_ERROR;
-				      break;
-				    }
-				  r->m_read.flags |= RTMP_READ_GOTFLVK;
-
-				  /* skip this packet?
-				   * check whether skippable:
-				   */
-				  if (pos + 11 + dataSize + 4 > nPacketLen)
-				    {
-				      RTMP_Log(RTMP_LOGWARNING,
-					  "Non skipable packet since it doesn't end with chunk, stream corrupt!");
-				      ret = RTMP_READ_ERROR;
-				      break;
-				    }
-				  packetBody += (pos + 11 + dataSize + 4);
-				  nPacketLen -= (pos + 11 + dataSize + 4);
-
-				  goto stopKeyframeSearch;
-
-				}
-			      else if (r->m_read.nResumeTS < ts)
-				{
-				  /* the timestamp ts will only increase with
-				   * further packets, wait for seek
-				   */
-				  goto stopKeyframeSearch;
-				}
-			    }
-			  pos += (11 + dataSize + 4);
-			}
-		      if (ts < r->m_read.nResumeTS)
-			{
-			  RTMP_Log(RTMP_LOGERROR,
-			      "First packet does not contain keyframe, all "
-			      "timestamps are smaller than the keyframe "
-			      "timestamp; probably the resume seek failed?");
-			}
-		    stopKeyframeSearch:
-		      ;
-		      if (!(r->m_read.flags & RTMP_READ_GOTFLVK))
-			{
-			  RTMP_Log(RTMP_LOGERROR,
-			      "Couldn't find the seeked keyframe in this chunk!");
-			  ret = RTMP_READ_IGNORE;
-			  break;
-			}
-		    }
-		}
-	    }
+                      /* ok, is it a keyframe?:
+                       * well doesn't work for audio!
+                       */
+                      if (packetBody[pos /*6928, test 0 */ ] == r->m_read.initialFrameType
+                          /* && (packetBody[11]&0xf0) == 0x10 */)
+                        {
+                          if (ts == r->m_read.nResumeTS)
+                            {
+                              RTMP_Log(RTMP_LOGDEBUG, "Found keyframe with resume-keyframe timestamp!");
+                              if (r->m_read.nInitialFrameSize != dataSize ||
+                                  memcmp(r->m_read.initialFrame, packetBody + pos + 11,
+                                         r->m_read.nInitialFrameSize) != 0)
+                                {
+                                  RTMP_Log(RTMP_LOGERROR, "FLV Stream: Keyframe doesn't match!");
+                                  ret = RTMP_READ_ERROR;
+                                  break;
+                                }
+                              r->m_read.flags |= RTMP_READ_GOTFLVK;
+
+                              /* skip this packet?
+                               * check whether skippable:
+                               */
+                              if (pos + 11 + dataSize + 4 > nPacketLen)
+                                {
+                                  RTMP_Log(RTMP_LOGWARNING, "Non skipable packet since it doesn't "
+                                           "end with chunk, stream corrupt!");
+                                  ret = RTMP_READ_ERROR;
+                                  break;
+                                }
+                              packetBody += (pos + 11 + dataSize + 4);
+                              nPacketLen -= (pos + 11 + dataSize + 4);
+
+                              goto stopKeyframeSearch;
+
+                            }
+                          else if (r->m_read.nResumeTS < ts)
+                            {
+                              /* the timestamp ts will only increase with
+                               * further packets, wait for seek
+                               */
+                              goto stopKeyframeSearch;
+                            }
+                        }
+                      pos += (11 + dataSize + 4);
+                    }
+                  if (ts < r->m_read.nResumeTS)
+                    {
+                      RTMP_Log(RTMP_LOGERROR,
+                               "First packet does not contain keyframe, all "
+                               "timestamps are smaller than the keyframe "
+                               "timestamp; probably the resume seek failed?");
+                    }
+                stopKeyframeSearch:
+                  if (!(r->m_read.flags & RTMP_READ_GOTFLVK))
+                    {
+                      RTMP_Log(RTMP_LOGERROR, "Couldn't find the seeked keyframe in this chunk!");
+                      ret = RTMP_READ_IGNORE;
+                      break;
+                    }
+                }
+            }
 
 	  if (packet.m_nTimeStamp > 0
 	      && (r->m_read.flags & (RTMP_READ_GOTKF|RTMP_READ_GOTFLVK)))
@@ -4972,7 +5673,7 @@ static const char flvHeader[] = { 'F', 'L', 'V', 0x01,
   0x00, 0x00, 0x00, 0x00
 };
 
-#define HEADERBUF	(128*1024)
+#define HEADERBUF	(1024*1024)
 int
 RTMP_Read(RTMP *r, char *buf, int size)
 {
@@ -5175,3 +5876,395 @@ RTMP_Write(RTMP *r, const char *buf, int size)
     }
   return size+s2;
 }
+
+AVal
+AVcopy(AVal src)
+{
+  AVal dst;
+  if (src.av_len)
+    {
+      dst.av_val = malloc(src.av_len + 1);
+      memcpy(dst.av_val, src.av_val, src.av_len);
+      dst.av_val[src.av_len] = '\0';
+      dst.av_len = src.av_len;
+    }
+  else
+    {
+      dst.av_val = NULL;
+      dst.av_len = 0;
+    }
+  return dst;
+}
+
+static int
+ConnectSocket(RTMP *r)
+{
+  int on = 1;
+  struct sockaddr_in service;
+  if (!r->Link.hostname.av_len)
+    return FALSE;
+
+  memset(&service, 0, sizeof (struct sockaddr_in));
+  service.sin_family = AF_INET;
+
+  if (r->Link.socksport)
+    {
+      /* Connect via SOCKS */
+      if (!add_addr_info(&service, &r->Link.sockshost, r->Link.socksport))
+        return FALSE;
+    }
+  else
+    {
+      /* Connect directly */
+      if (!add_addr_info(&service, &r->Link.hostname, r->Link.port))
+        return FALSE;
+    }
+
+  r->m_sb.sb_socket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP);
+  if (r->m_sb.sb_socket != -1)
+    {
+      if (connect(r->m_sb.sb_socket, (struct sockaddr *) &service, sizeof (struct sockaddr)) < 0)
+        {
+          int err = GetSockError();
+          RTMP_Log(RTMP_LOGERROR, "%s, failed to connect socket. %d (%s)",
+                   __FUNCTION__, err, strerror(err));
+          RTMP_Close(r);
+          return FALSE;
+        }
+
+      if (r->Link.socksport)
+        {
+          RTMP_Log(RTMP_LOGDEBUG, "%s ... SOCKS negotiation", __FUNCTION__);
+          if (!SocksNegotiate(r))
+            {
+              RTMP_Log(RTMP_LOGERROR, "%s, SOCKS negotiation failed.", __FUNCTION__);
+              RTMP_Close(r);
+              return FALSE;
+            }
+        }
+    }
+  else
+    {
+      RTMP_Log(RTMP_LOGERROR, "%s, failed to create socket. Error: %d",
+               __FUNCTION__, GetSockError());
+      return FALSE;
+    }
+
+  /* set timeout */
+  SET_RCVTIMEO(tv, r->Link.timeout);
+  if (setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_RCVTIMEO, (char *) &tv, sizeof (tv)))
+    {
+      RTMP_Log(RTMP_LOGERROR, "%s, Setting socket timeout to %d failed!",
+               __FUNCTION__, r->Link.timeout);
+    }
+
+  setsockopt(r->m_sb.sb_socket, IPPROTO_TCP, TCP_NODELAY, (char *) &on, sizeof (on));
+  if (r->Link.protocol & RTMP_FEATURE_HTTP)
+    setsockopt(r->m_sb.sb_socket, SOL_SOCKET, SO_KEEPALIVE, (char *) &on, sizeof (on));
+
+  return TRUE;
+}
+
+static int
+SendCommand(RTMP *r, char *method, int queue)
+{
+  char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc;
+  AVal av_command, methodName;
+
+  enc = pbuf;
+  methodName.av_val = method;
+  methodName.av_len = strlen(method);
+  enc = AMF_EncodeString(enc, pend, &methodName);
+  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+  *enc++ = AMF_NULL;
+  av_command.av_val = pbuf;
+  av_command.av_len = enc - pbuf;
+
+  return SendInvoke(r, &av_command, queue);
+}
+
+static int
+SendGetStreamLength(RTMP *r)
+{
+  char pbuf[256], *pend = pbuf + sizeof (pbuf), *enc;
+  AVal av_Command;
+  SAVC(getStreamLength);
+
+  enc = pbuf;
+  enc = AMF_EncodeString(enc, pend, &av_getStreamLength);
+  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+  *enc++ = AMF_NULL;
+  enc = AMF_EncodeString(enc, pend, &r->Link.playpath);
+  av_Command.av_val = pbuf;
+  av_Command.av_len = enc - pbuf;
+
+  return SendInvoke(r, &av_Command, TRUE);
+}
+
+static int
+SendInvoke(RTMP *r, AVal *command, int queue)
+{
+  RTMPPacket packet;
+  char pbuf[512], *enc;
+
+  packet.m_nChannel = 0x03; /* control channel (invoke) */
+  packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
+  packet.m_nTimeStamp = 0;
+  packet.m_nInfoField2 = 0;
+  packet.m_hasAbsTimestamp = 0;
+  packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
+
+  enc = packet.m_body;
+  if (command->av_len)
+    {
+      memcpy(enc, command->av_val, command->av_len);
+      enc += command->av_len;
+    }
+  else
+    return FALSE;
+  packet.m_nBodySize = enc - packet.m_body;
+
+  return RTMP_SendPacket(r, &packet, queue);
+}
+
+AVal
+StripParams(AVal *src)
+{
+  AVal str;
+  if (src->av_val)
+    {
+      str.av_val = calloc(src->av_len + 1, sizeof (char));
+      strncpy(str.av_val, src->av_val, src->av_len);
+      str.av_len = src->av_len;
+      char *start = str.av_val;
+      char *end = start + str.av_len;
+      char *ptr = start;
+
+      while (ptr < end)
+        {
+          if (*ptr == '?')
+            {
+              str.av_len = ptr - start;
+              break;
+            }
+          ptr++;
+        }
+      memset(start + str.av_len, 0, 1);
+
+      char *dynamic = strstr(start, "[[DYNAMIC]]");
+      if (dynamic)
+        {
+          dynamic -= 1;
+          memset(dynamic, 0, 1);
+          str.av_len = dynamic - start;
+          end = start + str.av_len;
+        }
+
+      char *import = strstr(start, "[[IMPORT]]");
+      if (import)
+        {
+          str.av_val = import + 11;
+          strcpy(start, "http://");
+          str.av_val = strcat(start, str.av_val);
+          str.av_len = strlen(str.av_val);
+        }
+      return str;
+    }
+  str = *src;
+  return str;
+}
+
+char *
+strreplace(char *srcstr, int srclen, char *orig, char *repl, int didAlloc)
+{
+  char *ptr = NULL, *sptr = srcstr;
+  int origlen = strlen(orig);
+  int repllen = strlen(repl);
+  if (!srclen)
+    srclen = strlen(srcstr);
+  char *srcend = srcstr + srclen;
+  int dstbuffer = srclen / origlen * repllen;
+  if (dstbuffer < srclen)
+    dstbuffer = srclen;
+  char *dststr = calloc(dstbuffer + 1, sizeof (char));
+  char *dptr = dststr;
+
+  if ((ptr = strstr(srcstr, orig)))
+    {
+      while (ptr < srcend && (ptr = strstr(sptr, orig)))
+        {
+          int len = ptr - sptr;
+          memcpy(dptr, sptr, len);
+          sptr += len + origlen;
+          dptr += len;
+          memcpy(dptr, repl, repllen);
+          dptr += repllen;
+        }
+      memcpy(dptr, sptr, srcend - sptr);
+      if (didAlloc)
+        free(srcstr);
+      return dststr;
+    }
+
+  memcpy(dststr, srcstr, srclen);
+  if (didAlloc)
+    free(srcstr);
+  return dststr;
+}
+
+int
+strsplit(char *src, int srclen, char delim, char ***params)
+{
+  char *sptr, *srcbeg, *srcend, *dstr;
+  int count = 1, i = 0, len = 0;
+
+  if (src == NULL)
+    return 0;
+  if (!srclen)
+    srclen = strlen(src);
+  srcbeg = src;
+  srcend = srcbeg + srclen;
+  sptr = srcbeg;
+
+  /* count the delimiters */
+  while (sptr < srcend)
+    {
+      if (*sptr++ == delim)
+        count++;
+    }
+  sptr = srcbeg;
+  *params = malloc(count * sizeof (size_t));
+  char **param = *params;
+
+  for (i = 0; i < (count - 1); i++)
+    {
+      dstr = strchr(sptr, delim);
+      len = dstr - sptr;
+      param[i] = malloc((len + 1) * sizeof (char));
+      memcpy(param[i], sptr, len);
+      *(param[i] + len) = '\0';
+      sptr += len + 1;
+    }
+
+  /* copy the last string */
+  if (sptr <= srcend)
+    {
+      len = srclen - (sptr - srcbeg);
+      param[i] = malloc((len + 1) * sizeof (char));
+      memcpy(param[i], sptr, len);
+      *(param[i] + len) = '\0';
+    }
+  return count;
+}
+
+void
+TransformRot13(AMFObject *obj, AVal *rindex, AVal *r)
+{
+  char *chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZABCDEFGHIJKLMabcdefghijklmnopqrstuvwxyzabcdefghijklm";
+  int i = 0, pos = 0;
+  AMFObject obj2;
+
+  AMFProp_GetObject(AMF_GetProp(obj, NULL, 3), &obj2);
+  AMFProp_GetString(AMF_GetProp(&obj2, rindex, -1), r);
+
+  for (i = 0; i < r->av_len; i++)
+    {
+      char *chr = &r->av_val[i];
+      chr = strchr(chars, *chr);
+      pos = chr ? chr - chars : -1;
+      if (pos > -1)
+        r->av_val[i] = chars[pos + 13];
+    }
+}
+
+void
+__TeaCrypt(uint32_t *block, uint32_t len, uint32_t *key)
+{
+  uint32_t z = block[len - 1], y = block[0], sum = 0, e, DELTA = 0x9e3779b9;
+  int32_t p, q;
+
+  q = 6 + 52 / len;
+  while (q-- > 0)
+    {
+      sum += DELTA;
+      e = (sum >> 2) & 3;
+      for (p = 0; p < len - 1; p++)
+        {
+          y = block[p + 1];
+          block[p] += ((z >> 5^y << 2) + (y >> 3^z << 4)) ^ ((sum^y) + (key[(p & 3)^e] ^ z));
+          z = block[p];
+        }
+      y = block[0];
+      block[len - 1] += ((z >> 5^y << 2) + (y >> 3^z << 4)) ^ ((sum^y) + (key[(p & 3)^e] ^ z));
+      z = block[len - 1];
+    }
+}
+
+AVal
+TeaEncrypt(AVal *srcData, AVal *srcKey)
+{
+  int i, reqPadding, longKeyBlocks, longDataBlocks;
+  unsigned char *key, *data;
+
+  // Prepare key
+  int srcKeyLen = srcKey->av_len;
+  int reqKeyLen = 16;
+  reqPadding = reqKeyLen - srcKeyLen;
+  if (reqPadding < 0)
+    {
+      reqPadding = 0;
+      srcKeyLen = reqKeyLen;
+    }
+  key = calloc((srcKeyLen + reqPadding + 1), sizeof (char));
+  memcpy(key, srcKey->av_val, srcKeyLen);
+  longKeyBlocks = reqKeyLen / 4;
+  uint32_t *longKeyBuf = (uint32_t *) malloc(longKeyBlocks * sizeof (uint32_t));
+  for (i = 0; i < longKeyBlocks; i++)
+    {
+      longKeyBuf[i] = 0;
+      longKeyBuf[i] |= (key[i * 4 + 0]) | (key[i * 4 + 1] << 8) | (key[i * 4 + 2] << 16) | (key[i * 4 + 3] << 24);
+    }
+
+  // Prepare data
+  int srcDataLen = srcData->av_len;
+  reqPadding = ((int) ((srcDataLen + 3) / 4))*4 - srcDataLen;
+  if ((srcDataLen + reqPadding) < 8)
+    reqPadding = 8 - srcDataLen;
+  data = calloc((srcDataLen + reqPadding + 1), sizeof (char));
+  memcpy(data, srcData->av_val, srcDataLen);
+  longDataBlocks = (srcDataLen + reqPadding) / 4;
+  uint32_t *longDataBuf = malloc(longDataBlocks * sizeof (uint32_t));
+  for (i = 0; i < longDataBlocks; i++)
+    {
+      longDataBuf[i] = 0;
+      longDataBuf[i] |= (data[i * 4 + 0]) | (data[i * 4 + 1] << 8) | (data[i * 4 + 2] << 16) | (data[i * 4 + 3] << 24);
+    }
+
+  // Encrypt data
+  __TeaCrypt(longDataBuf, longDataBlocks, longKeyBuf);
+
+  // Convert data back to char array
+  for (i = 0; i < longDataBlocks; i++)
+    {
+      data[i * 4 + 0] = longDataBuf[i] & 0xFF;
+      data[i * 4 + 1] = (longDataBuf[i] >> 8) & 0xFF;
+      data[i * 4 + 2] = (longDataBuf[i] >> 16) & 0xFF;
+      data[i * 4 + 3] = (longDataBuf[i] >> 24) & 0xFF;
+    }
+
+  // Convert to hex string
+  AVal hexData;
+  hexData.av_val = calloc((longDataBlocks * 4 * 2) + 1, sizeof (char));
+  for (i = 0; i < (longDataBlocks * 4); i++)
+    sprintf(&hexData.av_val[i * 2], "%.2X", data[i]);
+  hexData.av_len = strlen(hexData.av_val);
+
+  // Free allocated resources
+  free(key);
+  free(longKeyBuf);
+  free(data);
+  free(longDataBuf);
+
+  return hexData;
+}
diff --git librtmp/rtmp.h librtmp/rtmp.h
index 0248913..3e573da 100644
--- librtmp/rtmp.h
+++ librtmp/rtmp.h
@@ -150,12 +150,15 @@ extern "C"
     AVal playpath;	/* passed in explicitly */
     AVal tcUrl;
     AVal swfUrl;
+    AVal swfHash;
     AVal pageUrl;
     AVal app;
     AVal auth;
     AVal flashVer;
     AVal subscribepath;
+    AVal ccomm;
     AVal usherToken;
+    AVal WeebToken;
     AVal token;
     AVal pubUser;
     AVal pubPasswd;
@@ -175,9 +178,18 @@ extern "C"
     int lFlags;
 
     int swfAge;
+    int swfSize;
 
     int protocol;
+    int ConnectPacket;
+    int CombineConnectPacket;
+    int redirected;
     int timeout;		/* connection timeout in seconds */
+    int dynamicPublish;
+    AVal dynamicCommand;
+    AVal Extras;
+    AVal HandshakeResponse;
+    double publishId;
 
     int pFlags;			/* unused, but kept to avoid breaking ABI */
 
@@ -220,6 +232,7 @@ extern "C"
     /* if bResume == TRUE */
     uint8_t initialFrameType;
     uint32_t nResumeTS;
+    uint32_t nResumeDriftTS;
     char *metaHeader;
     char *initialFrame;
     uint32_t nMetaHeaderSize;
@@ -306,6 +319,8 @@ extern "C"
 			AVal *flashVer,
 			AVal *subscribepath,
 			AVal *usherToken,
+			AVal *WeebToken,
+			AVal *ccomm,
 			int dStart,
 			int dStop, int bLiveStream, long int timeout);
 
@@ -371,6 +386,11 @@ extern "C"
   int RTMP_HashSWF(const char *url, unsigned int *size, unsigned char *hash,
 		   int age);
 
+  AVal AVcopy(AVal src);
+  AVal StripParams(AVal *src);
+  char *strreplace(char *srcstr, int srclen, char *orig, char *repl, int didAlloc);
+  int strsplit(char *src, int srclen, char delim, char ***params);
+
 #ifdef __cplusplus
 };
 #endif
diff --git librtmp/rtmp_sys.h librtmp/rtmp_sys.h
index 85d7e53..b2a3438 100644
--- librtmp/rtmp_sys.h
+++ librtmp/rtmp_sys.h
@@ -65,6 +65,7 @@
 #include <polarssl/net.h>
 #include <polarssl/ssl.h>
 #include <polarssl/havege.h>
+#include <polarssl/md5.h>
 #if POLARSSL_VERSION_NUMBER < 0x01010000
 #define havege_random	havege_rand
 #endif
@@ -105,6 +106,7 @@ typedef struct tls_server_ctx {
 #define TLS_write(s,b,l)	ssl_write(s,(unsigned char *)b,l)
 #define TLS_shutdown(s)	ssl_close_notify(s)
 #define TLS_close(s)	ssl_free(s); free(s)
+#define md5_hash(i, ilen, o) md5(i, ilen, o)
 
 #elif defined(USE_GNUTLS)
 #include <gnutls/gnutls.h>
@@ -122,6 +124,8 @@ typedef struct tls_ctx {
 #define TLS_write(s,b,l)	gnutls_record_send(s,b,l)
 #define TLS_shutdown(s)	gnutls_bye(s, GNUTLS_SHUT_RDWR)
 #define TLS_close(s)	gnutls_deinit(s)
+#define md5_hash(i, ilen, o) gnutls_digest_algorithm_t algorithm = GNUTLS_DIG_MD5;\
+                             gnutls_hash_fast(algorithm, i, ilen, o);
 
 #else	/* USE_OPENSSL */
 #define TLS_CTX	SSL_CTX *
@@ -134,6 +138,7 @@ typedef struct tls_ctx {
 #define TLS_write(s,b,l)	SSL_write(s,b,l)
 #define TLS_shutdown(s)	SSL_shutdown(s)
 #define TLS_close(s)	SSL_free(s)
+#define md5_hash(i, ilen, o) MD5(i, ilen, o)
 
 #endif
 #endif
diff --git rtmpdump.c rtmpdump.c
index 13741a7..b3ae33f 100644
--- rtmpdump.c
+++ rtmpdump.c
@@ -36,6 +36,9 @@
 #ifdef WIN32
 #define fseeko fseeko64
 #define ftello ftello64
+#ifdef __MINGW32__
+#define off_t off64_t
+#endif
 #include <io.h>
 #include <fcntl.h>
 #define	SET_BINMODE(f)	setmode(fileno(f), O_BINARY)
@@ -67,7 +70,7 @@ InitSockets()
 #endif
 }
 
-inline void
+static inline void
 CleanupSockets()
 {
 #ifdef WIN32
@@ -148,9 +151,9 @@ OpenResumeFile(const char *flvFile,	// file name [in]
   if (!*file)
     return RD_SUCCESS;		// RD_SUCCESS, because we go to fresh file mode instead of quiting
 
-  fseek(*file, 0, SEEK_END);
+  fseeko(*file, 0, SEEK_END);
   *size = ftello(*file);
-  fseek(*file, 0, SEEK_SET);
+  fseeko(*file, 0, SEEK_SET);
 
   if (*size > 0)
     {
@@ -178,7 +181,7 @@ OpenResumeFile(const char *flvFile,	// file name [in]
 	}
 
       uint32_t dataOffset = AMF_DecodeInt32(hbuf + 5);
-      fseek(*file, dataOffset, SEEK_SET);
+      fseeko(*file, dataOffset, SEEK_SET);
 
       if (fread(hbuf, 1, 4, *file) != 4)
 	{
@@ -283,18 +286,24 @@ GetLastKeyframe(FILE * file,	// output file [in]
   uint8_t dataType;
   int bAudioOnly;
   off_t size;
+  char *syncbuf, *p;
 
-  fseek(file, 0, SEEK_END);
+  fseeko(file, 0, SEEK_END);
   size = ftello(file);
+  if (size <= 0)
+    {
+      dSeek = 0;
+      return RD_SUCCESS;
+    }
 
-  fseek(file, 4, SEEK_SET);
+  fseeko(file, 4, SEEK_SET);
   if (fread(&dataType, sizeof(uint8_t), 1, file) != 1)
     return RD_FAILED;
 
   bAudioOnly = (dataType & 0x4) && !(dataType & 0x1);
 
-  RTMP_Log(RTMP_LOGDEBUG, "bAudioOnly: %d, size: %llu", bAudioOnly,
-      (unsigned long long) size);
+  RTMP_Log(RTMP_LOGDEBUG, "bAudioOnly: %d, size: %lu", bAudioOnly,
+           (unsigned long) size);
 
   // ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams)
 
@@ -326,6 +335,51 @@ GetLastKeyframe(FILE * file,	// output file [in]
       prevTagSize = AMF_DecodeInt32(buffer);
       //RTMP_Log(RTMP_LOGDEBUG, "Last packet: prevTagSize: %d", prevTagSize);
 
+      if (prevTagSize <= 0 || prevTagSize > size - 4 - 13)
+        {
+          /* Last packet was not fully received - try to sync to last tag */
+          prevTagSize = 0;
+          tsize = size > 0x100000 ? 0x100000 : size; /* 1MB should be enough for 3500K bitrates */
+          if (tsize > 13 + 15)
+            {
+              tsize -= 13; // do not read header
+              syncbuf = (char *) malloc(tsize);
+              if (syncbuf)
+                {
+                  fseeko(file, size - tsize, SEEK_SET);
+                  if (fread(syncbuf, 1, tsize, file) == tsize)
+                    {
+                      p = syncbuf + tsize;
+                      while (p >= syncbuf + 15)
+                        {
+                          /* Check for StreamID */
+                          if (AMF_DecodeInt24(p - 7) == 0)
+                            {
+                              /* Check for Audio/Video/Script */
+                              dataType = p[-15] & 0x1F;
+                              if (dataType == 8 || dataType == 9 || dataType == 18)
+                                {
+                                  prevTagSize = AMF_DecodeInt24(p - 14);
+                                  if ((prevTagSize < tsize) && (p + prevTagSize + 11 <= syncbuf + tsize - 4)
+                                      && (AMF_DecodeInt32(p - 4 + prevTagSize) == prevTagSize + 11))
+                                    {
+                                      prevTagSize = syncbuf + tsize - p + 15;
+                                      RTMP_Log(RTMP_LOGDEBUG, "Sync success - found last tag at 0x%x", (uint32_t) (size - prevTagSize));
+                                      prevTagSize -= 4;
+                                      tsize = 0;
+                                      break;
+                                    }
+                                  else
+                                    prevTagSize = 0;
+                                }
+                            }
+                          --p;
+                        }
+                    }
+                  free(syncbuf);
+                }
+            }
+        }
       if (prevTagSize == 0)
 	{
 	  RTMP_Log(RTMP_LOGERROR, "Couldn't find keyframe to resume from!");
@@ -703,8 +757,12 @@ void usage(char *prog)
 	  RTMP_LogPrintf
 	    ("--token|-T key          Key for SecureToken response\n");
 	  RTMP_LogPrintf
+	    ("--ccommand|-K key       Send custom command before play\n");
+	  RTMP_LogPrintf
 	    ("--jtv|-j JSON           Authentication token for Justin.tv legacy servers\n");
 	  RTMP_LogPrintf
+	    ("--weeb|-J string        Authentication token for weeb.tv servers\n");
+	  RTMP_LogPrintf
 	    ("--hashes|-#             Display progress with hashes, not with the byte counter\n");
 	  RTMP_LogPrintf
 	    ("--buffer|-b             Buffer time in milliseconds (default: %u)\n",
@@ -751,7 +809,9 @@ main(int argc, char **argv)
   AVal hostname = { 0, 0 };
   AVal playpath = { 0, 0 };
   AVal subscribepath = { 0, 0 };
-  AVal usherToken = { 0, 0 }; //Justin.tv auth token
+  AVal usherToken = { 0, 0 }; // Justin.tv auth token
+  AVal WeebToken = { 0, 0 };  // Weeb.tv auth token
+  AVal ccomm = { 0, 0 };
   int port = -1;
   int protocol = RTMP_PROTOCOL_UNDEFINED;
   int retries = 0;
@@ -853,17 +913,19 @@ main(int argc, char **argv)
     {"start", 1, NULL, 'A'},
     {"stop", 1, NULL, 'B'},
     {"token", 1, NULL, 'T'},
+    {"ccommand", 1, NULL, 'K'},
     {"hashes", 0, NULL, '#'},
     {"debug", 0, NULL, 'z'},
     {"quiet", 0, NULL, 'q'},
     {"verbose", 0, NULL, 'V'},
     {"jtv", 1, NULL, 'j'},
+    {"weeb", 1, NULL, 'J'},
     {0, 0, 0, 0}
   };
 
   while ((opt =
 	  getopt_long(argc, argv,
-		      "hVveqzRr:s:t:i:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:",
+                      "hVveqzRr:s:t:i:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:K:w:x:W:X:S:#j:J:",
 		      longopts, NULL)) != -1)
     {
       switch (opt)
@@ -995,7 +1057,7 @@ main(int argc, char **argv)
 		  port = parsedPort;
 		if (playpath.av_len == 0 && parsedPlaypath.av_len)
 		  {
-		    playpath = parsedPlaypath;
+                    playpath = AVcopy(parsedPlaypath);
 		  }
 		if (protocol == RTMP_PROTOCOL_UNDEFINED)
 		  protocol = parsedProtocol;
@@ -1061,6 +1123,9 @@ main(int argc, char **argv)
 	  RTMP_SetOpt(&rtmp, &av_token, &token);
 	  }
 	  break;
+        case 'K':
+          STR2AVAL(ccomm, optarg);
+          break;
 	case '#':
 	  bHashes = TRUE;
 	  break;
@@ -1079,6 +1144,9 @@ main(int argc, char **argv)
 	case 'j':
 	  STR2AVAL(usherToken, optarg);
 	  break;
+	case 'J':
+	  STR2AVAL(WeebToken, optarg);
+	  break;
 	default:
 	  RTMP_LogPrintf("unknown option: %c\n", opt);
 	  usage(argv[0]);
@@ -1170,14 +1238,14 @@ main(int argc, char **argv)
 
   if (tcUrl.av_len == 0)
     {
-	  tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) +
-	  	hostname.av_len + app.av_len + sizeof("://:65535/");
+      tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) +
+              hostname.av_len + app.av_len + sizeof ("://:65535/");
       tcUrl.av_val = (char *) malloc(tcUrl.av_len);
-	  if (!tcUrl.av_val)
-	    return RD_FAILED;
+      if (!tcUrl.av_val)
+        return RD_FAILED;
       tcUrl.av_len = snprintf(tcUrl.av_val, tcUrl.av_len, "%s://%.*s:%d/%.*s",
-	  	   RTMPProtocolStringsLower[protocol], hostname.av_len,
-		   hostname.av_val, port, app.av_len, app.av_val);
+                              RTMPProtocolStringsLower[protocol], hostname.av_len,
+                              hostname.av_val, port, app.av_len, app.av_val);
     }
 
   int first = 1;
@@ -1197,8 +1265,9 @@ main(int argc, char **argv)
   if (!fullUrl.av_len)
     {
       RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath,
-		       &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
-		       &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout);
+                       &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
+                       &flashVer, &subscribepath, &usherToken, &WeebToken, &ccomm,
+                       dSeek, dStopOffset, bLiveStream, timeout);
     }
   else
     {
diff --git rtmpgw.c rtmpgw.c
index 3e47602..e56b855 100644
--- rtmpgw.c
+++ rtmpgw.c
@@ -96,7 +96,9 @@ typedef struct
   AVal flashVer;
   AVal token;
   AVal subscribepath;
-  AVal usherToken; //Justin.tv auth token
+  AVal ccomm;
+  AVal usherToken; // Justin.tv auth token
+  AVal WeebToken;  // Weeb.tv auth token
   AVal sockshost;
   AMFObject extras;
   int edepth;
@@ -556,8 +558,8 @@ void processTCPrequest(STREAMING_SERVER * server,	// server socket and state (ou
   if (!req.fullUrl.av_len)
     {
       RTMP_SetupStream(&rtmp, req.protocol, &req.hostname, req.rtmpport, &req.sockshost,
-		       &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, &req.usherToken, dSeek, req.dStopOffset,
-		       req.bLiveStream, req.timeout);
+                       &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath,
+                       &req.usherToken, &req.WeebToken, &req.ccomm, dSeek, req.dStopOffset, req.bLiveStream, req.timeout);
     }
   else
     {
@@ -972,6 +974,12 @@ ParseOption(char opt, char *arg, RTMP_REQUEST * req)
     case 'j':
       STR2AVAL(req->usherToken, arg);
       break;
+    case 'J':
+      STR2AVAL(req->WeebToken, arg);
+      break;
+    case 'K':
+      STR2AVAL(req->ccomm, arg);
+      break;
     default:
       RTMP_LogPrintf("unknown option: %c, arg: %s\n", opt, arg);
       return FALSE;
@@ -1044,6 +1052,8 @@ main(int argc, char **argv)
     {"quiet", 0, NULL, 'q'},
     {"verbose", 0, NULL, 'V'},
     {"jtv", 1, NULL, 'j'},
+    {"weeb", 1, NULL, 'J'},
+    {"ccommand", 1, NULL, 'K'},
     {0, 0, 0, 0}
   };
 
@@ -1056,7 +1066,7 @@ main(int argc, char **argv)
 
   while ((opt =
 	  getopt_long(argc, argv,
-		      "hvqVzr:s:t:i:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:j:", longopts,
+                      "hvqVzr:s:t:i:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:j:J:", longopts,
 		      NULL)) != -1)
     {
       switch (opt)
@@ -1119,8 +1129,12 @@ main(int argc, char **argv)
 	  RTMP_LogPrintf
 	    ("--token|-T key          Key for SecureToken response\n");
 	  RTMP_LogPrintf
+	    ("--ccommand|-K key       Send custom command before play\n");
+          RTMP_LogPrintf
 	    ("--jtv|-j JSON           Authentication token for Justin.tv legacy servers\n");
 	  RTMP_LogPrintf
+	    ("--weeb|-J string        Authentication token for weeb.tv servers\n");
+	  RTMP_LogPrintf
 	    ("--buffer|-b             Buffer time in milliseconds (default: %u)\n\n",
 	     defaultRTMPRequest.bufferTime);
 
diff --git rtmpsrv.c rtmpsrv.c
index 5df4d3a..eccaa9c 100644
--- rtmpsrv.c
+++ rtmpsrv.c
@@ -25,9 +25,13 @@
  */
 
 #include <stdlib.h>
+#ifdef __MINGW_H
+#include <unistd.h>
+#endif
 #include <string.h>
 #include <math.h>
 #include <limits.h>
+#include <time.h>
 
 #include <signal.h>
 #include <getopt.h>
@@ -94,12 +98,19 @@ typedef struct
 STREAMING_SERVER *rtmpServer = 0;	// server structure pointer
 void *sslCtx = NULL;
 
+int file_exists(const char *fname);
 STREAMING_SERVER *startStreaming(const char *address, int port);
 void stopStreaming(STREAMING_SERVER * server);
 void AVreplace(AVal *src, const AVal *orig, const AVal *repl);
 
 static const AVal av_dquote = AVC("\"");
 static const AVal av_escdquote = AVC("\\\"");
+#ifdef WIN32
+static const AVal av_caret = AVC("^");
+static const AVal av_esccaret = AVC("^^");
+static const AVal av_pipe = AVC("|");
+static const AVal av_escpipe = AVC("^|");
+#endif
 
 typedef struct
 {
@@ -168,6 +179,12 @@ SAVC(level);
 SAVC(code);
 SAVC(description);
 SAVC(secureToken);
+SAVC(_checkbw);
+SAVC(_onbwdone);
+SAVC(checkBandwidth);
+SAVC(onBWDone);
+SAVC(FCSubscribe);
+SAVC(onFCSubscribe);
 
 static int
 SendConnectResult(RTMP *r, double txn)
@@ -191,7 +208,7 @@ SendConnectResult(RTMP *r, double txn)
   enc = AMF_EncodeNumber(enc, pend, txn);
   *enc++ = AMF_OBJECT;
 
-  STR2AVAL(av, "FMS/3,5,1,525");
+  STR2AVAL(av, "FMS/3,5,7,7009");
   enc = AMF_EncodeNamedString(enc, pend, &av_fmsVer, &av);
   enc = AMF_EncodeNamedNumber(enc, pend, &av_capabilities, 31.0);
   enc = AMF_EncodeNamedNumber(enc, pend, &av_mode, 1.0);
@@ -213,7 +230,7 @@ SendConnectResult(RTMP *r, double txn)
   enc = AMF_EncodeNamedString(enc, pend, &av_secureToken, &av);
 #endif
   STR2AVAL(p.p_name, "version");
-  STR2AVAL(p.p_vu.p_aval, "3,5,1,525");
+  STR2AVAL(p.p_vu.p_aval, "3,5,7,7009");
   p.p_type = AMF_STRING;
   obj.o_num = 1;
   obj.o_props = &p;
@@ -234,7 +251,7 @@ static int
 SendResultNumber(RTMP *r, double txn, double ID)
 {
   RTMPPacket packet;
-  char pbuf[256], *pend = pbuf+sizeof(pbuf);
+  char pbuf[1024], *pend = pbuf + sizeof (pbuf);
 
   packet.m_nChannel = 0x03;     // control channel (invoke)
   packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
@@ -264,12 +281,13 @@ static const AVal av_Stopped_playing = AVC("Stopped playing");
 SAVC(details);
 SAVC(clientid);
 static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken");
+static const AVal av_FCSubscribe_message = AVC("FCSubscribe to stream");
 
 static int
 SendPlayStart(RTMP *r)
 {
   RTMPPacket packet;
-  char pbuf[512], *pend = pbuf+sizeof(pbuf);
+  char pbuf[1024], *pend = pbuf + sizeof (pbuf);
 
   packet.m_nChannel = 0x03;     // control channel (invoke)
   packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
@@ -301,7 +319,7 @@ static int
 SendPlayStop(RTMP *r)
 {
   RTMPPacket packet;
-  char pbuf[512], *pend = pbuf+sizeof(pbuf);
+  char pbuf[1024], *pend = pbuf + sizeof (pbuf);
 
   packet.m_nChannel = 0x03;     // control channel (invoke)
   packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
@@ -329,6 +347,83 @@ SendPlayStop(RTMP *r)
   return RTMP_SendPacket(r, &packet, FALSE);
 }
 
+static int
+SendCheckBWResponse(RTMP *r, int oldMethodType, int onBWDoneInit)
+{
+  RTMPPacket packet;
+  char pbuf[1024], *pend = pbuf + sizeof (pbuf);
+  char *enc;
+
+  packet.m_nChannel = 0x03; /* control channel (invoke) */
+  packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
+  packet.m_nTimeStamp = 0;
+  packet.m_nInfoField2 = 0;
+  packet.m_hasAbsTimestamp = 0;
+  packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
+
+  enc = packet.m_body;
+  if (oldMethodType)
+    {
+      enc = AMF_EncodeString(enc, pend, &av__onbwdone);
+      enc = AMF_EncodeNumber(enc, pend, 0);
+      *enc++ = AMF_NULL;
+      enc = AMF_EncodeNumber(enc, pend, 10240);
+      enc = AMF_EncodeNumber(enc, pend, 0);
+    }
+  else
+    {
+      enc = AMF_EncodeString(enc, pend, &av_onBWDone);
+      enc = AMF_EncodeNumber(enc, pend, 0);
+      *enc++ = AMF_NULL;
+      if (!onBWDoneInit)
+        {
+          enc = AMF_EncodeNumber(enc, pend, 10240);
+          enc = AMF_EncodeNumber(enc, pend, 0);
+          enc = AMF_EncodeNumber(enc, pend, 0);
+          enc = AMF_EncodeNumber(enc, pend, 20);
+        }
+    }
+
+  packet.m_nBodySize = enc - packet.m_body;
+
+  return RTMP_SendPacket(r, &packet, FALSE);
+}
+
+static int
+SendOnFCSubscribe(RTMP *r)
+{
+  RTMPPacket packet;
+  char pbuf[1024], *pend = pbuf + sizeof (pbuf);
+  char *enc;
+
+  packet.m_nChannel = 0x03; /* control channel (invoke) */
+  packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
+  packet.m_nTimeStamp = 0;
+  packet.m_nInfoField2 = 0;
+  packet.m_hasAbsTimestamp = 0;
+  packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
+
+  enc = packet.m_body;
+  enc = AMF_EncodeString(enc, pend, &av_onFCSubscribe);
+  enc = AMF_EncodeNumber(enc, pend, 0);
+  *enc++ = AMF_NULL;
+
+  *enc++ = AMF_OBJECT;
+  enc = AMF_EncodeNamedString(enc, pend, &av_level, &av_status);
+  enc = AMF_EncodeNamedString(enc, pend, &av_code, &av_NetStream_Play_Start);
+  enc = AMF_EncodeNamedString(enc, pend, &av_description, &av_FCSubscribe_message);
+  enc = AMF_EncodeNamedNumber(enc, pend, &av_clientid, 0);
+  *enc++ = 0;
+  *enc++ = 0;
+  *enc++ = AMF_OBJECT_END;
+
+  packet.m_nBodySize = enc - packet.m_body;
+
+  return RTMP_SendPacket(r, &packet, FALSE);
+}
+
 static void
 spawn_dumper(int argc, AVal *av, char *cmd)
 {
@@ -389,6 +484,8 @@ countAMF(AMFObject *obj, int *argc)
 	  len += 40;
 	  break;
 	case AMF_OBJECT:
+        case AMF_ECMA_ARRAY:
+        case AMF_STRICT_ARRAY:
 	  len += 9;
 	  len += countAMF(&p->p_vu.p_object, argc);
 	  (*argc) += 2;
@@ -407,9 +504,11 @@ dumpAMF(AMFObject *obj, char *ptr, AVal *argv, int *argc)
   int i, ac = *argc;
   const char opt[] = "NBSO Z";
 
-  for (i=0; i < obj->o_num; i++)
+  for (i = 0; i < obj->o_num; i++)
     {
       AMFObjectProperty *p = &obj->o_props[i];
+      if ((p->p_type == AMF_ECMA_ARRAY) || (p->p_type == AMF_STRICT_ARRAY))
+        p->p_type = AMF_OBJECT;
       argv[ac].av_val = ptr+1;
       argv[ac++].av_len = 2;
       ptr += sprintf(ptr, " -C ");
@@ -569,6 +668,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	  server->arglen += countAMF(&r->Link.extras, &server->argc);
 	}
       SendConnectResult(r, txn);
+      SendCheckBWResponse(r, FALSE, TRUE);
     }
   else if (AVMATCH(&method, &av_createStream))
     {
@@ -583,10 +683,26 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
       AVal usherToken;
       AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken);
       AVreplace(&usherToken, &av_dquote, &av_escdquote);
+#ifdef WIN32
+      AVreplace(&usherToken, &av_caret, &av_esccaret);
+      AVreplace(&usherToken, &av_pipe, &av_escpipe);
+#endif
       server->arglen += 6 + usherToken.av_len;
       server->argc += 2;
       r->Link.usherToken = usherToken;
     }
+  else if (AVMATCH(&method, &av__checkbw))
+    {
+      SendCheckBWResponse(r, TRUE, FALSE);
+    }
+  else if (AVMATCH(&method, &av_checkBandwidth))
+    {
+      SendCheckBWResponse(r, FALSE, FALSE);
+    }
+  else if (AVMATCH(&method, &av_FCSubscribe))
+    {
+      SendOnFCSubscribe(r);
+    }
   else if (AVMATCH(&method, &av_play))
     {
       char *file, *p, *q, *cmd, *ptr;
@@ -602,6 +718,17 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
       if (obj.o_num > 5)
 	r->Link.length = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 5));
       */
+      double StartFlag = 0;
+      AMFObjectProperty *Start = AMF_GetProp(&obj, NULL, 4);
+      if (!(Start->p_type == AMF_INVALID))
+        StartFlag = AMFProp_GetNumber(Start);
+      r->Link.app = AVcopy(r->Link.app);
+      if (StartFlag == -1000 || (r->Link.app.av_val && strstr(r->Link.app.av_val, "live")))
+        {
+          StartFlag = -1000;
+          server->arglen += 7;
+          server->argc += 1;
+        }
       if (r->Link.tcUrl.av_len)
 	{
 	  len = server->arglen + r->Link.playpath.av_len + 4 +
@@ -619,6 +746,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	  argv[argc].av_val = ptr + 1;
 	  argv[argc++].av_len = 2;
 	  argv[argc].av_val = ptr + 5;
+	  r->Link.tcUrl = StripParams(&r->Link.tcUrl);
 	  ptr += sprintf(ptr," -r \"%s\"", r->Link.tcUrl.av_val);
 	  argv[argc++].av_len = r->Link.tcUrl.av_len;
 
@@ -643,6 +771,7 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	      argv[argc].av_val = ptr + 1;
 	      argv[argc++].av_len = 2;
 	      argv[argc].av_val = ptr + 5;
+	      r->Link.swfUrl = StripParams(&r->Link.swfUrl);
 	      ptr += sprintf(ptr, " -W \"%s\"", r->Link.swfUrl.av_val);
 	      argv[argc++].av_len = r->Link.swfUrl.av_len;
 	    }
@@ -665,10 +794,17 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	      r->Link.usherToken.av_val = NULL;
 	      r->Link.usherToken.av_len = 0;
 	    }
-	  if (r->Link.extras.o_num) {
-	    ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
-	    AMF_Reset(&r->Link.extras);
-	  }
+          if (StartFlag == -1000)
+            {
+              argv[argc].av_val = ptr + 1;
+              argv[argc++].av_len = 6;
+              ptr += sprintf(ptr, " --live");
+            }
+          if (r->Link.extras.o_num)
+            {
+              ptr = dumpAMF(&r->Link.extras, ptr, argv, &argc);
+              AMF_Reset(&r->Link.extras);
+            }
 	  argv[argc].av_val = ptr + 1;
 	  argv[argc++].av_len = 2;
 	  argv[argc].av_val = ptr + 5;
@@ -676,7 +812,13 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	    r->Link.playpath.av_len, r->Link.playpath.av_val);
 	  argv[argc++].av_len = r->Link.playpath.av_len;
 
-	  av = r->Link.playpath;
+          if (r->Link.playpath.av_len)
+            av = r->Link.playpath;
+          else
+            {
+              av.av_val = "file";
+              av.av_len = 4;
+            }
 	  /* strip trailing URL parameters */
 	  q = memchr(av.av_val, '?', av.av_len);
 	  if (q)
@@ -710,25 +852,82 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 
 	  memcpy(file, av.av_val, av.av_len);
 	  file[av.av_len] = '\0';
-	  for (p=file; *p; p++)
-	    if (*p == ':')
-	      *p = '_';
 
-	  /* Add extension if none present */
-	  if (file[av.av_len - 4] != '.')
-	    {
-	      av.av_len += 4;
-	    }
-	  /* Always use flv extension, regardless of original */
-	  if (strcmp(file+av.av_len-4, ".flv"))
-	    {
-	      strcpy(file+av.av_len-4, ".flv");
-	    }
+          if (strlen(file) < 128)
+            {
+              /* Add extension if none present */
+              if (file[av.av_len - 4] != '.')
+                {
+                  av.av_len += 4;
+                }
+
+              /* Always use flv extension, regardless of original */
+              if (strcmp(file + av.av_len - 4, ".flv"))
+                {
+                  strcpy(file + av.av_len - 4, ".flv");
+                }
+
+              /* Remove invalid characters from filename */
+              file = strreplace(file, 0, ":", "_", TRUE);
+              file = strreplace(file, 0, "&", "_", TRUE);
+              file = strreplace(file, 0, "^", "_", TRUE);
+              file = strreplace(file, 0, "|", "_", TRUE);
+            }
+          else
+            {
+              /* Filename too long - generate unique name */
+              strcpy(file, "vXXXXXX");
+              mkstemp(file);
+              strcat(file, ".flv");
+            }
+
+          /* Add timestamp to the filename */
+          char *filename, *pfilename, timestamp[21];
+          int filename_len, timestamp_len;
+          time_t current_time;
+
+          time(&current_time);
+          timestamp_len = strftime(&timestamp[0], sizeof (timestamp), "%Y-%m-%d_%I-%M-%S_", localtime(&current_time));
+          timestamp[timestamp_len] = '\0';
+          filename_len = strlen(file);
+          filename = malloc(timestamp_len + filename_len + 1);
+          pfilename = filename;
+          memcpy(pfilename, timestamp, timestamp_len);
+          pfilename += timestamp_len;
+          memcpy(pfilename, file, filename_len);
+          pfilename += filename_len;
+          *pfilename++ = '\0';
+          file = filename;
+
 	  argv[argc].av_val = ptr + 1;
 	  argv[argc++].av_len = 2;
 	  argv[argc].av_val = file;
 	  argv[argc].av_len = av.av_len;
-	  ptr += sprintf(ptr, " -o %s", file);
+#ifdef VLC
+          char *vlc;
+          int didAlloc = FALSE;
+
+          if (getenv("VLC"))
+            vlc = getenv("VLC");
+          else if (getenv("ProgramFiles"))
+            {
+              vlc = malloc(512 * sizeof (char));
+              didAlloc = TRUE;
+              char *ProgramFiles = getenv("ProgramFiles");
+              sprintf(vlc, "\"%s%s", ProgramFiles, " (x86)\\VideoLAN\\VLC\\vlc.exe");
+              if (!file_exists(vlc + 1))
+                sprintf(vlc + 1, "%s%s", ProgramFiles, "\\VideoLAN\\VLC\\vlc.exe");
+              strcpy(vlc + strlen(vlc), "\" -");
+            }
+          else
+            vlc = "vlc -";
+
+          ptr += sprintf(ptr, " | %s", vlc);
+          if (didAlloc)
+            free(vlc);
+#else
+          ptr += sprintf(ptr, " -o \"%s\"", file);
+#endif
 	  now = RTMP_GetTime();
 	  if (now - server->filetime < DUPTIME && AVMATCH(&argv[argc], &server->filename))
 	    {
@@ -742,7 +941,21 @@ ServeInvoke(STREAMING_SERVER *server, RTMP * r, RTMPPacket *packet, unsigned int
 	      server->filetime = now;
 	      free(server->filename.av_val);
 	      server->filename = argv[argc++];
-	      spawn_dumper(argc, argv, cmd);
+#ifdef VLC
+              FILE *vlc_cmdfile = fopen("VLC.bat", "w");
+              char *vlc_batchcmd = strreplace(cmd, 0, "%", "%%", FALSE);
+              fprintf(vlc_cmdfile, "%s\n", vlc_batchcmd);
+              fclose(vlc_cmdfile);
+              free(vlc_batchcmd);
+              spawn_dumper(argc, argv, "VLC.bat");
+#else
+              spawn_dumper(argc, argv, cmd);
+#endif
+
+              /* Save command to text file */
+              FILE *cmdfile = fopen("Command.txt", "a");
+              fprintf(cmdfile, "%s\n", cmd);
+              fclose(cmdfile);
 	    }
 
 	  free(cmd);
@@ -861,12 +1074,18 @@ controlServerThread(void *unused)
 	{
 	case 'q':
 	  RTMP_LogPrintf("Exiting\n");
-	  stopStreaming(rtmpServer);
-	  exit(0);
+          if (rtmpServer)
+            stopStreaming(rtmpServer);
 	  break;
 	default:
 	  RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich);
 	}
+      sleep(1);
+      if (rtmpServer && (rtmpServer->state == STREAMING_STOPPED))
+        {
+          RTMP_Log(RTMP_LOGDEBUG, "Exiting text UI thread");
+          break;
+        }
     }
   TFRET();
 }
@@ -1054,7 +1273,6 @@ stopStreaming(STREAMING_SERVER * server)
     }
 }
 
-
 void
 sigIntHandler(int sig)
 {
@@ -1191,3 +1409,15 @@ AVreplace(AVal *src, const AVal *orig, const AVal *repl)
   src->av_val = dest;
   src->av_len = dptr - dest;
 }
+
+int
+file_exists(const char *fname)
+{
+  FILE *file;
+  if ((file = fopen(fname, "r")))
+    {
+      fclose(file);
+      return TRUE;
+    }
+  return FALSE;
+}
diff --git rtmpsuck.c rtmpsuck.c
index e886179..0abdba4 100644
--- rtmpsuck.c
+++ rtmpsuck.c
@@ -25,10 +25,13 @@
  */
 
 #include <stdlib.h>
+#ifdef __MINGW_H
+#include <unistd.h>
+#endif
 #include <string.h>
 #include <math.h>
 #include <limits.h>
-
+#include <time.h>
 #include <signal.h>
 #include <getopt.h>
 
@@ -141,18 +144,21 @@ SAVC(code);
 SAVC(secureToken);
 SAVC(onStatus);
 SAVC(close);
+SAVC(play2);
 static const AVal av_NetStream_Failed = AVC("NetStream.Failed");
 static const AVal av_NetStream_Play_Failed = AVC("NetStream.Play.Failed");
-static const AVal av_NetStream_Play_StreamNotFound =
-AVC("NetStream.Play.StreamNotFound");
-static const AVal av_NetConnection_Connect_InvalidApp =
-AVC("NetConnection.Connect.InvalidApp");
+static const AVal av_NetStream_Play_StreamNotFound = AVC("NetStream.Play.StreamNotFound");
+static const AVal av_NetConnection_Connect_InvalidApp = AVC("NetConnection.Connect.InvalidApp");
+static const AVal av_NetConnection_Connect_Rejected = AVC("NetConnection.Connect.Rejected");
 static const AVal av_NetStream_Play_Start = AVC("NetStream.Play.Start");
 static const AVal av_NetStream_Play_Complete = AVC("NetStream.Play.Complete");
 static const AVal av_NetStream_Play_Stop = AVC("NetStream.Play.Stop");
+static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken");
 
 static const char *cst[] = { "client", "server" };
 
+char *dumpAMF(AMFObject *obj, char *ptr);
+
 // Returns 0 for OK/Failed/error, 1 for 'Stop or Complete'
 int
 ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *body)
@@ -198,26 +204,28 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
           if (cobj.o_props[i].p_type == AMF_STRING)
             {
               pval = cobj.o_props[i].p_vu.p_aval;
-              RTMP_LogPrintf("%.*s: %.*s\n", pname.av_len, pname.av_val, pval.av_len, pval.av_val);
+              RTMP_LogPrintf("%10.*s : %.*s\n", pname.av_len, pname.av_val, pval.av_len, pval.av_val);
             }
           if (AVMATCH(&pname, &av_app))
             {
-              server->rc.Link.app = pval;
+              server->rc.Link.app = AVcopy(pval);
               pval.av_val = NULL;
             }
           else if (AVMATCH(&pname, &av_flashVer))
             {
-              server->rc.Link.flashVer = pval;
+              server->rc.Link.flashVer = AVcopy(pval);
               pval.av_val = NULL;
             }
           else if (AVMATCH(&pname, &av_swfUrl))
             {
 #ifdef CRYPTO
               if (pval.av_val)
-	        RTMP_HashSWF(pval.av_val, &server->rc.Link.SWFSize,
-		  (unsigned char *)server->rc.Link.SWFHash, 30);
+                {
+                  AVal swfUrl = StripParams(&pval);
+                  RTMP_HashSWF(swfUrl.av_val, &server->rc.Link.SWFSize, (unsigned char *) server->rc.Link.SWFHash, 30);
+                }
 #endif
-              server->rc.Link.swfUrl = pval;
+              server->rc.Link.swfUrl = AVcopy(pval);
               pval.av_val = NULL;
             }
           else if (AVMATCH(&pname, &av_tcUrl))
@@ -225,7 +233,7 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
               char *r1 = NULL, *r2;
               int len;
 
-              server->rc.Link.tcUrl = pval;
+              server->rc.Link.tcUrl = AVcopy(pval);
               if ((pval.av_val[0] | 0x40) == 'r' &&
                   (pval.av_val[1] | 0x40) == 't' &&
                   (pval.av_val[2] | 0x40) == 'm' &&
@@ -267,7 +275,7 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
             }
           else if (AVMATCH(&pname, &av_pageUrl))
             {
-              server->rc.Link.pageUrl = pval;
+              server->rc.Link.pageUrl = AVcopy(pval);
               pval.av_val = NULL;
             }
           else if (AVMATCH(&pname, &av_audioCodecs))
@@ -287,14 +295,21 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
           if (pval.av_val)
             free(pval.av_val);
         }
+
       if (obj.o_num > 3)
         {
-          if (AMFProp_GetBoolean(&obj.o_props[3]))
-            server->rc.Link.lFlags |= RTMP_LF_AUTH;
-          if (obj.o_num > 4)
-          {
-            AMFProp_GetString(&obj.o_props[4], &server->rc.Link.auth);
-          }
+          int i = obj.o_num - 3;
+          server->rc.Link.extras.o_num = i;
+          server->rc.Link.extras.o_props = malloc(i * sizeof (AMFObjectProperty));
+          memcpy(server->rc.Link.extras.o_props, obj.o_props + 3, i * sizeof (AMFObjectProperty));
+          obj.o_num = 3;
+        }
+
+      if (server->rc.Link.extras.o_num)
+        {
+          server->rc.Link.Extras.av_val = calloc(2048, sizeof (char));
+          dumpAMF(&server->rc.Link.extras, server->rc.Link.Extras.av_val);
+          server->rc.Link.Extras.av_len = strlen(server->rc.Link.Extras.av_val);
         }
 
       if (!RTMP_Connect(&server->rc, pack))
@@ -303,6 +318,37 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
           return 1;
         }
       server->rc.m_bSendCounter = FALSE;
+
+      if (server->rc.Link.extras.o_props)
+        {
+          AMF_Reset(&server->rc.Link.extras);
+        }
+    }
+  else if (AVMATCH(&method, &av_NetStream_Authenticate_UsherToken))
+    {
+      AVal usherToken = {0};
+      AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken);
+      server->rc.Link.usherToken = AVcopy(usherToken);
+      RTMP_LogPrintf("%10s : %.*s\n", "usherToken", server->rc.Link.usherToken.av_len, server->rc.Link.usherToken.av_val);
+    }
+  else if (AVMATCH(&method, &av_play2))
+    {
+      RTMP_Log(RTMP_LOGDEBUG, "%s: Detected play2 request\n", __FUNCTION__);
+      if (body && nBodySize > 0)
+        {
+          char* pCmd = (char*) body;
+          char* pEnd = pCmd + nBodySize - 4;
+          while (pCmd < pEnd)
+            {
+              if (pCmd[0] == 'p' && pCmd[1] == 'l' && pCmd[2] == 'a' && pCmd[3] == 'y' && pCmd[4] == '2')
+                {
+                  /* Disable bitrate transition by sending invalid command */
+                  pCmd[4] = 'z';
+                  break;
+                }
+              ++pCmd;
+            }
+        }
     }
   else if (AVMATCH(&method, &av_play))
     {
@@ -323,6 +369,14 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
       if (!av.av_val)
         goto out;
 
+      double StartFlag = 0;
+      AMFObjectProperty *Start = AMF_GetProp(&obj, NULL, 4);
+      if (!(Start->p_type == AMF_INVALID))
+        StartFlag = AMFProp_GetNumber(Start);
+      if (StartFlag == -1000 || (server->rc.Link.app.av_val && strstr(server->rc.Link.app.av_val, "live")))
+        StartFlag = -1000;
+      RTMP_LogPrintf("%10s : %s\n", "live", (StartFlag == -1000) ? "yes" : "no");
+
       /* check for duplicates */
       for (fl = server->f_head; fl; fl=fl->f_next)
         {
@@ -362,19 +416,104 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
       /* hope there aren't more than 255 dups */
       if (count)
         flen += 2;
-      file = malloc(flen+1);
+      file = malloc(flen + 5);
 
       memcpy(file, av.av_val, av.av_len);
       if (count)
         sprintf(file+av.av_len, "%02x", count);
       else
         file[av.av_len] = '\0';
-      for (p=file; *p; p++)
-        if (*p == ':')
-          *p = '_';
-      RTMP_LogPrintf("Playpath: %.*s\nSaving as: %s\n",
-        server->rc.Link.playpath.av_len, server->rc.Link.playpath.av_val,
-        file);
+
+      if (strlen(file) < 128)
+        {
+          /* Add extension if none present */
+          if (file[av.av_len - 4] != '.')
+            {
+              av.av_len += 4;
+            }
+
+          /* Always use flv extension, regardless of original */
+          if (strcmp(file + av.av_len - 4, ".flv"))
+            {
+              strcpy(file + av.av_len - 4, ".flv");
+            }
+
+          /* Remove invalid characters from filename */
+          file = strreplace(file, 0, ":", "_", TRUE);
+          file = strreplace(file, 0, "&", "_", TRUE);
+          file = strreplace(file, 0, "^", "_", TRUE);
+          file = strreplace(file, 0, "|", "_", TRUE);
+        }
+      else
+        {
+          /* Filename too long - generate unique name */
+          strcpy(file, "vXXXXXX");
+          mkstemp(file);
+          strcat(file, ".flv");
+        }
+
+      /* Add timestamp to the filename */
+      char *filename, *pfilename, timestamp[21];
+      int filename_len, timestamp_len;
+      time_t current_time;
+
+      time(&current_time);
+      timestamp_len = strftime(&timestamp[0], sizeof (timestamp), "%Y-%m-%d_%I-%M-%S_", localtime(&current_time));
+      timestamp[timestamp_len] = '\0';
+      filename_len = strlen(file);
+      filename = malloc(timestamp_len + filename_len + 1);
+      pfilename = filename;
+      memcpy(pfilename, timestamp, timestamp_len);
+      pfilename += timestamp_len;
+      memcpy(pfilename, file, filename_len);
+      pfilename += filename_len;
+      *pfilename++ = '\0';
+      file = filename;
+
+      RTMP_LogPrintf("%10s : %.*s\n%10s : %s\n", "Playpath", server->rc.Link.playpath.av_len,
+                     server->rc.Link.playpath.av_val, "Saving as", file);
+
+      /* Save command to text file */
+      char *cmd = NULL, *ptr = NULL;
+      AVal swfUrl, tcUrl;
+
+      cmd = calloc(4096, sizeof (char));
+      ptr = cmd;
+      tcUrl = StripParams(&server->rc.Link.tcUrl);
+      swfUrl = StripParams(&server->rc.Link.swfUrl);
+      ptr += sprintf(ptr, "rtmpdump -r \"%.*s\" -a \"%.*s\" -f \"%.*s\" -W \"%.*s\" -p \"%.*s\"",
+                     tcUrl.av_len, tcUrl.av_val,
+                     server->rc.Link.app.av_len, server->rc.Link.app.av_val,
+                     server->rc.Link.flashVer.av_len, server->rc.Link.flashVer.av_val,
+                     swfUrl.av_len, swfUrl.av_val,
+                     server->rc.Link.pageUrl.av_len, server->rc.Link.pageUrl.av_val);
+
+      if (server->rc.Link.usherToken.av_val)
+        {
+          char *usherToken = strreplace(server->rc.Link.usherToken.av_val, server->rc.Link.usherToken.av_len, "\"", "\\\"", TRUE);
+#ifdef WIN32
+          usherToken = strreplace(usherToken, 0, "^", "^^", TRUE);
+          usherToken = strreplace(usherToken, 0, "|", "^|", TRUE);
+#endif
+          ptr += sprintf(ptr, " --jtv \"%s\"", usherToken);
+          free(usherToken);
+        }
+
+      if (server->rc.Link.Extras.av_len)
+        {
+          ptr += sprintf(ptr, "%.*s", server->rc.Link.Extras.av_len, server->rc.Link.Extras.av_val);
+        }
+
+      if (StartFlag == -1000)
+        ptr += sprintf(ptr, "%s", " --live");
+      ptr += sprintf(ptr, " -y \"%.*s\"", server->rc.Link.playpath.av_len, server->rc.Link.playpath.av_val);
+      ptr += sprintf(ptr, " -o \"%s\"\n", file);
+
+      FILE *cmdfile = fopen("Command.txt", "a");
+      fprintf(cmdfile, "%s", cmd);
+      fclose(cmdfile);
+      free(cmd);
+
       out = fopen(file, "wb");
       free(file);
       if (!out)
@@ -407,9 +546,10 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b
 
       RTMP_Log(RTMP_LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.av_val);
       if (AVMATCH(&code, &av_NetStream_Failed)
-	  || AVMATCH(&code, &av_NetStream_Play_Failed)
-	  || AVMATCH(&code, &av_NetStream_Play_StreamNotFound)
-	  || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp))
+          || AVMATCH(&code, &av_NetStream_Play_Failed)
+          || AVMATCH(&code, &av_NetStream_Play_StreamNotFound)
+          || AVMATCH(&code, &av_NetConnection_Connect_Rejected)
+          || AVMATCH(&code, &av_NetConnection_Connect_InvalidApp))
 	{
 	  ret = 1;
 	}
@@ -719,13 +859,18 @@ controlServerThread(void *unused)
 	{
 	case 'q':
 	  RTMP_LogPrintf("Exiting\n");
-	  stopStreaming(rtmpServer);
-          free(rtmpServer);
-	  exit(0);
+          if (rtmpServer)
+            stopStreaming(rtmpServer);
 	  break;
 	default:
 	  RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich);
 	}
+      sleep(1);
+      if (rtmpServer && (rtmpServer->state == STREAMING_STOPPED))
+        {
+          RTMP_Log(RTMP_LOGDEBUG, "Exiting text UI thread");
+          break;
+        }
     }
   TFRET();
 }
@@ -815,7 +960,7 @@ TFTYPE doServe(void *arg)	// server socket and state (our listening socket)
 
 	  if (select(n + 1, &rfds, NULL, NULL, &tv) <= 0)
 	    {
-              if (server->f_cur && server->rc.m_mediaChannel && !paused)
+              if (server->f_cur && server->rc.m_mediaChannel && !paused && server->rc.m_channelTimestamp)
                 {
                   server->rc.m_pauseStamp = server->rc.m_channelTimestamp[server->rc.m_mediaChannel];
                   if (RTMP_ToggleStream(&server->rc))
@@ -1123,7 +1268,6 @@ stopStreaming(STREAMING_SERVER * server)
     }
 }
 
-
 void
 sigIntHandler(int sig)
 {
@@ -1196,3 +1340,48 @@ main(int argc, char **argv)
 #endif
   return nStatus;
 }
+
+char *
+dumpAMF(AMFObject *obj, char *ptr)
+{
+  int i;
+  const char opt[] = "NBSO Z";
+
+  for (i = 0; i < obj->o_num; i++)
+    {
+      AMFObjectProperty *p = &obj->o_props[i];
+      if ((p->p_type == AMF_ECMA_ARRAY) || (p->p_type == AMF_STRICT_ARRAY))
+        p->p_type = AMF_OBJECT;
+      if (p->p_type > 5)
+        continue;
+      ptr += sprintf(ptr, " -C ");
+      if (p->p_name.av_val)
+        *ptr++ = 'N';
+      *ptr++ = opt[p->p_type];
+      *ptr++ = ':';
+      if (p->p_name.av_val)
+        ptr += sprintf(ptr, "%.*s:", p->p_name.av_len, p->p_name.av_val);
+      switch (p->p_type)
+        {
+        case AMF_BOOLEAN:
+          *ptr++ = p->p_vu.p_number != 0 ? '1' : '0';
+          break;
+        case AMF_STRING:
+          memcpy(ptr, p->p_vu.p_aval.av_val, p->p_vu.p_aval.av_len);
+          ptr += p->p_vu.p_aval.av_len;
+          break;
+        case AMF_NUMBER:
+          ptr += sprintf(ptr, "%f", p->p_vu.p_number);
+          break;
+        case AMF_OBJECT:
+          *ptr++ = '1';
+          ptr = dumpAMF(&p->p_vu.p_object, ptr);
+          ptr += sprintf(ptr, " -C O:0");
+          break;
+        case AMF_NULL:
+        default:
+          break;
+        }
+    }
+  return ptr;
+}
diff --git thread.c thread.c
index 0913c98..13d624a 100644
--- thread.c
+++ thread.c
@@ -32,7 +32,7 @@ ThreadCreate(thrfunc *routine, void *args)
   HANDLE thd;
 
   thd = (HANDLE) _beginthread(routine, 0, args);
-  if (thd == -1L)
+  if (thd == INVALID_HANDLE_VALUE)
     RTMP_LogPrintf("%s, _beginthread failed with %d\n", __FUNCTION__, errno);
 
   return thd;
