Submitted by:            Zeckma
Date:                    2026-06-19
Initial Package Version: 2.3
Origin:                  Diff from 2.3 tarball to 2.6 tag of
                         https://git.ffmpeg.org/rtmpdump
Upstream Status:         Upstream
Description:             Brings 2.3 up to 2.6, since newer tags don't have
                         tarballs released anymore, but upstream is active.

Replicate this patch:
###
git clone --branch vNEW_TAG https://git.ffmpeg.org/rtmpdump.git
wget https://rtmpdump.mplayerhq.hu/download/rtmpdump-2.3.tgz
tar -xf rtmpdump-2.3.tgz
rm -rvf rtmpdump/.*
diff -Naurp rtmpdump-2.3 rtmpdump >> rtmpdump-2.3-git.patch
mv -v rtmpdump-2.3-{git,NEW_TAG}.patch
###

diff '--color=auto' -Naurp rtmpdump-2.3/ChangeLog rtmpdump/ChangeLog
--- rtmpdump-2.3/ChangeLog	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/ChangeLog	2026-06-19 14:15:18.299594947 -0600
@@ -1,9 +1,25 @@
 RTMPDump
 Copyright 2008-2009 Andrej Stepanchuk; Distributed under the GPL v2
-Copyright 2009-2010 Howard Chu
+Copyright 2009-2015 Howard Chu
 Copyright 2009 The Flvstreamer Team
 http://rtmpdump.mplayerhq.hu/
 
+1 March 2024, v2.6
+- miscellaneous cleanup of authentication and encryption support
+
+23 December 2015
+- miscellaneous protocol fixes
+- withdraw v2.5 RTMPE type 10 support, code was too broken/non-portable
+
+31 March 2012, v2.5
+- add RTMPE type 10 handshake support for x86 systems
+
+20 July 2011
+- add NetStream.Authenticate.UsherToken for Justin.tv
+
+11 July 2011, v2.4
+- add RTMPE type 9 handshake support
+
 30 June 2010, v2.3
 - fix RC4 cleanup for GnuTLS/gcrypt
 - declare RTMP_Write buf as const
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/amf.c rtmpdump/librtmp/amf.c
--- rtmpdump-2.3/librtmp/amf.c	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/amf.c	2026-06-19 14:15:18.301606620 -0600
@@ -33,6 +33,7 @@
 #include "bytes.h"
 
 static const AMFObjectProperty AMFProp_Invalid = { {0, 0}, AMF_INVALID };
+static const AMFObject AMFObj_Invalid = { 0, 0 };
 static const AVal AV_empty = { 0, 0 };
 
 /* Data is Big-Endian */
