[Buildroot] [PATCH v8 15/28] rtmpdump: Add KSV patch

Yann E. MORIN yann.morin.1998 at free.fr
Sat May 17 20:11:25 UTC 2014


Bernd, All,

On 2014-05-17 17:57 +0200, Bernd Kuhls spake thusly:
> Signed-off-by: Bernd Kuhls <bernd.kuhls at t-online.de>
> ---
>  package/rtmpdump/rtmpdump-0001-ksv.patch | 3397 ++++++++++++++++++++++++++++++

Wee... Huge patch without any explanation.

What is this 'KSV' stuff?
What does the patch do?
Why is it needed?

Regards,
Yann E. MORIN.

>  1 file changed, 3397 insertions(+)
>  create mode 100644 package/rtmpdump/rtmpdump-0001-ksv.patch
> 
> diff --git a/package/rtmpdump/rtmpdump-0001-ksv.patch b/package/rtmpdump/rtmpdump-0001-ksv.patch
> new file mode 100644
> index 0000000..2536187
> --- /dev/null
> +++ b/package/rtmpdump/rtmpdump-0001-ksv.patch
> @@ -0,0 +1,3397 @@
> +Several fixes made or collected by KSV:
> +http://stream-recorder.com/forum/customized-rtmpdump-binaries-patch-file-t16103.html
> +
> +This patch file is the -p1 converted version of Patch.diff, contained in
> +rtmpdump-2.4.zip, downloaded from https://github.com/K-S-V/Scripts/releases
> +It includes "Update 21/03/2014" as latest update.
> +
> +Signed-off-by: Bernd Kuhls <bernd.kuhls at t-online.de>
> +
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/amf.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/amf.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/amf.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/amf.c	2014-05-04 17:55:17.513338440 +0200
> +@@ -618,6 +618,9 @@
> +       return -1;
> +     }
> + 
> ++  if (*pBuffer == AMF_NULL)
> ++    bDecodeName = 0;
> ++
> +   if (bDecodeName && nSize < 4)
> +     {				/* at least name (length + at least 1 byte) and 1 byte of data */
> +       RTMP_Log(RTMP_LOGDEBUG,
> +@@ -729,13 +732,13 @@
> +       }
> +     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;
> +@@ -807,8 +810,8 @@
> +     }
> +   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;
> +@@ -1068,17 +1071,18 @@
> + 
> + 	  /*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 < cd.cd_num; i++)
> +-	    {
> +-	      AVal memberName;
> +-	      len = AMF3ReadString(pBuffer, &memberName);
> +-	      RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val);
> +-	      AMF3CD_AddProp(&cd, &memberName);
> ++            {
> ++              AVal memberName = {NULL, 0};
> ++              len = AMF3ReadString(pBuffer, &memberName);
> ++              if (memberName.av_val)
> ++                {
> ++                  RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val);
> ++                  AMF3CD_AddProp(&cd, &memberName);
> ++                }
> + 	      nSize -= len;
> + 	      pBuffer += len;
> + 	    }
> +@@ -1259,7 +1263,8 @@
> + {
> +   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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/handshake.h librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/handshake.h
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/handshake.h	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/handshake.h	2014-05-04 17:55:17.517338389 +0200
> +@@ -707,7 +707,7 @@
> +   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 @@
> + #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 @@
> + 
> +   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 @@
> +   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 @@
> + #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 @@
> +     __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 @@
> +       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 @@
> +       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 @@
> +     }
> +   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 @@
> +     {
> +       encrypted = FALSE;
> +     }
> +-  else if (type == 6 || type == 8)
> ++  else if (type == 6 || type == 8 || type == 9)
> +     {
> +       offalg = 1;
> +       encrypted = TRUE;
> +@@ -1148,7 +1156,7 @@
> + #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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/hashswf.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/hashswf.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/hashswf.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/hashswf.c	2014-05-04 17:55:17.517338389 +0200
> +@@ -70,7 +70,7 @@
> + 
> + #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 @@
> + 
> +   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 @@
> +     }
> + 
> +   p1 = strchr(sb.sb_buf, ' ');
> +-  rc = atoi(p1 + 1);
> ++  rc = p1 ? atoi(p1 + 1) : 400;
> +   http->status = rc;
> + 
> +   if (rc >= 300)
> +@@ -528,9 +530,11 @@
> + 
> + 	  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 @@
> + 	  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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/log.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/log.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/log.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/log.c	2014-05-04 17:55:17.517338389 +0200
> +@@ -52,8 +52,8 @@
> + 	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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/Makefile librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/Makefile
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/Makefile	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/Makefile	2014-05-04 17:55:17.513338440 +0200
> +@@ -26,7 +26,7 @@
> + 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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/parseurl.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/parseurl.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/parseurl.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/parseurl.c	2014-05-04 17:55:17.517338389 +0200
> +@@ -34,6 +34,7 @@
> + 	AVal *playpath, AVal *app)
> + {
> + 	char *p, *end, *col, *ques, *slash;
> ++	int doubleSlash = FALSE;
> + 
> + 	RTMP_Log(RTMP_LOGDEBUG, "Parsing...");
> + 
> +@@ -140,11 +141,19 @@
> + 	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 @@
> + 		applen = appnamelen;
> + 	}
> + 
> ++	if ((!ques) && doubleSlash)
> ++	  applen -= 1;
> + 	app->av_val = p;
> + 	app->av_len = applen;
> + 	RTMP_Log(RTMP_LOGDEBUG, "Parsed app     : %.*s", applen, p);
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp.c	2014-05-04 17:55:17.517338389 +0200
> +@@ -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 @@
> + 
> + #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,17 +110,21 @@
> +   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);
> + 
> + #if 0				/* unused */
> +@@ -336,10 +342,13 @@
> +   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;
> + }
> + 
> + void
> +@@ -357,6 +366,8 @@
> + int
> + RTMP_IsConnected(RTMP *r)
> + {
> ++  if (r->m_sb.sb_size > 0)
> ++    return TRUE;
> +   return r->m_sb.sb_socket != -1;
> + }
> + 
> +@@ -443,6 +454,7 @@
> + 		 AVal *flashVer,
> + 		 AVal *subscribepath,
> + 		 AVal *usherToken,
> ++		 AVal *WeebToken,
> + 		 int dStart,
> + 		 int dStop, int bLiveStream, long int timeout)
> + {
> +@@ -465,6 +477,8 @@
> +     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)
> +@@ -513,6 +527,8 @@
> +     r->Link.subscribepath = *subscribepath;
> +   if (usherToken && usherToken->av_len)
> +     r->Link.usherToken = *usherToken;
> ++  if (WeebToken && WeebToken->av_len)
> ++    r->Link.WeebToken = *WeebToken;
> +   r->Link.seekTime = dStart;
> +   r->Link.stopTime = dStop;
> +   if (bLiveStream)
> +@@ -570,14 +586,22 @@
> +   	"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("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,
> +@@ -765,7 +789,7 @@
> +   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';
> +@@ -842,9 +866,16 @@
> +     }
> + 
> + #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);
> +@@ -947,6 +978,8 @@
> +   }
> + 
> +   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;
> + }
> +@@ -1397,41 +1430,96 @@
> +   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;
> +@@ -1458,10 +1546,9 @@
> + 	  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_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
> +@@ -1472,7 +1559,8 @@
> + 	{
> + 	  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;
> + 	}
> + 
> +@@ -1497,6 +1585,7 @@
> + 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];
> +@@ -1512,6 +1601,15 @@
> +     }
> + #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;
> +@@ -1548,6 +1646,14 @@
> +     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;
> + }
> + 
> +@@ -1577,6 +1683,9 @@
> +   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);
> + 
> +@@ -1625,7 +1734,7 @@
> +       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);
> +@@ -1789,7 +1898,7 @@
> +   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);
> +@@ -2095,6 +2204,7 @@
> + }
> + 
> + SAVC(_checkbw);
> ++SAVC(checkBandwidth);
> + 
> + static int
> + SendCheckBW(RTMP *r)
> +@@ -2112,7 +2222,7 @@
> +   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;
> + 
> +@@ -2219,10 +2329,8 @@
> +     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;
> +@@ -2338,7 +2446,7 @@
> +   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;
> +@@ -2370,8 +2478,8 @@
> +     }
> +   else if (nType == 0x1A)
> +     {
> +-	  *buf = nObject & 0xff;
> +-	}
> ++      *buf = nObject & 0xff;
> ++    }
> +   else
> +     {
> +       if (nSize > 2)
> +@@ -2885,6 +2993,7 @@
> + #endif
> + 
> + 
> ++SAVC(onBWCheck);
> + SAVC(onBWDone);
> + SAVC(onFCSubscribe);
> + SAVC(onFCUnsubscribe);
> +@@ -2897,24 +3006,24 @@
> + SAVC(description);
> + SAVC(onStatus);
> + SAVC(playlist_ready);
> ++SAVC(cps);
> ++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
> +@@ -2924,6 +3033,11 @@
> +   AVal method;
> +   double txn;
> +   int ret = 0, nRes;
> ++  char pbuf[256], *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",
> +@@ -2985,46 +3099,221 @@
> + 	      RTMP_SendServerBW(r);
> + 	      RTMP_SendCtrl(r, 3, 0, 300);
> + 	    }
> +-	  RTMP_SendCreateStream(r);
> ++          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 (strstr(host, "featve.com") || strstr(pageUrl, "featve.com"))
> ++            {
> ++              AVal av_auth = AVC("yes");
> ++              SAVC(youCannotPlayMe);
> + 
> +-	  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);
> +-	    }
> +-	}
> ++              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, "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, "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(host, "pc3oot.us.to"))
> ++            {
> ++              SendCommand(r, "StreamPiraten", TRUE);
> ++              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, "jaSakamCarevataKerka", TRUE);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(pageUrl, "liveflash.tv"))
> ++            {
> ++              SendCommand(r, "kaskatija", TRUE);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(pageUrl, "mips.tv"))
> ++            {
> ++              SendCommand(r, "gaolVanus", TRUE);
> ++              RTMP_SendCreateStream(r);
> ++            }
> ++          else if (strstr(pageUrl, "ucaster.eu"))
> ++            {
> ++              SendCommand(r, "vujkoMiLazarBarakovOdMokrino", 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(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);
> ++                }
> ++            }
> ++          /* Weeb.tv specific authentication */
> ++          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
> ++            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);
> ++              /* 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))
> +@@ -3048,21 +3337,22 @@
> +     {
> +       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)
> +                 {
> +@@ -3074,12 +3364,12 @@
> +           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))
> +             {
> +@@ -3093,20 +3383,65 @@
> +               /* if PublisherAuth returns 1, then reconnect */
> +               PublisherAuth(r, &description);
> +             }
> +-        }
> +-      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;
> ++              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");
> ++          RTMP_Connect(r, NULL);
> ++        }
> ++      else
> ++        {
> ++          RTMP_Log(RTMP_LOGERROR, "rtmp server requested close");
> ++          RTMP_Close(r);
> ++        }
> + #ifdef CRYPTO
> +       if ((r->Link.protocol & RTMP_FEATURE_WRITE) &&
> +               !(r->Link.pFlags & RTMP_PUB_CLEAN) &&
> +@@ -3127,16 +3462,18 @@
> +   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);
> +@@ -3194,6 +3531,46 @@
> + 	    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))
> +     {
> +@@ -3207,6 +3584,85 @@
> + 	    }
> +         }
> +     }
> ++  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 if (AVMATCH(&method, &av_cps))
> ++    {
> ++      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
> ++        {
> ++          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
> +     {
> + 
> +@@ -3232,7 +3688,8 @@
> + 	  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;
> +@@ -3258,7 +3715,8 @@
> + 	  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;
> +@@ -3292,6 +3750,7 @@
> + 	  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);
> +@@ -3307,7 +3766,7 @@
> + 	}
> +       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);
> + 	}
> +     }
> +@@ -3389,7 +3848,7 @@
> +   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); */
> + 
> +@@ -3498,15 +3957,15 @@
> +       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);
> + 	}
> +@@ -3811,8 +4270,18 @@
> +       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;
> +@@ -4263,8 +4732,13 @@
> + {
> +   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)
> +     {
> +@@ -4278,6 +4752,8 @@
> + #endif
> + 	{
> + 	  nBytes = recv(sb->sb_socket, sb->sb_start + sb->sb_size, nBytes, 0);
> ++          if (!nBytes)
> ++            RTMP_Log(RTMP_LOGDEBUG, "Socket closed by server, nBytes: %d", nBytes);
> + 	}
> +       if (nBytes != -1)
> + 	{
> +@@ -4417,21 +4893,19 @@
> + 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;
> + }
> + 
> +@@ -4441,22 +4915,17 @@
> +   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-"))) {
> +@@ -4464,21 +4933,31 @@
> +     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)
> +     {
> +@@ -4498,10 +4977,17 @@
> +       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
> +@@ -4569,162 +5055,156 @@
> + #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);
> + 
> +-	      /* 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;
> +-			}
> +-		    }
> ++                  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;
> ++                }
> ++            }
> + 
> +-		  /* 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);
> ++          /* 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);
> + 
> + #ifdef _DEBUG
> +-			  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;
> +-			}
> +-		    }
> +-		}
> +-	    }
> ++                      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;
> ++                    }
> ++                }
> ++            }
> + 
> + 	  if (packet.m_nTimeStamp > 0
> + 	      && (r->m_read.flags & (RTMP_READ_GOTKF|RTMP_READ_GOTFLVK)))
> +@@ -4984,7 +5464,7 @@
> +   0x00, 0x00, 0x00, 0x00
> + };
> + 
> +-#define HEADERBUF	(128*1024)
> ++#define HEADERBUF	(1024*1024)
> + int
> + RTMP_Read(RTMP *r, char *buf, int size)
> + {
> +@@ -5187,3 +5667,284 @@
> +     }
> +   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;
> ++}
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp.h librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp.h
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp.h	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp.h	2014-05-04 17:55:17.517338389 +0200
> +@@ -150,12 +150,14 @@
> +     AVal playpath;	/* passed in explicitly */
> +     AVal tcUrl;
> +     AVal swfUrl;
> ++    AVal swfHash;
> +     AVal pageUrl;
> +     AVal app;
> +     AVal auth;
> +     AVal flashVer;
> +     AVal subscribepath;
> +     AVal usherToken;
> ++    AVal WeebToken;
> +     AVal token;
> +     AVal pubUser;
> +     AVal pubPasswd;
> +@@ -174,9 +176,15 @@
> +     int lFlags;
> + 
> +     int swfAge;
> ++    int swfSize;
> + 
> +     int protocol;
> ++    int ConnectPacket;
> ++    int CombineConnectPacket;
> ++    int redirected;
> +     int timeout;		/* connection timeout in seconds */
> ++    AVal Extras;
> ++    AVal HandshakeResponse;
> + 
> + #define RTMP_PUB_NAME   0x0001  /* send login to server */
> + #define RTMP_PUB_RESP   0x0002  /* send salted password hash */
> +@@ -224,6 +232,7 @@
> +     /* if bResume == TRUE */
> +     uint8_t initialFrameType;
> +     uint32_t nResumeTS;
> ++    uint32_t nResumeDriftTS;
> +     char *metaHeader;
> +     char *initialFrame;
> +     uint32_t nMetaHeaderSize;
> +@@ -310,6 +319,7 @@
> + 			AVal *flashVer,
> + 			AVal *subscribepath,
> + 			AVal *usherToken,
> ++			AVal *WeebToken,
> + 			int dStart,
> + 			int dStop, int bLiveStream, long int timeout);
> + 
> +@@ -375,6 +385,11 @@
> +   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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp_sys.h librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp_sys.h
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/librtmp/rtmp_sys.h	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/librtmp/rtmp_sys.h	2014-05-04 17:55:17.517338389 +0200
> +@@ -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 @@
> + #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 @@
> + #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 @@
> + #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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/Makefile librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/Makefile
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/Makefile	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/Makefile	2014-05-04 17:55:17.513338440 +0200
> +@@ -32,7 +32,7 @@
> + SBINDIR=$(DESTDIR)$(sbindir)
> + MANDIR=$(DESTDIR)$(mandir)
> + 
> +-LIBS_posix=
> ++LIBS_posix=-lm
> + LIBS_darwin=
> + LIBS_mingw=-lws2_32 -lwinmm -lgdi32
> + LIB_RTMP=-Llibrtmp -lrtmp
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpdump.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpdump.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpdump.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpdump.c	2014-05-04 17:55:17.517338389 +0200
> +@@ -283,6 +283,7 @@
> +   uint8_t dataType;
> +   int bAudioOnly;
> +   off_t size;
> ++  char *syncbuf, *p;
> + 
> +   fseek(file, 0, SEEK_END);
> +   size = ftello(file);
> +@@ -293,8 +294,8 @@
> + 
> +   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 +327,51 @@
> +       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!");
> +@@ -705,6 +751,8 @@
> + 	  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 +799,8 @@
> +   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
> +   int port = -1;
> +   int protocol = RTMP_PROTOCOL_UNDEFINED;
> +   int retries = 0;
> +@@ -858,12 +907,13 @@
> +     {"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:w:x:W:X:S:#j:J:",
> + 		      longopts, NULL)) != -1)
> +     {
> +       switch (opt)
> +@@ -995,7 +1045,7 @@
> + 		  port = parsedPort;
> + 		if (playpath.av_len == 0 && parsedPlaypath.av_len)
> + 		  {
> +-		    playpath = parsedPlaypath;
> ++                    playpath = AVcopy(parsedPlaypath);
> + 		  }
> + 		if (protocol == RTMP_PROTOCOL_UNDEFINED)
> + 		  protocol = parsedProtocol;
> +@@ -1079,6 +1129,9 @@
> + 	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 +1223,14 @@
> + 
> +   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 +1250,8 @@
> +   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, dSeek, dStopOffset, bLiveStream, timeout);
> +     }
> +   else
> +     {
> +diff -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpgw.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpgw.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpgw.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpgw.c	2014-05-04 17:55:17.525338289 +0200
> +@@ -96,7 +96,8 @@
> +   AVal flashVer;
> +   AVal token;
> +   AVal subscribepath;
> +-  AVal usherToken; //Justin.tv auth token
> ++  AVal usherToken; // Justin.tv auth token
> ++  AVal WeebToken;  // Weeb.tv auth token
> +   AVal sockshost;
> +   AMFObject extras;
> +   int edepth;
> +@@ -556,8 +557,8 @@
> +   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, dSeek, req.dStopOffset,
> ++                       req.bLiveStream, req.timeout);
> +     }
> +   else
> +     {
> +@@ -972,6 +973,9 @@
> +     case 'j':
> +       STR2AVAL(req->usherToken, arg);
> +       break;
> ++    case 'J':
> ++      STR2AVAL(req->WeebToken, arg);
> ++      break;
> +     default:
> +       RTMP_LogPrintf("unknown option: %c, arg: %s\n", opt, arg);
> +       return FALSE;
> +@@ -1044,6 +1048,7 @@
> +     {"quiet", 0, NULL, 'q'},
> +     {"verbose", 0, NULL, 'V'},
> +     {"jtv", 1, NULL, 'j'},
> ++    {"weeb", 1, NULL, 'J'},
> +     {0, 0, 0, 0}
> +   };
> + 
> +@@ -1056,7 +1061,7 @@
> + 
> +   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)
> +@@ -1121,6 +1126,8 @@
> + 	  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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpsrv.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpsrv.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpsrv.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpsrv.c	2014-05-04 17:55:17.525338289 +0200
> +@@ -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 @@
> + 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(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 @@
> +   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 @@
> +   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 @@
> + 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 @@
> + 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 @@
> + 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 @@
> +   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 @@
> + 	  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;
> +@@ -404,12 +501,14 @@
> + static char *
> + dumpAMF(AMFObject *obj, char *ptr, AVal *argv, int *argc)
> + {
> +-  int i, len, ac = *argc;
> ++  int i, ac = *argc;
> +   const char opt[] = "NBSO Z";
> + 
> +-  for (i=0, len=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 @@
> + 	  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 @@
> +       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;
> +@@ -600,6 +716,17 @@
> +       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 +
> +@@ -617,6 +744,7 @@
> + 	  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;
> + 
> +@@ -641,6 +769,7 @@
> + 	      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;
> + 	    }
> +@@ -663,10 +792,17 @@
> + 	      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;
> +@@ -674,7 +810,13 @@
> + 	    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)
> +@@ -708,25 +850,82 @@
> + 
> + 	  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");
> ++              mktemp(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))
> + 	    {
> +@@ -740,7 +939,21 @@
> + 	      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);
> +@@ -859,12 +1072,18 @@
> + 	{
> + 	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();
> + }
> +@@ -1052,7 +1271,6 @@
> +     }
> + }
> + 
> +-
> + void
> + sigIntHandler(int sig)
> + {
> +@@ -1189,3 +1407,15 @@
> +   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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpsuck.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpsuck.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/rtmpsuck.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/rtmpsuck.c	2014-05-04 17:55:17.525338289 +0200
> +@@ -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(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 @@
> +           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 @@
> +               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 @@
> +             }
> +           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 @@
> +           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 @@
> +           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 @@
> +       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 @@
> +       /* 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");
> ++          mktemp(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 @@
> + 
> +       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 @@
> + 	{
> + 	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();
> + }
> +@@ -1123,7 +1268,6 @@
> +     }
> + }
> + 
> +-
> + void
> + sigIntHandler(int sig)
> + {
> +@@ -1196,3 +1340,48 @@
> + #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 -uNr librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/thread.c librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/thread.c
> +--- librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497.org/thread.c	2014-03-02 19:20:23.000000000 +0100
> ++++ librtmp-79459a2b43f41ac44a2ec001139bcb7b1b8f7497/thread.c	2014-05-04 17:55:17.525338289 +0200
> +@@ -32,7 +32,7 @@
> +   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;
> -- 
> 1.7.10.4
> 
> _______________________________________________
> buildroot mailing list
> buildroot at busybox.net
> http://lists.busybox.net/mailman/listinfo/buildroot

-- 
.-----------------.--------------------.------------------.--------------------.
|  Yann E. MORIN  | Real-Time Embedded | /"\ ASCII RIBBON | Erics' conspiracy: |
| +33 662 376 056 | Software  Designer | \ / CAMPAIGN     |  ___               |
| +33 223 225 172 `------------.-------:  X  AGAINST      |  \e/  There is no  |
| http://ymorin.is-a-geek.org/ | _/*\_ | / \ HTML MAIL    |   v   conspiracy.  |
'------------------------------^-------^------------------^--------------------'


More information about the buildroot mailing list