@@ -340,13 +341,19 @@ AMFProp_GetBoolean(AMFObjectProperty *pr
 void
 AMFProp_GetString(AMFObjectProperty *prop, AVal *str)
 {
-  *str = prop->p_vu.p_aval;
+  if (prop->p_type == AMF_STRING)
+    *str = prop->p_vu.p_aval;
+  else
+    *str = AV_empty;
 }
 
 void
 AMFProp_GetObject(AMFObjectProperty *prop, AMFObject *obj)
 {
-  *obj = prop->p_vu.p_object;
+  if (prop->p_type == AMF_OBJECT)
+    *obj = prop->p_vu.p_object;
+  else
+    *obj = AMFObj_Invalid;
 }
 
 int
@@ -396,6 +403,14 @@ AMFProp_Encode(AMFObjectProperty *prop,
       pBuffer = AMF_Encode(&prop->p_vu.p_object, pBuffer, pBufEnd);
       break;
 
+    case AMF_ECMA_ARRAY:
+      pBuffer = AMF_EncodeEcmaArray(&prop->p_vu.p_object, pBuffer, pBufEnd);
+      break;
+
+    case AMF_STRICT_ARRAY:
+      pBuffer = AMF_EncodeArray(&prop->p_vu.p_object, pBuffer, pBufEnd);
+      break;
+
     default:
       RTMP_Log(RTMP_LOGERROR, "%s, invalid type. %d", __FUNCTION__, prop->p_type);
       pBuffer = NULL;
@@ -463,6 +478,8 @@ AMF3ReadString(const char *data, AVal *s
       RTMP_Log(RTMP_LOGDEBUG,
 	  "%s, string reference, index: %d, not supported, ignoring!",
 	  __FUNCTION__, refIndex);
+	  str->av_val = NULL;
+	  str->av_len = 0;
       return len;
     }
   else
@@ -502,9 +519,11 @@ AMF3Prop_Decode(AMFObjectProperty *prop,
       if (name.av_len <= 0)
 	return nRes;
 
+      nSize -= nRes;
+      if (nSize <= 0)
+	return -1;
       prop->p_name = name;
       pBuffer += nRes;
-      nSize -= nRes;
     }
 
   /* decode */
@@ -586,10 +605,12 @@ AMF3Prop_Decode(AMFObjectProperty *prop,
     case AMF3_ARRAY:
     case AMF3_BYTE_ARRAY:
     default:
-      RTMP_Log(RTMP_LOGDEBUG, "%s - AMF3 unknown/unsupported datatype 0x%02x, @0x%08X",
+      RTMP_Log(RTMP_LOGDEBUG, "%s - AMF3 unknown/unsupported datatype 0x%02x, @%p",
 	  __FUNCTION__, (unsigned char)(*pBuffer), pBuffer);
       return -1;
     }
+  if (nSize < 0)
+    return -1;
 
   return nOriginalSize - nSize;
 }
@@ -700,7 +721,6 @@ AMFProp_Decode(AMFObjectProperty *prop,
 	if (nRes == -1)
 	  return -1;
 	nSize -= nRes;
-	prop->p_type = AMF_OBJECT;
 	break;
       }
     case AMF_OBJECT_END:
@@ -718,7 +738,6 @@ AMFProp_Decode(AMFObjectProperty *prop,
 	if (nRes == -1)
 	  return -1;
 	nSize -= nRes;
-	prop->p_type = AMF_OBJECT;
 	break;
       }
     case AMF_DATE:
@@ -735,13 +754,15 @@ AMFProp_Decode(AMFObjectProperty *prop,
 	break;
       }
     case AMF_LONG_STRING:
+    case AMF_XML_DOC:
       {
 	unsigned int nStringSize = AMF_DecodeInt32(pBuffer);
 	if (nSize < (long)nStringSize + 4)
 	  return -1;
 	AMF_DecodeLongString(pBuffer, &prop->p_vu.p_aval);
 	nSize -= (4 + nStringSize);
-	prop->p_type = AMF_STRING;
+	if (prop->p_type == AMF_LONG_STRING)
+	  prop->p_type = AMF_STRING;
 	break;
       }
     case AMF_RECORDSET:
@@ -750,12 +771,6 @@ AMFProp_Decode(AMFObjectProperty *prop,
 	return -1;
 	break;
       }
-    case AMF_XML_DOC:
-      {
-	RTMP_Log(RTMP_LOGERROR, "AMF_XML_DOC not supported!");
-	return -1;
-	break;
-      }
     case AMF_TYPED_OBJECT:
       {
 	RTMP_Log(RTMP_LOGERROR, "AMF_TYPED_OBJECT not supported!");
@@ -772,7 +787,7 @@ AMFProp_Decode(AMFObjectProperty *prop,
 	break;
       }
     default:
-      RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @0x%08X", __FUNCTION__,
+      RTMP_Log(RTMP_LOGDEBUG, "%s - unknown datatype 0x%02x, @%p", __FUNCTION__,
 	  prop->p_type, pBuffer - 1);
       return -1;
     }
@@ -819,6 +834,18 @@ AMFProp_Dump(AMFObjectProperty *prop)
       AMF_Dump(&prop->p_vu.p_object);
       return;
     }
+  else if (prop->p_type == AMF_ECMA_ARRAY)
+    {
+      RTMP_Log(RTMP_LOGDEBUG, "Property: <%sECMA_ARRAY>", strRes);
+      AMF_Dump(&prop->p_vu.p_object);
+      return;
+    }
+  else if (prop->p_type == AMF_STRICT_ARRAY)
+    {
+      RTMP_Log(RTMP_LOGDEBUG, "Property: <%sSTRICT_ARRAY>", strRes);
+      AMF_Dump(&prop->p_vu.p_object);
+      return;
+    }
 
   switch (prop->p_type)
     {
@@ -847,7 +874,8 @@ AMFProp_Dump(AMFObjectProperty *prop)
 void
 AMFProp_Reset(AMFObjectProperty *prop)
 {
-  if (prop->p_type == AMF_OBJECT)
+  if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY ||
+      prop->p_type == AMF_STRICT_ARRAY)
     AMF_Reset(&prop->p_vu.p_object);
   else
     {
@@ -892,6 +920,76 @@ AMF_Encode(AMFObject *obj, char *pBuffer
   return pBuffer;
 }
 
+char *
+AMF_EncodeEcmaArray(AMFObject *obj, char *pBuffer, char *pBufEnd)
+{
+  int i;
+
+  if (pBuffer+4 >= pBufEnd)
+    return NULL;
+
+  *pBuffer++ = AMF_ECMA_ARRAY;
+
+  pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num);
+
+  for (i = 0; i < obj->o_num; i++)
+    {
+      char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
+      if (res == NULL)
+	{
+	  RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
+	      i);
+	  break;
+	}
+      else
+	{
+	  pBuffer = res;
+	}
+    }
+
+  if (pBuffer + 3 >= pBufEnd)
+    return NULL;			/* no room for the end marker */
+
+  pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
+
+  return pBuffer;
+}
+
+char *
+AMF_EncodeArray(AMFObject *obj, char *pBuffer, char *pBufEnd)
+{
+  int i;
+
+  if (pBuffer+4 >= pBufEnd)
+    return NULL;
+
+  *pBuffer++ = AMF_STRICT_ARRAY;
+
+  pBuffer = AMF_EncodeInt32(pBuffer, pBufEnd, obj->o_num);
+
+  for (i = 0; i < obj->o_num; i++)
+    {
+      char *res = AMFProp_Encode(&obj->o_props[i], pBuffer, pBufEnd);
+      if (res == NULL)
+	{
+	  RTMP_Log(RTMP_LOGERROR, "AMF_Encode - failed to encode property in index %d",
+	      i);
+	  break;
+	}
+      else
+	{
+	  pBuffer = res;
+	}
+    }
+
+  //if (pBuffer + 3 >= pBufEnd)
+  //  return NULL;			/* no room for the end marker */
+
+  //pBuffer = AMF_EncodeInt24(pBuffer, pBufEnd, AMF_OBJECT_END);
+
+  return pBuffer;
+}
+
 int
 AMF_DecodeArray(AMFObject *obj, const char *pBuffer, int nSize,
 		int nArrayLen, int bDecodeName)
@@ -907,9 +1005,17 @@ AMF_DecodeArray(AMFObject *obj, const ch
       int nRes;
       nArrayLen--;
 
+      if (nSize <= 0)
+	{
+	  bError = TRUE;
+	  break;
+	}
       nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
       if (nRes == -1)
-	bError = TRUE;
+	{
+	  bError = TRUE;
+	  break;
+	}
       else
 	{
 	  nSize -= nRes;
@@ -968,12 +1074,12 @@ AMF3_Decode(AMFObject *obj, const char *
       else
 	{
 	  int32_t classExtRef = (classRef >> 1);
-	  int i;
+	  int i, cdnum;
 
 	  cd.cd_externalizable = (classExtRef & 0x1) == 1;
 	  cd.cd_dynamic = ((classExtRef >> 1) & 0x1) == 1;
 
-	  cd.cd_num = classExtRef >> 2;
+	  cdnum = classExtRef >> 2;
 
 	  /* class name */
 
@@ -988,9 +1094,16 @@ AMF3_Decode(AMFObject *obj, const char *
 	      cd.cd_name.av_val, cd.cd_externalizable, cd.cd_dynamic,
 	      cd.cd_num);
 
-	  for (i = 0; i < cd.cd_num; i++)
+	  for (i = 0; i < cdnum; i++)
 	    {
 	      AVal memberName;
+	      if (nSize <=0)
+		{
+invalid:
+		  RTMP_Log(RTMP_LOGDEBUG, "%s, invalid class encoding!",
+		    __FUNCTION__);
+		  return nOriginalSize;
+		}
 	      len = AMF3ReadString(pBuffer, &memberName);
 	      RTMP_Log(RTMP_LOGDEBUG, "Member: %s", memberName.av_val);
 	      AMF3CD_AddProp(&cd, &memberName);
@@ -1026,6 +1139,8 @@ AMF3_Decode(AMFObject *obj, const char *
 	  int nRes, i;
 	  for (i = 0; i < cd.cd_num; i++)	/* non-dynamic */
 	    {
+	      if (nSize <=0)
+	        goto invalid;
 	      nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, FALSE);
 	      if (nRes == -1)
 		RTMP_Log(RTMP_LOGDEBUG, "%s, failed to decode AMF3 property!",
@@ -1043,6 +1158,8 @@ AMF3_Decode(AMFObject *obj, const char *
 
 	      do
 		{
+		  if (nSize <=0)
+		    goto invalid;
 		  nRes = AMF3Prop_Decode(&prop, pBuffer, nSize, TRUE);
 		  AMF_AddProp(obj, &prop);
 
@@ -1090,10 +1207,18 @@ AMF_Decode(AMFObject *obj, const char *p
 
       nRes = AMFProp_Decode(&prop, pBuffer, nSize, bDecodeName);
       if (nRes == -1)
-	bError = TRUE;
+	{
+	  bError = TRUE;
+	  break;
+	}
       else
 	{
 	  nSize -= nRes;
+	  if (nSize < 0)
+	    {
+	      bError = TRUE;
+	      break;
+	    }
 	  pBuffer += nRes;
 	  AMF_AddProp(obj, &prop);
 	}
@@ -1111,7 +1236,7 @@ AMF_AddProp(AMFObject *obj, const AMFObj
   if (!(obj->o_num & 0x0f))
     obj->o_props =
       realloc(obj->o_props, (obj->o_num + 16) * sizeof(AMFObjectProperty));
-  obj->o_props[obj->o_num++] = *prop;
+  memcpy(&obj->o_props[obj->o_num++], prop, sizeof(AMFObjectProperty));
 }
 
 int
@@ -1125,7 +1250,7 @@ AMF_GetProp(AMFObject *obj, const AVal *
 {
   if (nIndex >= 0)
     {
-      if (nIndex <= obj->o_num)
+      if (nIndex < obj->o_num)
 	return &obj->o_props[nIndex];
     }
   else
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/amf.h rtmpdump/librtmp/amf.h
--- rtmpdump-2.3/librtmp/amf.h	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/amf.h	2026-06-19 14:15:18.301606620 -0600
@@ -104,6 +104,9 @@ extern "C"
   double AMF_DecodeNumber(const char *data);
 
   char *AMF_Encode(AMFObject * obj, char *pBuffer, char *pBufEnd);
+  char *AMF_EncodeEcmaArray(AMFObject *obj, char *pBuffer, char *pBufEnd);
+  char *AMF_EncodeArray(AMFObject *obj, char *pBuffer, char *pBufEnd);
+
   int AMF_Decode(AMFObject * obj, const char *pBuffer, int nSize,
 		 int bDecodeName);
   int AMF_DecodeArray(AMFObject * obj, const char *pBuffer, int nSize,
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/dh.h rtmpdump/librtmp/dh.h
--- rtmpdump-2.3/librtmp/dh.h	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/dh.h	2026-06-19 14:15:18.301606620 -0600
@@ -30,18 +30,22 @@
 #ifdef USE_POLARSSL
 #include <polarssl/dhm.h>
 typedef mpi * MP_t;
-#define MP_new(m)	m = malloc(sizeof(mpi)); mpi_init(m, NULL)
+#define MP_new(m)	m = malloc(sizeof(mpi)); mpi_init(m)
 #define MP_set_w(mpi, w)	mpi_lset(mpi, w)
 #define MP_cmp(u, v)	mpi_cmp_mpi(u, v)
 #define MP_set(u, v)	mpi_copy(u, v)
 #define MP_sub_w(mpi, w)	mpi_sub_int(mpi, mpi, w)
 #define MP_cmp_1(mpi)	mpi_cmp_int(mpi, 1)
 #define MP_modexp(r, y, q, p)	mpi_exp_mod(r, y, q, p, NULL)
-#define MP_free(mpi)	mpi_free(mpi, NULL); free(mpi)
+#define MP_free(mpi)	mpi_free(mpi); free(mpi)
 #define MP_gethex(u, hex, res)	MP_new(u); res = mpi_read_string(u, 16, hex) == 0
 #define MP_bytes(u)	mpi_size(u)
 #define MP_setbin(u,buf,len)	mpi_write_binary(u,buf,len)
 #define MP_getbin(u,buf,len)	MP_new(u); mpi_read_binary(u,buf,len)
+#define MP_setpg(dh, p, g)	dh->p = p; dh->g = g
+#define MP_setlength(dh, l)	dh->length = l
+#define MP_getp(dh)	dh->p
+#define MP_getpubkey(dh)	dh->pub_key
 
 typedef struct MDH {
   MP_t p;
@@ -53,7 +57,7 @@ typedef struct MDH {
 } MDH;
 
 #define MDH_new()	calloc(1,sizeof(MDH))
-#define MDH_free(vp)	{MDH *dh = vp; dhm_free(&dh->ctx); MP_free(dh->p); MP_free(dh->g); MP_free(dh->pub_key); MP_free(dh->priv_key); free(dh);}
+#define MDH_free(vp)	{MDH *_dh = vp; dhm_free(&_dh->ctx); MP_free(_dh->p); MP_free(_dh->g); MP_free(_dh->pub_key); MP_free(_dh->priv_key); free(_dh);}
 
 static int MDH_generate_key(MDH *dh)
 {
@@ -61,7 +65,7 @@ static int MDH_generate_key(MDH *dh)
   MP_set(&dh->ctx.P, dh->p);
   MP_set(&dh->ctx.G, dh->g);
   dh->ctx.len = 128;
-  dhm_make_public(&dh->ctx, 1024, out, 1, havege_rand, &RTMP_TLS_ctx->hs);
+  dhm_make_public(&dh->ctx, 1024, out, 1, havege_random, &RTMP_TLS_ctx->hs);
   MP_new(dh->pub_key);
   MP_new(dh->priv_key);
   MP_set(dh->pub_key, &dh->ctx.GX);
@@ -71,27 +75,32 @@ static int MDH_generate_key(MDH *dh)
 
 static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh)
 {
-  int n = len;
   MP_set(&dh->ctx.GY, pub);
-  dhm_calc_secret(&dh->ctx, secret, &n);
+  dhm_calc_secret(&dh->ctx, secret, &len);
   return 0;
 }
 
 #elif defined(USE_GNUTLS)
-#include <gcrypt.h>
-typedef gcry_mpi_t MP_t;
-#define MP_new(m)	m = gcry_mpi_new(1)
-#define MP_set_w(mpi, w)	gcry_mpi_set_ui(mpi, w)
-#define MP_cmp(u, v)	gcry_mpi_cmp(u, v)
-#define MP_set(u, v)	gcry_mpi_set(u, v)
-#define MP_sub_w(mpi, w)	gcry_mpi_sub_ui(mpi, mpi, w)
-#define MP_cmp_1(mpi)	gcry_mpi_cmp_ui(mpi, 1)
-#define MP_modexp(r, y, q, p)	gcry_mpi_powm(r, y, q, p)
-#define MP_free(mpi)	gcry_mpi_release(mpi)
-#define MP_gethex(u, hex, res)	res = (gcry_mpi_scan(&u, GCRYMPI_FMT_HEX, hex, 0, 0) == 0)
-#define MP_bytes(u)	(gcry_mpi_get_nbits(u) + 7) / 8
-#define MP_setbin(u,buf,len)	gcry_mpi_print(GCRYMPI_FMT_USG,buf,len,NULL,u)
-#define MP_getbin(u,buf,len)	gcry_mpi_scan(&u,GCRYMPI_FMT_USG,buf,len,NULL)
+#include <gmp.h>
+#include <nettle/bignum.h>
+#include <gnutls/crypto.h>
+typedef mpz_ptr MP_t;
+#define MP_new(m)	m = malloc(sizeof(*m)); mpz_init2(m, 1)
+#define MP_set_w(mpi, w)	mpz_set_ui(mpi, w)
+#define MP_cmp(u, v)	mpz_cmp(u, v)
+#define MP_set(u, v)	mpz_set(u, v)
+#define MP_sub_w(mpi, w)	mpz_sub_ui(mpi, mpi, w)
+#define MP_cmp_1(mpi)	mpz_cmp_ui(mpi, 1)
+#define MP_modexp(r, y, q, p)	mpz_powm(r, y, q, p)
+#define MP_free(mpi)	mpz_clear(mpi); free(mpi)
+#define MP_gethex(u, hex, res)	u = malloc(sizeof(*u)); mpz_init2(u, 1); res = (mpz_set_str(u, hex, 16) == 0)
+#define MP_bytes(u)	(mpz_sizeinbase(u, 2) + 7) / 8
+#define MP_setbin(u,buf,len)	nettle_mpz_get_str_256(len,buf,u)
+#define MP_getbin(u,buf,len)	u = malloc(sizeof(*u)); mpz_init2(u, 1); nettle_mpz_set_str_256_u(u,len,buf)
+#define MP_setpg(dh, p, g)	dh->p = p; dh->g = g
+#define MP_setlength(dh, l)	dh->length = l
+#define MP_getp(dh)	dh->p
+#define MP_getpubkey(dh)	dh->pub_key
 
 typedef struct MDH {
   MP_t p;
@@ -104,21 +113,62 @@ typedef struct MDH {
 #define	MDH_new()	calloc(1,sizeof(MDH))
 #define MDH_free(dh)	do {MP_free(((MDH*)(dh))->p); MP_free(((MDH*)(dh))->g); MP_free(((MDH*)(dh))->pub_key); MP_free(((MDH*)(dh))->priv_key); free(dh);} while(0)
 
-extern MP_t gnutls_calc_dh_secret(MP_t *priv, MP_t g, MP_t p);
-extern MP_t gnutls_calc_dh_key(MP_t y, MP_t x, MP_t p);
+static int MDH_generate_key(MDH *dh)
+{
+  int num_bytes;
+  uint32_t seed;
+  gmp_randstate_t rs;
+
+  num_bytes = (mpz_sizeinbase(dh->p, 2) + 7) / 8 - 1;
+  if (num_bytes <= 0 || num_bytes > 18000)
+    return 0;
+
+  dh->priv_key = calloc(1, sizeof(*dh->priv_key));
+  if (!dh->priv_key)
+    return 0;
+  mpz_init2(dh->priv_key, 1);
+  gnutls_rnd(GNUTLS_RND_RANDOM, &seed, sizeof(seed));
+  gmp_randinit_mt(rs);
+  gmp_randseed_ui(rs, seed);
+  mpz_urandomb(dh->priv_key, rs, num_bytes);
+  gmp_randclear(rs);
+
+  dh->pub_key = calloc(1, sizeof(*dh->pub_key));
+  if (!dh->pub_key)
+    return 0;
+  mpz_init2(dh->pub_key, 1);
+  if (!dh->pub_key) {
+    mpz_clear(dh->priv_key);
+    free(dh->priv_key);
+    return 0;
+  }
+
+  mpz_powm(dh->pub_key, dh->g, dh->priv_key, dh->p);
+
+  return 1;
+}
 
-#define MDH_generate_key(dh)	(dh->pub_key = gnutls_calc_dh_secret(&dh->priv_key, dh->g, dh->p))
 static int MDH_compute_key(uint8_t *secret, size_t len, MP_t pub, MDH *dh)
 {
-  MP_t sec = gnutls_calc_dh_key(pub, dh->priv_key, dh->p);
-  if (sec)
-    {
-	  MP_setbin(sec, secret, len);
-	  MP_free(sec);
-	  return 0;
-	}
-  else
+  mpz_ptr k;
+  int num_bytes;
+
+  num_bytes = (mpz_sizeinbase(dh->p, 2) + 7) / 8;
+  if (num_bytes <= 0 || num_bytes > 18000)
+    return -1;
+
+  k = calloc(1, sizeof(*k));
+  if (!k)
     return -1;
+  mpz_init2(k, 1);
+
+  mpz_powm(k, pub, dh->priv_key, dh->p);
+  nettle_mpz_get_str_256(len, secret, k);
+  mpz_clear(k);
+  free(k);
+
+  /* return the length of the shared secret key like DH_compute_key */
+  return len;
 }
 
 #else /* USE_OPENSSL */
@@ -145,6 +195,17 @@ typedef BIGNUM * MP_t;
 #define MDH_generate_key(dh)	DH_generate_key(dh)
 #define MDH_compute_key(secret, seclen, pub, dh)	DH_compute_key(secret, pub, dh)
 
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+#define MP_setpg(dh, p, g)	DH_set0_pqg(dh, p, NULL, g)
+#define MP_setlength(dh, l)	DH_set_length(dh, l)
+#define MP_getp(dh)	DH_get0_p(dh)
+#define MP_getpubkey(dh)	DH_get0_pub_key(dh)
+#else
+#define MP_setpg(dh, p, g)	dh->p = p; dh->g = g
+#define MP_setlength(dh, l)	dh->length = l
+#define MP_getp(dh)	dh->p
+#define MP_getpubkey(dh)	dh->pub_key
+#endif
 #endif
 
 #include "log.h"
@@ -207,24 +268,26 @@ DHInit(int nKeyBits)
 {
   size_t res;
   MDH *dh = MDH_new();
+  MP_t g, p;
 
   if (!dh)
     goto failed;
 
-  MP_new(dh->g);
+  MP_new(g);
 
-  if (!dh->g)
+  if (!g)
     goto failed;
 
-  MP_gethex(dh->p, P1024, res);	/* prime P1024, see dhgroups.h */
+  MP_gethex(p, P1024, res);	/* prime P1024, see dhgroups.h */
   if (!res)
     {
       goto failed;
     }
 
-  MP_set_w(dh->g, 2);	/* base 2 */
+  MP_set_w(g, 2);	/* base 2 */
+  MP_setpg(dh, p, g);
 
-  dh->length = nKeyBits;
+  MP_setlength(dh, nKeyBits);
   return dh;
 
 failed:
@@ -237,31 +300,35 @@ failed:
 static int
 DHGenerateKey(MDH *dh)
 {
-  size_t res = 0;
+  MP_t q1;
+  size_t res;
   if (!dh)
     return 0;
 
-  while (!res)
-    {
-      MP_t q1 = NULL;
-
-      if (!MDH_generate_key(dh))
-	return 0;
-
-      MP_gethex(q1, Q1024, res);
-      assert(res);
+  MP_gethex(q1, Q1024, res);
+  assert(res);
 
-      res = isValidPublicKey(dh->pub_key, dh->p, q1);
-      if (!res)
+  do
+    {
+      if (MDH_generate_key(dh))
+        {
+	  MP_t key = (MP_t)MP_getpubkey(dh);
+	  MP_t p = (MP_t)MP_getp(dh);
+	  res = isValidPublicKey(key, p, q1);
+        }
+      else
 	{
+#if !defined(OPENSSL_VERSION_NUMBER) || OPENSSL_VERSION_NUMBER < 0x10100000
 	  MP_free(dh->pub_key);
 	  MP_free(dh->priv_key);
 	  dh->pub_key = dh->priv_key = 0;
+#endif
+	  res = 0;
+	  break;
 	}
-
-      MP_free(q1);
-    }
-  return 1;
+    } while (!res);
+  MP_free(q1);
+  return res;
 }
 
 /* fill pubkey with the public key in BIG ENDIAN order
@@ -272,15 +339,16 @@ static int
 DHGetPublicKey(MDH *dh, uint8_t *pubkey, size_t nPubkeyLen)
 {
   int len;
-  if (!dh || !dh->pub_key)
+  MP_t pub_key;
+  if (!dh || !(pub_key = (MP_t)MP_getpubkey(dh)))
     return 0;
 
-  len = MP_bytes(dh->pub_key);
+  len = MP_bytes(pub_key);
   if (len <= 0 || len > (int) nPubkeyLen)
     return 0;
 
   memset(pubkey, 0, nPubkeyLen);
-  MP_setbin(dh->pub_key, pubkey + (nPubkeyLen - len), len);
+  MP_setbin(pub_key, pubkey + (nPubkeyLen - len), len);
   return 1;
 }
 
@@ -322,7 +390,7 @@ DHComputeSharedSecretKey(MDH *dh, uint8_
   MP_gethex(q1, Q1024, len);
   assert(len);
 
-  if (isValidPublicKey(pubkeyBn, dh->p, q1))
+  if (isValidPublicKey(pubkeyBn, (MP_t)MP_getp(dh), q1))
     res = MDH_compute_key(secret, nPubkeyLen, pubkeyBn, dh);
   else
     res = -1;
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/handshake.h rtmpdump/librtmp/handshake.h
--- rtmpdump-2.3/librtmp/handshake.h	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/handshake.h	2026-06-19 14:15:18.301606620 -0600
@@ -43,21 +43,24 @@ typedef arc4_context *	RC4_handle;
 #define RC4_free(h)	free(h)
 
 #elif defined(USE_GNUTLS)
-#include <gcrypt.h>
+#include <nettle/hmac.h>
+#include <nettle/arcfour.h>
 #ifndef SHA256_DIGEST_LENGTH
 #define SHA256_DIGEST_LENGTH	32
 #endif
-#define HMAC_CTX	gcry_md_hd_t
-#define HMAC_setup(ctx, key, len)	gcry_md_open(&ctx, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); gcry_md_setkey(ctx, key, len)
-#define HMAC_crunch(ctx, buf, len)	gcry_md_write(ctx, buf, len)
-#define HMAC_finish(ctx, dig, dlen)	dlen = SHA256_DIGEST_LENGTH; memcpy(dig, gcry_md_read(ctx, 0), dlen); gcry_md_close(ctx)
-
-typedef gcry_cipher_hd_t	RC4_handle;
-#define	RC4_alloc(h)	gcry_cipher_open(h, GCRY_CIPHER_ARCFOUR, GCRY_CIPHER_MODE_STREAM, 0)
-#define RC4_setkey(h,l,k)	gcry_cipher_setkey(h,k,l)
-#define RC4_encrypt(h,l,d)	gcry_cipher_encrypt(h,(void *)d,l,NULL,0)
-#define RC4_encrypt2(h,l,s,d)	gcry_cipher_encrypt(h,(void *)d,l,(void *)s,l)
-#define RC4_free(h)	gcry_cipher_close(h)
+#undef HMAC_CTX
+#define HMAC_CTX	struct hmac_sha256_ctx
+#define HMAC_setup(ctx, key, len)	hmac_sha256_set_key(&ctx, len, key)
+#define HMAC_crunch(ctx, buf, len)	hmac_sha256_update(&ctx, len, buf)
+#define HMAC_finish(ctx, dig, dlen)	dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig)
+#define HMAC_close(ctx)
+
+typedef struct arcfour_ctx*	RC4_handle;
+#define RC4_alloc(h)	*h = malloc(sizeof(struct arcfour_ctx))
+#define RC4_setkey(h,l,k)	arcfour_set_key(h, l, k)
+#define RC4_encrypt(h,l,d)	arcfour_crypt(h,l,(uint8_t *)d,(uint8_t *)d)
+#define RC4_encrypt2(h,l,s,d)	arcfour_crypt(h,l,(uint8_t *)d,(uint8_t *)s)
+#define RC4_free(h)	free(h)
 
 #else	/* USE_OPENSSL */
 #include <openssl/sha.h>
@@ -66,9 +69,16 @@ typedef gcry_cipher_hd_t	RC4_handle;
 #if OPENSSL_VERSION_NUMBER < 0x0090800 || !defined(SHA256_DIGEST_LENGTH)
 #error Your OpenSSL is too old, need 0.9.8 or newer with SHA256
 #endif
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+#define HMAC_CTX HMAC_CTX *
+#define HMAC_setup(ctx, key, len)	ctx = HMAC_CTX_new(); HMAC_Init_ex(ctx, key, len, EVP_sha256(), 0)
+#define HMAC_crunch(ctx, buf, len)	HMAC_Update(ctx, buf, len)
+#define HMAC_finish(ctx, dig, dlen)	HMAC_Final(ctx, dig, &dlen); HMAC_CTX_free(ctx)
+#else
 #define HMAC_setup(ctx, key, len)	HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, key, len, EVP_sha256(), 0)
 #define HMAC_crunch(ctx, buf, len)	HMAC_Update(&ctx, buf, len)
 #define HMAC_finish(ctx, dig, dlen)	HMAC_Final(&ctx, dig, &dlen); HMAC_CTX_cleanup(&ctx)
+#endif
 
 typedef RC4_KEY *	RC4_handle;
 #define RC4_alloc(h)	*h = malloc(sizeof(RC4_KEY))
@@ -358,6 +368,337 @@ static void rtmpe8_sig(uint8_t *in, uint
   out[7] = v1;
 }
 
+/* RTMPE type 9 uses Blowfish on the regular signature
+ * http://en.wikipedia.org/wiki/Blowfish_(cipher)
+ */
+#define	BF_ROUNDS	16
+typedef struct bf_key {
+	uint32_t s[4][256];
+	uint32_t p[BF_ROUNDS+2];
+} bf_key;
+
+static const uint32_t bf_sinit[][256] = {
+
+  /* S-Box 0 */
+  { 0xd1310ba6, 0x98dfb5ac, 0x2ffd72db, 0xd01adfb7, 0xb8e1afed, 0x6a267e96,
+    0xba7c9045, 0xf12c7f99, 0x24a19947, 0xb3916cf7, 0x0801f2e2, 0x858efc16,
+    0x636920d8, 0x71574e69, 0xa458fea3, 0xf4933d7e, 0x0d95748f, 0x728eb658,
+    0x718bcd58, 0x82154aee, 0x7b54a41d, 0xc25a59b5, 0x9c30d539, 0x2af26013,
+    0xc5d1b023, 0x286085f0, 0xca417918, 0xb8db38ef, 0x8e79dcb0, 0x603a180e,
+    0x6c9e0e8b, 0xb01e8a3e, 0xd71577c1, 0xbd314b27, 0x78af2fda, 0x55605c60,
+    0xe65525f3, 0xaa55ab94, 0x57489862, 0x63e81440, 0x55ca396a, 0x2aab10b6,
+    0xb4cc5c34, 0x1141e8ce, 0xa15486af, 0x7c72e993, 0xb3ee1411, 0x636fbc2a,
+    0x2ba9c55d, 0x741831f6, 0xce5c3e16, 0x9b87931e, 0xafd6ba33, 0x6c24cf5c,
+    0x7a325381, 0x28958677, 0x3b8f4898, 0x6b4bb9af, 0xc4bfe81b, 0x66282193,
+    0x61d809cc, 0xfb21a991, 0x487cac60, 0x5dec8032, 0xef845d5d, 0xe98575b1,
+    0xdc262302, 0xeb651b88, 0x23893e81, 0xd396acc5, 0x0f6d6ff3, 0x83f44239,
+    0x2e0b4482, 0xa4842004, 0x69c8f04a, 0x9e1f9b5e, 0x21c66842, 0xf6e96c9a,
+    0x670c9c61, 0xabd388f0, 0x6a51a0d2, 0xd8542f68, 0x960fa728, 0xab5133a3,
+    0x6eef0b6c, 0x137a3be4, 0xba3bf050, 0x7efb2a98, 0xa1f1651d, 0x39af0176,
+    0x66ca593e, 0x82430e88, 0x8cee8619, 0x456f9fb4, 0x7d84a5c3, 0x3b8b5ebe,
+    0xe06f75d8, 0x85c12073, 0x401a449f, 0x56c16aa6, 0x4ed3aa62, 0x363f7706,
+    0x1bfedf72, 0x429b023d, 0x37d0d724, 0xd00a1248, 0xdb0fead3, 0x49f1c09b,
+    0x075372c9, 0x80991b7b, 0x25d479d8, 0xf6e8def7, 0xe3fe501a, 0xb6794c3b,
+    0x976ce0bd, 0x04c006ba, 0xc1a94fb6, 0x409f60c4, 0x5e5c9ec2, 0x196a2463,
+    0x68fb6faf, 0x3e6c53b5, 0x1339b2eb, 0x3b52ec6f, 0x6dfc511f, 0x9b30952c,
+    0xcc814544, 0xaf5ebd09, 0xbee3d004, 0xde334afd, 0x660f2807, 0x192e4bb3,
+    0xc0cba857, 0x45c8740f, 0xd20b5f39, 0xb9d3fbdb, 0x5579c0bd, 0x1a60320a,
+    0xd6a100c6, 0x402c7279, 0x679f25fe, 0xfb1fa3cc, 0x8ea5e9f8, 0xdb3222f8,
+    0x3c7516df, 0xfd616b15, 0x2f501ec8, 0xad0552ab, 0x323db5fa, 0xfd238760,
+    0x53317b48, 0x3e00df82, 0x9e5c57bb, 0xca6f8ca0, 0x1a87562e, 0xdf1769db,
+    0xd542a8f6, 0x287effc3, 0xac6732c6, 0x8c4f5573, 0x695b27b0, 0xbbca58c8,
+    0xe1ffa35d, 0xb8f011a0, 0x10fa3d98, 0xfd2183b8, 0x4afcb56c, 0x2dd1d35b,
+    0x9a53e479, 0xb6f84565, 0xd28e49bc, 0x4bfb9790, 0xe1ddf2da, 0xa4cb7e33,
+    0x62fb1341, 0xcee4c6e8, 0xef20cada, 0x36774c01, 0xd07e9efe, 0x2bf11fb4,
+    0x95dbda4d, 0xae909198, 0xeaad8e71, 0x6b93d5a0, 0xd08ed1d0, 0xafc725e0,
+    0x8e3c5b2f, 0x8e7594b7, 0x8ff6e2fb, 0xf2122b64, 0x8888b812, 0x900df01c,
+    0x4fad5ea0, 0x688fc31c, 0xd1cff191, 0xb3a8c1ad, 0x2f2f2218, 0xbe0e1777,
+    0xea752dfe, 0x8b021fa1, 0xe5a0cc0f, 0xb56f74e8, 0x18acf3d6, 0xce89e299,
+    0xb4a84fe0, 0xfd13e0b7, 0x7cc43b81, 0xd2ada8d9, 0x165fa266, 0x80957705,
+    0x93cc7314, 0x211a1477, 0xe6ad2065, 0x77b5fa86, 0xc75442f5, 0xfb9d35cf,
+    0xebcdaf0c, 0x7b3e89a0, 0xd6411bd3, 0xae1e7e49, 0x00250e2d, 0x2071b35e,
+    0x226800bb, 0x57b8e0af, 0x2464369b, 0xf009b91e, 0x5563911d, 0x59dfa6aa,
+    0x78c14389, 0xd95a537f, 0x207d5ba2, 0x02e5b9c5, 0x83260376, 0x6295cfa9,
+    0x11c81968, 0x4e734a41, 0xb3472dca, 0x7b14a94a, 0x1b510052, 0x9a532915,
+    0xd60f573f, 0xbc9bc6e4, 0x2b60a476, 0x81e67400, 0x08ba6fb5, 0x571be91f,
+    0xf296ec6b, 0x2a0dd915, 0xb6636521, 0xe7b9f9b6, 0xff34052e, 0xc5855664,
+    0x53b02d5d, 0xa99f8fa1, 0x08ba4799, 0x6e85076a, },
+
+  /* S-Box 1 */
+  { 0x4b7a70e9, 0xb5b32944, 0xdb75092e, 0xc4192623, 0xad6ea6b0, 0x49a7df7d,
+    0x9cee60b8, 0x8fedb266, 0xecaa8c71, 0x699a17ff, 0x5664526c, 0xc2b19ee1,
+    0x193602a5, 0x75094c29, 0xa0591340, 0xe4183a3e, 0x3f54989a, 0x5b429d65,
+    0x6b8fe4d6, 0x99f73fd6, 0xa1d29c07, 0xefe830f5, 0x4d2d38e6, 0xf0255dc1,
+    0x4cdd2086, 0x8470eb26, 0x6382e9c6, 0x021ecc5e, 0x09686b3f, 0x3ebaefc9,
+    0x3c971814, 0x6b6a70a1, 0x687f3584, 0x52a0e286, 0xb79c5305, 0xaa500737,
+    0x3e07841c, 0x7fdeae5c, 0x8e7d44ec, 0x5716f2b8, 0xb03ada37, 0xf0500c0d,
+    0xf01c1f04, 0x0200b3ff, 0xae0cf51a, 0x3cb574b2, 0x25837a58, 0xdc0921bd,
+    0xd19113f9, 0x7ca92ff6, 0x94324773, 0x22f54701, 0x3ae5e581, 0x37c2dadc,
+    0xc8b57634, 0x9af3dda7, 0xa9446146, 0x0fd0030e, 0xecc8c73e, 0xa4751e41,
+    0xe238cd99, 0x3bea0e2f, 0x3280bba1, 0x183eb331, 0x4e548b38, 0x4f6db908,
+    0x6f420d03, 0xf60a04bf, 0x2cb81290, 0x24977c79, 0x5679b072, 0xbcaf89af,
+    0xde9a771f, 0xd9930810, 0xb38bae12, 0xdccf3f2e, 0x5512721f, 0x2e6b7124,
+    0x501adde6, 0x9f84cd87, 0x7a584718, 0x7408da17, 0xbc9f9abc, 0xe94b7d8c,
+    0xec7aec3a, 0xdb851dfa, 0x63094366, 0xc464c3d2, 0xef1c1847, 0x3215d908,
+    0xdd433b37, 0x24c2ba16, 0x12a14d43, 0x2a65c451, 0x50940002, 0x133ae4dd,
+    0x71dff89e, 0x10314e55, 0x81ac77d6, 0x5f11199b, 0x043556f1, 0xd7a3c76b,
+    0x3c11183b, 0x5924a509, 0xf28fe6ed, 0x97f1fbfa, 0x9ebabf2c, 0x1e153c6e,
+    0x86e34570, 0xeae96fb1, 0x860e5e0a, 0x5a3e2ab3, 0x771fe71c, 0x4e3d06fa,
+    0x2965dcb9, 0x99e71d0f, 0x803e89d6, 0x5266c825, 0x2e4cc978, 0x9c10b36a,
+    0xc6150eba, 0x94e2ea78, 0xa5fc3c53, 0x1e0a2df4, 0xf2f74ea7, 0x361d2b3d,
+    0x1939260f, 0x19c27960, 0x5223a708, 0xf71312b6, 0xebadfe6e, 0xeac31f66,
+    0xe3bc4595, 0xa67bc883, 0xb17f37d1, 0x018cff28, 0xc332ddef, 0xbe6c5aa5,
+    0x65582185, 0x68ab9802, 0xeecea50f, 0xdb2f953b, 0x2aef7dad, 0x5b6e2f84,
+    0x1521b628, 0x29076170, 0xecdd4775, 0x619f1510, 0x13cca830, 0xeb61bd96,
+    0x0334fe1e, 0xaa0363cf, 0xb5735c90, 0x4c70a239, 0xd59e9e0b, 0xcbaade14,
+    0xeecc86bc, 0x60622ca7, 0x9cab5cab, 0xb2f3846e, 0x648b1eaf, 0x19bdf0ca,
+    0xa02369b9, 0x655abb50, 0x40685a32, 0x3c2ab4b3, 0x319ee9d5, 0xc021b8f7,
+    0x9b540b19, 0x875fa099, 0x95f7997e, 0x623d7da8, 0xf837889a, 0x97e32d77,
+    0x11ed935f, 0x16681281, 0x0e358829, 0xc7e61fd6, 0x96dedfa1, 0x7858ba99,
+    0x57f584a5, 0x1b227263, 0x9b83c3ff, 0x1ac24696, 0xcdb30aeb, 0x532e3054,
+    0x8fd948e4, 0x6dbc3128, 0x58ebf2ef, 0x34c6ffea, 0xfe28ed61, 0xee7c3c73,
+    0x5d4a14d9, 0xe864b7e3, 0x42105d14, 0x203e13e0, 0x45eee2b6, 0xa3aaabea,
+    0xdb6c4f15, 0xfacb4fd0, 0xc742f442, 0xef6abbb5, 0x654f3b1d, 0x41cd2105,
+    0xd81e799e, 0x86854dc7, 0xe44b476a, 0x3d816250, 0xcf62a1f2, 0x5b8d2646,
+    0xfc8883a0, 0xc1c7b6a3, 0x7f1524c3, 0x69cb7492, 0x47848a0b, 0x5692b285,
+    0x095bbf00, 0xad19489d, 0x1462b174, 0x23820e00, 0x58428d2a, 0x0c55f5ea,
+    0x1dadf43e, 0x233f7061, 0x3372f092, 0x8d937e41, 0xd65fecf1, 0x6c223bdb,
+    0x7cde3759, 0xcbee7460, 0x4085f2a7, 0xce77326e, 0xa6078084, 0x19f8509e,
+    0xe8efd855, 0x61d99735, 0xa969a7aa, 0xc50c06c2, 0x5a04abfc, 0x800bcadc,
+    0x9e447a2e, 0xc3453484, 0xfdd56705, 0x0e1e9ec9, 0xdb73dbd3, 0x105588cd,
+    0x675fda79, 0xe3674340, 0xc5c43465, 0x713e38d8, 0x3d28f89e, 0xf16dff20,
+    0x153e21e7, 0x8fb03d4a, 0xe6e39f2b, 0xdb83adf7, },
+
+  /* S-Box 2 */
+  { 0xe93d5a68, 0x948140f7, 0xf64c261c, 0x94692934, 0x411520f7, 0x7602d4f7,
+    0xbcf46b2e, 0xd4a20068, 0xd4082471, 0x3320f46a, 0x43b7d4b7, 0x500061af,
+    0x1e39f62e, 0x97244546, 0x14214f74, 0xbf8b8840, 0x4d95fc1d, 0x96b591af,
+    0x70f4ddd3, 0x66a02f45, 0xbfbc09ec, 0x03bd9785, 0x7fac6dd0, 0x31cb8504,
+    0x96eb27b3, 0x55fd3941, 0xda2547e6, 0xabca0a9a, 0x28507825, 0x530429f4,
+    0x0a2c86da, 0xe9b66dfb, 0x68dc1462, 0xd7486900, 0x680ec0a4, 0x27a18dee,
+    0x4f3ffea2, 0xe887ad8c, 0xb58ce006, 0x7af4d6b6, 0xaace1e7c, 0xd3375fec,
+    0xce78a399, 0x406b2a42, 0x20fe9e35, 0xd9f385b9, 0xee39d7ab, 0x3b124e8b,
+    0x1dc9faf7, 0x4b6d1856, 0x26a36631, 0xeae397b2, 0x3a6efa74, 0xdd5b4332,
+    0x6841e7f7, 0xca7820fb, 0xfb0af54e, 0xd8feb397, 0x454056ac, 0xba489527,
+    0x55533a3a, 0x20838d87, 0xfe6ba9b7, 0xd096954b, 0x55a867bc, 0xa1159a58,
+    0xcca92963, 0x99e1db33, 0xa62a4a56, 0x3f3125f9, 0x5ef47e1c, 0x9029317c,
+    0xfdf8e802, 0x04272f70, 0x80bb155c, 0x05282ce3, 0x95c11548, 0xe4c66d22,
+    0x48c1133f, 0xc70f86dc, 0x07f9c9ee, 0x41041f0f, 0x404779a4, 0x5d886e17,
+    0x325f51eb, 0xd59bc0d1, 0xf2bcc18f, 0x41113564, 0x257b7834, 0x602a9c60,
+    0xdff8e8a3, 0x1f636c1b, 0x0e12b4c2, 0x02e1329e, 0xaf664fd1, 0xcad18115,
+    0x6b2395e0, 0x333e92e1, 0x3b240b62, 0xeebeb922, 0x85b2a20e, 0xe6ba0d99,
+    0xde720c8c, 0x2da2f728, 0xd0127845, 0x95b794fd, 0x647d0862, 0xe7ccf5f0,
+    0x5449a36f, 0x877d48fa, 0xc39dfd27, 0xf33e8d1e, 0x0a476341, 0x992eff74,
+    0x3a6f6eab, 0xf4f8fd37, 0xa812dc60, 0xa1ebddf8, 0x991be14c, 0xdb6e6b0d,
+    0xc67b5510, 0x6d672c37, 0x2765d43b, 0xdcd0e804, 0xf1290dc7, 0xcc00ffa3,
+    0xb5390f92, 0x690fed0b, 0x667b9ffb, 0xcedb7d9c, 0xa091cf0b, 0xd9155ea3,
+    0xbb132f88, 0x515bad24, 0x7b9479bf, 0x763bd6eb, 0x37392eb3, 0xcc115979,
+    0x8026e297, 0xf42e312d, 0x6842ada7, 0xc66a2b3b, 0x12754ccc, 0x782ef11c,
+    0x6a124237, 0xb79251e7, 0x06a1bbe6, 0x4bfb6350, 0x1a6b1018, 0x11caedfa,
+    0x3d25bdd8, 0xe2e1c3c9, 0x44421659, 0x0a121386, 0xd90cec6e, 0xd5abea2a,
+    0x64af674e, 0xda86a85f, 0xbebfe988, 0x64e4c3fe, 0x9dbc8057, 0xf0f7c086,
+    0x60787bf8, 0x6003604d, 0xd1fd8346, 0xf6381fb0, 0x7745ae04, 0xd736fccc,
+    0x83426b33, 0xf01eab71, 0xb0804187, 0x3c005e5f, 0x77a057be, 0xbde8ae24,
+    0x55464299, 0xbf582e61, 0x4e58f48f, 0xf2ddfda2, 0xf474ef38, 0x8789bdc2,
+    0x5366f9c3, 0xc8b38e74, 0xb475f255, 0x46fcd9b9, 0x7aeb2661, 0x8b1ddf84,
+    0x846a0e79, 0x915f95e2, 0x466e598e, 0x20b45770, 0x8cd55591, 0xc902de4c,
+    0xb90bace1, 0xbb8205d0, 0x11a86248, 0x7574a99e, 0xb77f19b6, 0xe0a9dc09,
+    0x662d09a1, 0xc4324633, 0xe85a1f02, 0x09f0be8c, 0x4a99a025, 0x1d6efe10,
+    0x1ab93d1d, 0x0ba5a4df, 0xa186f20f, 0x2868f169, 0xdcb7da83, 0x573906fe,
+    0xa1e2ce9b, 0x4fcd7f52, 0x50115e01, 0xa70683fa, 0xa002b5c4, 0x0de6d027,
+    0x9af88c27, 0x773f8641, 0xc3604c06, 0x61a806b5, 0xf0177a28, 0xc0f586e0,
+    0x006058aa, 0x30dc7d62, 0x11e69ed7, 0x2338ea63, 0x53c2dd94, 0xc2c21634,
+    0xbbcbee56, 0x90bcb6de, 0xebfc7da1, 0xce591d76, 0x6f05e409, 0x4b7c0188,
+    0x39720a3d, 0x7c927c24, 0x86e3725f, 0x724d9db9, 0x1ac15bb4, 0xd39eb8fc,
+    0xed545578, 0x08fca5b5, 0xd83d7cd3, 0x4dad0fc4, 0x1e50ef5e, 0xb161e6f8,
+    0xa28514d9, 0x6c51133c, 0x6fd5c7e7, 0x56e14ec4, 0x362abfce, 0xddc6c837,
+    0xd79a3234, 0x92638212, 0x670efa8e, 0x406000e0, },
+
+  /* S-Box 3 */
+  { 0x3a39ce37, 0xd3faf5cf, 0xabc27737, 0x5ac52d1b, 0x5cb0679e, 0x4fa33742,
+    0xd3822740, 0x99bc9bbe, 0xd5118e9d, 0xbf0f7315, 0xd62d1c7e, 0xc700c47b,
+    0xb78c1b6b, 0x21a19045, 0xb26eb1be, 0x6a366eb4, 0x5748ab2f, 0xbc946e79,
+    0xc6a376d2, 0x6549c2c8, 0x530ff8ee, 0x468dde7d, 0xd5730a1d, 0x4cd04dc6,
+    0x2939bbdb, 0xa9ba4650, 0xac9526e8, 0xbe5ee304, 0xa1fad5f0, 0x6a2d519a,
+    0x63ef8ce2, 0x9a86ee22, 0xc089c2b8, 0x43242ef6, 0xa51e03aa, 0x9cf2d0a4,
+    0x83c061ba, 0x9be96a4d, 0x8fe51550, 0xba645bd6, 0x2826a2f9, 0xa73a3ae1,
+    0x4ba99586, 0xef5562e9, 0xc72fefd3, 0xf752f7da, 0x3f046f69, 0x77fa0a59,
+    0x80e4a915, 0x87b08601, 0x9b09e6ad, 0x3b3ee593, 0xe990fd5a, 0x9e34d797,
+    0x2cf0b7d9, 0x022b8b51, 0x96d5ac3a, 0x017da67d, 0xd1cf3ed6, 0x7c7d2d28,
+    0x1f9f25cf, 0xadf2b89b, 0x5ad6b472, 0x5a88f54c, 0xe029ac71, 0xe019a5e6,
+    0x47b0acfd, 0xed93fa9b, 0xe8d3c48d, 0x283b57cc, 0xf8d56629, 0x79132e28,
+    0x785f0191, 0xed756055, 0xf7960e44, 0xe3d35e8c, 0x15056dd4, 0x88f46dba,
+    0x03a16125, 0x0564f0bd, 0xc3eb9e15, 0x3c9057a2, 0x97271aec, 0xa93a072a,
+    0x1b3f6d9b, 0x1e6321f5, 0xf59c66fb, 0x26dcf319, 0x7533d928, 0xb155fdf5,
+    0x03563482, 0x8aba3cbb, 0x28517711, 0xc20ad9f8, 0xabcc5167, 0xccad925f,
+    0x4de81751, 0x3830dc8e, 0x379d5862, 0x9320f991, 0xea7a90c2, 0xfb3e7bce,
+    0x5121ce64, 0x774fbe32, 0xa8b6e37e, 0xc3293d46, 0x48de5369, 0x6413e680,
+    0xa2ae0810, 0xdd6db224, 0x69852dfd, 0x09072166, 0xb39a460a, 0x6445c0dd,
+    0x586cdecf, 0x1c20c8ae, 0x5bbef7dd, 0x1b588d40, 0xccd2017f, 0x6bb4e3bb,
+    0xdda26a7e, 0x3a59ff45, 0x3e350a44, 0xbcb4cdd5, 0x72eacea8, 0xfa6484bb,
+    0x8d6612ae, 0xbf3c6f47, 0xd29be463, 0x542f5d9e, 0xaec2771b, 0xf64e6370,
+    0x740e0d8d, 0xe75b1357, 0xf8721671, 0xaf537d5d, 0x4040cb08, 0x4eb4e2cc,
+    0x34d2466a, 0x0115af84, 0xe1b00428, 0x95983a1d, 0x06b89fb4, 0xce6ea048,
+    0x6f3f3b82, 0x3520ab82, 0x011a1d4b, 0x277227f8, 0x611560b1, 0xe7933fdc,
+    0xbb3a792b, 0x344525bd, 0xa08839e1, 0x51ce794b, 0x2f32c9b7, 0xa01fbac9,
+    0xe01cc87e, 0xbcc7d1f6, 0xcf0111c3, 0xa1e8aac7, 0x1a908749, 0xd44fbd9a,
+    0xd0dadecb, 0xd50ada38, 0x0339c32a, 0xc6913667, 0x8df9317c, 0xe0b12b4f,
+    0xf79e59b7, 0x43f5bb3a, 0xf2d519ff, 0x27d9459c, 0xbf97222c, 0x15e6fc2a,
+    0x0f91fc71, 0x9b941525, 0xfae59361, 0xceb69ceb, 0xc2a86459, 0x12baa8d1,
+    0xb6c1075e, 0xe3056a0c, 0x10d25065, 0xcb03a442, 0xe0ec6e0e, 0x1698db3b,
+    0x4c98a0be, 0x3278e964, 0x9f1f9532, 0xe0d392df, 0xd3a0342b, 0x8971f21e,
+    0x1b0a7441, 0x4ba3348c, 0xc5be7120, 0xc37632d8, 0xdf359f8d, 0x9b992f2e,
+    0xe60b6f47, 0x0fe3f11d, 0xe54cda54, 0x1edad891, 0xce6279cf, 0xcd3e7e6f,
+    0x1618b166, 0xfd2c1d05, 0x848fd2c5, 0xf6fb2299, 0xf523f357, 0xa6327623,
+    0x93a83531, 0x56cccd02, 0xacf08162, 0x5a75ebb5, 0x6e163697, 0x88d273cc,
+    0xde966292, 0x81b949d0, 0x4c50901b, 0x71c65614, 0xe6c6c7bd, 0x327a140a,
+    0x45e1d006, 0xc3f27b9a, 0xc9aa53fd, 0x62a80f00, 0xbb25bfe2, 0x35bdd2f6,
+    0x71126905, 0xb2040222, 0xb6cbcf7c, 0xcd769c2b, 0x53113ec0, 0x1640e3d3,
+    0x38abbd60, 0x2547adf0, 0xba38209c, 0xf746ce76, 0x77afa1c5, 0x20756060,
+    0x85cbfe4e, 0x8ae88dd8, 0x7aaaf9b0, 0x4cf9aa7e, 0x1948c25c, 0x02fb8a8c,
+    0x01c36ae4, 0xd6ebe1f9, 0x90d4f869, 0xa65cdea0, 0x3f09252d, 0xc208e69f,
+    0xb74e6132, 0xce77e25b, 0x578fdfe3, 0x3ac372e6, },
+};
+
+static const uint32_t bf_pinit[] = {
+  /* P-Box */
+  0x243f6a88, 0x85a308d3, 0x13198a2e, 0x03707344, 0xa4093822, 0x299f31d0,
+  0x082efa98, 0xec4e6c89, 0x452821e6, 0x38d01377, 0xbe5466cf, 0x34e90c6c,
+  0xc0ac29b7, 0xc97c50dd, 0x3f84d5b5, 0xb5470917, 0x9216d5d9, 0x8979fb1b,
+};
+
+#define KEYBYTES	24
+
+static const unsigned char rtmpe9_keys[16][KEYBYTES] = {
+ { 0x79, 0x34, 0x77, 0x4c, 0x67, 0xd1, 0x38, 0x3a, 0xdf, 0xb3, 0x56, 0xbe,
+   0x8b, 0x7b, 0xd0, 0x24, 0x38, 0xe0, 0x73, 0x58, 0x41, 0x5d, 0x69, 0x67, },
+ { 0x46, 0xf6, 0xb4, 0xcc, 0x01, 0x93, 0xe3, 0xa1, 0x9e, 0x7d, 0x3c, 0x65,
+   0x55, 0x86, 0xfd, 0x09, 0x8f, 0xf7, 0xb3, 0xc4, 0x6f, 0x41, 0xca, 0x5c, },
+ { 0x1a, 0xe7, 0xe2, 0xf3, 0xf9, 0x14, 0x79, 0x94, 0xc0, 0xd3, 0x97, 0x43,
+   0x08, 0x7b, 0xb3, 0x84, 0x43, 0x2f, 0x9d, 0x84, 0x3f, 0x21, 0x01, 0x9b, },
+ { 0xd3, 0xe3, 0x54, 0xb0, 0xf7, 0x1d, 0xf6, 0x2b, 0x5a, 0x43, 0x4d, 0x04,
+   0x83, 0x64, 0x3e, 0x0d, 0x59, 0x2f, 0x61, 0xcb, 0xb1, 0x6a, 0x59, 0x0d, },
+ { 0xc8, 0xc1, 0xe9, 0xb8, 0x16, 0x56, 0x99, 0x21, 0x7b, 0x5b, 0x36, 0xb7,
+   0xb5, 0x9b, 0xdf, 0x06, 0x49, 0x2c, 0x97, 0xf5, 0x95, 0x48, 0x85, 0x7e, },
+ { 0xeb, 0xe5, 0xe6, 0x2e, 0xa4, 0xba, 0xd4, 0x2c, 0xf2, 0x16, 0xe0, 0x8f,
+   0x66, 0x23, 0xa9, 0x43, 0x41, 0xce, 0x38, 0x14, 0x84, 0x95, 0x00, 0x53, },
+ { 0x66, 0xdb, 0x90, 0xf0, 0x3b, 0x4f, 0xf5, 0x6f, 0xe4, 0x9c, 0x20, 0x89,
+   0x35, 0x5e, 0xd2, 0xb2, 0xc3, 0x9e, 0x9f, 0x7f, 0x63, 0xb2, 0x28, 0x81, },
+ { 0xbb, 0x20, 0xac, 0xed, 0x2a, 0x04, 0x6a, 0x19, 0x94, 0x98, 0x9b, 0xc8,
+   0xff, 0xcd, 0x93, 0xef, 0xc6, 0x0d, 0x56, 0xa7, 0xeb, 0x13, 0xd9, 0x30, },
+ { 0xbc, 0xf2, 0x43, 0x82, 0x09, 0x40, 0x8a, 0x87, 0x25, 0x43, 0x6d, 0xe6,
+   0xbb, 0xa4, 0xb9, 0x44, 0x58, 0x3f, 0x21, 0x7c, 0x99, 0xbb, 0x3f, 0x24, },
+ { 0xec, 0x1a, 0xaa, 0xcd, 0xce, 0xbd, 0x53, 0x11, 0xd2, 0xfb, 0x83, 0xb6,
+   0xc3, 0xba, 0xab, 0x4f, 0x62, 0x79, 0xe8, 0x65, 0xa9, 0x92, 0x28, 0x76, },
+ { 0xc6, 0x0c, 0x30, 0x03, 0x91, 0x18, 0x2d, 0x7b, 0x79, 0xda, 0xe1, 0xd5,
+   0x64, 0x77, 0x9a, 0x12, 0xc5, 0xb1, 0xd7, 0x91, 0x4f, 0x96, 0x4c, 0xa3, },
+ { 0xd7, 0x7c, 0x2a, 0xbf, 0xa6, 0xe7, 0x85, 0x7c, 0x45, 0xad, 0xff, 0x12,
+   0x94, 0xd8, 0xde, 0xa4, 0x5c, 0x3d, 0x79, 0xa4, 0x44, 0x02, 0x5d, 0x22, },
+ { 0x16, 0x19, 0x0d, 0x81, 0x6a, 0x4c, 0xc7, 0xf8, 0xb8, 0xf9, 0x4e, 0xcd,
+   0x2c, 0x9e, 0x90, 0x84, 0xb2, 0x08, 0x25, 0x60, 0xe1, 0x1e, 0xae, 0x18, },
+ { 0xe9, 0x7c, 0x58, 0x26, 0x1b, 0x51, 0x9e, 0x49, 0x82, 0x60, 0x61, 0xfc,
+   0xa0, 0xa0, 0x1b, 0xcd, 0xf5, 0x05, 0xd6, 0xa6, 0x6d, 0x07, 0x88, 0xa3, },
+ { 0x2b, 0x97, 0x11, 0x8b, 0xd9, 0x4e, 0xd9, 0xdf, 0x20, 0xe3, 0x9c, 0x10,
+   0xe6, 0xa1, 0x35, 0x21, 0x11, 0xf9, 0x13, 0x0d, 0x0b, 0x24, 0x65, 0xb2, },
+ { 0x53, 0x6a, 0x4c, 0x54, 0xac, 0x8b, 0x9b, 0xb8, 0x97, 0x29, 0xfc, 0x60,
+   0x2c, 0x5b, 0x3a, 0x85, 0x68, 0xb5, 0xaa, 0x6a, 0x44, 0xcd, 0x3f, 0xa7, },
+};
+
+#define	BF_ENC(X,S) \
+	(((S[0][X>>24] + S[1][X>>16 & 0xff]) ^ S[2][(X>>8) & 0xff]) + S[3][X & 0xff])
+
+static void bf_enc(uint32_t *x, bf_key *key)
+{
+  uint32_t  Xl;
+  uint32_t  Xr;
+  uint32_t  temp;
+  int	i;
+
+  Xl = x[0];
+  Xr = x[1];
+
+  for (i = 0; i < BF_ROUNDS; ++i) {
+    Xl ^= key->p[i];
+    Xr ^= BF_ENC(Xl,key->s);
+
+    temp = Xl;
+    Xl = Xr;
+    Xr = temp;
+  }
+
+  Xl ^= key->p[BF_ROUNDS];
+  Xr ^= key->p[BF_ROUNDS + 1];
+
+  x[0] = Xr;
+  x[1] = Xl;
+}
+
+static void bf_setkey(const unsigned char *kp, int keybytes, bf_key *key)
+{
+  int          i;
+  int          j;
+  int          k;
+  uint32_t  data;
+  uint32_t  d[2];
+
+  memcpy(key->p, bf_pinit, sizeof(key->p));
+  memcpy(key->s, bf_sinit, sizeof(key->s));
+
+  j = 0;
+  for (i = 0; i < BF_ROUNDS + 2; ++i) {
+    data = 0x00000000;
+	for (k = 0; k < 4; ++k) {
+	  data = (data << 8) | kp[j];
+	  j = j + 1;
+	  if (j >= keybytes) {
+	    j = 0;
+	  }
+	}
+	key->p[i] ^= data;
+  }
+
+  d[0] = 0x00000000;
+  d[1] = 0x00000000;
+
+  for (i = 0; i < BF_ROUNDS + 2; i += 2) {
+    bf_enc(d, key);
+
+	key->p[i] = d[0];
+	key->p[i + 1] = d[1];
+  }
+
+  for (i = 0; i < 4; ++i) {
+	for (j = 0; j < 256; j += 2) {
+
+	  bf_enc(d, key);
+
+	  key->s[i][j] = d[0];
+	  key->s[i][j + 1] = d[1];
+	}
+  }
+}
+
+static void rtmpe9_sig(uint8_t *in, uint8_t *out, int keyid)
+{
+  uint32_t d[2];
+  bf_key key;
+
+  bf_setkey(rtmpe9_keys[keyid], KEYBYTES, &key);
+
+  /* input is little-endian */
+  d[0] = in[0] | (in[1] << 8) | (in[2] << 16) | (in[3] << 24);
+  d[1] = in[4] | (in[5] << 8) | (in[6] << 16) | (in[7] << 24);
+  bf_enc(d, &key);
+  out[0] = d[0] & 0xff;
+  out[1] = (d[0] >> 8) & 0xff;
+  out[2] = (d[0] >> 16) & 0xff;
+  out[3] = (d[0] >> 24) & 0xff;
+  out[4] = d[1] & 0xff;
+  out[5] = (d[1] >> 8) & 0xff;
+  out[6] = (d[1] >> 16) & 0xff;
+  out[7] = (d[1] >> 24) & 0xff;
+}
+
 static int
 HandShake(RTMP * r, int FP9HandShake)
 {
@@ -605,8 +946,7 @@ HandShake(RTMP * r, int FP9HandShake)
           for (i=0; i<SHA256_DIGEST_LENGTH; i+=8)
 	    rtmpe8_sig(sig+i, sig+i, dptr[i] % 15);
         }
-#if 0
-      else if (type == 9))
+      else if (type == 9)
         {
 	  uint8_t *dptr = digestResp;
 	  uint8_t *sig = signatureResp;
@@ -615,7 +955,6 @@ HandShake(RTMP * r, int FP9HandShake)
             rtmpe9_sig(sig+i, sig+i, dptr[i] % 15);
         }
 #endif
-#endif
       RTMP_Log(RTMP_LOGDEBUG, "%s: Client signature calculated:", __FUNCTION__);
       RTMP_LogHex(RTMP_LOGDEBUG, signatureResp, SHA256_DIGEST_LENGTH);
     }
@@ -680,7 +1019,6 @@ HandShake(RTMP * r, int FP9HandShake)
           for (i=0; i<SHA256_DIGEST_LENGTH; i+=8)
 	    rtmpe8_sig(sig+i, sig+i, dptr[i] % 15);
         }
-#if 0
       else if (type == 9)
         {
 	  uint8_t *dptr = digest;
@@ -690,7 +1028,6 @@ HandShake(RTMP * r, int FP9HandShake)
             rtmpe9_sig(sig+i, sig+i, dptr[i] % 15);
         }
 #endif
-#endif
       RTMP_Log(RTMP_LOGDEBUG, "%s: Signature calculated:", __FUNCTION__);
       RTMP_LogHex(RTMP_LOGDEBUG, signature, SHA256_DIGEST_LENGTH);
       if (memcmp
@@ -964,8 +1301,7 @@ SHandShake(RTMP * r)
           for (i=0; i<SHA256_DIGEST_LENGTH; i+=8)
 	    rtmpe8_sig(sig+i, sig+i, dptr[i] % 15);
         }
-#if 0
-      else if (type == 9))
+      else if (type == 9)
         {
 	  uint8_t *dptr = digestResp;
 	  uint8_t *sig = signatureResp;
@@ -974,7 +1310,6 @@ SHandShake(RTMP * r)
             rtmpe9_sig(sig+i, sig+i, dptr[i] % 15);
         }
 #endif
-#endif
 
       /* some info output */
       RTMP_Log(RTMP_LOGDEBUG,
@@ -1030,17 +1365,15 @@ SHandShake(RTMP * r)
           for (i=0; i<SHA256_DIGEST_LENGTH; i+=8)
 	    rtmpe8_sig(sig+i, sig+i, dptr[i] % 15);
         }
-#if 0
-      else if (type == 9))
+      else if (type == 9)
         {
-	  uint8_t *dptr = digestResp;
-	  uint8_t *sig = signatureResp;
+	  uint8_t *dptr = digest;
+	  uint8_t *sig = signature;
 	  /* encrypt signatureResp */
           for (i=0; i<SHA256_DIGEST_LENGTH; i+=8)
             rtmpe9_sig(sig+i, sig+i, dptr[i] % 15);
         }
 #endif
-#endif
 
       /* show some information */
       RTMP_Log(RTMP_LOGDEBUG, "%s: Digest key: ", __FUNCTION__);
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/hashswf.c rtmpdump/librtmp/hashswf.c
--- rtmpdump-2.3/librtmp/hashswf.c	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/hashswf.c	2026-06-19 14:15:18.301606620 -0600
@@ -25,6 +25,7 @@
 #include <string.h>
 #include <ctype.h>
 #include <time.h>
+#include <limits.h>
 
 #include "rtmp_sys.h"
 #include "log.h"
@@ -42,33 +43,43 @@
 #define HMAC_finish(ctx, dig, dlen)	dlen = SHA256_DIGEST_LENGTH; sha2_hmac_finish(&ctx, dig)
 #define HMAC_close(ctx)
 #elif defined(USE_GNUTLS)
-#include <gnutls/gnutls.h>
-#include <gcrypt.h>
+#include <nettle/hmac.h>
 #ifndef SHA256_DIGEST_LENGTH
 #define SHA256_DIGEST_LENGTH	32
 #endif
-#define HMAC_CTX	gcry_md_hd_t
-#define HMAC_setup(ctx, key, len)	gcry_md_open(&ctx, GCRY_MD_SHA256, GCRY_MD_FLAG_HMAC); gcry_md_setkey(ctx, key, len)
-#define HMAC_crunch(ctx, buf, len)	gcry_md_write(ctx, buf, len)
-#define HMAC_finish(ctx, dig, dlen)	dlen = SHA256_DIGEST_LENGTH; memcpy(dig, gcry_md_read(ctx, 0), dlen)
-#define HMAC_close(ctx)	gcry_md_close(ctx)
+#undef HMAC_CTX
+#define HMAC_CTX	struct hmac_sha256_ctx
+#define HMAC_setup(ctx, key, len)	hmac_sha256_set_key(&ctx, len, key)
+#define HMAC_crunch(ctx, buf, len)	hmac_sha256_update(&ctx, len, buf)
+#define HMAC_finish(ctx, dig, dlen)	dlen = SHA256_DIGEST_LENGTH; hmac_sha256_digest(&ctx, SHA256_DIGEST_LENGTH, dig)
+#define HMAC_close(ctx)
 #else	/* USE_OPENSSL */
 #include <openssl/ssl.h>
 #include <openssl/sha.h>
 #include <openssl/hmac.h>
 #include <openssl/rc4.h>
+#if OPENSSL_VERSION_NUMBER >= 0x10100000
+#define HMAC_CTX HMAC_CTX *
+#define HMAC_setup(ctx, key, len)	ctx = HMAC_CTX_new(); HMAC_Init_ex(ctx, key, len, EVP_sha256(), 0)
+#define HMAC_crunch(ctx, buf, len)	HMAC_Update(ctx, buf, len)
+#define HMAC_finish(ctx, dig, dlen)	HMAC_Final(ctx, dig, &dlen)
+#define HMAC_close(ctx) HMAC_CTX_free(ctx)
+#else
 #define HMAC_setup(ctx, key, len)	HMAC_CTX_init(&ctx); HMAC_Init_ex(&ctx, (unsigned char *)key, len, EVP_sha256(), 0)
 #define HMAC_crunch(ctx, buf, len)	HMAC_Update(&ctx, (unsigned char *)buf, len)
 #define HMAC_finish(ctx, dig, dlen)	HMAC_Final(&ctx, (unsigned char *)dig, &dlen);
 #define HMAC_close(ctx)	HMAC_CTX_cleanup(&ctx)
 #endif
+#endif
 
 extern void RTMP_TLS_Init();
 extern TLS_CTX RTMP_TLS_ctx;
 
+#include <zlib.h>
+
 #endif /* CRYPTO */
 
-#include <zlib.h>
+#define DATELEN	64
 
 #define	AGENT	"Mozilla/5.0"
 
@@ -82,7 +93,8 @@ HTTP_get(struct HTTP_ctx *http, const ch
 #ifdef CRYPTO
   int ssl = 0;
 #endif
-  int hlen, flen = 0;
+  int hlen;
+  long flen = 0;
   int rc, i;
   int len_known;
   HTTPResult ret = HTTPRES_OK;
@@ -141,7 +153,7 @@ HTTP_get(struct HTTP_ctx *http, const ch
     return HTTPRES_LOST_CONNECTION;
   i =
     sprintf(sb.sb_buf,
-	    "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferrer: %.*s\r\n",
+	    "GET %s HTTP/1.0\r\nUser-Agent: %s\r\nHost: %s\r\nReferer: %.*s\r\n",
 	    path, AGENT, host, (int)(path - url + 1), url);
   if (http->date[0])
     i += sprintf(sb.sb_buf + i, "If-Modified-Since: %s\r\n", http->date);
@@ -163,7 +175,7 @@ HTTP_get(struct HTTP_ctx *http, const ch
 #else
       TLS_client(RTMP_TLS_ctx, sb.sb_ssl);
       TLS_setfd(sb.sb_ssl, sb.sb_socket);
-      if ((i = TLS_connect(sb.sb_ssl)) < 0)
+      if (TLS_connect(sb.sb_ssl) < 0)
 	{
 	  RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
 	  ret = HTTPRES_LOST_CONNECTION;
@@ -241,14 +253,20 @@ HTTP_get(struct HTTP_ctx *http, const ch
 	if (!strncasecmp
 	    (sb.sb_start, "Content-Length: ", sizeof("Content-Length: ") - 1))
 	{
-	  flen = atoi(sb.sb_start + sizeof("Content-Length: ") - 1);
+	  flen = strtol(sb.sb_start + sizeof("Content-Length: ") - 1, NULL, 10);
+	  if (flen < 1 || flen > INT_MAX)
+	  {
+	    ret = HTTPRES_BAD_REQUEST;
+	    goto leave;
+	  }
 	}
       else
 	if (!strncasecmp
 	    (sb.sb_start, "Last-Modified: ", sizeof("Last-Modified: ") - 1))
 	{
 	  *p2 = '\0';
-	  strcpy(http->date, sb.sb_start + sizeof("Last-Modified: ") - 1);
+	  strncpy(http->date, sb.sb_start + sizeof("Last-Modified: ") - 1, DATELEN-1);
+	  http->date[DATELEN-1] = '\0';
 	}
       p2 += 2;
       sb.sb_size -= p2 - sb.sb_start;
@@ -435,7 +453,7 @@ make_unix_time(char *s)
 /* Convert a Unix time to a network time string
  * Weekday, DD-MMM-YYYY HH:MM:SS GMT
  */
-void
+static void
 strtime(time_t * t, char *s)
 {
   struct tm *tm;
@@ -453,7 +471,7 @@ RTMP_HashSWF(const char *url, unsigned i
 	     int age)
 {
   FILE *f = NULL;
-  char *path, date[64], cctim[64];
+  char *path, date[DATELEN], cctim[DATELEN];
   long pos = 0;
   time_t ctim = -1, cnow;
   int i, got = 0, ret = 0;
@@ -466,7 +484,7 @@ RTMP_HashSWF(const char *url, unsigned i
 
   date[0] = '\0';
 #ifdef _WIN32
-#ifdef _XBOX
+#ifdef XBMC4XBOX
   hpre.av_val = "Q:";
   hpre.av_len = 2;
   home.av_val = "\\UserData";
@@ -554,7 +572,8 @@ RTMP_HashSWF(const char *url, unsigned i
 	      else if (!strncmp(buf, "date: ", 6))
 		{
 		  buf[strlen(buf) - 1] = '\0';
-		  strncpy(date, buf + 6, sizeof(date));
+		  strncpy(date, buf + 6, sizeof(date)-1);
+		  date[DATELEN-1] = '\0';
 		  got++;
 		}
 	      else if (!strncmp(buf, "ctim: ", 6))
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/librtmp.3 rtmpdump/librtmp/librtmp.3
--- rtmpdump-2.3/librtmp/librtmp.3	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/librtmp.3	2026-06-19 14:15:18.301606620 -0600
@@ -1,5 +1,5 @@
-.TH LIBRTMP 3 "2010-05-29" "RTMPDump v2.2e"
-.\" Copyright 2010 Howard Chu.
+.TH LIBRTMP 3 "2011-07-20" "RTMPDump v2.4"
+.\" Copyright 2011 Howard Chu.
 .\" Copying permitted according to the GNU General Public License V2.
 .SH NAME
 librtmp \- RTMPDump Real-Time Messaging Protocol API
@@ -57,14 +57,19 @@ The session handle is freed using
 .BR RTMP_Free ().
 
 All data is transferred using FLV format. The basic session requires
-an RTMP URL. Additional options may be specified by appending
-space-separated key=value pairs to the URL. The RTMP URL format
-is of the form
+an RTMP URL.  The RTMP URL format is of the form
 .nf
   rtmp[t][e|s]://hostname[:port][/app[/playpath]]
 .fi
 
 Plain rtmp, as well as tunneled and encrypted sessions are supported.
+
+Additional options may be specified by appending space-separated
+key=value pairs to the URL. Special characters in values may need
+to be escaped to prevent misinterpretation by the option parser.
+The escape encoding uses a backslash followed by two hexadecimal digits
+representing the ASCII value of the character. E.g., spaces must
+be escaped as \fB\\20\fP and backslashes must be escaped as \fB\\5c\fP.
 .SH OPTIONS
 .SS "Network Parameters"
 These options define how to connect to the media server.
@@ -156,6 +161,9 @@ These options handle additional authenti
 Key for SecureToken response, used if the server requires SecureToken
 authentication.
 .TP
+.BI jtv= JSON
+JSON token used by legacy Justin.tv servers. Invokes NetStream.Authenticate.UsherToken
+.TP
 .BI swfVfy= 0|1
 If the value is 1 or TRUE, the SWF player is retrieved from the
 specified
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/librtmp.3.html rtmpdump/librtmp/librtmp.3.html
--- rtmpdump-2.3/librtmp/librtmp.3.html	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/librtmp.3.html	2026-06-19 14:15:18.301606620 -0600
@@ -6,10 +6,10 @@
 <tr><td>LIBRTMP(3)<td align="center"><td align="right">LIBRTMP(3)
 </thead>
 <tfoot>
-<tr><td>RTMPDump v2.2e<td align="center">2010-05-29<td align="right">LIBRTMP(3)
+<tr><td>RTMPDump v2.4<td align="center">2011-07-20<td align="right">LIBRTMP(3)
 </tfoot>
 <tbody><tr><td colspan="3"><br><br><ul>
-<!-- Copyright 2010 Howard Chu.
+<!-- Copyright 2011 Howard Chu.
  Copying permitted according to the GNU General Public License V2.-->
 </ul>
 
@@ -75,14 +75,19 @@ The session handle is freed using
 <b>RTMP_Free</b>().
 <p>
 All data is transferred using FLV format. The basic session requires
-an RTMP URL. Additional options may be specified by appending
-space-separated key=value pairs to the URL. The RTMP URL format
-is of the form
+an RTMP URL.  The RTMP URL format is of the form
 <pre>
   rtmp[t][e|s]://hostname[:port][/app[/playpath]]
 </pre>
 <p>
 Plain rtmp, as well as tunneled and encrypted sessions are supported.
+<p>
+Additional options may be specified by appending space-separated
+key=value pairs to the URL. Special characters in values may need
+to be escaped to prevent misinterpretation by the option parser.
+The escape encoding uses a backslash followed by two hexadecimal digits
+representing the ASCII value of the character. E.g., spaces must
+be escaped as <b>\20</b> and backslashes must be escaped as <b>\5c</b>.
 </ul>
 
 <h3>OPTIONS</h3><ul>
@@ -233,6 +238,12 @@ authentication.
 </dl>
 <p>
 <dl compact><dt>
+<b>jtv=</b><i>JSON</i>
+<dd>
+JSON token used by legacy Justin.tv servers. Invokes NetStream.Authenticate.UsherToken
+</dl>
+<p>
+<dl compact><dt>
 <b>swfVfy=</b><i>0|1</i>
 <dd>
 If the value is 1 or TRUE, the SWF player is retrieved from the
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/librtmp.pc.in rtmpdump/librtmp/librtmp.pc.in
--- rtmpdump-2.3/librtmp/librtmp.pc.in	2010-06-30 13:58:34.000000000 -0600
+++ rtmpdump/librtmp/librtmp.pc.in	2026-06-19 14:15:18.301606620 -0600
@@ -1,6 +1,6 @@
 prefix=@prefix@
 exec_prefix=${prefix}
-libdir=${exec_prefix}/lib
+libdir=@libdir@
 incdir=${prefix}/include
 
 Name: librtmp
@@ -8,5 +8,6 @@ Description: RTMP implementation
 Version: @VERSION@
 Requires: @CRYPTO_REQ@
 URL: http://rtmpdump.mplayerhq.hu
-Libs: -L${libdir} -lrtmp -lz
+Libs: -L${libdir} -lrtmp -lz @PUBLIC_LIBS@
+Libs.private: @PRIVATE_LIBS@
 Cflags: -I${incdir}
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/log.c rtmpdump/librtmp/log.c
--- rtmpdump-2.3/librtmp/log.c	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/log.c	2026-06-19 14:15:18.301606620 -0600
@@ -92,6 +92,10 @@ RTMP_LogLevel RTMP_LogGetLevel()
 void RTMP_Log(int level, const char *format, ...)
 {
 	va_list args;
+
+	if ( level > RTMP_debuglevel )
+		return;
+
 	va_start(args, format);
 	cb(level, format, args);
 	va_end(args);
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/log.h rtmpdump/librtmp/log.h
--- rtmpdump-2.3/librtmp/log.h	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/log.h	2026-06-19 14:15:18.301606620 -0600
@@ -48,9 +48,15 @@ extern RTMP_LogLevel RTMP_debuglevel;
 typedef void (RTMP_LogCallback)(int level, const char *fmt, va_list);
 void RTMP_LogSetCallback(RTMP_LogCallback *cb);
 void RTMP_LogSetOutput(FILE *file);
+#ifdef __GNUC__
+void RTMP_LogPrintf(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+void RTMP_LogStatus(const char *format, ...) __attribute__ ((__format__ (__printf__, 1, 2)));
+void RTMP_Log(int level, const char *format, ...) __attribute__ ((__format__ (__printf__, 2, 3)));
+#else
 void RTMP_LogPrintf(const char *format, ...);
 void RTMP_LogStatus(const char *format, ...);
 void RTMP_Log(int level, const char *format, ...);
+#endif
 void RTMP_LogHex(int level, const uint8_t *data, unsigned long len);
 void RTMP_LogHexString(int level, const uint8_t *data, unsigned long len);
 void RTMP_LogSetLevel(RTMP_LogLevel lvl);
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/Makefile rtmpdump/librtmp/Makefile
--- rtmpdump-2.3/librtmp/Makefile	2010-06-30 14:01:28.000000000 -0600
+++ rtmpdump/librtmp/Makefile	2026-06-19 14:15:18.301606620 -0600
@@ -1,7 +1,16 @@
-VERSION=v2.3
+VERSION=v2.6
 
 prefix=/usr/local
 
+incdir=$(prefix)/include/librtmp
+bindir=$(prefix)/bin
+libdir=$(prefix)/lib
+mandir=$(prefix)/man
+BINDIR=$(DESTDIR)$(bindir)
+INCDIR=$(DESTDIR)$(incdir)
+LIBDIR=$(DESTDIR)$(libdir)
+MANDIR=$(DESTDIR)$(mandir)
+
 CC=$(CROSS_COMPILE)gcc
 LD=$(CROSS_COMPILE)ld
 AR=$(CROSS_COMPILE)ar
@@ -13,26 +22,52 @@ DEF_POLARSSL=-DUSE_POLARSSL
 DEF_OPENSSL=-DUSE_OPENSSL
 DEF_GNUTLS=-DUSE_GNUTLS
 DEF_=-DNO_CRYPTO
-REQ_GNUTLS=gnutls
+REQ_GNUTLS=gnutls,hogweed,nettle
 REQ_OPENSSL=libssl,libcrypto
+PUB_GNUTLS=-lgmp
 LIBZ=-lz
 LIBS_posix=
+LIBS_darwin=
 LIBS_mingw=-lws2_32 -lwinmm -lgdi32
-LIB_GNUTLS=-lgnutls -lgcrypt $(LIBZ)
+LIB_GNUTLS=-lgnutls -lhogweed -lnettle -lgmp $(LIBZ)
 LIB_OPENSSL=-lssl -lcrypto $(LIBZ)
 LIB_POLARSSL=-lpolarssl $(LIBZ)
-CRYPTO_LIB=$(LIB_$(CRYPTO)) $(LIBS_$(SYS))
+PRIVATE_LIBS=$(LIBS_$(SYS))
+CRYPTO_LIB=$(LIB_$(CRYPTO)) $(PRIVATE_LIBS)
 CRYPTO_REQ=$(REQ_$(CRYPTO))
 CRYPTO_DEF=$(DEF_$(CRYPTO))
+PUBLIC_LIBS=$(PUB_$(CRYPTO))
 
-SO_posix=so.0
-SO_mingw=dll
+SO_VERSION=1
+SOX_posix=so
+SOX_darwin=dylib
+SOX_mingw=dll
+SOX=$(SOX_$(SYS))
+SO_posix=.$(SOX).$(SO_VERSION)
+SO_darwin=.$(SO_VERSION).$(SOX)
+SO_mingw=-$(SO_VERSION).$(SOX)
 SO_EXT=$(SO_$(SYS))
 
+SODIR_posix=$(LIBDIR)
+SODIR_darwin=$(LIBDIR)
+SODIR_mingw=$(BINDIR)
+SODIR=$(SODIR_$(SYS))
+
+SO_LDFLAGS_posix=-shared -Wl,-soname,$@
+SO_LDFLAGS_darwin=-dynamiclib -twolevel_namespace -undefined dynamic_lookup \
+	-fno-common -headerpad_max_install_names -install_name $(libdir)/$@
+SO_LDFLAGS_mingw=-shared -Wl,--out-implib,librtmp.dll.a
+SO_LDFLAGS=$(SO_LDFLAGS_$(SYS))
+
+INSTALL_IMPLIB_posix=
+INSTALL_IMPLIB_darwin=
+INSTALL_IMPLIB_mingw=cp librtmp.dll.a $(LIBDIR)
+INSTALL_IMPLIB=$(INSTALL_IMPLIB_$(SYS))
+
 SHARED=yes
 SODEF_yes=-fPIC
-SOLIB_yes=librtmp.$(SO_EXT)
-SOINST_yes=install_$(SO_EXT)
+SOLIB_yes=librtmp$(SO_EXT)
+SOINST_yes=install_so
 SO_DEF=$(SODEF_$(SHARED))
 SO_LIB=$(SOLIB_$(SHARED))
 SO_INST=$(SOINST_$(SHARED))
@@ -40,29 +75,22 @@ SO_INST=$(SOINST_$(SHARED))
 DEF=-DRTMPDUMP_VERSION=\"$(VERSION)\" $(CRYPTO_DEF) $(XDEF)
 OPT=-O2
 CFLAGS=-Wall $(XCFLAGS) $(INC) $(DEF) $(OPT) $(SO_DEF)
+LDFLAGS=$(XLDFLAGS)
 
-incdir=$(prefix)/include/librtmp
-bindir=$(prefix)/bin
-libdir=$(prefix)/lib
-mandir=$(prefix)/man
-BINDIR=$(DESTDIR)$(bindir)
-INCDIR=$(DESTDIR)$(incdir)
-LIBDIR=$(DESTDIR)$(libdir)
-MANDIR=$(DESTDIR)$(mandir)
 
 OBJS=rtmp.o log.o amf.o hashswf.o parseurl.o
 
 all:	librtmp.a $(SO_LIB)
 
 clean:
-	rm -f *.o *.a *.so *.$(SO_EXT)
+	rm -f *.o *.a *.$(SOX) *$(SO_EXT) librtmp.pc
 
 librtmp.a: $(OBJS)
 	$(AR) rs $@ $?
 
-librtmp.$(SO_EXT): $(OBJS)
-	$(CC) -shared -Wl,-soname,$@ $(LDFLAGS) -o $@ $^ $> $(CRYPTO_LIB)
-	ln -sf $@ librtmp.so
+librtmp$(SO_EXT): $(OBJS)
+	$(CC) $(SO_LDFLAGS) $(LDFLAGS) -o $@ $^ $> $(CRYPTO_LIB)
+	ln -sf $@ librtmp.$(SOX)
 
 log.o: log.c log.h Makefile
 rtmp.o: rtmp.c rtmp.h rtmp_sys.h handshake.h dh.h log.h amf.h Makefile
@@ -71,8 +99,11 @@ hashswf.o: hashswf.c http.h rtmp.h rtmp_
 parseurl.o: parseurl.c rtmp.h rtmp_sys.h log.h Makefile
 
 librtmp.pc: librtmp.pc.in Makefile
-	sed -e "s;@prefix@;$(prefix);" -e "s;@VERSION@;$(VERSION);" \
-		-e "s;@CRYPTO_REQ@;$(CRYPTO_REQ);" librtmp.pc.in > $@
+	sed -e "s;@prefix@;$(prefix);" -e "s;@libdir@;$(libdir);" \
+		-e "s;@VERSION@;$(VERSION);" \
+		-e "s;@CRYPTO_REQ@;$(CRYPTO_REQ);" \
+		-e "s;@PUBLIC_LIBS@;$(PUBLIC_LIBS);" \
+		-e "s;@PRIVATE_LIBS@;$(PRIVATE_LIBS);" librtmp.pc.in > $@
 
 install:	install_base $(SO_INST)
 
@@ -83,9 +114,9 @@ install_base:	librtmp.a librtmp.pc
 	cp librtmp.pc $(LIBDIR)/pkgconfig
 	cp librtmp.3 $(MANDIR)/man3
 
-install_so.0:	librtmp.so.0
-	cp librtmp.so.0 $(LIBDIR)
-	cd $(LIBDIR); ln -sf librtmp.so.0 librtmp.so
+install_so:	librtmp$(SO_EXT)
+	-mkdir -p $(SODIR)
+	cp librtmp$(SO_EXT) $(SODIR)
+	$(INSTALL_IMPLIB)
+	cd $(SODIR); ln -sf librtmp$(SO_EXT) librtmp.$(SOX)
 
-install_dll:	librtmp.dll
-	cp librtmp.dll $(BINDIR)
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/parseurl.c rtmpdump/librtmp/parseurl.c
--- rtmpdump-2.3/librtmp/parseurl.c	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/parseurl.c	2026-06-19 14:15:18.301606620 -0600
@@ -137,12 +137,14 @@ parsehost:
 	 * application = app[/appinstance]
 	 */
 
-	char *slash2, *slash3 = NULL;
+	char *slash2, *slash3 = NULL, *slash4 = NULL;
 	int applen, appnamelen;
 
 	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 */
@@ -156,7 +158,9 @@ parsehost:
                 appnamelen = 8;
         }
 	else { /* app!=ondemand, so app is app[/appinstance] */
-		if(slash3)
+		if(slash4)
+			appnamelen = slash4-p;
+		else if(slash3)
 			appnamelen = slash3-p;
 		else if(slash2)
 			appnamelen = slash2-p;
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/rtmp.c rtmpdump/librtmp/rtmp.c
--- rtmpdump-2.3/librtmp/rtmp.c	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/rtmp.c	2026-06-19 14:15:18.303594982 -0600
@@ -27,6 +27,8 @@
 #include <stdlib.h>
 #include <string.h>
 #include <assert.h>
+#include <time.h>
+#include <limits.h>
 
 #include "rtmp_sys.h"
 #include "log.h"
@@ -34,11 +36,33 @@
 #ifdef CRYPTO
 #ifdef USE_POLARSSL
 #include <polarssl/havege.h>
+#include <polarssl/md5.h>
+#include <polarssl/base64.h>
+#define MD5_DIGEST_LENGTH 16
+
+static const char *my_dhm_P =
+    "E4004C1F94182000103D883A448B3F80" \
+    "2CE4B44A83301270002C20D0321CFD00" \
+    "11CCEF784C26A400F43DFB901BCA7538" \
+    "F2C6B176001CF5A0FD16D2C48B1D0C1C" \
+    "F6AC8E1DA6BCC3B4E1F96B0564965300" \
+    "FFA1D0B601EB2800F489AA512C4B248C" \
+    "01F76949A60BB7F00A40B1EAB64BDD48" \
+    "E8A700D60B7F1200FA8E77B0A979DABF";
+
+static const char *my_dhm_G = "4";
+
 #elif defined(USE_GNUTLS)
 #include <gnutls/gnutls.h>
+#define MD5_DIGEST_LENGTH 16
+#include <nettle/base64.h>
+#include <nettle/md5.h>
 #else	/* USE_OPENSSL */
 #include <openssl/ssl.h>
 #include <openssl/rc4.h>
+#include <openssl/md5.h>
+#include <openssl/bio.h>
+#include <openssl/buffer.h>
 #endif
 TLS_CTX RTMP_TLS_ctx;
 #endif
@@ -96,6 +120,7 @@ static int SendDeleteStream(RTMP *r, dou
 static int SendFCSubscribe(RTMP *r, AVal *subscribepath);
 static int SendPlay(RTMP *r);
 static int SendBytesReceived(RTMP *r);
+static int SendUsherToken(RTMP *r, AVal *usherToken);
 
 #if 0				/* unused */
 static int SendBGHasStream(RTMP *r, double dId, AVal *playpath);
@@ -118,6 +143,8 @@ static void DecodeTEA(AVal *key, AVal *t
 static int HTTP_Post(RTMP *r, RTMPTCmd cmd, const char *buf, int len);
 static int HTTP_read(RTMP *r, int fill);
 
+static void CloseInternal(RTMP *r, int reconnect);
+
 #ifndef _WIN32
 static int clk_tck;
 #endif
@@ -160,9 +187,12 @@ RTMPPacket_Reset(RTMPPacket *p)
 }
 
 int
-RTMPPacket_Alloc(RTMPPacket *p, int nSize)
+RTMPPacket_Alloc(RTMPPacket *p, uint32_t nSize)
 {
-  char *ptr = calloc(1, nSize + RTMP_MAX_HEADER_SIZE);
+  char *ptr;
+  if (nSize > SIZE_MAX - RTMP_MAX_HEADER_SIZE)
+    return FALSE;
+  ptr = calloc(1, nSize + RTMP_MAX_HEADER_SIZE);
   if (!ptr)
     return FALSE;
   p->m_body = ptr + RTMP_MAX_HEADER_SIZE;
@@ -184,7 +214,7 @@ void
 RTMPPacket_Dump(RTMPPacket *p)
 {
   RTMP_Log(RTMP_LOGDEBUG,
-      "RTMP PACKET: packet type: 0x%02x. channel: 0x%02x. info 1: %d info 2: %d. Body size: %lu. body: 0x%02x",
+      "RTMP PACKET: packet type: 0x%02x. channel: 0x%02x. info 1: %d info 2: %d. Body size: %u. body: 0x%02x",
       p->m_packetType, p->m_nChannel, p->m_nTimeStamp, p->m_nInfoField2,
       p->m_nBodySize, p->m_body ? (unsigned char)p->m_body[0] : 0);
 }
@@ -226,6 +256,64 @@ RTMP_TLS_Init()
 #endif
 }
 
+void *
+RTMP_TLS_AllocServerContext(const char* cert, const char* key)
+{
+  void *ctx = NULL;
+#ifdef CRYPTO
+  if (!RTMP_TLS_ctx)
+    RTMP_TLS_Init();
+#ifdef USE_POLARSSL
+  tls_server_ctx *tc = ctx = calloc(1, sizeof(struct tls_server_ctx));
+  tc->dhm_P = my_dhm_P;
+  tc->dhm_G = my_dhm_G;
+  tc->hs = &RTMP_TLS_ctx->hs;
+  if (x509parse_crtfile(&tc->cert, cert)) {
+      free(tc);
+      return NULL;
+  }
+  if (x509parse_keyfile(&tc->key, key, NULL)) {
+      x509_free(&tc->cert);
+      free(tc);
+      return NULL;
+  }
+#elif defined(USE_GNUTLS) && !defined(NO_SSL)
+  gnutls_certificate_allocate_credentials((gnutls_certificate_credentials*) &ctx);
+  if (gnutls_certificate_set_x509_key_file(ctx, cert, key, GNUTLS_X509_FMT_PEM) != 0) {
+    gnutls_certificate_free_credentials(ctx);
+    return NULL;
+  }
+#elif !defined(NO_SSL) /* USE_OPENSSL */
+  ctx = SSL_CTX_new(SSLv23_server_method());
+  if (!SSL_CTX_use_certificate_chain_file(ctx, cert)) {
+      SSL_CTX_free(ctx);
+      return NULL;
+  }
+  if (!SSL_CTX_use_PrivateKey_file(ctx, key, SSL_FILETYPE_PEM)) {
+      SSL_CTX_free(ctx);
+      return NULL;
+  }
+#endif
+#endif
+  return ctx;
+}
+
+void
+RTMP_TLS_FreeServerContext(void *ctx)
+{
+#ifdef CRYPTO
+#ifdef USE_POLARSSL
+  x509_free(&((tls_server_ctx*)ctx)->cert);
+  rsa_free(&((tls_server_ctx*)ctx)->key);
+  free(ctx);
+#elif defined(USE_GNUTLS) && !defined(NO_SSL)
+  gnutls_certificate_free_credentials(ctx);
+#elif !defined(NO_SSL) /* USE_OPENSSL */
+  SSL_CTX_free(ctx);
+#endif
+#endif
+}
+
 RTMP *
 RTMP_Alloc()
 {
@@ -319,6 +407,31 @@ static const char DEFAULT_FLASH_VER[] =
 const AVal RTMP_DefaultFlashVer =
   { (char *)DEFAULT_FLASH_VER, sizeof(DEFAULT_FLASH_VER) - 1 };
 
+static void
+SocksSetup(RTMP *r, AVal *sockshost)
+{
+  if (sockshost->av_len)
+    {
+      const char *socksport = strchr(sockshost->av_val, ':');
+      char *hostname = strdup(sockshost->av_val);
+
+      if (socksport)
+	hostname[socksport - sockshost->av_val] = '\0';
+      r->Link.sockshost.av_val = hostname;
+      r->Link.sockshost.av_len = strlen(hostname);
+
+      r->Link.socksport = socksport ? atoi(socksport + 1) : 1080;
+      RTMP_Log(RTMP_LOGDEBUG, "Connecting via SOCKS proxy: %s:%d", r->Link.sockshost.av_val,
+	  r->Link.socksport);
+    }
+  else
+    {
+      r->Link.sockshost.av_val = NULL;
+      r->Link.sockshost.av_len = 0;
+      r->Link.socksport = 0;
+    }
+}
+
 void
 RTMP_SetupStream(RTMP *r,
 		 int protocol,
@@ -335,6 +448,7 @@ RTMP_SetupStream(RTMP *r,
 		 uint32_t swfSize,
 		 AVal *flashVer,
 		 AVal *subscribepath,
+		 AVal *usherToken,
 		 int dStart,
 		 int dStop, int bLiveStream, long int timeout)
 {
@@ -355,6 +469,8 @@ RTMP_SetupStream(RTMP *r,
     RTMP_Log(RTMP_LOGDEBUG, "auth     : %s", auth->av_val);
   if (subscribepath && subscribepath->av_val)
     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 (flashVer && flashVer->av_val)
     RTMP_Log(RTMP_LOGDEBUG, "flashVer : %s", flashVer->av_val);
   if (dStart > 0)
@@ -363,7 +479,7 @@ RTMP_SetupStream(RTMP *r,
     RTMP_Log(RTMP_LOGDEBUG, "StopTime      : %d msec", dStop);
 
   RTMP_Log(RTMP_LOGDEBUG, "live     : %s", bLiveStream ? "yes" : "no");
-  RTMP_Log(RTMP_LOGDEBUG, "timeout  : %d sec", timeout);
+  RTMP_Log(RTMP_LOGDEBUG, "timeout  : %ld sec", timeout);
 
 #ifdef CRYPTO
   if (swfSHA256Hash != NULL && swfSize > 0)
@@ -372,7 +488,7 @@ RTMP_SetupStream(RTMP *r,
       r->Link.SWFSize = swfSize;
       RTMP_Log(RTMP_LOGDEBUG, "SWFSHA256:");
       RTMP_LogHex(RTMP_LOGDEBUG, r->Link.SWFHash, sizeof(r->Link.SWFHash));
-      RTMP_Log(RTMP_LOGDEBUG, "SWFSize  : %lu", r->Link.SWFSize);
+      RTMP_Log(RTMP_LOGDEBUG, "SWFSize  : %u", r->Link.SWFSize);
     }
   else
     {
@@ -380,26 +496,7 @@ RTMP_SetupStream(RTMP *r,
     }
 #endif
 
-  if (sockshost->av_len)
-    {
-      const char *socksport = strchr(sockshost->av_val, ':');
-      char *hostname = strdup(sockshost->av_val);
-
-      if (socksport)
-	hostname[socksport - sockshost->av_val] = '\0';
-      r->Link.sockshost.av_val = hostname;
-      r->Link.sockshost.av_len = strlen(hostname);
-
-      r->Link.socksport = socksport ? atoi(socksport + 1) : 1080;
-      RTMP_Log(RTMP_LOGDEBUG, "Connecting via SOCKS proxy: %s:%d", r->Link.sockshost.av_val,
-	  r->Link.socksport);
-    }
-  else
-    {
-      r->Link.sockshost.av_val = NULL;
-      r->Link.sockshost.av_len = 0;
-      r->Link.socksport = 0;
-    }
+  SocksSetup(r, sockshost);
 
   if (tcUrl && tcUrl->av_len)
     r->Link.tcUrl = *tcUrl;
@@ -420,6 +517,8 @@ RTMP_SetupStream(RTMP *r,
     r->Link.flashVer = RTMP_DefaultFlashVer;
   if (subscribepath && subscribepath->av_len)
     r->Link.subscribepath = *subscribepath;
+  if (usherToken && usherToken->av_len)
+    r->Link.usherToken = *usherToken;
   r->Link.seekTime = dStart;
   r->Link.stopTime = dStop;
   if (bLiveStream)
@@ -477,6 +576,8 @@ static struct urlopt {
   	"Stream is live, no seeking possible" },
   { AVC("subscribe"), OFF(Link.subscribepath), OPT_STR, 0,
   	"Stream to subscribe to" },
+  { AVC("jtv"), OFF(Link.usherToken),          OPT_STR, 0,
+	"Justin.tv authentication token" },
   { AVC("token"),     OFF(Link.token),	       OPT_STR, 0,
   	"Key for SecureToken response" },
   { AVC("swfVfy"),    OFF(Link.lFlags),        OPT_BOOL, RTMP_LF_SWFV,
@@ -491,6 +592,10 @@ static struct urlopt {
   	"Buffer time in milliseconds" },
   { AVC("timeout"),   OFF(Link.timeout),       OPT_INT, 0,
   	"Session timeout in seconds" },
+  { AVC("pubUser"),   OFF(Link.pubUser),       OPT_STR, 0,
+        "Publisher username" },
+  { AVC("pubPasswd"), OFF(Link.pubPasswd),     OPT_STR, 0,
+        "Publisher password" },
   { {NULL,0}, 0, 0}
 };
 
@@ -748,6 +853,8 @@ int RTMP_SetupURL(RTMP *r, char *url)
 	  (unsigned char *)r->Link.SWFHash, r->Link.swfAge);
 #endif
 
+  SocksSetup(r, &r->Link.sockshost);
+
   if (r->Link.port == 0)
     {
       if (r->Link.protocol & RTMP_FEATURE_SSL)
@@ -851,6 +958,23 @@ RTMP_Connect0(RTMP *r, struct sockaddr *
 }
 
 int
+RTMP_TLS_Accept(RTMP *r, void *ctx)
+{
+#if defined(CRYPTO) && !defined(NO_SSL)
+  TLS_server(ctx, r->m_sb.sb_ssl);
+  TLS_setfd(r->m_sb.sb_ssl, r->m_sb.sb_socket);
+  if (TLS_accept(r->m_sb.sb_ssl) < 0)
+    {
+      RTMP_Log(RTMP_LOGERROR, "%s, TLS_Connect failed", __FUNCTION__);
+      return FALSE;
+    }
+  return TRUE;
+#else
+  return FALSE;
+#endif
+}
+
+int
 RTMP_Connect1(RTMP *r, RTMPPacket *cp)
 {
   if (r->Link.protocol & RTMP_FEATURE_SSL)
@@ -877,7 +1001,13 @@ RTMP_Connect1(RTMP *r, RTMPPacket *cp)
       r->m_clientID.av_val = NULL;
       r->m_clientID.av_len = 0;
       HTTP_Post(r, RTMPT_OPEN, "", 1);
-      HTTP_read(r, 1);
+      if (HTTP_read(r, 1) != 0)
+	{
+	  r->m_msgCounter = 0;
+	  RTMP_Log(RTMP_LOGDEBUG, "%s, Could not connect for handshake", __FUNCTION__);
+	  RTMP_Close(r);
+	  return 0;
+	}
       r->m_msgCounter = 0;
     }
   RTMP_Log(RTMP_LOGDEBUG, "%s, ... connected, handshaking", __FUNCTION__);
@@ -960,7 +1090,7 @@ SocksNegotiate(RTMP *r)
       }
     else
       {
-        RTMP_Log(RTMP_LOGERROR, "%s, SOCKS returned error code %d", packet[1]);
+        RTMP_Log(RTMP_LOGERROR, "%s, SOCKS returned error code %d", __FUNCTION__, packet[1]);
         return FALSE;
       }
   }
@@ -1019,6 +1149,9 @@ RTMP_ToggleStream(RTMP *r)
 
   if (!r->m_pausing)
     {
+      if (RTMP_IsTimedout(r) && r->m_read.status == RTMP_READ_EOF)
+        r->m_read.status = 0;
+
       res = RTMP_SendPause(r, TRUE, r->m_pauseStamp);
       if (!res)
 	return res;
@@ -1051,7 +1184,7 @@ RTMP_GetNextMediaPacket(RTMP *r, RTMPPac
   while (!bHasMediaPacket && RTMP_IsConnected(r)
 	 && RTMP_ReadPacket(r, packet))
     {
-      if (!RTMPPacket_IsReady(packet))
+      if (!RTMPPacket_IsReady(packet) || !packet->m_nBodySize)
 	{
 	  continue;
 	}
@@ -1074,6 +1207,7 @@ RTMP_GetNextMediaPacket(RTMP *r, RTMPPac
 		  packet->m_nTimeStamp, packet->m_hasAbsTimestamp,
 		  r->m_mediaStamp);
 #endif
+	      RTMPPacket_Free(packet);
 	      continue;
 	    }
 	  r->m_pausing = 0;
@@ -1083,7 +1217,8 @@ RTMP_GetNextMediaPacket(RTMP *r, RTMPPac
   if (bHasMediaPacket)
     r->m_bPlaying = TRUE;
   else if (r->m_sb.sb_timedout && !r->m_pausing)
-    r->m_pauseStamp = r->m_channelTimestamp[r->m_mediaChannel];
+    r->m_pauseStamp = r->m_mediaChannel < r->m_channelsAllocatedIn ?
+                      r->m_channelTimestamp[r->m_mediaChannel] : 0;
 
   return bHasMediaPacket;
 }
@@ -1094,32 +1229,32 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *p
   int bHasMediaPacket = 0;
   switch (packet->m_packetType)
     {
-    case 0x01:
+    case RTMP_PACKET_TYPE_CHUNK_SIZE:
       /* chunk size */
       HandleChangeChunkSize(r, packet);
       break;
 
-    case 0x03:
+    case RTMP_PACKET_TYPE_BYTES_READ_REPORT:
       /* bytes read report */
       RTMP_Log(RTMP_LOGDEBUG, "%s, received: bytes read report", __FUNCTION__);
       break;
 
-    case 0x04:
+    case RTMP_PACKET_TYPE_CONTROL:
       /* ctrl */
       HandleCtrl(r, packet);
       break;
 
-    case 0x05:
+    case RTMP_PACKET_TYPE_SERVER_BW:
       /* server bw */
       HandleServerBW(r, packet);
       break;
 
-    case 0x06:
+    case RTMP_PACKET_TYPE_CLIENT_BW:
       /* client bw */
       HandleClientBW(r, packet);
       break;
 
-    case 0x08:
+    case RTMP_PACKET_TYPE_AUDIO:
       /* audio data */
       /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize); */
       HandleAudio(r, packet);
@@ -1130,7 +1265,7 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *p
 	r->m_mediaStamp = packet->m_nTimeStamp;
       break;
 
-    case 0x09:
+    case RTMP_PACKET_TYPE_VIDEO:
       /* video data */
       /*RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize); */
       HandleVideo(r, packet);
@@ -1141,22 +1276,25 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *p
 	r->m_mediaStamp = packet->m_nTimeStamp;
       break;
 
-    case 0x0F:			/* flex stream send */
+    case RTMP_PACKET_TYPE_FLEX_STREAM_SEND:
+      /* flex stream send */
       RTMP_Log(RTMP_LOGDEBUG,
-	  "%s, flex stream send, size %lu bytes, not supported, ignoring",
+	  "%s, flex stream send, size %u bytes, not supported, ignoring",
 	  __FUNCTION__, packet->m_nBodySize);
       break;
 
-    case 0x10:			/* flex shared object */
+    case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT:
+      /* flex shared object */
       RTMP_Log(RTMP_LOGDEBUG,
-	  "%s, flex shared object, size %lu bytes, not supported, ignoring",
+	  "%s, flex shared object, size %u bytes, not supported, ignoring",
 	  __FUNCTION__, packet->m_nBodySize);
       break;
 
-    case 0x11:			/* flex message */
+    case RTMP_PACKET_TYPE_FLEX_MESSAGE:
+      /* flex message */
       {
 	RTMP_Log(RTMP_LOGDEBUG,
-	    "%s, flex message, size %lu bytes, not fully supported",
+	    "%s, flex message, size %u bytes, not fully supported",
 	    __FUNCTION__, packet->m_nBodySize);
 	/*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
 
@@ -1176,22 +1314,22 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *p
 	  bHasMediaPacket = 2;
 	break;
       }
-    case 0x12:
+    case RTMP_PACKET_TYPE_INFO:
       /* metadata (notify) */
-      RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %lu bytes", __FUNCTION__,
+      RTMP_Log(RTMP_LOGDEBUG, "%s, received: notify %u bytes", __FUNCTION__,
 	  packet->m_nBodySize);
       if (HandleMetadata(r, packet->m_body, packet->m_nBodySize))
 	bHasMediaPacket = 1;
       break;
 
-    case 0x13:
+    case RTMP_PACKET_TYPE_SHARED_OBJECT:
       RTMP_Log(RTMP_LOGDEBUG, "%s, shared object, not supported, ignoring",
 	  __FUNCTION__);
       break;
 
-    case 0x14:
+    case RTMP_PACKET_TYPE_INVOKE:
       /* invoke */
-      RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__,
+      RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,
 	  packet->m_nBodySize);
       /*RTMP_LogHex(packet.m_body, packet.m_nBodySize); */
 
@@ -1199,7 +1337,7 @@ RTMP_ClientPacket(RTMP *r, RTMPPacket *p
 	bHasMediaPacket = 2;
       break;
 
-    case 0x16:
+    case RTMP_PACKET_TYPE_FLASH_VIDEO:
       {
 	/* go through FLV packets and handle metadata packets */
 	unsigned int pos = 0;
@@ -1268,9 +1406,11 @@ ReadN(RTMP *r, char *buffer, int n)
       int nBytes = 0, nRead;
       if (r->Link.protocol & RTMP_FEATURE_HTTP)
         {
+	  int refill = 0;
 	  while (!r->m_resplen)
 	    {
-	      if (r->m_sb.sb_size < 144)
+	      int ret;
+	      if (r->m_sb.sb_size < 13 || refill)
 	        {
 		  if (!r->m_unackd)
 		    HTTP_Post(r, RTMPT_IDLE, "", 1);
@@ -1281,7 +1421,20 @@ ReadN(RTMP *r, char *buffer, int n)
 		      return 0;
 		    }
 		}
-	      HTTP_read(r, 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)
+                {
+                  refill = 1;
+                }
+              else
+                {
+                  refill = 0;
+                }
 	    }
 	  if (r->m_resplen && !r->m_sb.sb_size)
 	    RTMPSockBuf_Fill(&r->m_sb);
@@ -1312,8 +1465,9 @@ ReadN(RTMP *r, char *buffer, int n)
 	  nBytes = nRead;
 	  r->m_nBytesIn += nRead;
 	  if (r->m_bSendCounter
-	      && r->m_nBytesIn > r->m_nBytesInSent + r->m_nClientBW / 2)
-	    SendBytesReceived(r);
+	      && 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
@@ -1434,7 +1588,7 @@ SendConnectPacket(RTMP *r, RTMPPacket *c
 
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1545,7 +1699,7 @@ SendBGHasStream(RTMP *r, double dId, AVa
 
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1577,7 +1731,7 @@ RTMP_SendCreateStream(RTMP *r)
 
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1603,7 +1757,7 @@ SendFCSubscribe(RTMP *r, AVal *subscribe
   char *enc;
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1624,6 +1778,39 @@ SendFCSubscribe(RTMP *r, AVal *subscribe
   return RTMP_SendPacket(r, &packet, TRUE);
 }
 
+/* Justin.tv specific authentication */
+static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken");
+
+static int
+SendUsherToken(RTMP *r, AVal *usherToken)
+{
+  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;
+
+  RTMP_Log(RTMP_LOGDEBUG, "UsherToken: %s", usherToken->av_val);
+  enc = packet.m_body;
+  enc = AMF_EncodeString(enc, pend, &av_NetStream_Authenticate_UsherToken);
+  enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes);
+  *enc++ = AMF_NULL;
+  enc = AMF_EncodeString(enc, pend, usherToken);
+
+  if (!enc)
+    return FALSE;
+
+  packet.m_nBodySize = enc - packet.m_body;
+
+  return RTMP_SendPacket(r, &packet, FALSE);
+}
+/******************************************/
+
 SAVC(releaseStream);
 
 static int
@@ -1635,7 +1822,7 @@ SendReleaseStream(RTMP *r)
 
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1665,7 +1852,7 @@ SendFCPublish(RTMP *r)
 
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1695,7 +1882,7 @@ SendFCUnpublish(RTMP *r)
 
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1727,7 +1914,7 @@ SendPublish(RTMP *r)
 
   packet.m_nChannel = 0x04;	/* source channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = r->m_stream_id;
   packet.m_hasAbsTimestamp = 0;
@@ -1762,7 +1949,7 @@ SendDeleteStream(RTMP *r, double dStream
 
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1791,7 +1978,7 @@ RTMP_SendPause(RTMP *r, int DoPause, int
 
   packet.m_nChannel = 0x08;	/* video channel */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;	/* invoke */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1813,7 +2000,8 @@ RTMP_SendPause(RTMP *r, int DoPause, int
 int RTMP_Pause(RTMP *r, int DoPause)
 {
   if (DoPause)
-    r->m_pauseStamp = r->m_channelTimestamp[r->m_mediaChannel];
+    r->m_pauseStamp = r->m_mediaChannel < r->m_channelsAllocatedIn ?
+                      r->m_channelTimestamp[r->m_mediaChannel] : 0;
   return RTMP_SendPause(r, DoPause, r->m_pauseStamp);
 }
 
@@ -1828,7 +2016,7 @@ RTMP_SendSeek(RTMP *r, int iTime)
 
   packet.m_nChannel = 0x08;	/* video channel */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;	/* invoke */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1856,7 +2044,7 @@ RTMP_SendServerBW(RTMP *r)
 
   packet.m_nChannel = 0x02;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
-  packet.m_packetType = 0x05;	/* Server BW */
+  packet.m_packetType = RTMP_PACKET_TYPE_SERVER_BW;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1876,7 +2064,7 @@ RTMP_SendClientBW(RTMP *r)
 
   packet.m_nChannel = 0x02;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
-  packet.m_packetType = 0x06;	/* Client BW */
+  packet.m_packetType = RTMP_PACKET_TYPE_CLIENT_BW;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1897,7 +2085,7 @@ SendBytesReceived(RTMP *r)
 
   packet.m_nChannel = 0x02;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x03;	/* bytes in */
+  packet.m_packetType = RTMP_PACKET_TYPE_BYTES_READ_REPORT;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1923,7 +2111,7 @@ SendCheckBW(RTMP *r)
 
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;	/* RTMP_GetTime(); */
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1951,7 +2139,7 @@ SendCheckBWResult(RTMP *r, double txn)
 
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0x16 * r->m_nBWCheckCounter;	/* temp inc value. till we figure it out. */
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -1980,7 +2168,7 @@ SendPong(RTMP *r, double txn)
 
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0x16 * r->m_nBWCheckCounter;	/* temp inc value. till we figure it out. */
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -2007,7 +2195,7 @@ SendPlay(RTMP *r)
 
   packet.m_nChannel = 0x08;	/* we make 8 our stream channel */
   packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = r->m_stream_id;	/*0x01000000; */
   packet.m_hasAbsTimestamp = 0;
@@ -2075,7 +2263,7 @@ SendPlaylist(RTMP *r)
 
   packet.m_nChannel = 0x08;	/* we make 8 our stream channel */
   packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
-  packet.m_packetType = 0x14;	/* INVOKE */
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = r->m_stream_id;	/*0x01000000; */
   packet.m_hasAbsTimestamp = 0;
@@ -2113,7 +2301,7 @@ SendSecureTokenResponse(RTMP *r, AVal *r
 
   packet.m_nChannel = 0x03;	/* control channel (invoke) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x14;
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -2160,7 +2348,7 @@ RTMP_SendCtrl(RTMP *r, short nType, unsi
 
   packet.m_nChannel = 0x02;	/* control channel (ping) */
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
-  packet.m_packetType = 0x04;	/* ctrl */
+  packet.m_packetType = RTMP_PACKET_TYPE_CONTROL;
   packet.m_nTimeStamp = 0;	/* RTMP_GetTime(); */
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -2246,6 +2434,449 @@ AV_clear(RTMP_METHOD *vals, int num)
   free(vals);
 }
 
+
+#ifdef CRYPTO
+static int
+b64enc(const unsigned char *input, int length, char *output, int maxsize)
+{
+#ifdef USE_POLARSSL
+  size_t buf_size = maxsize;
+  if(base64_encode((unsigned char *) output, &buf_size, input, length) == 0)
+    {
+      output[buf_size] = '\0';
+      return 1;
+    }
+  else
+    {
+      RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__);
+      return 0;
+    }
+#elif defined(USE_GNUTLS)
+  if (BASE64_ENCODE_RAW_LENGTH(length) <= maxsize)
+    base64_encode_raw((uint8_t*) output, length, input);
+  else
+    {
+      RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__);
+      return 0;
+    }
+#else   /* USE_OPENSSL */
+  BIO *bmem, *b64;
+  BUF_MEM *bptr;
+
+  b64 = BIO_new(BIO_f_base64());
+  bmem = BIO_new(BIO_s_mem());
+  b64 = BIO_push(b64, bmem);
+  BIO_write(b64, input, length);
+  if (BIO_flush(b64) == 1)
+    {
+      BIO_get_mem_ptr(b64, &bptr);
+      memcpy(output, bptr->data, bptr->length-1);
+      output[bptr->length-1] = '\0';
+    }
+  else
+    {
+      RTMP_Log(RTMP_LOGDEBUG, "%s, error", __FUNCTION__);
+      return 0;
+    }
+  BIO_free_all(b64);
+#endif
+  return 1;
+}
+
+#ifdef USE_POLARSSL
+#define MD5_CTX	md5_context
+#define MD5_Init(ctx)	md5_starts(ctx)
+#define MD5_Update(ctx,data,len)	md5_update(ctx,(unsigned char *)data,len)
+#define MD5_Final(dig,ctx)	md5_finish(ctx,dig)
+#elif defined(USE_GNUTLS)
+typedef struct md5_ctx	MD5_CTX;
+#define MD5_Init(ctx)	md5_init(ctx)
+#define MD5_Update(ctx,data,len)	md5_update(ctx,len,data)
+#define MD5_Final(dig,ctx)	md5_digest(ctx,MD5_DIGEST_LENGTH,dig)
+#else
+#endif
+
+static const AVal av_authmod_adobe = AVC("authmod=adobe");
+static const AVal av_authmod_llnw  = AVC("authmod=llnw");
+
+static void hexenc(unsigned char *inbuf, int len, char *dst)
+{
+    char *ptr = dst;
+    while(len--) {
+        sprintf(ptr, "%02x", *inbuf++);
+        ptr += 2;
+    }
+    *ptr = '\0';
+}
+
+static char *
+AValChr(AVal *av, char c)
+{
+  int i;
+  for (i = 0; i < av->av_len; i++)
+    if (av->av_val[i] == c)
+      return &av->av_val[i];
+  return NULL;
+}
+
+static int
+PublisherAuth(RTMP *r, AVal *description)
+{
+  char *token_in = NULL;
+  char *ptr;
+  unsigned char md5sum_val[MD5_DIGEST_LENGTH+1];
+  MD5_CTX md5ctx;
+  int challenge2_data;
+#define RESPONSE_LEN 32
+#define CHALLENGE2_LEN 16
+#define SALTED2_LEN (32+8+8+8)
+#define B64DIGEST_LEN	24	/* 16 byte digest => 22 b64 chars + 2 chars padding */
+#define B64INT_LEN	8	/* 4 byte int => 6 b64 chars + 2 chars padding */
+#define HEXHASH_LEN	(2*MD5_DIGEST_LENGTH)
+  char response[RESPONSE_LEN];
+  char challenge2[CHALLENGE2_LEN];
+  char salted2[SALTED2_LEN];
+  AVal pubToken;
+
+  if (strstr(description->av_val, av_authmod_adobe.av_val) != NULL)
+    {
+      if(strstr(description->av_val, "code=403 need auth") != NULL)
+        {
+            if (strstr(r->Link.app.av_val, av_authmod_adobe.av_val) != NULL) {
+              RTMP_Log(RTMP_LOGERROR, "%s, wrong pubUser & pubPasswd for publisher auth", __FUNCTION__);
+              return 0;
+            } else if(r->Link.pubUser.av_len && r->Link.pubPasswd.av_len) {
+              pubToken.av_val = malloc(r->Link.pubUser.av_len + av_authmod_adobe.av_len + 8);
+              pubToken.av_len = sprintf(pubToken.av_val, "?%s&user=%s",
+                      av_authmod_adobe.av_val,
+                      r->Link.pubUser.av_val);
+              RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken1: %s", __FUNCTION__, pubToken.av_val);
+            } else {
+              RTMP_Log(RTMP_LOGERROR, "%s, need to set pubUser & pubPasswd for publisher auth", __FUNCTION__);
+              return 0;
+            }
+        }
+      else if((token_in = strstr(description->av_val, "?reason=needauth")) != NULL)
+        {
+          char *par, *val = NULL, *orig_ptr;
+	  AVal user, salt, opaque, challenge, *aptr = NULL;
+	  opaque.av_len = 0;
+	  challenge.av_len = 0;
+
+          ptr = orig_ptr = strdup(token_in);
+          while (ptr)
+            {
+              par = ptr;
+              ptr = strchr(par, '&');
+              if(ptr)
+                  *ptr++ = '\0';
+
+              val =  strchr(par, '=');
+              if(val)
+                  *val++ = '\0';
+
+	      if (aptr) {
+		aptr->av_len = par - aptr->av_val - 1;
+		aptr = NULL;
+	      }
+              if (strcmp(par, "user") == 0){
+                  user.av_val = val;
+		  aptr = &user;
+              } else if (strcmp(par, "salt") == 0){
+                  salt.av_val = val;
+		  aptr = &salt;
+              } else if (strcmp(par, "opaque") == 0){
+                  opaque.av_val = val;
+		  aptr = &opaque;
+              } else if (strcmp(par, "challenge") == 0){
+                  challenge.av_val = val;
+		  aptr = &challenge;
+              }
+
+              RTMP_Log(RTMP_LOGDEBUG, "%s, par:\"%s\" = val:\"%s\"", __FUNCTION__, par, val);
+            }
+	  if (aptr)
+	    aptr->av_len = strlen(aptr->av_val);
+
+	  /* hash1 = base64enc(md5(user + _aodbeAuthSalt + password)) */
+	  MD5_Init(&md5ctx);
+	  MD5_Update(&md5ctx, user.av_val, user.av_len);
+	  MD5_Update(&md5ctx, salt.av_val, salt.av_len);
+	  MD5_Update(&md5ctx, r->Link.pubPasswd.av_val, r->Link.pubPasswd.av_len);
+	  MD5_Final(md5sum_val, &md5ctx);
+          RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s%s%s) =>", __FUNCTION__,
+	    user.av_val, salt.av_val, r->Link.pubPasswd.av_val);
+          RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH);
+
+          b64enc(md5sum_val, MD5_DIGEST_LENGTH, salted2, SALTED2_LEN);
+          RTMP_Log(RTMP_LOGDEBUG, "%s, b64(md5_1) = %s", __FUNCTION__, salted2);
+
+            challenge2_data = rand();
+
+            b64enc((unsigned char *) &challenge2_data, sizeof(int), challenge2, CHALLENGE2_LEN);
+            RTMP_Log(RTMP_LOGDEBUG, "%s, b64(%d) = %s", __FUNCTION__, challenge2_data, challenge2);
+
+	  MD5_Init(&md5ctx);
+	  MD5_Update(&md5ctx, salted2, B64DIGEST_LEN);
+            /* response = base64enc(md5(hash1 + opaque + challenge2)) */
+	  if (opaque.av_len)
+	    MD5_Update(&md5ctx, opaque.av_val, opaque.av_len);
+	  else if (challenge.av_len)
+	    MD5_Update(&md5ctx, challenge.av_val, challenge.av_len);
+	  MD5_Update(&md5ctx, challenge2, B64INT_LEN);
+	  MD5_Final(md5sum_val, &md5ctx);
+
+          RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s%s%s) =>", __FUNCTION__,
+	    salted2, opaque.av_len ? opaque.av_val : "", challenge2);
+          RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH);
+
+          b64enc(md5sum_val, MD5_DIGEST_LENGTH, response, RESPONSE_LEN);
+          RTMP_Log(RTMP_LOGDEBUG, "%s, b64(md5_2) = %s", __FUNCTION__, response);
+
+            /* have all hashes, create auth token for the end of app */
+            pubToken.av_val = malloc(32 + B64INT_LEN + B64DIGEST_LEN + opaque.av_len);
+            pubToken.av_len = sprintf(pubToken.av_val,
+                    "&challenge=%s&response=%s&opaque=%s",
+                    challenge2,
+                    response,
+                    opaque.av_len ? opaque.av_val : "");
+            RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken2: %s", __FUNCTION__, pubToken.av_val);
+            free(orig_ptr);
+        }
+      else if(strstr(description->av_val, "?reason=authfailed") != NULL)
+        {
+          RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: wrong password", __FUNCTION__);
+          return 0;
+        }
+      else if(strstr(description->av_val, "?reason=nosuchuser") != NULL)
+        {
+          RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: no such user", __FUNCTION__);
+          return 0;
+        }
+      else
+        {
+          RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: unknown auth mode: %s",
+                  __FUNCTION__, description->av_val);
+          return 0;
+        }
+
+      ptr = malloc(r->Link.app.av_len + pubToken.av_len);
+      strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len);
+      strncpy(ptr + r->Link.app.av_len, pubToken.av_val, pubToken.av_len);
+      r->Link.app.av_len += pubToken.av_len;
+      if(r->Link.lFlags & RTMP_LF_FAPU)
+          free(r->Link.app.av_val);
+      r->Link.app.av_val = ptr;
+
+      ptr = malloc(r->Link.tcUrl.av_len + pubToken.av_len);
+      strncpy(ptr, r->Link.tcUrl.av_val, r->Link.tcUrl.av_len);
+      strncpy(ptr + r->Link.tcUrl.av_len, pubToken.av_val, pubToken.av_len);
+      r->Link.tcUrl.av_len += pubToken.av_len;
+      if(r->Link.lFlags & RTMP_LF_FTCU)
+          free(r->Link.tcUrl.av_val);
+      r->Link.tcUrl.av_val = ptr;
+
+      free(pubToken.av_val);
+      r->Link.lFlags |= RTMP_LF_FTCU | RTMP_LF_FAPU;
+
+      RTMP_Log(RTMP_LOGDEBUG, "%s, new app: %.*s tcUrl: %.*s playpath: %s", __FUNCTION__,
+              r->Link.app.av_len, r->Link.app.av_val,
+              r->Link.tcUrl.av_len, r->Link.tcUrl.av_val,
+              r->Link.playpath.av_val);
+    }
+  else if (strstr(description->av_val, av_authmod_llnw.av_val) != NULL)
+    {
+      if(strstr(description->av_val, "code=403 need auth") != NULL)
+        {
+            /* This part seems to be the same for llnw and adobe */
+
+            if (strstr(r->Link.app.av_val, av_authmod_llnw.av_val) != NULL) {
+              RTMP_Log(RTMP_LOGERROR, "%s, wrong pubUser & pubPasswd for publisher auth", __FUNCTION__);
+              return 0;
+            } else if(r->Link.pubUser.av_len && r->Link.pubPasswd.av_len) {
+              pubToken.av_val = malloc(r->Link.pubUser.av_len + av_authmod_llnw.av_len + 8);
+              pubToken.av_len = sprintf(pubToken.av_val, "?%s&user=%s",
+                      av_authmod_llnw.av_val,
+                      r->Link.pubUser.av_val);
+              RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken1: %s", __FUNCTION__, pubToken.av_val);
+            } else {
+              RTMP_Log(RTMP_LOGERROR, "%s, need to set pubUser & pubPasswd for publisher auth", __FUNCTION__);
+              return 0;
+            }
+        }
+      else if((token_in = strstr(description->av_val, "?reason=needauth")) != NULL)
+        {
+          char *orig_ptr;
+          char *par, *val = NULL;
+	  char hash1[HEXHASH_LEN+1], hash2[HEXHASH_LEN+1], hash3[HEXHASH_LEN+1];
+	  AVal user, nonce, *aptr = NULL;
+	  AVal apptmp;
+
+          /* llnw auth method
+           * Seems to be closely based on HTTP Digest Auth:
+           *    http://tools.ietf.org/html/rfc2617
+           *    http://en.wikipedia.org/wiki/Digest_access_authentication
+           */
+
+          const char authmod[] = "llnw";
+          const char realm[] = "live";
+          const char method[] = "publish";
+          const char qop[] = "auth";
+          /* nc = 1..connection count (or rather, number of times cnonce has been reused) */
+          int nc = 1;
+          /* nchex = hexenc(nc) (8 hex digits according to RFC 2617) */
+          char nchex[9];
+          /* cnonce = hexenc(4 random bytes) (initialized on first connection) */
+          char cnonce[9];
+
+          ptr = orig_ptr = strdup(token_in);
+          /* Extract parameters (we need user and nonce) */
+          while (ptr)
+            {
+              par = ptr;
+              ptr = strchr(par, '&');
+              if(ptr)
+                  *ptr++ = '\0';
+
+              val =  strchr(par, '=');
+              if(val)
+                  *val++ = '\0';
+
+	      if (aptr) {
+		aptr->av_len = par - aptr->av_val - 1;
+		aptr = NULL;
+	      }
+              if (strcmp(par, "user") == 0){
+                user.av_val = val;
+		aptr = &user;
+              } else if (strcmp(par, "nonce") == 0){
+                nonce.av_val = val;
+		aptr = &nonce;
+              }
+
+              RTMP_Log(RTMP_LOGDEBUG, "%s, par:\"%s\" = val:\"%s\"", __FUNCTION__, par, val);
+            }
+	  if (aptr)
+	    aptr->av_len = strlen(aptr->av_val);
+
+          /* FIXME: handle case where user==NULL or nonce==NULL */
+
+          sprintf(nchex, "%08x", nc);
+          sprintf(cnonce, "%08x", rand());
+
+          /* hash1 = hexenc(md5(user + ":" + realm + ":" + password)) */
+	  MD5_Init(&md5ctx);
+	  MD5_Update(&md5ctx, user.av_val, user.av_len);
+	  MD5_Update(&md5ctx, ":", 1);
+	  MD5_Update(&md5ctx, realm, sizeof(realm)-1);
+	  MD5_Update(&md5ctx, ":", 1);
+	  MD5_Update(&md5ctx, r->Link.pubPasswd.av_val, r->Link.pubPasswd.av_len);
+	  MD5_Final(md5sum_val, &md5ctx);
+          RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s:%s:%s) =>", __FUNCTION__,
+	    user.av_val, realm, r->Link.pubPasswd.av_val);
+          RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH);
+          hexenc(md5sum_val, MD5_DIGEST_LENGTH, hash1);
+
+          /* hash2 = hexenc(md5(method + ":/" + app + "/" + appInstance)) */
+          /* Extract appname + appinstance without query parameters */
+	  apptmp = r->Link.app;
+	  ptr = AValChr(&apptmp, '?');
+	  if (ptr)
+	    apptmp.av_len = ptr - apptmp.av_val;
+
+	  MD5_Init(&md5ctx);
+	  MD5_Update(&md5ctx, method, sizeof(method)-1);
+	  MD5_Update(&md5ctx, ":/", 2);
+	  MD5_Update(&md5ctx, apptmp.av_val, apptmp.av_len);
+	  if (!AValChr(&apptmp, '/'))
+	    MD5_Update(&md5ctx, "/_definst_", sizeof("/_definst_") - 1);
+	  MD5_Final(md5sum_val, &md5ctx);
+          RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s:/%.*s) =>", __FUNCTION__,
+	    method, apptmp.av_len, apptmp.av_val);
+          RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH);
+          hexenc(md5sum_val, MD5_DIGEST_LENGTH, hash2);
+
+          /* hash3 = hexenc(md5(hash1 + ":" + nonce + ":" + nchex + ":" + cnonce + ":" + qop + ":" + hash2)) */
+	  MD5_Init(&md5ctx);
+	  MD5_Update(&md5ctx, hash1, HEXHASH_LEN);
+	  MD5_Update(&md5ctx, ":", 1);
+	  MD5_Update(&md5ctx, nonce.av_val, nonce.av_len);
+	  MD5_Update(&md5ctx, ":", 1);
+	  MD5_Update(&md5ctx, nchex, sizeof(nchex)-1);
+	  MD5_Update(&md5ctx, ":", 1);
+	  MD5_Update(&md5ctx, cnonce, sizeof(cnonce)-1);
+	  MD5_Update(&md5ctx, ":", 1);
+	  MD5_Update(&md5ctx, qop, sizeof(qop)-1);
+	  MD5_Update(&md5ctx, ":", 1);
+	  MD5_Update(&md5ctx, hash2, HEXHASH_LEN);
+	  MD5_Final(md5sum_val, &md5ctx);
+          RTMP_Log(RTMP_LOGDEBUG, "%s, md5(%s:%s:%s:%s:%s:%s) =>", __FUNCTION__,
+	    hash1, nonce.av_val, nchex, cnonce, qop, hash2);
+          RTMP_LogHexString(RTMP_LOGDEBUG, md5sum_val, MD5_DIGEST_LENGTH);
+          hexenc(md5sum_val, MD5_DIGEST_LENGTH, hash3);
+
+          /* pubToken = &authmod=<authmod>&user=<username>&nonce=<nonce>&cnonce=<cnonce>&nc=<nchex>&response=<hash3> */
+          /* Append nonces and response to query string which already contains
+           * user + authmod */
+          pubToken.av_val = malloc(64 + sizeof(authmod)-1 + user.av_len + nonce.av_len + sizeof(cnonce)-1 + sizeof(nchex)-1 + HEXHASH_LEN);
+          sprintf(pubToken.av_val,
+                  "&nonce=%s&cnonce=%s&nc=%s&response=%s",
+                  nonce.av_val, cnonce, nchex, hash3);
+          pubToken.av_len = strlen(pubToken.av_val);
+          RTMP_Log(RTMP_LOGDEBUG, "%s, pubToken2: %s", __FUNCTION__, pubToken.av_val);
+
+          free(orig_ptr);
+        }
+      else if(strstr(description->av_val, "?reason=authfail") != NULL)
+        {
+          RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed", __FUNCTION__);
+          return 0;
+        }
+      else if(strstr(description->av_val, "?reason=nosuchuser") != NULL)
+        {
+          RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: no such user", __FUNCTION__);
+          return 0;
+        }
+      else
+        {
+          RTMP_Log(RTMP_LOGERROR, "%s, Authentication failed: unknown auth mode: %s",
+                  __FUNCTION__, description->av_val);
+          return 0;
+        }
+
+      ptr = malloc(r->Link.app.av_len + pubToken.av_len);
+      strncpy(ptr, r->Link.app.av_val, r->Link.app.av_len);
+      strncpy(ptr + r->Link.app.av_len, pubToken.av_val, pubToken.av_len);
+      r->Link.app.av_len += pubToken.av_len;
+      if(r->Link.lFlags & RTMP_LF_FAPU)
+          free(r->Link.app.av_val);
+      r->Link.app.av_val = ptr;
+
+      ptr = malloc(r->Link.tcUrl.av_len + pubToken.av_len);
+      strncpy(ptr, r->Link.tcUrl.av_val, r->Link.tcUrl.av_len);
+      strncpy(ptr + r->Link.tcUrl.av_len, pubToken.av_val, pubToken.av_len);
+      r->Link.tcUrl.av_len += pubToken.av_len;
+      if(r->Link.lFlags & RTMP_LF_FTCU)
+          free(r->Link.tcUrl.av_val);
+      r->Link.tcUrl.av_val = ptr;
+
+      free(pubToken.av_val);
+      r->Link.lFlags |= RTMP_LF_FTCU | RTMP_LF_FAPU;
+
+      RTMP_Log(RTMP_LOGDEBUG, "%s, new app: %.*s tcUrl: %.*s playpath: %s", __FUNCTION__,
+              r->Link.app.av_len, r->Link.app.av_val,
+              r->Link.tcUrl.av_len, r->Link.tcUrl.av_val,
+              r->Link.playpath.av_val);
+    }
+  else
+    {
+      return 0;
+    }
+  return 1;
+}
+#endif
+
+
 SAVC(onBWDone);
 SAVC(onFCSubscribe);
 SAVC(onFCUnsubscribe);
@@ -2255,6 +2886,7 @@ SAVC(_error);
 SAVC(close);
 SAVC(code);
 SAVC(level);
+SAVC(description);
 SAVC(onStatus);
 SAVC(playlist_ready);
 static const AVal av_NetStream_Failed = AVC("NetStream.Failed");
@@ -2268,9 +2900,13 @@ static const AVal av_NetStream_Play_Comp
 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_Publish_Start = AVC("NetStream.Publish.Start");
+static const AVal av_NetConnection_Connect_Rejected =
+AVC("NetConnection.Connect.Rejected");
 
 /* Returns 0 for OK/Failed/error, 1 for 'Stop or Complete' */
 static int
@@ -2278,7 +2914,7 @@ HandleInvoke(RTMP *r, const char *body,
 {
   AMFObject obj;
   AVal method;
-  int txn;
+  double txn;
   int ret = 0, nRes;
   if (body[0] != 0x02)		/* make sure it is a string method name we start with */
     {
@@ -2296,7 +2932,7 @@ HandleInvoke(RTMP *r, const char *body,
 
   AMF_Dump(&obj);
   AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method);
-  txn = (int)AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));
+  txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));
   RTMP_Log(RTMP_LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.av_val);
 
   if (AVMATCH(&method, &av__result))
@@ -2305,14 +2941,14 @@ HandleInvoke(RTMP *r, const char *body,
       int i;
 
       for (i=0; i<r->m_numCalls; i++) {
-  	if (r->m_methodCalls[i].num == txn) {
+	if (r->m_methodCalls[i].num == (int)txn) {
 	  methodInvoked = r->m_methodCalls[i].name;
 	  AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE);
 	  break;
 	}
       }
       if (!methodInvoked.av_val) {
-        RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %d without matching request",
+        RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request",
 	  __FUNCTION__, txn);
 	goto leave;
       }
@@ -2345,6 +2981,9 @@ HandleInvoke(RTMP *r, const char *body,
 
 	  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);
@@ -2409,7 +3048,57 @@ HandleInvoke(RTMP *r, const char *body,
     }
   else if (AVMATCH(&method, &av__error))
     {
+#ifdef CRYPTO
+      AVal methodInvoked = {0};
+      int i;
+
+      if (r->Link.protocol & RTMP_FEATURE_WRITE)
+        {
+          for (i=0; i<r->m_numCalls; i++)
+            {
+              if (r->m_methodCalls[i].num == txn)
+                {
+                  methodInvoked = r->m_methodCalls[i].name;
+                  AV_erase(r->m_methodCalls, &r->m_numCalls, i, FALSE);
+                  break;
+                }
+            }
+          if (!methodInvoked.av_val)
+            {
+              RTMP_Log(RTMP_LOGDEBUG, "%s, received result id %f without matching request",
+                    __FUNCTION__, txn);
+              goto leave;
+            }
+
+          RTMP_Log(RTMP_LOGDEBUG, "%s, received error for method call <%s>", __FUNCTION__,
+          methodInvoked.av_val);
+
+          if (AVMATCH(&methodInvoked, &av_connect))
+            {
+              AMFObject obj2;
+              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, error description: %s", __FUNCTION__, description.av_val);
+              /* if PublisherAuth returns 1, then reconnect */
+              if (PublisherAuth(r, &description) == 1)
+              {
+                CloseInternal(r, 1);
+                if (!RTMP_Connect(r, NULL) || !RTMP_ConnectStream(r, 0))
+                  goto leave;
+              }
+            }
+        }
+      else
+        {
+          RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
+        }
+      free(methodInvoked.av_val);
+#else
       RTMP_Log(RTMP_LOGERROR, "rtmp server sent error");
+#endif
     }
   else if (AVMATCH(&method, &av_close))
     {
@@ -2435,7 +3124,8 @@ HandleInvoke(RTMP *r, const char *body,
 	  RTMP_Log(RTMP_LOGERROR, "Closing connection: %s", code.av_val);
 	}
 
-      else if (AVMATCH(&code, &av_NetStream_Play_Start))
+      else if (AVMATCH(&code, &av_NetStream_Play_Start)
+           || AVMATCH(&code, &av_NetStream_Play_PublishNotify))
 	{
 	  int i;
 	  r->m_bPlaying = TRUE;
@@ -2519,11 +3209,11 @@ RTMP_FindFirstMatchingProperty(AMFObject
 
       if (AVMATCH(&prop->p_name, name))
 	{
-	  *p = *prop;
+	  memcpy(p, prop, sizeof(*prop));
 	  return TRUE;
 	}
 
-      if (prop->p_type == AMF_OBJECT)
+      if (prop->p_type == AMF_OBJECT || prop->p_type == AMF_ECMA_ARRAY)
 	{
 	  if (RTMP_FindFirstMatchingProperty(&prop->p_vu.p_object, name, p))
 	    return TRUE;
@@ -2545,7 +3235,7 @@ RTMP_FindPrefixProperty(AMFObject *obj,
       if (prop->p_name.av_len > name->av_len &&
       	  !memcmp(prop->p_name.av_val, name->av_val, name->av_len))
 	{
-	  *p = *prop;
+	  memcpy(p, prop, sizeof(*prop));
 	  return TRUE;
 	}
 
@@ -2562,47 +3252,44 @@ static int
 DumpMetaData(AMFObject *obj)
 {
   AMFObjectProperty *prop;
-  int n;
+  int n, len;
   for (n = 0; n < obj->o_num; n++)
     {
+      char str[256] = "";
       prop = AMF_GetProp(obj, NULL, n);
-      if (prop->p_type != AMF_OBJECT)
-	{
-	  char str[256] = "";
-	  switch (prop->p_type)
-	    {
-	    case AMF_NUMBER:
-	      snprintf(str, 255, "%.2f", prop->p_vu.p_number);
-	      break;
-	    case AMF_BOOLEAN:
-	      snprintf(str, 255, "%s",
-		       prop->p_vu.p_number != 0. ? "TRUE" : "FALSE");
-	      break;
-	    case AMF_STRING:
-	      snprintf(str, 255, "%.*s", prop->p_vu.p_aval.av_len,
-		       prop->p_vu.p_aval.av_val);
-	      break;
-	    case AMF_DATE:
-	      snprintf(str, 255, "timestamp:%.2f", prop->p_vu.p_number);
-	      break;
-	    default:
-	      snprintf(str, 255, "INVALID TYPE 0x%02x",
-		       (unsigned char)prop->p_type);
-	    }
-	  if (prop->p_name.av_len)
-	    {
-	      /* chomp */
-	      if (strlen(str) >= 1 && str[strlen(str) - 1] == '\n')
-		str[strlen(str) - 1] = '\0';
-	      RTMP_Log(RTMP_LOGINFO, "  %-22.*s%s", prop->p_name.av_len,
-			prop->p_name.av_val, str);
-	    }
-	}
-      else
+      switch (prop->p_type)
 	{
+	case AMF_OBJECT:
+	case AMF_ECMA_ARRAY:
+	case AMF_STRICT_ARRAY:
 	  if (prop->p_name.av_len)
 	    RTMP_Log(RTMP_LOGINFO, "%.*s:", prop->p_name.av_len, prop->p_name.av_val);
 	  DumpMetaData(&prop->p_vu.p_object);
+	  break;
+	case AMF_NUMBER:
+	  snprintf(str, 255, "%.2f", prop->p_vu.p_number);
+	  break;
+	case AMF_BOOLEAN:
+	  snprintf(str, 255, "%s",
+		   prop->p_vu.p_number != 0. ? "TRUE" : "FALSE");
+	  break;
+	case AMF_STRING:
+	  len = snprintf(str, 255, "%.*s", prop->p_vu.p_aval.av_len,
+		   prop->p_vu.p_aval.av_val);
+	  if (len >= 1 && str[len-1] == '\n')
+	    str[len-1] = '\0';
+	  break;
+	case AMF_DATE:
+	  snprintf(str, 255, "timestamp:%.2f", prop->p_vu.p_number);
+	  break;
+	default:
+	  snprintf(str, 255, "INVALID TYPE 0x%02x",
+		   (unsigned char)prop->p_type);
+	}
+      if (str[0] && prop->p_name.av_len)
+	{
+	  RTMP_Log(RTMP_LOGINFO, "  %-22.*s%s", prop->p_name.av_len,
+		    prop->p_name.av_val, str);
 	}
     }
   return FALSE;
@@ -2762,7 +3449,8 @@ HandleCtrl(RTMP *r, const RTMPPacket *pa
 	    break;
 	  if (!r->m_pausing)
 	    {
-	      r->m_pauseStamp = r->m_channelTimestamp[r->m_mediaChannel];
+	      r->m_pauseStamp = r->m_mediaChannel < r->m_channelsAllocatedIn ?
+	                        r->m_channelTimestamp[r->m_mediaChannel] : 0;
 	      RTMP_SendPause(r, TRUE, r->m_pauseStamp);
 	      r->m_pausing = 1;
 	    }
@@ -2789,11 +3477,17 @@ HandleCtrl(RTMP *r, const RTMPPacket *pa
   if (nType == 0x1A)
     {
       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]);
+	}
 #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 */
-      if (r->Link.SWFSize)
+      else if (r->Link.SWFSize)
 	{
 	  RTMP_SendCtrl(r, 0x1B, 0, 0);
 	}
@@ -2859,7 +3553,7 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac
   uint8_t hbuf[RTMP_MAX_HEADER_SIZE] = { 0 };
   char *header = (char *)hbuf;
   int nSize, hSize, nToRead, nChunk;
-  int didAlloc = FALSE;
+  int extendedTimestamp;
 
   RTMP_Log(RTMP_LOGDEBUG2, "%s: fd=%d", __FUNCTION__, r->m_sb.sb_socket);
 
@@ -2901,6 +3595,26 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac
 
   nSize = packetSize[packet->m_headerType];
 
+  if (packet->m_nChannel >= r->m_channelsAllocatedIn)
+    {
+      int n = packet->m_nChannel + 10;
+      int *timestamp = realloc(r->m_channelTimestamp, sizeof(int) * n);
+      RTMPPacket **packets = realloc(r->m_vecChannelsIn, sizeof(RTMPPacket*) * n);
+      if (!timestamp)
+        free(r->m_channelTimestamp);
+      if (!packets)
+        free(r->m_vecChannelsIn);
+      r->m_channelTimestamp = timestamp;
+      r->m_vecChannelsIn = packets;
+      if (!timestamp || !packets) {
+        r->m_channelsAllocatedIn = 0;
+        return FALSE;
+      }
+      memset(r->m_channelTimestamp + r->m_channelsAllocatedIn, 0, sizeof(int) * (n - r->m_channelsAllocatedIn));
+      memset(r->m_vecChannelsIn + r->m_channelsAllocatedIn, 0, sizeof(RTMPPacket*) * (n - r->m_channelsAllocatedIn));
+      r->m_channelsAllocatedIn = n;
+    }
+
   if (nSize == RTMP_LARGE_HEADER_SIZE)	/* if we get a full header the timestamp is absolute */
     packet->m_hasAbsTimestamp = TRUE;
 
@@ -2933,6 +3647,8 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac
 	  packet->m_nBodySize = AMF_DecodeInt24(header + 3);
 	  packet->m_nBytesRead = 0;
 	  RTMPPacket_Free(packet);
+	  if (r->m_vecChannelsIn[packet->m_nChannel])
+	    r->m_vecChannelsIn[packet->m_nChannel]->m_body = NULL;
 
 	  if (nSize > 6)
 	    {
@@ -2942,17 +3658,19 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac
 		packet->m_nInfoField2 = DecodeInt32LE(header + 7);
 	    }
 	}
-      if (packet->m_nTimeStamp == 0xffffff)
+    }
+
+  extendedTimestamp = packet->m_nTimeStamp == 0xffffff;
+  if (extendedTimestamp)
+    {
+      if (ReadN(r, header + nSize, 4) != 4)
 	{
-	  if (ReadN(r, header + nSize, 4) != 4)
-	    {
-	      RTMP_Log(RTMP_LOGERROR, "%s, failed to read extended timestamp",
-		  __FUNCTION__);
-	      return FALSE;
-	    }
-	  packet->m_nTimeStamp = AMF_DecodeInt32(header + nSize);
-	  hSize += 4;
+	  RTMP_Log(RTMP_LOGERROR, "%s, failed to read extended timestamp",
+	      __FUNCTION__);
+	  return FALSE;
 	}
+      packet->m_nTimeStamp = AMF_DecodeInt32(header + nSize);
+      hSize += 4;
     }
 
   RTMP_LogHexString(RTMP_LOGDEBUG2, (uint8_t *)hbuf, hSize);
@@ -2964,7 +3682,6 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac
 	  RTMP_Log(RTMP_LOGDEBUG, "%s, failed to allocate packet", __FUNCTION__);
 	  return FALSE;
 	}
-      didAlloc = TRUE;
       packet->m_headerType = (hbuf[0] & 0xc0) >> 6;
     }
 
@@ -2984,7 +3701,7 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac
 
   if (ReadN(r, packet->m_body + packet->m_nBytesRead, nChunk) != nChunk)
     {
-      RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet body. len: %lu",
+      RTMP_Log(RTMP_LOGERROR, "%s, failed to read RTMP packet body. len: %u",
 	  __FUNCTION__, packet->m_nBodySize);
       return FALSE;
     }
@@ -2997,6 +3714,10 @@ RTMP_ReadPacket(RTMP *r, RTMPPacket *pac
   if (!r->m_vecChannelsIn[packet->m_nChannel])
     r->m_vecChannelsIn[packet->m_nChannel] = malloc(sizeof(RTMPPacket));
   memcpy(r->m_vecChannelsIn[packet->m_nChannel], packet, sizeof(RTMPPacket));
+  if (extendedTimestamp)
+    {
+      r->m_vecChannelsIn[packet->m_nChannel]->m_nTimeStamp = 0xffffff;
+    }
 
   if (RTMPPacket_IsReady(packet))
     {
@@ -3176,7 +3897,7 @@ RTMP_SendChunk(RTMP *r, RTMPChunk *chunk
 int
 RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue)
 {
-  const RTMPPacket *prevPacket = r->m_vecChannelsOut[packet->m_nChannel];
+  const RTMPPacket *prevPacket;
   uint32_t last = 0;
   int nSize;
   int hSize, cSize;
@@ -3186,6 +3907,22 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac
   int nChunkSize;
   int tlen;
 
+  if (packet->m_nChannel >= r->m_channelsAllocatedOut)
+    {
+      int n = packet->m_nChannel + 10;
+      RTMPPacket **packets = realloc(r->m_vecChannelsOut, sizeof(RTMPPacket*) * n);
+      if (!packets) {
+        free(r->m_vecChannelsOut);
+        r->m_vecChannelsOut = NULL;
+        r->m_channelsAllocatedOut = 0;
+        return FALSE;
+      }
+      r->m_vecChannelsOut = packets;
+      memset(r->m_vecChannelsOut + r->m_channelsAllocatedOut, 0, sizeof(RTMPPacket*) * (n - r->m_channelsAllocatedOut));
+      r->m_channelsAllocatedOut = n;
+    }
+
+  prevPacket = r->m_vecChannelsOut[packet->m_nChannel];
   if (prevPacket && packet->m_headerType != RTMP_PACKET_SIZE_LARGE)
     {
       /* compress a bit by using the prev packet's attributes */
@@ -3232,10 +3969,11 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac
       hSize += cSize;
     }
 
-  if (nSize > 1 && t >= 0xffffff)
+  if (t >= 0xffffff)
     {
       header -= 4;
       hSize += 4;
+      RTMP_Log(RTMP_LOGWARNING, "Larger timestamp than 24-bit: 0x%x", t);
     }
 
   hptr = header;
@@ -3274,7 +4012,7 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac
   if (nSize > 8)
     hptr += EncodeInt32LE(hptr, packet->m_nInfoField2);
 
-  if (nSize > 1 && t >= 0xffffff)
+  if (t >= 0xffffff)
     hptr = AMF_EncodeInt32(hptr, hend, t);
 
   nSize = packet->m_nBodySize;
@@ -3329,6 +4067,11 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac
 	      header -= cSize;
 	      hSize += cSize;
 	    }
+          if (t >= 0xffffff)
+            {
+              header -= 4;
+              hSize += 4;
+            }
 	  *header = (0xc0 | c);
 	  if (cSize)
 	    {
@@ -3337,6 +4080,11 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac
 	      if (cSize == 2)
 		header[2] = tmp >> 8;
 	    }
+          if (t >= 0xffffff)
+            {
+              char* extendedTimestamp = header + 1 + cSize;
+              AMF_EncodeInt32(extendedTimestamp, extendedTimestamp + 4, t);
+            }
 	}
     }
   if (tbuf)
@@ -3349,7 +4097,7 @@ RTMP_SendPacket(RTMP *r, RTMPPacket *pac
     }
 
   /* we invoked a remote method */
-  if (packet->m_packetType == 0x14)
+  if (packet->m_packetType == RTMP_PACKET_TYPE_INVOKE)
     {
       AVal method;
       char *ptr;
@@ -3380,16 +4128,22 @@ RTMP_Serve(RTMP *r)
 void
 RTMP_Close(RTMP *r)
 {
+  CloseInternal(r, 0);
+}
+
+static void
+CloseInternal(RTMP *r, int reconnect)
+{
   int i;
 
   if (RTMP_IsConnected(r))
     {
       if (r->m_stream_id > 0)
         {
-          if ((r->Link.protocol & RTMP_FEATURE_WRITE))
-	    SendFCUnpublish(r);
 	  i = r->m_stream_id;
 	  r->m_stream_id = 0;
+          if ((r->Link.protocol & RTMP_FEATURE_WRITE))
+	    SendFCUnpublish(r);
 	  SendDeleteStream(r, i);
 	}
       if (r->m_clientID.av_val)
@@ -3422,7 +4176,7 @@ RTMP_Close(RTMP *r)
   r->m_write.m_nBytesRead = 0;
   RTMPPacket_Free(&r->m_write);
 
-  for (i = 0; i < RTMP_CHANNELS; i++)
+  for (i = 0; i < r->m_channelsAllocatedIn; i++)
     {
       if (r->m_vecChannelsIn[i])
 	{
@@ -3430,12 +4184,23 @@ RTMP_Close(RTMP *r)
 	  free(r->m_vecChannelsIn[i]);
 	  r->m_vecChannelsIn[i] = NULL;
 	}
+    }
+  free(r->m_vecChannelsIn);
+  r->m_vecChannelsIn = NULL;
+  free(r->m_channelTimestamp);
+  r->m_channelTimestamp = NULL;
+  r->m_channelsAllocatedIn = 0;
+  for (i = 0; i < r->m_channelsAllocatedOut; i++)
+    {
       if (r->m_vecChannelsOut[i])
 	{
 	  free(r->m_vecChannelsOut[i]);
 	  r->m_vecChannelsOut[i] = NULL;
 	}
     }
+  free(r->m_vecChannelsOut);
+  r->m_vecChannelsOut = NULL;
+  r->m_channelsAllocatedOut = 0;
   AV_clear(r->m_methodCalls, r->m_numCalls);
   r->m_methodCalls = NULL;
   r->m_numCalls = 0;
@@ -3448,16 +4213,24 @@ RTMP_Close(RTMP *r)
   r->m_resplen = 0;
   r->m_unackd = 0;
 
-  free(r->Link.playpath0.av_val);
-  r->Link.playpath0.av_val = NULL;
-
-  if (r->Link.lFlags & RTMP_LF_FTCU)
+  if (r->Link.lFlags & RTMP_LF_FTCU && !reconnect)
     {
       free(r->Link.tcUrl.av_val);
       r->Link.tcUrl.av_val = NULL;
       r->Link.lFlags ^= RTMP_LF_FTCU;
     }
+  if (r->Link.lFlags & RTMP_LF_FAPU && !reconnect)
+    {
+      free(r->Link.app.av_val);
+      r->Link.app.av_val = NULL;
+      r->Link.lFlags ^= RTMP_LF_FAPU;
+    }
 
+  if (!reconnect)
+    {
+      free(r->Link.playpath0.av_val);
+      r->Link.playpath0.av_val = NULL;
+    }
 #ifdef CRYPTO
   if (r->Link.dh)
     {
@@ -3487,7 +4260,7 @@ RTMPSockBuf_Fill(RTMPSockBuf *sb)
 
   while (1)
     {
-      nBytes = sizeof(sb->sb_buf) - sb->sb_size - (sb->sb_start - sb->sb_buf);
+      nBytes = sizeof(sb->sb_buf) - 1 - sb->sb_size - (sb->sb_start - sb->sb_buf);
 #if defined(CRYPTO) && !defined(NO_SSL)
       if (sb->sb_ssl)
 	{
@@ -3555,7 +4328,9 @@ RTMPSockBuf_Close(RTMPSockBuf *sb)
       sb->sb_ssl = NULL;
     }
 #endif
-  return closesocket(sb->sb_socket);
+  if (sb->sb_socket != -1)
+      return closesocket(sb->sb_socket);
+  return 0;
 }
 
 #define HEX2BIN(a)	(((a)&0x40)?((a)&0xf)+9:((a)&0xf))
@@ -3637,8 +4412,8 @@ HTTP_Post(RTMP *r, RTMPTCmd cmd, const c
   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\n"
-    "Connection: Keep-Alive\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],
@@ -3656,22 +4431,45 @@ static int
 HTTP_read(RTMP *r, int fill)
 {
   char *ptr;
-  int hlen;
+  long hlen;
 
+restart:
   if (fill)
     RTMPSockBuf_Fill(&r->m_sb);
-  if (r->m_sb.sb_size < 144)
-    return -1;
+  if (r->m_sb.sb_size < 13) {
+    if (fill)
+      goto restart;
+    return -2;
+  }
   if (strncmp(r->m_sb.sb_start, "HTTP/1.1 200 ", 13))
     return -1;
-  ptr = strstr(r->m_sb.sb_start, "Content-Length:");
+  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-"))) {
+    if (!strncasecmp(ptr+8, "length:", 7)) break;
+    ptr += 8;
+  }
   if (!ptr)
     return -1;
-  hlen = atoi(ptr+16);
-  ptr = strstr(ptr, "\r\n\r\n");
+  hlen = strtol(ptr+16, NULL, 10);
+  if (hlen < 1 || hlen > INT_MAX)
+    return -1;
+  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--;
@@ -3721,8 +4519,8 @@ Read_1_Packet(RTMP *r, char *buf, unsign
       char *packetBody = packet.m_body;
       unsigned int nPacketLen = packet.m_nBodySize;
 
-      /* Return -3 if this was completed nicely with invoke message
-       * Play.Stop or Play.Complete
+      /* Return RTMP_READ_COMPLETE if this was completed nicely with
+       * invoke message Play.Stop or Play.Complete
        */
       if (rtnGetNextMediaPacket == 2)
 	{
@@ -3733,17 +4531,17 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 	  break;
 	}
 
-      r->m_read.dataType |= (((packet.m_packetType == 0x08) << 2) |
-			     (packet.m_packetType == 0x09));
+      r->m_read.dataType |= (((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO) << 2) |
+			     (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO));
 
-      if (packet.m_packetType == 0x09 && nPacketLen <= 5)
+      if (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO && nPacketLen <= 5)
 	{
 	  RTMP_Log(RTMP_LOGDEBUG, "ignoring too small video packet: size: %d",
 	      nPacketLen);
 	  ret = RTMP_READ_IGNORE;
 	  break;
 	}
-      if (packet.m_packetType == 0x08 && nPacketLen <= 1)
+      if (packet.m_packetType == RTMP_PACKET_TYPE_AUDIO && nPacketLen <= 1)
 	{
 	  RTMP_Log(RTMP_LOGDEBUG, "ignoring too small audio packet: size: %d",
 	      nPacketLen);
@@ -3760,7 +4558,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign
       RTMP_Log(RTMP_LOGDEBUG, "type: %02X, size: %d, TS: %d ms, abs TS: %d",
 	  packet.m_packetType, nPacketLen, packet.m_nTimeStamp,
 	  packet.m_hasAbsTimestamp);
-      if (packet.m_packetType == 0x09)
+      if (packet.m_packetType == RTMP_PACKET_TYPE_VIDEO)
 	RTMP_Log(RTMP_LOGDEBUG, "frametype: %02X", (*packetBody & 0xf0));
 #endif
 
@@ -3770,7 +4568,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 	  if (packet.m_nTimeStamp == 0)
 	    {
 	      if (r->m_read.nMetaHeaderSize > 0
-		  && packet.m_packetType == 0x12)
+		  && packet.m_packetType == RTMP_PACKET_TYPE_INFO)
 		{
 		  AMFObject metaObj;
 		  int nRes =
@@ -3831,7 +4629,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 		   * in the first FLV stream chunk and we have to compare
 		   * it and filter it out !!
 		   */
-		  if (packet.m_packetType == 0x16)
+		  if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO)
 		    {
 		      /* basically we have to find the keyframe with the
 		       * correct TS being nResumeTS
@@ -3943,7 +4741,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 	   * (seeking might put us somewhere before it)
 	   */
 	  if (!(r->m_read.flags & RTMP_READ_GOTKF) &&
-	  	packet.m_packetType != 0x16)
+	  	packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO)
 	    {
 	      RTMP_Log(RTMP_LOGWARNING,
 		  "Stream does not start with requested frame, ignoring data... ");
@@ -3956,7 +4754,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 	    }
 	  /* ok, do the same for FLV streams */
 	  if (!(r->m_read.flags & RTMP_READ_GOTFLVK) &&
-	  	packet.m_packetType == 0x16)
+	  	packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO)
 	    {
 	      RTMP_Log(RTMP_LOGWARNING,
 		  "Stream does not start with requested FLV frame, ignoring data... ");
@@ -3975,9 +4773,11 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 	   * the preceding if clause)
 	   */
 	  if (!(r->m_read.flags & RTMP_READ_NO_IGNORE) &&
-	  	packet.m_packetType != 0x16)
-	    {			/* exclude type 0x16 (FLV) since it can
-				 * contain several FLV packets */
+	  	packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO)
+	    {
+              /* exclude type RTMP_PACKET_TYPE_FLASH_VIDEO since it can
+               * contain several FLV packets
+               */
 	      if (packet.m_nTimeStamp == 0)
 		{
 		  ret = RTMP_READ_IGNORE;
@@ -3993,9 +4793,10 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 
       /* calculate packet size and allocate slop buffer if necessary */
       size = nPacketLen +
-	((packet.m_packetType == 0x08 || packet.m_packetType == 0x09
-	  || packet.m_packetType == 0x12) ? 11 : 0) +
-	(packet.m_packetType != 0x16 ? 4 : 0);
+	((packet.m_packetType == RTMP_PACKET_TYPE_AUDIO
+          || packet.m_packetType == RTMP_PACKET_TYPE_VIDEO
+	  || packet.m_packetType == RTMP_PACKET_TYPE_INFO) ? 11 : 0) +
+	(packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO ? 4 : 0);
 
       if (size + 4 > buflen)
 	{
@@ -4021,8 +4822,9 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 
       /* audio (0x08), video (0x09) or metadata (0x12) packets :
        * construct 11 byte header then add rtmp packet's data */
-      if (packet.m_packetType == 0x08 || packet.m_packetType == 0x09
-	  || packet.m_packetType == 0x12)
+      if (packet.m_packetType == RTMP_PACKET_TYPE_AUDIO
+          || packet.m_packetType == RTMP_PACKET_TYPE_VIDEO
+	  || packet.m_packetType == RTMP_PACKET_TYPE_INFO)
 	{
 	  nTimeStamp = r->m_read.nResumeTS + packet.m_nTimeStamp;
 	  prevTagSize = 11 + nPacketLen;
@@ -4032,7 +4834,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 	  ptr = AMF_EncodeInt24(ptr, pend, nPacketLen);
 
 #if 0
-	    if(packet.m_packetType == 0x09) { /* video */
+	    if(packet.m_packetType == RTMP_PACKET_TYPE_VIDEO) {
 
 	     /* H264 fix: */
 	     if((packetBody[0] & 0x0f) == 7) { /* CodecId = H264 */
@@ -4062,7 +4864,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign
       len = nPacketLen;
 
       /* correct tagSize and obtain timestamp if we have an FLV stream */
-      if (packet.m_packetType == 0x16)
+      if (packet.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO)
 	{
 	  unsigned int pos = 0;
 	  int delta;
@@ -4070,7 +4872,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 	  /* grab first timestamp and see if it needs fixing */
 	  nTimeStamp = AMF_DecodeInt24(packetBody + 4);
 	  nTimeStamp |= (packetBody[7] << 24);
-	  delta = packet.m_nTimeStamp - nTimeStamp;
+	  delta = packet.m_nTimeStamp - nTimeStamp + r->m_read.nResumeTS;
 
 	  while (pos + 11 < nPacketLen)
 	    {
@@ -4095,7 +4897,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 		  if (pos + 11 + dataSize > nPacketLen)
 		    {
 		      RTMP_Log(RTMP_LOGERROR,
-			  "Wrong data size (%lu), stream corrupted, aborting!",
+			  "Wrong data size (%u), stream corrupted, aborting!",
 			  dataSize);
 		      ret = RTMP_READ_ERROR;
 		      break;
@@ -4140,7 +4942,7 @@ Read_1_Packet(RTMP *r, char *buf, unsign
 	}
       ptr += len;
 
-      if (packet.m_packetType != 0x16)
+      if (packet.m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO)
 	{
 	  /* FLV tag packets contain their own prevTagSize */
 	  AMF_EncodeInt32(ptr, pend, prevTagSize);
@@ -4208,6 +5010,7 @@ fail:
 	  memcpy(mybuf, flvHeader, sizeof(flvHeader));
 	  r->m_read.buf += sizeof(flvHeader);
 	  r->m_read.buflen -= sizeof(flvHeader);
+	  cnt += sizeof(flvHeader);
 
 	  while (r->m_read.timestamp == 0)
 	    {
@@ -4224,6 +5027,7 @@ fail:
 	      if (r->m_read.buf < mybuf || r->m_read.buf > end) {
 	      	mybuf = realloc(mybuf, cnt + nRead);
 		memcpy(mybuf+cnt, r->m_read.buf, nRead);
+		free(r->m_read.buf);
 		r->m_read.buf = mybuf+cnt+nRead;
 	        break;
 	      }
@@ -4325,11 +5129,12 @@ RTMP_Write(RTMP *r, const char *buf, int
 	  buf += 3;
 	  s2 -= 11;
 
-	  if (((pkt->m_packetType == 0x08 || pkt->m_packetType == 0x09) &&
-	    !pkt->m_nTimeStamp) || pkt->m_packetType == 0x12)
+	  if (((pkt->m_packetType == RTMP_PACKET_TYPE_AUDIO
+                || pkt->m_packetType == RTMP_PACKET_TYPE_VIDEO) &&
+            !pkt->m_nTimeStamp) || pkt->m_packetType == RTMP_PACKET_TYPE_INFO)
 	    {
 	      pkt->m_headerType = RTMP_PACKET_SIZE_LARGE;
-	      if (pkt->m_packetType == 0x12)
+	      if (pkt->m_packetType == RTMP_PACKET_TYPE_INFO)
 		pkt->m_nBodySize += 16;
 	    }
 	  else
@@ -4344,7 +5149,7 @@ RTMP_Write(RTMP *r, const char *buf, int
 	    }
 	  enc = pkt->m_body;
 	  pend = enc + pkt->m_nBodySize;
-	  if (pkt->m_packetType == 0x12)
+	  if (pkt->m_packetType == RTMP_PACKET_TYPE_INFO)
 	    {
 	      enc = AMF_EncodeString(enc, pend, &av_setDataFrame);
 	      pkt->m_nBytesRead = enc - pkt->m_body;
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/rtmp.h rtmpdump/librtmp/rtmp.h
--- rtmpdump-2.3/librtmp/rtmp.h	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/rtmp.h	2026-06-19 14:15:18.303594982 -0600
@@ -71,9 +71,29 @@ extern "C"
 
   uint32_t RTMP_GetTime(void);
 
-#define RTMP_PACKET_TYPE_AUDIO 0x08
-#define RTMP_PACKET_TYPE_VIDEO 0x09
-#define RTMP_PACKET_TYPE_INFO  0x12
+/*      RTMP_PACKET_TYPE_...                0x00 */
+#define RTMP_PACKET_TYPE_CHUNK_SIZE         0x01
+/*      RTMP_PACKET_TYPE_...                0x02 */
+#define RTMP_PACKET_TYPE_BYTES_READ_REPORT  0x03
+#define RTMP_PACKET_TYPE_CONTROL            0x04
+#define RTMP_PACKET_TYPE_SERVER_BW          0x05
+#define RTMP_PACKET_TYPE_CLIENT_BW          0x06
+/*      RTMP_PACKET_TYPE_...                0x07 */
+#define RTMP_PACKET_TYPE_AUDIO              0x08
+#define RTMP_PACKET_TYPE_VIDEO              0x09
+/*      RTMP_PACKET_TYPE_...                0x0A */
+/*      RTMP_PACKET_TYPE_...                0x0B */
+/*      RTMP_PACKET_TYPE_...                0x0C */
+/*      RTMP_PACKET_TYPE_...                0x0D */
+/*      RTMP_PACKET_TYPE_...                0x0E */
+#define RTMP_PACKET_TYPE_FLEX_STREAM_SEND   0x0F
+#define RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT 0x10
+#define RTMP_PACKET_TYPE_FLEX_MESSAGE       0x11
+#define RTMP_PACKET_TYPE_INFO               0x12
+#define RTMP_PACKET_TYPE_SHARED_OBJECT      0x13
+#define RTMP_PACKET_TYPE_INVOKE             0x14
+/*      RTMP_PACKET_TYPE_...                0x15 */
+#define RTMP_PACKET_TYPE_FLASH_VIDEO        0x16
 
 #define RTMP_MAX_HEADER_SIZE 18
 
@@ -116,7 +136,7 @@ extern "C"
 
   void RTMPPacket_Reset(RTMPPacket *p);
   void RTMPPacket_Dump(RTMPPacket *p);
-  int RTMPPacket_Alloc(RTMPPacket *p, int nSize);
+  int RTMPPacket_Alloc(RTMPPacket *p, uint32_t nSize);
   void RTMPPacket_Free(RTMPPacket *p);
 
 #define RTMPPacket_IsReady(a)	((a)->m_nBytesRead == (a)->m_nBodySize)
@@ -135,7 +155,10 @@ extern "C"
     AVal auth;
     AVal flashVer;
     AVal subscribepath;
+    AVal usherToken;
     AVal token;
+    AVal pubUser;
+    AVal pubPasswd;
     AMFObject extras;
     int edepth;
 
@@ -148,6 +171,7 @@ extern "C"
 #define RTMP_LF_PLST	0x0008	/* send playlist before play */
 #define RTMP_LF_BUFX	0x0010	/* toggle stream on BufferEmpty msg */
 #define RTMP_LF_FTCU	0x0020	/* free tcUrl on close */
+#define RTMP_LF_FAPU	0x0040	/* free app on close */
     int lFlags;
 
     int swfAge;
@@ -155,6 +179,8 @@ extern "C"
     int protocol;
     int timeout;		/* connection timeout in seconds */
 
+    int pFlags;			/* unused, but kept to avoid breaking ABI */
+
     unsigned short socksport;
     unsigned short port;
 
@@ -232,9 +258,11 @@ extern "C"
     int m_numCalls;
     RTMP_METHOD *m_methodCalls;	/* remote method calls queue */
 
-    RTMPPacket *m_vecChannelsIn[RTMP_CHANNELS];
-    RTMPPacket *m_vecChannelsOut[RTMP_CHANNELS];
-    int m_channelTimestamp[RTMP_CHANNELS];	/* abs timestamp of last packet */
+    int m_channelsAllocatedIn;
+    int m_channelsAllocatedOut;
+    RTMPPacket **m_vecChannelsIn;
+    RTMPPacket **m_vecChannelsOut;
+    int *m_channelTimestamp;	/* abs timestamp of last packet */
 
     double m_fAudioCodecs;	/* audioCodecs for the connect packet */
     double m_fVideoCodecs;	/* videoCodecs for the connect packet */
@@ -277,6 +305,7 @@ extern "C"
 			uint32_t swfSize,
 			AVal *flashVer,
 			AVal *subscribepath,
+			AVal *usherToken,
 			int dStart,
 			int dStop, int bLiveStream, long int timeout);
 
@@ -285,6 +314,7 @@ extern "C"
   int RTMP_Connect0(RTMP *r, struct sockaddr *svc);
   int RTMP_Connect1(RTMP *r, RTMPPacket *cp);
   int RTMP_Serve(RTMP *r);
+  int RTMP_TLS_Accept(RTMP *r, void *ctx);
 
   int RTMP_ReadPacket(RTMP *r, RTMPPacket *packet);
   int RTMP_SendPacket(RTMP *r, RTMPPacket *packet, int queue);
@@ -307,6 +337,9 @@ extern "C"
   void RTMP_Free(RTMP *r);
   void RTMP_EnableWrite(RTMP *r);
 
+  void *RTMP_TLS_AllocServerContext(const char* cert, const char* key);
+  void RTMP_TLS_FreeServerContext(void *ctx);
+
   int RTMP_LibVersion(void);
   void RTMP_UserInterrupt(void);	/* user typed Ctrl-C */
 
diff '--color=auto' -Naurp rtmpdump-2.3/librtmp/rtmp_sys.h rtmpdump/librtmp/rtmp_sys.h
--- rtmpdump-2.3/librtmp/rtmp_sys.h	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/librtmp/rtmp_sys.h	2026-06-19 14:15:18.303594982 -0600
@@ -24,17 +24,14 @@
 
 #ifdef _WIN32
 
-#ifdef _XBOX
-#include <xtl.h>
-#include <winsockx.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+
+#ifdef _MSC_VER	/* MSVC */
 #define snprintf _snprintf
 #define strcasecmp stricmp
 #define strncasecmp strnicmp
 #define vsnprintf _vsnprintf
-
-#else /* !_XBOX */
-#include <winsock2.h>
-#include <ws2tcpip.h>
 #endif
 
 #define GetSockError()	WSAGetLastError()
@@ -49,10 +46,10 @@
 #include <sys/socket.h>
 #include <sys/times.h>
 #include <netdb.h>
-#include <arpa/inet.h>
 #include <unistd.h>
 #include <netinet/in.h>
 #include <netinet/tcp.h>
+#include <arpa/inet.h>
 #define GetSockError()	errno
 #define SetSockError(e)	errno = e
 #undef closesocket
@@ -64,20 +61,46 @@
 #include "rtmp.h"
 
 #ifdef USE_POLARSSL
+#include <polarssl/version.h>
 #include <polarssl/net.h>
 #include <polarssl/ssl.h>
 #include <polarssl/havege.h>
+#if POLARSSL_VERSION_NUMBER < 0x01010000
+#define havege_random	havege_rand
+#endif
+#if POLARSSL_VERSION_NUMBER >= 0x01020000
+#define	SSL_SET_SESSION(S,resume,timeout,ctx)	ssl_set_session(S,ctx)
+#else
+#define	SSL_SET_SESSION(S,resume,timeout,ctx)	ssl_set_session(S,resume,timeout,ctx)
+#endif
 typedef struct tls_ctx {
 	havege_state hs;
 	ssl_session ssn;
 } tls_ctx;
+typedef struct tls_server_ctx {
+	havege_state *hs;
+	x509_cert cert;
+	rsa_context key;
+	ssl_session ssn;
+	const char *dhm_P, *dhm_G;
+} tls_server_ctx;
+
 #define TLS_CTX tls_ctx *
 #define TLS_client(ctx,s)	s = malloc(sizeof(ssl_context)); ssl_init(s);\
 	ssl_set_endpoint(s, SSL_IS_CLIENT); ssl_set_authmode(s, SSL_VERIFY_NONE);\
-	ssl_set_rng(s, havege_rand, &ctx->hs); ssl_set_ciphers(s, ssl_default_ciphers);\
-	ssl_set_session(s, 1, 600, &ctx->ssn)
+	ssl_set_rng(s, havege_random, &ctx->hs);\
+	ssl_set_ciphersuites(s, ssl_default_ciphersuites);\
+	SSL_SET_SESSION(s, 1, 600, &ctx->ssn)
+#define TLS_server(ctx,s)	s = malloc(sizeof(ssl_context)); ssl_init(s);\
+	ssl_set_endpoint(s, SSL_IS_SERVER); ssl_set_authmode(s, SSL_VERIFY_NONE);\
+	ssl_set_rng(s, havege_random, ((tls_server_ctx*)ctx)->hs);\
+	ssl_set_ciphersuites(s, ssl_default_ciphersuites);\
+	SSL_SET_SESSION(s, 1, 600, &((tls_server_ctx*)ctx)->ssn);\
+	ssl_set_own_cert(s, &((tls_server_ctx*)ctx)->cert, &((tls_server_ctx*)ctx)->key);\
+	ssl_set_dh_param(s, ((tls_server_ctx*)ctx)->dhm_P, ((tls_server_ctx*)ctx)->dhm_G)
 #define TLS_setfd(s,fd)	ssl_set_bio(s, net_recv, &fd, net_send, &fd)
 #define TLS_connect(s)	ssl_handshake(s)
+#define TLS_accept(s)	ssl_handshake(s)
 #define TLS_read(s,b,l)	ssl_read(s,(unsigned char *)b,l)
 #define TLS_write(s,b,l)	ssl_write(s,(unsigned char *)b,l)
 #define TLS_shutdown(s)	ssl_close_notify(s)
@@ -91,8 +114,10 @@ typedef struct tls_ctx {
 } tls_ctx;
 #define TLS_CTX	tls_ctx *
 #define TLS_client(ctx,s)	gnutls_init((gnutls_session_t *)(&s), GNUTLS_CLIENT); gnutls_priority_set(s, ctx->prios); gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, ctx->cred)
+#define TLS_server(ctx,s)	gnutls_init((gnutls_session_t *)(&s), GNUTLS_SERVER); gnutls_priority_set_direct(s, "NORMAL", NULL); gnutls_credentials_set(s, GNUTLS_CRD_CERTIFICATE, ctx)
 #define TLS_setfd(s,fd)	gnutls_transport_set_ptr(s, (gnutls_transport_ptr_t)(long)fd)
 #define TLS_connect(s)	gnutls_handshake(s)
+#define TLS_accept(s)	gnutls_handshake(s)
 #define TLS_read(s,b,l)	gnutls_record_recv(s,b,l)
 #define TLS_write(s,b,l)	gnutls_record_send(s,b,l)
 #define TLS_shutdown(s)	gnutls_bye(s, GNUTLS_SHUT_RDWR)
@@ -101,8 +126,10 @@ typedef struct tls_ctx {
 #else	/* USE_OPENSSL */
 #define TLS_CTX	SSL_CTX *
 #define TLS_client(ctx,s)	s = SSL_new(ctx)
+#define TLS_server(ctx,s)	s = SSL_new(ctx)
 #define TLS_setfd(s,fd)	SSL_set_fd(s,fd)
 #define TLS_connect(s)	SSL_connect(s)
+#define TLS_accept(s)	SSL_accept(s)
 #define TLS_read(s,b,l)	SSL_read(s,b,l)
 #define TLS_write(s,b,l)	SSL_write(s,b,l)
 #define TLS_shutdown(s)	SSL_shutdown(s)
diff '--color=auto' -Naurp rtmpdump-2.3/Makefile rtmpdump/Makefile
--- rtmpdump-2.3/Makefile	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/Makefile	2026-06-19 14:15:18.300594955 -0600
@@ -1,4 +1,4 @@
-VERSION=v2.3
+VERSION=v2.6
 
 prefix=/usr/local
 
@@ -11,9 +11,10 @@ SYS=posix
 CRYPTO=OPENSSL
 #CRYPTO=POLARSSL
 #CRYPTO=GNUTLS
-LIB_GNUTLS=-lgnutls -lgcrypt
-LIB_OPENSSL=-lssl -lcrypto
-LIB_POLARSSL=-lpolarssl
+LIBZ=-lz
+LIB_GNUTLS=-lgnutls -lhogweed -lnettle -lgmp $(LIBZ)
+LIB_OPENSSL=-lssl -lcrypto $(LIBZ)
+LIB_POLARSSL=-lpolarssl $(LIBZ)
 CRYPTO_LIB=$(LIB_$(CRYPTO))
 DEF_=-DNO_CRYPTO
 CRYPTO_DEF=$(DEF_$(CRYPTO))
@@ -32,10 +33,13 @@ SBINDIR=$(DESTDIR)$(sbindir)
 MANDIR=$(DESTDIR)$(mandir)
 
 LIBS_posix=
+LIBS_darwin=
 LIBS_mingw=-lws2_32 -lwinmm -lgdi32
-LIBS=$(CRYPTO_LIB) -lz $(LIBS_$(SYS)) $(XLIBS)
+LIB_RTMP=-Llibrtmp -lrtmp
+LIBS=$(LIB_RTMP) $(CRYPTO_LIB) $(LIBS_$(SYS)) $(XLIBS)
 
 THREADLIB_posix=-lpthread
+THREADLIB_darwin=-lpthread
 THREADLIB_mingw=
 THREADLIB=$(THREADLIB_$(SYS))
 SLIBS=$(THREADLIB) $(LIBS)
@@ -44,14 +48,17 @@ LIBRTMP=librtmp/librtmp.a
 INCRTMP=librtmp/rtmp_sys.h librtmp/rtmp.h librtmp/log.h librtmp/amf.h
 
 EXT_posix=
+EXT_darwin=
 EXT_mingw=.exe
 EXT=$(EXT_$(SYS))
 
-all:	$(LIBRTMP) progs
+PROGS=rtmpdump rtmpgw rtmpsrv rtmpsuck
 
-progs:	rtmpdump rtmpgw rtmpsrv rtmpsuck
+all:	$(LIBRTMP) $(PROGS)
 
-install:	progs
+$(PROGS): $(LIBRTMP)
+
+install:	$(PROGS)
 	-mkdir -p $(BINDIR) $(SBINDIR) $(MANDIR)/man1 $(MANDIR)/man8
 	cp rtmpdump$(EXT) $(BINDIR)
 	cp rtmpgw$(EXT) rtmpsrv$(EXT) rtmpsuck$(EXT) $(SBINDIR)
@@ -68,20 +75,17 @@ FORCE:
 $(LIBRTMP): FORCE
 	@cd librtmp; $(MAKE) all
 
-# note: $^ is GNU Make's equivalent to BSD $>
-# we use both since either make will ignore the one it doesn't recognize
-
-rtmpdump: rtmpdump.o $(LIBRTMP)
-	$(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(LIBS)
+rtmpdump: rtmpdump.o
+	$(CC) $(LDFLAGS) -o $@$(EXT) $@.o $(LIBS)
 
-rtmpsrv: rtmpsrv.o thread.o $(LIBRTMP)
-	$(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(SLIBS)
+rtmpsrv: rtmpsrv.o thread.o
+	$(CC) $(LDFLAGS) -o $@$(EXT) $@.o thread.o $(SLIBS)
 
-rtmpsuck: rtmpsuck.o thread.o $(LIBRTMP)
-	$(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(SLIBS)
+rtmpsuck: rtmpsuck.o thread.o
+	$(CC) $(LDFLAGS) -o $@$(EXT) $@.o thread.o $(SLIBS)
 
-rtmpgw: rtmpgw.o thread.o $(LIBRTMP)
-	$(CC) $(LDFLAGS) $^ $> -o $@$(EXT) $(SLIBS)
+rtmpgw: rtmpgw.o thread.o
+	$(CC) $(LDFLAGS) -o $@$(EXT) $@.o thread.o $(SLIBS)
 
 rtmpgw.o: rtmpgw.c $(INCRTMP) Makefile
 rtmpdump.o: rtmpdump.c $(INCRTMP) Makefile
diff '--color=auto' -Naurp rtmpdump-2.3/README rtmpdump/README
--- rtmpdump-2.3/README	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/README	2026-06-19 14:15:18.300594955 -0600
@@ -1,7 +1,8 @@
-RTMP Dump v2.3
+RTMP Dump v2.4
 (C) 2009 Andrej Stepanchuk
-(C) 2009-2010 Howard Chu
+(C) 2009-2011 Howard Chu
 (C) 2010 2a665470ced7adb7156fcef47f8199a6371c117b8a79e399a2771e0b36384090
+(C) 2011 33ae1ce77301f4b4494faaa5f609f3c48b9dcf82
 License: GPLv2
 librtmp license: LGPLv2.1
 http://rtmpdump.mplayerhq.hu/
@@ -10,7 +11,11 @@ To compile type "make" with SYS=<platfor
 
   $ make SYS=posix
 
-for Linux, MacOSX, Unix, etc. or
+for Linux, Unix, etc. or
+
+  $ make SYS=darwin
+
+for MacOSX or
 
   $ make SYS=mingw
 
@@ -45,6 +50,7 @@ library. You can also turn it off if des
 The rtmpdump programs still link to the static library, regardless.
 
 Note that if using OpenSSL, you must have version 0.9.8 or newer.
+For Polar SSL you must have version 1.0.0 or newer.
 
 Credit goes to team boxee for the XBMC RTMP code originally used in RTMPDumper.
 The current code is based on the XBMC code but rewritten in C by Howard Chu.
diff '--color=auto' -Naurp rtmpdump-2.3/rtmpdump.1 rtmpdump/rtmpdump.1
--- rtmpdump-2.3/rtmpdump.1	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/rtmpdump.1	2026-06-19 14:15:18.303594982 -0600
@@ -1,5 +1,5 @@
-.TH RTMPDUMP 1 "2010-05-02" "RTMPDump v2.2e"
-.\" Copyright 2010 Howard Chu.
+.TH RTMPDUMP 1 "2012-07-24" "RTMPDump v2.4"
+.\" Copyright 2011 Howard Chu.
 .\" Copying permitted according to the GNU General Public License V2.
 .SH NAME
 rtmpdump \- RTMP streaming media client
@@ -51,6 +51,8 @@ rtmpdump \- RTMP streaming media client
 [\c
 .BI \-T \ key\fR]
 [\c
+.BI \-j \ JSON\fR]
+[\c
 .BI \-w \ swfHash\fR]
 [\c
 .BI \-x \ swfSize\fR]
@@ -175,6 +177,12 @@ live streams is possible.
 Name of live stream to subscribe to. Defaults to
 .IR playpath .
 .TP
+.B \-\-realtime	\-R
+Download approximately in realtime, without attempting to speed up via
+Pause/Unpause commands ("the BUFX hack").
+Useful for servers that jump backwards in time at the Unpause command.
+Resuming and seeking in realtime streams is still possible.
+.TP
 .B \-\-resume		\-e
 Resume an incomplete RTMP download.
 .TP
@@ -210,6 +218,9 @@ These options handle additional authenti
 Key for SecureToken response, used if the server requires SecureToken
 authentication.
 .TP
+\fB\-\-jtv		\-j\fP\ \fIJSON\fP
+JSON token used by legacy Justin.tv servers. Invokes NetStream.Authenticate.UsherToken
+.TP
 \fB\-\-swfhash		\-w\fP\ \fIhexstring\fP
 SHA256 hash of the decompressed SWF file. This option may be needed if
 the server uses SWF Verification, but see the
diff '--color=auto' -Naurp rtmpdump-2.3/rtmpdump.1.html rtmpdump/rtmpdump.1.html
--- rtmpdump-2.3/rtmpdump.1.html	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/rtmpdump.1.html	2026-06-19 14:15:18.303594982 -0600
@@ -6,10 +6,10 @@
 <tr><td>RTMPDUMP(1)<td align="center"><td align="right">RTMPDUMP(1)
 </thead>
 <tfoot>
-<tr><td>RTMPDump v2.2e<td align="center">2010-05-02<td align="right">RTMPDUMP(1)
+<tr><td>RTMPDump v2.4<td align="center">2012-07-24<td align="right">RTMPDUMP(1)
 </tfoot>
 <tbody><tr><td colspan="3"><br><br><ul>
-<!-- Copyright 2010 Howard Chu.
+<!-- Copyright 2011 Howard Chu.
  Copying permitted according to the GNU General Public License V2.-->
 </ul>
 
@@ -34,6 +34,7 @@ rtmpdump &minus; RTMP streaming media cl
 [<b>&minus;y</b><i>&nbsp;playpath</i>]
 [<b>&minus;Y</b>]
 [<b>&minus;v</b>]
+[<b>&minus;R</b>]
 [<b>&minus;d</b><i>&nbsp;subscription</i>]
 [<b>&minus;e</b>]
 [<b>&minus;k</b><i>&nbsp;skip</i>]
@@ -42,6 +43,7 @@ rtmpdump &minus; RTMP streaming media cl
 [<b>&minus;b</b><i>&nbsp;buffer</i>]
 [<b>&minus;m</b><i>&nbsp;timeout</i>]
 [<b>&minus;T</b><i>&nbsp;key</i>]
+[<b>&minus;j</b><i>&nbsp;JSON</i>]
 [<b>&minus;w</b><i>&nbsp;swfHash</i>]
 [<b>&minus;x</b><i>&nbsp;swfSize</i>]
 [<b>&minus;W</b><i>&nbsp;swfUrl</i>]
@@ -217,6 +219,15 @@ Name of live stream to subscribe to. Def
 </dl>
 <p>
 <dl compact><dt>
+<b>&minus;&minus;realtime &minus;R</b>
+<dd>
+Download approximately in realtime, without attempting to speed up via
+Pause/Unpause commands (&quot;the BUFX hack&quot;).
+Useful for servers that jump backwards in time at the Unpause command.
+Resuming and seeking in realtime streams is still possible.
+</dl>
+<p>
+<dl compact><dt>
 <b>&minus;&minus;resume &minus;e</b>
 <dd>
 Resume an incomplete RTMP download.
@@ -275,6 +286,12 @@ authentication.
 </dl>
 <p>
 <dl compact><dt>
+<b>&minus;&minus;jtv		&minus;j</b>&nbsp;<i>JSON</i>
+<dd>
+JSON token used by legacy Justin.tv servers. Invokes NetStream.Authenticate.UsherToken
+</dl>
+<p>
+<dl compact><dt>
 <b>&minus;&minus;swfhash		&minus;w</b>&nbsp;<i>hexstring</i>
 <dd>
 SHA256 hash of the decompressed SWF file. This option may be needed if
diff '--color=auto' -Naurp rtmpdump-2.3/rtmpdump.c rtmpdump/rtmpdump.c
--- rtmpdump-2.3/rtmpdump.c	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/rtmpdump.c	2026-06-19 14:15:18.304594991 -0600
@@ -46,6 +46,7 @@
 #define RD_SUCCESS		0
 #define RD_FAILED		1
 #define RD_INCOMPLETE		2
+#define RD_NO_CONNECT		3
 
 #define DEF_TIMEOUT	30	/* seconds */
 #define DEF_BUFTIME	(10 * 60 * 60 * 1000)	/* 10 hours default */
@@ -440,11 +441,11 @@ GetLastKeyframe(FILE * file,	// output f
 
 int
 Download(RTMP * rtmp,		// connected RTMP object
-	 FILE * file, uint32_t dSeek, uint32_t dStopOffset, double duration, int bResume, char *metaHeader, uint32_t nMetaHeaderSize, char *initialFrame, int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames, int bStdoutMode, int bLiveStream, int bHashes, int bOverrideBufferTime, uint32_t bufferTime, double *percent)	// percentage downloaded [out]
+	 FILE * file, uint32_t dSeek, uint32_t dStopOffset, double duration, int bResume, char *metaHeader, uint32_t nMetaHeaderSize, char *initialFrame, int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames, int bStdoutMode, int bLiveStream, int bRealtimeStream, int bHashes, int bOverrideBufferTime, uint32_t bufferTime, double *percent)	// percentage downloaded [out]
 {
   int32_t now, lastUpdate;
   int bufferSize = 64 * 1024;
-  char *buffer = (char *) malloc(bufferSize);
+  char *buffer;
   int nRead = 0;
   off_t size = ftello(file);
   unsigned long lastPercent = 0;
@@ -491,6 +492,8 @@ Download(RTMP * rtmp,		// connected RTMP
 		    bResume ? "Resuming" : "Starting",
 		    (double) size / 1024.0);
 	}
+      if (bRealtimeStream)
+	RTMP_LogPrintf("  in approximately realtime (disabled BUFX speedup hack)\n");
     }
 
   if (dStopOffset > 0)
@@ -505,6 +508,8 @@ Download(RTMP * rtmp,		// connected RTMP
   rtmp->m_read.nMetaHeaderSize = nMetaHeaderSize;
   rtmp->m_read.nInitialFrameSize = nInitialFrameSize;
 
+  buffer = (char *) malloc(bufferSize);
+
   now = RTMP_GetTime();
   lastUpdate = now - 1000;
   do
@@ -575,15 +580,17 @@ Download(RTMP * rtmp,		// connected RTMP
 		}
 	    }
 	}
-#ifdef _DEBUG
       else
 	{
+#ifdef _DEBUG
 	  RTMP_Log(RTMP_LOGDEBUG, "zero read!");
-	}
 #endif
+	  if (rtmp->m_read.status == RTMP_READ_EOF)
+	    break;
+	}
 
     }
-  while (!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp));
+  while (!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp) && !RTMP_IsTimedout(rtmp));
   free(buffer);
   if (nRead < 0)
     nRead = rtmp->m_read.status;
@@ -635,6 +642,8 @@ void usage(char *prog)
 	    ("\n%s: This program dumps the media content streamed over RTMP.\n\n", prog);
 	  RTMP_LogPrintf("--help|-h               Prints this help screen.\n");
 	  RTMP_LogPrintf
+	    ("--url|-i url            URL with options included (e.g. rtmp://host[:port]/path swfUrl=url tcUrl=url)\n");
+	  RTMP_LogPrintf
 	    ("--rtmp|-r url           URL (e.g. rtmp://host[:port]/path)\n");
 	  RTMP_LogPrintf
 	    ("--host|-n hostname      Overrides the hostname in the rtmp url\n");
@@ -679,11 +688,13 @@ void usage(char *prog)
 	  RTMP_LogPrintf
 	    ("--subscribe|-d string   Stream name to subscribe to (otherwise defaults to playpath if live is specifed)\n");
 	  RTMP_LogPrintf
+	    ("--realtime|-R           Don't attempt to speed up download via the Pause/Unpause BUFX hack\n");
+	  RTMP_LogPrintf
 	    ("--flv|-o string         FLV output file name, if the file name is - print stream to stdout\n");
 	  RTMP_LogPrintf
 	    ("--resume|-e             Resume a partial RTMP download\n");
 	  RTMP_LogPrintf
-	    ("--timeout|-m num        Timeout connection num seconds (default: %lu)\n",
+	    ("--timeout|-m num        Timeout connection num seconds (default: %u)\n",
 	     DEF_TIMEOUT);
 	  RTMP_LogPrintf
 	    ("--start|-A num          Start at num seconds into stream (not valid when using --live)\n");
@@ -692,9 +703,11 @@ void usage(char *prog)
 	  RTMP_LogPrintf
 	    ("--token|-T key          Key for SecureToken response\n");
 	  RTMP_LogPrintf
+	    ("--jtv|-j JSON           Authentication token for Justin.tv legacy servers\n");
+	  RTMP_LogPrintf
 	    ("--hashes|-#             Display progress with hashes, not with the byte counter\n");
 	  RTMP_LogPrintf
-	    ("--buffer|-b             Buffer time in milliseconds (default: %lu)\n",
+	    ("--buffer|-b             Buffer time in milliseconds (default: %u)\n",
 	     DEF_BUFTIME);
 	  RTMP_LogPrintf
 	    ("--skip|-k num           Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: %d)\n\n",
@@ -738,10 +751,12 @@ main(int argc, char **argv)
   AVal hostname = { 0, 0 };
   AVal playpath = { 0, 0 };
   AVal subscribepath = { 0, 0 };
+  AVal usherToken = { 0, 0 }; //Justin.tv auth token
   int port = -1;
   int protocol = RTMP_PROTOCOL_UNDEFINED;
   int retries = 0;
   int bLiveStream = FALSE;	// is it a live stream? then we can't seek/resume
+  int bRealtimeStream = FALSE;  // If true, disable the BUFX hack (be patient)
   int bHashes = FALSE;		// display byte counters not hashes by default
 
   long int timeout = DEF_TIMEOUT;	// timeout connection after 120 seconds
@@ -749,6 +764,7 @@ main(int argc, char **argv)
   uint32_t dStopOffset = 0;
   RTMP rtmp = { 0 };
 
+  AVal fullUrl = { 0, 0 };
   AVal swfUrl = { 0, 0 };
   AVal tcUrl = { 0, 0 };
   AVal pageUrl = { 0, 0 };
@@ -811,6 +827,7 @@ main(int argc, char **argv)
     {"protocol", 1, NULL, 'l'},
     {"playpath", 1, NULL, 'y'},
     {"playlist", 0, NULL, 'Y'},
+    {"url", 1, NULL, 'i'},
     {"rtmp", 1, NULL, 'r'},
     {"swfUrl", 1, NULL, 's'},
     {"tcUrl", 1, NULL, 't'},
@@ -826,6 +843,7 @@ main(int argc, char **argv)
 #endif
     {"flashVer", 1, NULL, 'f'},
     {"live", 0, NULL, 'v'},
+    {"realtime", 0, NULL, 'R'},
     {"flv", 1, NULL, 'o'},
     {"resume", 0, NULL, 'e'},
     {"timeout", 1, NULL, 'm'},
@@ -839,12 +857,13 @@ main(int argc, char **argv)
     {"debug", 0, NULL, 'z'},
     {"quiet", 0, NULL, 'q'},
     {"verbose", 0, NULL, 'V'},
+    {"jtv", 1, NULL, 'j'},
     {0, 0, 0, 0}
   };
 
   while ((opt =
 	  getopt_long(argc, argv,
-		      "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#",
+		      "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:",
 		      longopts, NULL)) != -1)
     {
       switch (opt)
@@ -929,6 +948,9 @@ main(int argc, char **argv)
 	case 'v':
 	  bLiveStream = TRUE;	// no seeking or resuming possible!
 	  break;
+	case 'R':
+	  bRealtimeStream = TRUE; // seeking and resuming is still possible
+	  break;
 	case 'd':
 	  STR2AVAL(subscribepath, optarg);
 	  break;
@@ -984,6 +1006,9 @@ main(int argc, char **argv)
 	      }
 	    break;
 	  }
+	case 'i':
+	  STR2AVAL(fullUrl, optarg);
+          break;
 	case 's':
 	  STR2AVAL(swfUrl, optarg);
 	  break;
@@ -1051,6 +1076,9 @@ main(int argc, char **argv)
 	case 'S':
 	  STR2AVAL(sockshost, optarg);
 	  break;
+	case 'j':
+	  STR2AVAL(usherToken, optarg);
+	  break;
 	default:
 	  RTMP_LogPrintf("unknown option: %c\n", opt);
 	  usage(argv[0]);
@@ -1059,32 +1087,32 @@ main(int argc, char **argv)
 	}
     }
 
-  if (!hostname.av_len)
+  if (!hostname.av_len && !fullUrl.av_len)
     {
       RTMP_Log(RTMP_LOGERROR,
 	  "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname");
       return RD_FAILED;
     }
-  if (playpath.av_len == 0)
+  if (playpath.av_len == 0 && !fullUrl.av_len)
     {
       RTMP_Log(RTMP_LOGERROR,
 	  "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath");
       return RD_FAILED;
     }
 
-  if (protocol == RTMP_PROTOCOL_UNDEFINED)
+  if (protocol == RTMP_PROTOCOL_UNDEFINED && !fullUrl.av_len)
     {
       RTMP_Log(RTMP_LOGWARNING,
 	  "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP");
       protocol = RTMP_PROTOCOL_RTMP;
     }
-  if (port == -1)
+  if (port == -1 && !fullUrl.av_len)
     {
       RTMP_Log(RTMP_LOGWARNING,
 	  "You haven't specified a port (--port) or rtmp url (-r), using default port 1935");
       port = 0;
     }
-  if (port == 0)
+  if (port == 0 && !fullUrl.av_len)
     {
       if (protocol & RTMP_FEATURE_SSL)
 	port = 443;
@@ -1142,13 +1170,14 @@ main(int argc, char **argv)
 
   if (tcUrl.av_len == 0)
     {
-      char str[512] = { 0 };
-
-      tcUrl.av_len = snprintf(str, 511, "%s://%.*s:%d/%.*s",
+	  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;
+      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);
-      tcUrl.av_val = (char *) malloc(tcUrl.av_len + 1);
-      strcpy(tcUrl.av_val, str);
     }
 
   int first = 1;
@@ -1165,12 +1194,23 @@ main(int argc, char **argv)
 	}
     }
 
-  RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath,
-		   &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
-		   &flashVer, &subscribepath, dSeek, dStopOffset, bLiveStream, timeout);
+  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);
+    }
+  else
+    {
+      if (RTMP_SetupURL(&rtmp, fullUrl.av_val) == FALSE)
+        {
+          RTMP_Log(RTMP_LOGERROR, "Couldn't parse URL: %s", fullUrl.av_val);
+          return RD_FAILED;
+	}
+    }
 
   /* Try to keep the stream moving if it pauses on us */
-  if (!bLiveStream && !(protocol & RTMP_FEATURE_HTTP))
+  if (!bLiveStream && !bRealtimeStream && !(protocol & RTMP_FEATURE_HTTP))
     rtmp.Link.lFlags |= RTMP_LF_BUFX;
 
   off_t size = 0;
@@ -1244,7 +1284,7 @@ main(int argc, char **argv)
 
 	  if (!RTMP_Connect(&rtmp, NULL))
 	    {
-	      nStatus = RD_FAILED;
+	      nStatus = RD_NO_CONNECT;
 	      break;
 	    }
 
@@ -1337,8 +1377,8 @@ main(int argc, char **argv)
 
       nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume,
 			 metaHeader, nMetaHeaderSize, initialFrame,
-			 initialFrameType, nInitialFrameSize,
-			 nSkipKeyFrames, bStdoutMode, bLiveStream, bHashes,
+			 initialFrameType, nInitialFrameSize, nSkipKeyFrames,
+			 bStdoutMode, bLiveStream, bRealtimeStream, bHashes,
 			 bOverrideBufferTime, bufferTime, &percent);
       free(initialFrame);
       initialFrame = NULL;
diff '--color=auto' -Naurp rtmpdump-2.3/rtmpgw.8 rtmpdump/rtmpgw.8
--- rtmpdump-2.3/rtmpgw.8	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/rtmpgw.8	2026-06-19 14:15:18.304594991 -0600
@@ -1,5 +1,5 @@
-.TH RTMPGW 8 "2010-05-02" "RTMPDump v2.2e"
-.\" Copyright 2010 Howard Chu.
+.TH RTMPGW 8 "2011-07-20" "RTMPDump v2.4"
+.\" Copyright 2011 Howard Chu.
 .\" Copying permitted according to the GNU General Public License V2.
 .SH NAME
 rtmpgw \- RTMP streaming media gateway
@@ -50,6 +50,8 @@ rtmpgw \- RTMP streaming media gateway
 [\c
 .BI \-T \ key\fR]
 [\c
+.BI \-j \ JSON\fR]
+[\c
 .BI \-w \ swfHash\fR]
 [\c
 .BI \-x \ swfSize\fR]
@@ -193,6 +195,9 @@ These options handle additional authenti
 Key for SecureToken response, used if the server requires SecureToken
 authentication.
 .TP
+\fB\-\-jtv		\-j\fP\ \fIJSON\fP
+JSON token used by legacy Justin.tv servers. Invokes NetStream.Authenticate.UsherToken
+.TP
 \fB\-\-swfhash		\-w\fP\ \fIhexstring\fP
 SHA256 hash of the decompressed SWF file. This option may be needed if
 the server uses SWF Verification, but see the
diff '--color=auto' -Naurp rtmpdump-2.3/rtmpgw.8.html rtmpdump/rtmpgw.8.html
--- rtmpdump-2.3/rtmpgw.8.html	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/rtmpgw.8.html	2026-06-19 14:15:18.304594991 -0600
@@ -6,10 +6,10 @@
 <tr><td>RTMPGW(8)<td align="center"><td align="right">RTMPGW(8)
 </thead>
 <tfoot>
-<tr><td>RTMPDump v2.2e<td align="center">2010-05-02<td align="right">RTMPGW(8)
+<tr><td>RTMPDump v2.4<td align="center">2011-07-20<td align="right">RTMPGW(8)
 </tfoot>
 <tbody><tr><td colspan="3"><br><br><ul>
-<!-- Copyright 2010 Howard Chu.
+<!-- Copyright 2011 Howard Chu.
  Copying permitted according to the GNU General Public License V2.-->
 </ul>
 
@@ -41,6 +41,7 @@ rtmpgw &minus; RTMP streaming media gate
 [<b>&minus;b</b><i>&nbsp;buffer</i>]
 [<b>&minus;m</b><i>&nbsp;timeout</i>]
 [<b>&minus;T</b><i>&nbsp;key</i>]
+[<b>&minus;j</b><i>&nbsp;JSON</i>]
 [<b>&minus;w</b><i>&nbsp;swfHash</i>]
 [<b>&minus;x</b><i>&nbsp;swfSize</i>]
 [<b>&minus;W</b><i>&nbsp;swfUrl</i>]
@@ -249,6 +250,12 @@ authentication.
 </dl>
 <p>
 <dl compact><dt>
+<b>&minus;&minus;jtv		&minus;j</b>&nbsp;<i>JSON</i>
+<dd>
+JSON token used by legacy Justin.tv servers. Invokes NetStream.Authenticate.UsherToken
+</dl>
+<p>
+<dl compact><dt>
 <b>&minus;&minus;swfhash		&minus;w</b>&nbsp;<i>hexstring</i>
 <dd>
 SHA256 hash of the decompressed SWF file. This option may be needed if
diff '--color=auto' -Naurp rtmpdump-2.3/rtmpgw.c rtmpdump/rtmpgw.c
--- rtmpdump-2.3/rtmpgw.c	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/rtmpgw.c	2026-06-19 14:15:18.304594991 -0600
@@ -85,6 +85,7 @@ typedef struct
   uint32_t bufferTime;
 
   char *rtmpurl;
+  AVal fullUrl;
   AVal playpath;
   AVal swfUrl;
   AVal tcUrl;
@@ -95,6 +96,7 @@ typedef struct
   AVal flashVer;
   AVal token;
   AVal subscribepath;
+  AVal usherToken; //Justin.tv auth token
   AVal sockshost;
   AMFObject extras;
   int edepth;
@@ -468,14 +470,14 @@ void processTCPrequest(STREAMING_SERVER
     }
 
   // do necessary checks right here to make sure the combined request of default values and GET parameters is correct
-  if (!req.hostname.av_len)
+  if (!req.hostname.av_len && !req.fullUrl.av_len)
     {
       RTMP_Log(RTMP_LOGERROR,
 	  "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname");
       status = "400 Missing Hostname";
       goto filenotfound;
     }
-  if (req.playpath.av_len == 0)
+  if (req.playpath.av_len == 0 && !req.fullUrl.av_len)
     {
       RTMP_Log(RTMP_LOGERROR,
 	  "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath");
@@ -483,19 +485,19 @@ void processTCPrequest(STREAMING_SERVER
       goto filenotfound;;
     }
 
-  if (req.protocol == RTMP_PROTOCOL_UNDEFINED)
+  if (req.protocol == RTMP_PROTOCOL_UNDEFINED && !req.fullUrl.av_len)
     {
       RTMP_Log(RTMP_LOGWARNING,
 	  "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP");
       req.protocol = RTMP_PROTOCOL_RTMP;
     }
-  if (req.rtmpport == -1)
+  if (req.rtmpport == -1 && !req.fullUrl.av_len)
     {
       RTMP_Log(RTMP_LOGWARNING,
 	  "You haven't specified a port (--port) or rtmp url (-r), using default port");
       req.rtmpport = 0;
     }
-  if (req.rtmpport == 0)
+  if (req.rtmpport == 0 && !req.fullUrl.av_len)
     {
       if (req.protocol & RTMP_FEATURE_SSL)
 	req.rtmpport = 443;
@@ -551,9 +553,20 @@ void processTCPrequest(STREAMING_SERVER
   RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", req.bufferTime);
   RTMP_Init(&rtmp);
   RTMP_SetBufferMS(&rtmp, req.bufferTime);
-  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, dSeek, req.dStopOffset,
-		   req.bLiveStream, req.timeout);
+  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);
+    }
+  else
+    {
+      if (RTMP_SetupURL(&rtmp, req.fullUrl.av_val) == FALSE)
+        {
+          RTMP_Log(RTMP_LOGERROR, "Couldn't parse URL: %s", req.fullUrl.av_val);
+          return;
+        }
+    }
   /* backward compatibility, we always sent this as true before */
   if (req.auth.av_len)
     rtmp.Link.lFlags |= RTMP_LF_AUTH;
@@ -562,7 +575,7 @@ void processTCPrequest(STREAMING_SERVER
   rtmp.Link.token = req.token;
   rtmp.m_read.timestamp = dSeek;
 
-  RTMP_LogPrintf("Connecting ... port: %d, app: %s\n", req.rtmpport, req.app);
+  RTMP_LogPrintf("Connecting ... port: %d, app: %s\n", req.rtmpport, req.app.av_val);
   if (!RTMP_Connect(&rtmp, NULL))
     {
       RTMP_LogPrintf("%s, failed to connect!\n", __FUNCTION__);
@@ -737,7 +750,7 @@ stopStreaming(STREAMING_SERVER * server)
 
       if (closesocket(server->socket))
 	RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d",
-	    GetSockError());
+	    __FUNCTION__, GetSockError());
 
       server->state = STREAMING_STOPPED;
     }
@@ -907,6 +920,9 @@ ParseOption(char opt, char *arg, RTMP_RE
 	  }
 	break;
       }
+    case 'i':
+      STR2AVAL(req->fullUrl, arg);
+      break;
     case 's':
       STR2AVAL(req->swfUrl, arg);
       break;
@@ -926,7 +942,7 @@ ParseOption(char opt, char *arg, RTMP_RE
       STR2AVAL(req->auth, arg);
       break;
     case 'C':
-      parseAMF(&req->extras, optarg, &req->edepth);
+      parseAMF(&req->extras, arg, &req->edepth);
       break;
     case 'm':
       req->timeout = atoi(arg);
@@ -953,6 +969,9 @@ ParseOption(char opt, char *arg, RTMP_RE
     case 'z':
       RTMP_debuglevel = RTMP_LOGALL;
       break;
+    case 'j':
+      STR2AVAL(req->usherToken, arg);
+      break;
     default:
       RTMP_LogPrintf("unknown option: %c, arg: %s\n", opt, arg);
       return FALSE;
@@ -989,6 +1008,7 @@ main(int argc, char **argv)
   int opt;
   struct option longopts[] = {
     {"help", 0, NULL, 'h'},
+    {"url", 1, NULL, 'i'},
     {"host", 1, NULL, 'n'},
     {"port", 1, NULL, 'c'},
     {"socks", 1, NULL, 'S'},
@@ -1023,6 +1043,7 @@ main(int argc, char **argv)
     {"debug", 0, NULL, 'z'},
     {"quiet", 0, NULL, 'q'},
     {"verbose", 0, NULL, 'V'},
+    {"jtv", 1, NULL, 'j'},
     {0, 0, 0, 0}
   };
 
@@ -1035,7 +1056,7 @@ main(int argc, char **argv)
 
   while ((opt =
 	  getopt_long(argc, argv,
-		      "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:", 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:", longopts,
 		      NULL)) != -1)
     {
       switch (opt)
@@ -1045,6 +1066,8 @@ main(int argc, char **argv)
 	    ("\nThis program serves media content streamed from RTMP onto HTTP.\n\n");
 	  RTMP_LogPrintf("--help|-h               Prints this help screen.\n");
 	  RTMP_LogPrintf
+	    ("--url|-i url            URL with options included (e.g. rtmp://host[:port]/path swfUrl=url tcUrl=url)\n");
+	  RTMP_LogPrintf
 	    ("--rtmp|-r url           URL (e.g. rtmp://host[:port]/path)\n");
 	  RTMP_LogPrintf
 	    ("--host|-n hostname      Overrides the hostname in the rtmp url\n");
@@ -1096,7 +1119,9 @@ main(int argc, char **argv)
 	  RTMP_LogPrintf
 	    ("--token|-T key          Key for SecureToken response\n");
 	  RTMP_LogPrintf
-	    ("--buffer|-b             Buffer time in milliseconds (default: %lu)\n\n",
+	    ("--jtv|-j JSON           Authentication token for Justin.tv legacy servers\n");
+	  RTMP_LogPrintf
+	    ("--buffer|-b             Buffer time in milliseconds (default: %u)\n\n",
 	     defaultRTMPRequest.bufferTime);
 
 	  RTMP_LogPrintf
diff '--color=auto' -Naurp rtmpdump-2.3/rtmpsrv.c rtmpdump/rtmpsrv.c
--- rtmpdump-2.3/rtmpsrv.c	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/rtmpsrv.c	2026-06-19 14:15:18.304594991 -0600
@@ -1,6 +1,6 @@
 /*  Simple RTMP Server
  *  Copyright (C) 2009 Andrej Stepanchuk
- *  Copyright (C) 2009 Howard Chu
+ *  Copyright (C) 2009-2011 Howard Chu
  *
  *  This Program is free software; you can redistribute it and/or modify
  *  it under the terms of the GNU General Public License as published by
@@ -92,9 +92,14 @@ typedef struct
 } STREAMING_SERVER;
 
 STREAMING_SERVER *rtmpServer = 0;	// server structure pointer
+void *sslCtx = NULL;
 
 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("\\\"");
 
 typedef struct
 {
@@ -175,7 +180,7 @@ SendConnectResult(RTMP *r, double txn)
 
   packet.m_nChannel = 0x03;     // control channel (invoke)
   packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
-  packet.m_packetType = 0x14;   // INVOKE
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -219,9 +224,6 @@ SendConnectResult(RTMP *r, double txn)
   *enc++ = 0;
   *enc++ = 0;
   *enc++ = AMF_OBJECT_END;
-  *enc++ = 0;
-  *enc++ = 0;
-  *enc++ = AMF_OBJECT_END;
 
   packet.m_nBodySize = enc - packet.m_body;
 
@@ -236,7 +238,7 @@ SendResultNumber(RTMP *r, double txn, do
 
   packet.m_nChannel = 0x03;     // control channel (invoke)
   packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
-  packet.m_packetType = 0x14;   // INVOKE
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -261,16 +263,17 @@ static const AVal av_NetStream_Play_Stop
 static const AVal av_Stopped_playing = AVC("Stopped playing");
 SAVC(details);
 SAVC(clientid);
+static const AVal av_NetStream_Authenticate_UsherToken = AVC("NetStream.Authenticate.UsherToken");
 
 static int
 SendPlayStart(RTMP *r)
 {
   RTMPPacket packet;
-  char pbuf[384], *pend = pbuf+sizeof(pbuf);
+  char pbuf[512], *pend = pbuf+sizeof(pbuf);
 
   packet.m_nChannel = 0x03;     // control channel (invoke)
   packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
-  packet.m_packetType = 0x14;   // INVOKE
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -298,11 +301,11 @@ static int
 SendPlayStop(RTMP *r)
 {
   RTMPPacket packet;
-  char pbuf[384], *pend = pbuf+sizeof(pbuf);
+  char pbuf[512], *pend = pbuf+sizeof(pbuf);
 
   packet.m_nChannel = 0x03;     // control channel (invoke)
   packet.m_headerType = 1; /* RTMP_PACKET_SIZE_MEDIUM; */
-  packet.m_packetType = 0x14;   // INVOKE
+  packet.m_packetType = RTMP_PACKET_TYPE_INVOKE;
   packet.m_nTimeStamp = 0;
   packet.m_nInfoField2 = 0;
   packet.m_hasAbsTimestamp = 0;
@@ -401,10 +404,10 @@ countAMF(AMFObject *obj, int *argc)
 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];
       argv[ac].av_val = ptr+1;
@@ -575,6 +578,15 @@ ServeInvoke(STREAMING_SERVER *server, RT
     {
       SendResultNumber(r, txn, 10.0);
     }
+  else if (AVMATCH(&method, &av_NetStream_Authenticate_UsherToken))
+    {
+      AVal usherToken;
+      AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken);
+      AVreplace(&usherToken, &av_dquote, &av_escdquote);
+      server->arglen += 6 + usherToken.av_len;
+      server->argc += 2;
+      r->Link.usherToken = usherToken;
+    }
   else if (AVMATCH(&method, &av_play))
     {
       char *file, *p, *q, *cmd, *ptr;
@@ -583,6 +595,8 @@ ServeInvoke(STREAMING_SERVER *server, RT
       uint32_t now;
       RTMPPacket pc = {0};
       AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &r->Link.playpath);
+      if (!r->Link.playpath.av_len)
+        return 0;
       /*
       r->Link.seekTime = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 4));
       if (obj.o_num > 5)
@@ -640,6 +654,17 @@ ServeInvoke(STREAMING_SERVER *server, RT
 	      ptr += sprintf(ptr, " -p \"%s\"", r->Link.pageUrl.av_val);
 	      argv[argc++].av_len = r->Link.pageUrl.av_len;
 	    }
+	  if (r->Link.usherToken.av_val)
+	    {
+	      argv[argc].av_val = ptr + 1;
+	      argv[argc++].av_len = 2;
+	      argv[argc].av_val = ptr + 5;
+	      ptr += sprintf(ptr, " -j \"%s\"", r->Link.usherToken.av_val);
+	      argv[argc++].av_len = r->Link.usherToken.av_len;
+	      free(r->Link.usherToken.av_val);
+	      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);
@@ -740,54 +765,47 @@ ServePacket(STREAMING_SERVER *server, RT
 {
   int ret = 0;
 
-  RTMP_Log(RTMP_LOGDEBUG, "%s, received packet type %02X, size %lu bytes", __FUNCTION__,
+  RTMP_Log(RTMP_LOGDEBUG, "%s, received packet type %02X, size %u bytes", __FUNCTION__,
     packet->m_packetType, packet->m_nBodySize);
 
   switch (packet->m_packetType)
     {
-    case 0x01:
-      // chunk size
+    case RTMP_PACKET_TYPE_CHUNK_SIZE:
 //      HandleChangeChunkSize(r, packet);
       break;
 
-    case 0x03:
-      // bytes read report
+    case RTMP_PACKET_TYPE_BYTES_READ_REPORT:
       break;
 
-    case 0x04:
-      // ctrl
+    case RTMP_PACKET_TYPE_CONTROL:
 //      HandleCtrl(r, packet);
       break;
 
-    case 0x05:
-      // server bw
+    case RTMP_PACKET_TYPE_SERVER_BW:
 //      HandleServerBW(r, packet);
       break;
 
-    case 0x06:
-      // client bw
+    case RTMP_PACKET_TYPE_CLIENT_BW:
  //     HandleClientBW(r, packet);
       break;
 
-    case 0x08:
-      // audio data
+    case RTMP_PACKET_TYPE_AUDIO:
       //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
       break;
 
-    case 0x09:
-      // video data
+    case RTMP_PACKET_TYPE_VIDEO:
       //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
       break;
 
-    case 0x0F:			// flex stream send
+    case RTMP_PACKET_TYPE_FLEX_STREAM_SEND:
       break;
 
-    case 0x10:			// flex shared object
+    case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT:
       break;
 
-    case 0x11:			// flex message
+    case RTMP_PACKET_TYPE_FLEX_MESSAGE:
       {
-	RTMP_Log(RTMP_LOGDEBUG, "%s, flex message, size %lu bytes, not fully supported",
+	RTMP_Log(RTMP_LOGDEBUG, "%s, flex message, size %u bytes, not fully supported",
 	    __FUNCTION__, packet->m_nBodySize);
 	//RTMP_LogHex(packet.m_body, packet.m_nBodySize);
 
@@ -805,17 +823,14 @@ ServePacket(STREAMING_SERVER *server, RT
 	  RTMP_Close(r);
 	break;
       }
-    case 0x12:
-      // metadata (notify)
+    case RTMP_PACKET_TYPE_INFO:
       break;
 
-    case 0x13:
-      /* shared object */
+    case RTMP_PACKET_TYPE_SHARED_OBJECT:
       break;
 
-    case 0x14:
-      // invoke
-      RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__,
+    case RTMP_PACKET_TYPE_INVOKE:
+      RTMP_Log(RTMP_LOGDEBUG, "%s, received: invoke %u bytes", __FUNCTION__,
 	  packet->m_nBodySize);
       //RTMP_LogHex(packet.m_body, packet.m_nBodySize);
 
@@ -823,8 +838,7 @@ ServePacket(STREAMING_SERVER *server, RT
 	RTMP_Close(r);
       break;
 
-    case 0x16:
-      /* flv */
+    case RTMP_PACKET_TYPE_FLASH_VIDEO:
 	break;
     default:
       RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,
@@ -864,7 +878,7 @@ void doServe(STREAMING_SERVER * server,
 {
   server->state = STREAMING_IN_PROGRESS;
 
-  RTMP rtmp = { 0 };		/* our session with the real client */
+  RTMP *rtmp = RTMP_Alloc();		/* our session with the real client */
   RTMPPacket packet = { 0 };
 
   // timeout for http requests
@@ -884,33 +898,44 @@ void doServe(STREAMING_SERVER * server,
     }
   else
     {
-      RTMP_Init(&rtmp);
-      rtmp.m_sb.sb_socket = sockfd;
-      if (!RTMP_Serve(&rtmp))
+      RTMP_Init(rtmp);
+      rtmp->m_sb.sb_socket = sockfd;
+      if (sslCtx && !RTMP_TLS_Accept(rtmp, sslCtx))
+        {
+	  RTMP_Log(RTMP_LOGERROR, "TLS handshake failed");
+	  goto cleanup;
+        }
+      if (!RTMP_Serve(rtmp))
 	{
 	  RTMP_Log(RTMP_LOGERROR, "Handshake failed");
 	  goto cleanup;
 	}
     }
   server->arglen = 0;
-  while (RTMP_IsConnected(&rtmp) && RTMP_ReadPacket(&rtmp, &packet))
+  while (RTMP_IsConnected(rtmp) && RTMP_ReadPacket(rtmp, &packet))
     {
       if (!RTMPPacket_IsReady(&packet))
 	continue;
-      ServePacket(server, &rtmp, &packet);
+      ServePacket(server, rtmp, &packet);
       RTMPPacket_Free(&packet);
     }
 
 cleanup:
   RTMP_LogPrintf("Closing connection... ");
-  RTMP_Close(&rtmp);
+  RTMP_Close(rtmp);
   /* Should probably be done by RTMP_Close() ... */
-  rtmp.Link.playpath.av_val = NULL;
-  rtmp.Link.tcUrl.av_val = NULL;
-  rtmp.Link.swfUrl.av_val = NULL;
-  rtmp.Link.pageUrl.av_val = NULL;
-  rtmp.Link.app.av_val = NULL;
-  rtmp.Link.flashVer.av_val = NULL;
+  rtmp->Link.playpath.av_val = NULL;
+  rtmp->Link.tcUrl.av_val = NULL;
+  rtmp->Link.swfUrl.av_val = NULL;
+  rtmp->Link.pageUrl.av_val = NULL;
+  rtmp->Link.app.av_val = NULL;
+  rtmp->Link.flashVer.av_val = NULL;
+  if (rtmp->Link.usherToken.av_val)
+    {
+      free(rtmp->Link.usherToken.av_val);
+      rtmp->Link.usherToken.av_val = NULL;
+    }
+  RTMP_Free(rtmp);
   RTMP_LogPrintf("done!\n\n");
 
 quit:
@@ -1023,7 +1048,7 @@ stopStreaming(STREAMING_SERVER * server)
 
       if (closesocket(server->socket))
 	RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d",
-	    GetSockError());
+	    __FUNCTION__, GetSockError());
 
       server->state = STREAMING_STOPPED;
     }
@@ -1044,20 +1069,32 @@ int
 main(int argc, char **argv)
 {
   int nStatus = RD_SUCCESS;
+  int i;
 
   // http streaming server
   char DEFAULT_HTTP_STREAMING_DEVICE[] = "0.0.0.0";	// 0.0.0.0 is any device
 
   char *rtmpStreamingDevice = DEFAULT_HTTP_STREAMING_DEVICE;	// streaming device, default 0.0.0.0
   int nRtmpStreamingPort = 1935;	// port
+  char *cert = NULL, *key = NULL;
 
   RTMP_LogPrintf("RTMP Server %s\n", RTMPDUMP_VERSION);
   RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n");
 
   RTMP_debuglevel = RTMP_LOGINFO;
 
-  if (argc > 1 && !strcmp(argv[1], "-z"))
-    RTMP_debuglevel = RTMP_LOGALL;
+  for (i = 1; i < argc; i++)
+    {
+      if (!strcmp(argv[i], "-z"))
+        RTMP_debuglevel = RTMP_LOGALL;
+      else if (!strcmp(argv[i], "-c") && i + 1 < argc)
+        cert = argv[++i];
+      else if (!strcmp(argv[i], "-k") && i + 1 < argc)
+        key = argv[++i];
+    }
+
+  if (cert && key)
+    sslCtx = RTMP_TLS_AllocServerContext(cert, key);
 
   // init request
   memset(&defaultRTMPRequest, 0, sizeof(RTMP_REQUEST));
@@ -1101,6 +1138,9 @@ main(int argc, char **argv)
     }
   RTMP_Log(RTMP_LOGDEBUG, "Done, exiting...");
 
+  if (sslCtx)
+    RTMP_TLS_FreeServerContext(sslCtx);
+
   CleanupSockets();
 
 #ifdef _DEBUG
@@ -1111,3 +1151,43 @@ main(int argc, char **argv)
 #endif
   return nStatus;
 }
+
+void
+AVreplace(AVal *src, const AVal *orig, const AVal *repl)
+{
+  char *srcbeg = src->av_val;
+  char *srcend = src->av_val + src->av_len;
+  char *dest, *sptr, *dptr;
+  int n = 0;
+
+  /* count occurrences of orig in src */
+  sptr = src->av_val;
+  while (sptr < srcend && (sptr = strstr(sptr, orig->av_val)))
+    {
+      n++;
+      sptr += orig->av_len;
+    }
+  if (!n)
+    return;
+
+  dest = malloc(src->av_len + 1 + (repl->av_len - orig->av_len) * n);
+
+  sptr = src->av_val;
+  dptr = dest;
+  while (sptr < srcend && (sptr = strstr(sptr, orig->av_val)))
+    {
+      n = sptr - srcbeg;
+      memcpy(dptr, srcbeg, n);
+      dptr += n;
+      memcpy(dptr, repl->av_val, repl->av_len);
+      dptr += repl->av_len;
+      sptr += orig->av_len;
+      srcbeg = sptr;
+    }
+  n = srcend - srcbeg;
+  memcpy(dptr, srcbeg, n);
+  dptr += n;
+  *dptr = '\0';
+  src->av_val = dest;
+  src->av_len = dptr - dest;
+}
diff '--color=auto' -Naurp rtmpdump-2.3/rtmpsuck.c rtmpdump/rtmpsuck.c
--- rtmpdump-2.3/rtmpsuck.c	2010-06-30 13:58:35.000000000 -0600
+++ rtmpdump/rtmpsuck.c	2026-06-19 14:15:18.305595000 -0600
@@ -456,72 +456,76 @@ ServePacket(STREAMING_SERVER *server, in
 {
   int ret = 0;
 
-  RTMP_Log(RTMP_LOGDEBUG, "%s, %s sent packet type %02X, size %lu bytes", __FUNCTION__,
+  RTMP_Log(RTMP_LOGDEBUG, "%s, %s sent packet type %02X, size %u bytes", __FUNCTION__,
     cst[which], packet->m_packetType, packet->m_nBodySize);
 
   switch (packet->m_packetType)
     {
-    case 0x01:
+    case RTMP_PACKET_TYPE_CHUNK_SIZE:
       // chunk size
 //      HandleChangeChunkSize(r, packet);
       break;
 
-    case 0x03:
+    case RTMP_PACKET_TYPE_BYTES_READ_REPORT:
       // bytes read report
       break;
 
-    case 0x04:
+    case RTMP_PACKET_TYPE_CONTROL:
       // ctrl
 //      HandleCtrl(r, packet);
       break;
 
-    case 0x05:
+    case RTMP_PACKET_TYPE_SERVER_BW:
       // server bw
 //      HandleServerBW(r, packet);
       break;
 
-    case 0x06:
+    case RTMP_PACKET_TYPE_CLIENT_BW:
       // client bw
  //     HandleClientBW(r, packet);
       break;
 
-    case 0x08:
+    case RTMP_PACKET_TYPE_AUDIO:
       // audio data
       //RTMP_Log(RTMP_LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
       break;
 
-    case 0x09:
+    case RTMP_PACKET_TYPE_VIDEO:
       // video data
       //RTMP_Log(RTMP_LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
       break;
 
-    case 0x0F:			// flex stream send
+    case RTMP_PACKET_TYPE_FLEX_STREAM_SEND:
+      // flex stream send
       break;
 
-    case 0x10:			// flex shared object
+    case RTMP_PACKET_TYPE_FLEX_SHARED_OBJECT:
+      // flex shared object
       break;
 
-    case 0x11:			// flex message
+    case RTMP_PACKET_TYPE_FLEX_MESSAGE:
+      // flex message
       {
 	ret = ServeInvoke(server, which, packet, packet->m_body + 1);
 	break;
       }
-    case 0x12:
+    case RTMP_PACKET_TYPE_INFO:
       // metadata (notify)
       break;
 
-    case 0x13:
+    case RTMP_PACKET_TYPE_SHARED_OBJECT:
       /* shared object */
       break;
 
-    case 0x14:
+    case RTMP_PACKET_TYPE_INVOKE:
       // invoke
       ret = ServeInvoke(server, which, packet, packet->m_body);
       break;
 
-    case 0x16:
+    case RTMP_PACKET_TYPE_FLASH_VIDEO:
       /* flv */
 	break;
+
     default:
       RTMP_Log(RTMP_LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__,
 	  packet->m_packetType);
@@ -547,21 +551,21 @@ WriteStream(char **buf,	// target pointe
       unsigned int nPacketLen = packet->m_nBodySize;
 
       // skip video info/command packets
-      if (packet->m_packetType == 0x09 &&
+      if (packet->m_packetType == RTMP_PACKET_TYPE_VIDEO &&
 	  nPacketLen == 2 && ((*packetBody & 0xf0) == 0x50))
 	{
 	  ret = 0;
 	  break;
 	}
 
-      if (packet->m_packetType == 0x09 && nPacketLen <= 5)
+      if (packet->m_packetType == RTMP_PACKET_TYPE_VIDEO && nPacketLen <= 5)
 	{
 	  RTMP_Log(RTMP_LOGWARNING, "ignoring too small video packet: size: %d",
 	      nPacketLen);
 	  ret = 0;
 	  break;
 	}
-      if (packet->m_packetType == 0x08 && nPacketLen <= 1)
+      if (packet->m_packetType == RTMP_PACKET_TYPE_AUDIO && nPacketLen <= 1)
 	{
 	  RTMP_Log(RTMP_LOGWARNING, "ignoring too small audio packet: size: %d",
 	      nPacketLen);
@@ -571,19 +575,22 @@ WriteStream(char **buf,	// target pointe
 #ifdef _DEBUG
       RTMP_Log(RTMP_LOGDEBUG, "type: %02X, size: %d, TS: %d ms", packet->m_packetType,
 	  nPacketLen, packet->m_nTimeStamp);
-      if (packet->m_packetType == 0x09)
+      if (packet->m_packetType == RTMP_PACKET_TYPE_VIDEO)
 	RTMP_Log(RTMP_LOGDEBUG, "frametype: %02X", (*packetBody & 0xf0));
 #endif
 
       // calculate packet size and reallocate buffer if necessary
       unsigned int size = nPacketLen
 	+
-	((packet->m_packetType == 0x08 || packet->m_packetType == 0x09
-	  || packet->m_packetType == 0x12) ? 11 : 0) + (packet->m_packetType !=
-						       0x16 ? 4 : 0);
+	((packet->m_packetType == RTMP_PACKET_TYPE_AUDIO
+          || packet->m_packetType == RTMP_PACKET_TYPE_VIDEO
+	  || packet->m_packetType == RTMP_PACKET_TYPE_INFO) ? 11 : 0)
+        + (packet->m_packetType != 0x16 ? 4 : 0);
 
       if (size + 4 > len)
-	{			// the extra 4 is for the case of an FLV stream without a last prevTagSize (we need extra 4 bytes to append it)
+	{
+          /* The extra 4 is for the case of an FLV stream without a last
+           * prevTagSize (we need extra 4 bytes to append it).  */
 	  *buf = (char *) realloc(*buf, size + 4);
 	  if (*buf == 0)
 	    {
@@ -594,13 +601,15 @@ WriteStream(char **buf,	// target pointe
 	}
       char *ptr = *buf, *pend = ptr + size+4;
 
-      // audio (0x08), video (0x09) or metadata (0x12) packets :
-      // construct 11 byte header then add rtmp packet's data
-      if (packet->m_packetType == 0x08 || packet->m_packetType == 0x09
-	  || packet->m_packetType == 0x12)
+      /* audio (RTMP_PACKET_TYPE_AUDIO), video (RTMP_PACKET_TYPE_VIDEO)
+       * or metadata (RTMP_PACKET_TYPE_INFO) packets: construct 11 byte
+       * header then add rtmp packet's data.  */
+      if (packet->m_packetType == RTMP_PACKET_TYPE_AUDIO
+          || packet->m_packetType == RTMP_PACKET_TYPE_VIDEO
+	  || packet->m_packetType == RTMP_PACKET_TYPE_INFO)
 	{
 	  // set data type
-	  //*dataType |= (((packet->m_packetType == 0x08)<<2)|(packet->m_packetType == 0x09));
+	  //*dataType |= (((packet->m_packetType == RTMP_PACKET_TYPE_AUDIO)<<2)|(packet->m_packetType == RTMP_PACKET_TYPE_VIDEO));
 
 	  (*nTimeStamp) = packet->m_nTimeStamp;
 	  prevTagSize = 11 + nPacketLen;
@@ -619,7 +628,7 @@ WriteStream(char **buf,	// target pointe
       unsigned int len = nPacketLen;
 
       // correct tagSize and obtain timestamp if we have an FLV stream
-      if (packet->m_packetType == 0x16)
+      if (packet->m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO)
 	{
 	  unsigned int pos = 0;
 
@@ -629,15 +638,18 @@ WriteStream(char **buf,	// target pointe
 	      *nTimeStamp = AMF_DecodeInt24(packetBody + pos + 4);
 	      *nTimeStamp |= (packetBody[pos + 7] << 24);
 
-	      // set data type
-	      //*dataType |= (((*(packetBody+pos) == 0x08)<<2)|(*(packetBody+pos) == 0x09));
+#if 0
+	      /* set data type */
+	      *dataType |= (((*(packetBody+pos) == RTMP_PACKET_TYPE_AUDIO) << 2)
+                            | (*(packetBody+pos) == RTMP_PACKET_TYPE_VIDEO));
+#endif
 
 	      if (pos + 11 + dataSize + 4 > nPacketLen)
 		{
 		  if (pos + 11 + dataSize > nPacketLen)
 		    {
 		      RTMP_Log(RTMP_LOGERROR,
-			  "Wrong data size (%lu), stream corrupted, aborting!",
+			  "Wrong data size (%u), stream corrupted, aborting!",
 			  dataSize);
 		      ret = -2;
 		      break;
@@ -680,7 +692,7 @@ WriteStream(char **buf,	// target pointe
 	}
       ptr += len;
 
-      if (packet->m_packetType != 0x16)
+      if (packet->m_packetType != RTMP_PACKET_TYPE_FLASH_VIDEO)
 	{			// FLV tag packets contain their own prevTagSize
 	  AMF_EncodeInt32(ptr, pend, prevTagSize);
 	  //ptr += 4;
@@ -718,15 +730,15 @@ controlServerThread(void *unused)
   TFRET();
 }
 
-void doServe(STREAMING_SERVER * server,	// server socket and state (our listening socket)
-  int sockfd	// client connection socket
-  )
+TFTYPE doServe(void *arg)	// server socket and state (our listening socket)
 {
+  STREAMING_SERVER *server = arg;
   RTMPPacket pc = { 0 }, ps = { 0 };
   RTMPChunk rk = { 0 };
   char *buf = NULL;
   unsigned int buflen = 131072;
   int paused = FALSE;
+  int sockfd = server->socket;
 
   // timeout for http requests
   fd_set rfds;
@@ -828,7 +840,7 @@ void doServe(STREAMING_SERVER * server,
             if (RTMPPacket_IsReady(&ps))
               {
                 /* change chunk size */
-                if (ps.m_packetType == 0x01)
+                if (ps.m_packetType == RTMP_PACKET_TYPE_CHUNK_SIZE)
                   {
                     if (ps.m_nBodySize >= 4)
                       {
@@ -839,7 +851,7 @@ void doServe(STREAMING_SERVER * server,
                       }
                   }
                 /* bytes received */
-                else if (ps.m_packetType == 0x03)
+                else if (ps.m_packetType == RTMP_PACKET_TYPE_BYTES_READ_REPORT)
                   {
                     if (ps.m_nBodySize >= 4)
                       {
@@ -849,7 +861,7 @@ void doServe(STREAMING_SERVER * server,
                       }
                   }
                 /* ctrl */
-                else if (ps.m_packetType == 0x04)
+                else if (ps.m_packetType == RTMP_PACKET_TYPE_CONTROL)
                   {
                     short nType = AMF_DecodeInt16(ps.m_body);
                     /* UpdateBufferMS */
@@ -875,13 +887,16 @@ void doServe(STREAMING_SERVER * server,
                           }
                       }
                   }
-                else if (ps.m_packetType == 0x11 || ps.m_packetType == 0x14)
-                  if (ServePacket(server, 0, &ps) && server->f_cur)
-                    {
-                      fclose(server->f_cur->f_file);
-                      server->f_cur->f_file = NULL;
-                      server->f_cur = NULL;
-                    }
+                else if (ps.m_packetType == RTMP_PACKET_TYPE_FLEX_MESSAGE
+                         || ps.m_packetType == RTMP_PACKET_TYPE_INVOKE)
+                  {
+                    if (ServePacket(server, 0, &ps) && server->f_cur)
+                      {
+                        fclose(server->f_cur->f_file);
+                        server->f_cur->f_file = NULL;
+                        server->f_cur = NULL;
+                      }
+                  }
                 RTMP_SendPacket(&server->rc, &ps, FALSE);
                 RTMPPacket_Free(&ps);
                 break;
@@ -902,7 +917,7 @@ void doServe(STREAMING_SERVER * server,
                       server->rc.m_pausing = 0;
                     }
                   /* change chunk size */
-                  if (pc.m_packetType == 0x01)
+                  if (pc.m_packetType == RTMP_PACKET_TYPE_CHUNK_SIZE)
                     {
                       if (pc.m_nBodySize >= 4)
                         {
@@ -912,7 +927,7 @@ void doServe(STREAMING_SERVER * server,
                           server->rs.m_outChunkSize = server->rc.m_inChunkSize;
                         }
                     }
-                  else if (pc.m_packetType == 0x04)
+                  else if (pc.m_packetType == RTMP_PACKET_TYPE_CONTROL)
                     {
                       short nType = AMF_DecodeInt16(pc.m_body);
                       /* SWFverification */
@@ -929,17 +944,18 @@ void doServe(STREAMING_SERVER * server,
 #endif
                     }
                   else if (server->f_cur && (
-                       pc.m_packetType == 0x08 ||
-                       pc.m_packetType == 0x09 ||
-                       pc.m_packetType == 0x12 ||
-                       pc.m_packetType == 0x16) &&
+                       pc.m_packetType == RTMP_PACKET_TYPE_AUDIO ||
+                       pc.m_packetType == RTMP_PACKET_TYPE_VIDEO ||
+                       pc.m_packetType == RTMP_PACKET_TYPE_INFO ||
+                       pc.m_packetType == RTMP_PACKET_TYPE_FLASH_VIDEO) &&
                        RTMP_ClientPacket(&server->rc, &pc))
                     {
                       int len = WriteStream(&buf, &buflen, &server->stamp, &pc);
                       if (len > 0 && fwrite(buf, 1, len, server->f_cur->f_file) != len)
                         goto cleanup;
                     }
-                  else if ( pc.m_packetType == 0x11 || pc.m_packetType == 0x14)
+                  else if (pc.m_packetType == RTMP_PACKET_TYPE_FLEX_MESSAGE ||
+                           pc.m_packetType == RTMP_PACKET_TYPE_INVOKE)
                     {
                       if (ServePacket(server, 1, &pc) && server->f_cur)
                         {
@@ -990,7 +1006,7 @@ quit:
   if (server->state == STREAMING_IN_PROGRESS)
     server->state = STREAMING_ACCEPTING;
 
-  return;
+  TFRET();
 }
 
 TFTYPE
@@ -1003,6 +1019,7 @@ serverThread(void *arg)
     {
       struct sockaddr_in addr;
       socklen_t addrlen = sizeof(struct sockaddr_in);
+      STREAMING_SERVER *srv2 = malloc(sizeof(STREAMING_SERVER));
       int sockfd =
 	accept(server->socket, (struct sockaddr *) &addr, &addrlen);
 
@@ -1020,8 +1037,10 @@ serverThread(void *arg)
 	  RTMP_Log(RTMP_LOGDEBUG, "%s: accepted connection from %s\n", __FUNCTION__,
 	      inet_ntoa(addr.sin_addr));
 #endif
+	  *srv2 = *server;
+	  srv2->socket = sockfd;
 	  /* Create a new thread and transfer the control to that */
-	  doServe(server, sockfd);
+	  ThreadCreate(doServe, srv2);
 	  RTMP_Log(RTMP_LOGDEBUG, "%s: processed request\n", __FUNCTION__);
 	}
       else
@@ -1098,7 +1117,7 @@ stopStreaming(STREAMING_SERVER * server)
 
       if (fd && closesocket(fd))
 	RTMP_Log(RTMP_LOGERROR, "%s: Failed to close listening socket, error %d",
-	    GetSockError());
+	    __FUNCTION__, GetSockError());
 
       server->state = STREAMING_STOPPED;
     }
