[rtmpdump] r9 - AMFObject.h Makefile MakefileARM bytes.c bytes.cpp bytes.h dh.c dh.cpp dh.h handshake.cpp log.c log.cpp log.h parseurl.c parseurl.cpp parseurl.h rtmp.cpp rtmp.h rtmpdump.cpp rtmppacket.cpp rtm...

hyc subversion at mplayerhq.hu
Fri Oct 30 02:26:20 CET 2009


Author: hyc
Date: Fri Oct 30 02:26:19 2009
New Revision: 9

Log:
Portability fixes: change some sources to plain C, fix endian issues,
fix m_nChannels bug.

Added:
   bytes.c
      - copied, changed from r8, bytes.cpp
   dh.c
      - copied, changed from r8, dh.cpp
   log.c
      - copied, changed from r8, log.cpp
   parseurl.c
      - copied, changed from r8, parseurl.cpp
Deleted:
   bytes.cpp
   dh.cpp
   log.cpp
   parseurl.cpp
Modified:
   AMFObject.h
   Makefile
   MakefileARM
   bytes.h
   dh.h
   handshake.cpp
   log.h
   parseurl.h
   rtmp.cpp
   rtmp.h
   rtmpdump.cpp
   rtmppacket.cpp
   rtmppacket.h
   streams.cpp

Modified: AMFObject.h
==============================================================================
--- AMFObject.h	Sun Oct 11 06:33:52 2009	(r8)
+++ AMFObject.h	Fri Oct 30 02:26:19 2009	(r9)
@@ -24,6 +24,7 @@
 
 #include <string>
 #include <vector>
+#include <stdint.h>
 
 namespace RTMP_LIB
 {

Modified: Makefile
==============================================================================
--- Makefile	Sun Oct 11 06:33:52 2009	(r8)
+++ Makefile	Fri Oct 30 02:26:19 2009	(r9)
@@ -2,8 +2,9 @@ CC=gcc
 CXX=g++
 LD=ld
 
-CFLAGS=-Wall
-CXXFLAGS=-Wall
+OPT=-O2
+CFLAGS=-Wall $(OPT)
+CXXFLAGS=-Wall $(OPT)
 LDFLAGS=-Wall
 
 all: rtmpdump
@@ -17,14 +18,14 @@ streams: bytes.o log.o rtmp.o AMFObject.
 rtmpdump: bytes.o log.o rtmp.o AMFObject.o rtmppacket.o rtmpdump.o parseurl.o dh.o handshake.o
 	$(CXX) $(LDFLAGS) $^ -o $@_x86 -lssl -lcrypto
 
-bytes.o: bytes.cpp bytes.h Makefile
-log.o: log.cpp log.h Makefile
+bytes.o: bytes.c bytes.h Makefile
+log.o: log.c log.h Makefile
 rtmp.o: rtmp.cpp rtmp.h log.h AMFObject.h Makefile
 AMFObject.o: AMFObject.cpp AMFObject.h log.h rtmp.h Makefile
 rtmppacket.o: rtmppacket.cpp rtmppacket.h log.h Makefile
 rtmpdump.o: rtmpdump.cpp rtmp.h log.h AMFObject.h Makefile
-parseurl.o: parseurl.cpp parseurl.h log.h Makefile
+parseurl.o: parseurl.c parseurl.h log.h Makefile
 streams.o: streams.cpp log.h Makefile
-dh.o: dh.cpp dh.h log.h Makefile
+dh.o: dh.c dh.h log.h Makefile
 handshake.o: handshake.cpp log.h Makefile
 

Modified: MakefileARM
==============================================================================
--- MakefileARM	Sun Oct 11 06:33:52 2009	(r8)
+++ MakefileARM	Fri Oct 30 02:26:19 2009	(r9)
@@ -1,13 +1,13 @@
-CC=arm-linux-gcc
-CXX=arm-linux-g++
-LD=arm-linux-ld
-
-CFLAGS=-Wall
-CXXFLAGS=-Wall
-LDFLAGS=-Wall
+CROSS_COMPILE=arm-angstrom-linux-gnueabi-
+CC=$(CROSS_COMPILE)gcc -mcpu=cortex-a8
+CXX=$(CROSS_COMPILE)g++ -mcpu=cortex-a8
+LD=$(CROSS_COMPILE)ld
 
-CXXFLAGS=-I openssl-0.9.8k/include/
-LDFLAGS=-L openssl-0.9.8k/armlibs/lib/
+OPT=-O2
+STAGING=/home/hyc/oe/tmp/staging/armv7a-angstrom-linux-gnueabi
+CFLAGS=-Wall -I$(STAGING)/usr/include $(OPT)
+CXXFLAGS=-Wall -I$(STAGING)/usr/include $(OPT)
+LDFLAGS=-Wall -L$(STAGING)/usr/lib
 
 all: rtmpdump
 
@@ -20,14 +20,14 @@ streams: bytes.o log.o rtmp.o AMFObject.
 rtmpdump: bytes.o log.o rtmp.o AMFObject.o rtmppacket.o rtmpdump.o parseurl.o dh.o handshake.o
 	$(CXX) $(LDFLAGS) $(ARMFLAGS) $^ -o $@_arm -lcrypto
 
-bytes.o: bytes.cpp bytes.h Makefile
-log.o: log.cpp log.h Makefile
+bytes.o: bytes.c bytes.h Makefile
+log.o: log.c log.h Makefile
 rtmp.o: rtmp.cpp rtmp.h log.h AMFObject.h Makefile
 AMFObject.o: AMFObject.cpp AMFObject.h log.h rtmp.h Makefile
 rtmppacket.o: rtmppacket.cpp rtmppacket.h log.h Makefile
 rtmpdump.o: rtmpdump.cpp rtmp.h log.h AMFObject.h Makefile
-parseurl.o: parseurl.cpp parseurl.h log.h Makefile
+parseurl.o: parseurl.c parseurl.h log.h Makefile
 streams.o: streams.cpp log.h Makefile
-dh.o: dh.cpp dh.h log.h Makefile
+dh.o: dh.c dh.h log.h Makefile
 handshake.o: handshake.cpp log.h Makefile
 

Copied and modified: bytes.c (from r8, bytes.cpp)
==============================================================================
--- bytes.cpp	Sun Oct 11 06:33:52 2009	(r8, copy source)
+++ bytes.c	Fri Oct 30 02:26:19 2009	(r9)
@@ -25,32 +25,49 @@
 // write dVal as 64bit little endian double
 void WriteNumber(char *data, double dVal)
 {
-	uint64_t res;
-
 #if __FLOAT_WORD_ORDER == __BYTE_ORDER
 #if __BYTE_ORDER == __BIG_ENDIAN
-	res = *((uint64_t*)&dVal);
+	memcpy(data, &dVal, 8);
 #elif __BYTE_ORDER == __LITTLE_ENDIAN
-        uint64_t in  = *((uint64_t*)&dVal);
-        res = __bswap_64(in);
+	unsigned char *ci, *co;
+	ci = (unsigned char *)&dVal;
+	co = (unsigned char *)data;
+	co[0] = ci[7];
+	co[1] = ci[6];
+	co[2] = ci[5];
+	co[3] = ci[4];
+	co[4] = ci[3];
+	co[5] = ci[2];
+	co[6] = ci[1];
+	co[7] = ci[0];
 #endif
 #else
 #if __BYTE_ORDER == __LITTLE_ENDIAN // __FLOAT_WORD_ORER == __BIG_ENDIAN
-        uint32_t in1 = *((uint32_t*)&dVal);
-        uint32_t in2 = *((uint32_t*)((char *)&dVal+4));
-        
-        in1 = __bswap_32(in1);
-        in2 = __bswap_32(in2);
-
-        res = ((uint64_t)in2<<32) | (uint64_t)in1;
+	unsigned char *ci, *co;
+	ci = (unsigned char *)&dVal;
+	co = (unsigned char *)data;
+	co[0] = ci[3];
+	co[1] = ci[2];
+	co[2] = ci[1];
+	co[3] = ci[0];
+	co[4] = ci[7];
+	co[5] = ci[6];
+	co[6] = ci[5];
+	co[7] = ci[4];
 #else // __BYTE_ORDER == __BIG_ENDIAN && __FLOAT_WORD_ORER == __LITTLE_ENDIAN
-        uint32_t in1 = *((uint32_t*)&dVal);
-        uint32_t in2 = *((uint32_t*)((char*)&dVal+4));
-
-        res = ((uint64_t)in1<<32) | (uint64_t)in2;
+	unsigned char *ci, *co;
+	ci = (unsigned char *)&dVal;
+	co = (unsigned char *)data;
+	co[0] = ci[4];
+	co[1] = ci[5];
+	co[2] = ci[6];
+	co[3] = ci[7];
+	co[4] = ci[0];
+	co[5] = ci[1];
+	co[6] = ci[2];
+	co[7] = ci[3];
 #endif
 #endif
-	memcpy(data, &res, 8);
 }
 
 // reads a little endian 64bit double from data
@@ -62,10 +79,26 @@ double ReadNumber(const char *data)
 	memcpy(&dVal, data, 8);
 	return dVal;
 #elif __BYTE_ORDER == __LITTLE_ENDIAN
+#if 1
+	double dVal;
+	unsigned char *ci, *co;
+	ci = (unsigned char *)data;
+	co = (unsigned char *)&dVal;
+	co[0] = ci[7];
+	co[1] = ci[6];
+	co[2] = ci[5];
+	co[3] = ci[4];
+	co[4] = ci[3];
+	co[5] = ci[2];
+	co[6] = ci[1];
+	co[7] = ci[0];
+	return dVal;
+#else
 	uint64_t in  = *((uint64_t*)data);
 	uint64_t res = __bswap_64(in);
 	return *((double *)&res);
 #endif
+#endif
 #else
 #if __BYTE_ORDER == __LITTLE_ENDIAN // __FLOAT_WORD_ORER == __BIG_ENDIAN
 	uint32_t in1 = *((uint32_t*)data);

Modified: bytes.h
==============================================================================
--- bytes.h	Sun Oct 11 06:33:52 2009	(r8)
+++ bytes.h	Fri Oct 30 02:26:19 2009	(r9)
@@ -3,6 +3,10 @@
 
 #include <stdint.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+
 #ifdef WIN32
 // Windows is little endian only 
 #define __LITTLE_ENDIAN 1234
@@ -98,5 +102,9 @@ double ReadNumber(const char *data);
 int ReadInt32LE(const char *data);
 int EncodeInt32LE(char *output, int nVal);
 
+#ifdef __cplusplus
+}
+#endif
+
 #endif
 

Copied and modified: dh.c (from r8, dh.cpp)
==============================================================================
--- dh.cpp	Sun Oct 11 06:33:52 2009	(r8, copy source)
+++ dh.c	Fri Oct 30 02:26:19 2009	(r9)
@@ -50,7 +50,7 @@ void dh_pg_init()
 */
 
 // RFC 2631, Section 2.1.5, http://www.ietf.org/rfc/rfc2631.txt
-bool isValidPublicKey(BIGNUM *y, BIGNUM *p , BIGNUM *q)
+int isValidPublicKey(BIGNUM *y, BIGNUM *p , BIGNUM *q)
 {
 	assert(y);
 
@@ -96,11 +96,11 @@ bool isValidPublicKey(BIGNUM *y, BIGNUM 
 
 	BN_free(bn);
 
-	return true;
+	return 1;
 failed:
 	//Log(LOGDEBUG, "Insecure DH public key: %s", BN_bn2hex(y));
 	BN_free(bn);
-	return false;
+	return 0;
 }
 
 DH* DHInit(int nKeyBits)
@@ -133,16 +133,16 @@ failed:
 	return 0;
 }
 
-bool DHGenerateKey(DH *dh)
+int DHGenerateKey(DH *dh)
 {
 	if(!dh)
-		return false;
+		return 0;
 
-	bool res = false;
+	int res = 0;
 	while(!res)
 	{
 		if(!DH_generate_key(dh))
-			return false;
+			return 0;
 	
 		BIGNUM *q1 = BN_new();
 		assert(BN_hex2bn(&q1, Q1024));
@@ -156,38 +156,38 @@ bool DHGenerateKey(DH *dh)
 
 		BN_free(q1);
 	}
-	return true;
+	return 1;
 }
 
 // fill pubkey with the public key in BIG ENDIAN order
 // 00 00 00 00 00 x1 x2 x3 .....
 
-bool DHGetPublicKey(DH *dh, uint8_t *pubkey, size_t nPubkeyLen)
+int DHGetPublicKey(DH *dh, uint8_t *pubkey, size_t nPubkeyLen)
 {
 	if(!dh || !dh->pub_key)
-		return false;
+		return 0;
 	
 	int len = BN_num_bytes(dh->pub_key);
 	if(len <= 0 || len > (int)nPubkeyLen)
-		return false;
+		return 0;
 
 	memset(pubkey, 0, nPubkeyLen);
 	BN_bn2bin(dh->pub_key, pubkey + (nPubkeyLen - len));
-	return true;
+	return 1;
 }
 
-bool DHGetPrivateKey(DH *dh, uint8_t *privkey, size_t nPrivkeyLen)
+int DHGetPrivateKey(DH *dh, uint8_t *privkey, size_t nPrivkeyLen)
 {
         if(!dh || !dh->priv_key)
-                return false;
+                return 0;
         
         int len = BN_num_bytes(dh->priv_key);
         if(len <= 0 || len > (int)nPrivkeyLen)
-                return false;
+                return 0;
 
         memset(privkey, 0, nPrivkeyLen);
         BN_bn2bin(dh->priv_key, privkey + (nPrivkeyLen - len));
-        return true;
+        return 1;
 }
 
 // computes the shared secret key from the private DH value and the othe parties public key (pubkey)

Modified: dh.h
==============================================================================
--- dh.h	Sun Oct 11 06:33:52 2009	(r8)
+++ dh.h	Fri Oct 30 02:26:19 2009	(r9)
@@ -27,11 +27,16 @@
 
 #include "bytes.h"
 
-bool isValidPublicKey(BIGNUM *y, BIGNUM *p, BIGNUM *q);
+#ifdef __cplusplus
+extern "C" {
+#endif
+int isValidPublicKey(BIGNUM *y, BIGNUM *p, BIGNUM *q);
 DH* DHInit(int nKeyBits);
-bool DHGenerateKey(DH *dh);
-bool DHGetPublicKey(DH *dh, uint8_t *pubkey, size_t nPubkeyLen);
-bool DHGetPrivateKey(DH *dh, uint8_t *privkey, size_t nPrivkeyLen);
+int DHGenerateKey(DH *dh);
+int DHGetPublicKey(DH *dh, uint8_t *pubkey, size_t nPubkeyLen);
+int DHGetPrivateKey(DH *dh, uint8_t *privkey, size_t nPrivkeyLen);
 int DHComputeSharedSecretKey(DH *dh, uint8_t *pubkey, size_t nPubkeyLen, uint8_t *secret);
 void DHFree(DH *dh);
-
+#ifdef __cplusplus
+}
+#endif

Modified: handshake.cpp
==============================================================================
--- handshake.cpp	Sun Oct 11 06:33:52 2009	(r8)
+++ handshake.cpp	Fri Oct 30 02:26:19 2009	(r9)
@@ -91,7 +91,7 @@ void InitRC4Encryption
         HMAC_CTX_cleanup(&ctx);
 
 	Log(LOGDEBUG, "RC4 Out Key: ");
-	LogHex((char*)digest, 16);
+	LogHex(LOGDEBUG, (char*)digest, 16);
 
         RC4_set_key(*rc4keyOut, 16, digest);
 
@@ -102,7 +102,7 @@ void InitRC4Encryption
         HMAC_CTX_cleanup(&ctx);
 
 	Log(LOGDEBUG, "RC4 In Key: ");
-	LogHex((char*)digest, 16);
+	LogHex(LOGDEBUG, (char*)digest, 16);
         
 	RC4_set_key(*rc4keyIn, 16, digest);
 }
@@ -275,8 +275,15 @@ bool CRTMP::HandShake(bool FP9HandShake)
 	else
 		clientsig[0] = 0x03;
 
+#if 0
 	uint32_t uptime = htonl(GetTime());
 	memcpy(clientsig + 1, &uptime, 4);
+#else
+	clientsig[1] = 0;
+	clientsig[2] = 0;
+	clientsig[3] = 0;
+	clientsig[4] = 0;
+#endif
 
 	if(FP9HandShake) {
 		//* TODO RTMPE ;), its just RC4 with diffie-hellman
@@ -353,12 +360,12 @@ bool CRTMP::HandShake(bool FP9HandShake)
 		CalculateDigest(digestPosClient, clientsig+1, GenuineFPKey, 30, &clientsig[1+digestPosClient]);
 		
 		Log(LOGDEBUG, "%s: Initial client digest: ", __FUNCTION__);
-		LogHex((char *)clientsig+1+digestPosClient, SHA256_DIGEST_LENGTH);
+		LogHex(LOGDEBUG, (char *)clientsig+1+digestPosClient, SHA256_DIGEST_LENGTH);
 	}
 
 	#ifdef _DEBUG
 	Log(LOGDEBUG, "Clientsig: ");
-	LogHex(&clientsig[1], RTMP_SIG_SIZE);
+	LogHex(LOGDEBUG, &clientsig[1], RTMP_SIG_SIZE);
 	#endif
 
 	if(!WriteN(clientsig, RTMP_SIG_SIZE + 1))
@@ -387,7 +394,7 @@ bool CRTMP::HandShake(bool FP9HandShake)
 
 	#ifdef _DEBUG
 	Log(LOGDEBUG,"Server signature:");
-	LogHex(serversig, RTMP_SIG_SIZE);
+	LogHex(LOGDEBUG, serversig, RTMP_SIG_SIZE);
 	#endif
 
 	// we have to use this signature now to find the correct algorithms for getting the digest and DH positions
@@ -451,7 +458,7 @@ bool CRTMP::HandShake(bool FP9HandShake)
 		}
 
 		Log(LOGDEBUG, "%s: Secret key: ", __FUNCTION__);
-		LogHex((char *)secretKey, 128);
+		LogHex(LOGDEBUG, (char *)secretKey, 128);
 		
 		InitRC4Encryption(
 			secretKey, 
@@ -473,7 +480,7 @@ bool CRTMP::HandShake(bool FP9HandShake)
 
 	#ifdef _DEBUG
 	Log(LOGDEBUG, "%s: 2nd handshake: ", __FUNCTION__);
-	LogHex(resp, RTMP_SIG_SIZE);
+	LogHex(LOGDEBUG, resp, RTMP_SIG_SIZE);
 	#endif
 
 	if(FP9HandShake && resp[4] == 0 && resp[5] == 0 && resp[6] == 0 && resp[7] == 0) {
@@ -501,13 +508,13 @@ bool CRTMP::HandShake(bool FP9HandShake)
 
 		// show some information
 		Log(LOGDEBUG, "%s: Digest key: ", __FUNCTION__);
-		LogHex(digest, SHA256_DIGEST_LENGTH);
+		LogHex(LOGDEBUG, digest, SHA256_DIGEST_LENGTH);
 
 		Log(LOGDEBUG, "%s: Signature calculated:", __FUNCTION__);
-		LogHex(signature, SHA256_DIGEST_LENGTH);
+		LogHex(LOGDEBUG, signature, SHA256_DIGEST_LENGTH);
 
 		Log(LOGDEBUG, "%s: Server sent signature:", __FUNCTION__);
-		LogHex(&resp[RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH], SHA256_DIGEST_LENGTH);
+		LogHex(LOGDEBUG, &resp[RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH], SHA256_DIGEST_LENGTH);
 
 		if(memcmp(signature, &resp[RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH], SHA256_DIGEST_LENGTH) != 0) {
 			Log(LOGWARNING, "%s: Server not genuine Adobe!", __FUNCTION__);
@@ -535,16 +542,16 @@ bool CRTMP::HandShake(bool FP9HandShake)
 
 		// some info output
 		Log(LOGDEBUG, "%s: Calculated digest key from secure key and server digest: ", __FUNCTION__);
-                LogHex(digestResp, SHA256_DIGEST_LENGTH);
+                LogHex(LOGDEBUG, digestResp, SHA256_DIGEST_LENGTH);
 
                 Log(LOGDEBUG, "%s: Client signature calculated:", __FUNCTION__);
-                LogHex(signatureResp, SHA256_DIGEST_LENGTH);
+                LogHex(LOGDEBUG, signatureResp, SHA256_DIGEST_LENGTH);
 
 		memcpy(&clientResp[RTMP_SIG_SIZE-SHA256_DIGEST_LENGTH], signatureResp, SHA256_DIGEST_LENGTH);
 
 		#ifdef _DEBUG
 		Log(LOGDEBUG, "%s: Sending final signed handshake response: ", __FUNCTION__);
-		LogHex(clientResp, RTMP_SIG_SIZE);
+		LogHex(LOGDEBUG, clientResp, RTMP_SIG_SIZE);
 		#endif
 
 		if(!WriteN(clientResp, RTMP_SIG_SIZE))

Copied and modified: log.c (from r8, log.cpp)
==============================================================================
--- log.cpp	Sun Oct 11 06:33:52 2009	(r8, copy source)
+++ log.c	Fri Oct 30 02:26:19 2009	(r9)
@@ -20,12 +20,15 @@
 
 #include <stdio.h>
 #include <stdarg.h>
+#include <string.h>
 
 #include "log.h"
 
 #define MAX_PRINT_LEN	2048
 
-FILE *fmsg = stderr;
+extern int debuglevel;
+
+FILE *fmsg;
 
 void LogSetOutput(FILE *file)
 {
@@ -34,38 +37,52 @@ void LogSetOutput(FILE *file)
 
 void LogPrintf(const char *format, ...)
 {
-        char str[MAX_PRINT_LEN]="";
+	char str[MAX_PRINT_LEN]="";
+	va_list args;
+	va_start(args, format);
+	vsnprintf(str, MAX_PRINT_LEN-1, format, args);
+	va_end(args);
 
-        va_list args;
-        va_start(args, format);
-        vsnprintf(str, MAX_PRINT_LEN-1, format, args);
-        va_end(args);
+	if ( debuglevel==LOGCRIT )
+		return;
 
-        fprintf(fmsg, "%s", str);
-	#ifdef _DEBUG
+	if ( !fmsg ) fmsg = stderr;
+
+	fprintf(fmsg, "%s", str);
+#ifdef _DEBUG
 	fflush(fmsg);
-	#endif
+#endif
 }
 
 void Log(int level, const char *format, ...)
 {
-        char str[MAX_PRINT_LEN]="";
+	char str[MAX_PRINT_LEN]="";
+	va_list args;
+	va_start(args, format);
+	vsnprintf(str, MAX_PRINT_LEN-1, format, args);
+	va_end(args);
 
-        va_list args;
-        va_start(args, format);
-        vsnprintf(str, MAX_PRINT_LEN-1, format, args);
-        va_end(args);
+	// Filter out 'no-name'
+	if ( debuglevel<LOGALL && strstr(str, "no-name" ) != NULL )
+		return;
 
-	//if(level != LOGDEBUG)
-        fprintf(fmsg, "\r%s: %s\n", level==LOGDEBUG?"DEBUG":(level==LOGERROR?"ERROR":(level==LOGWARNING?"WARNING":"INFO")), str);
-	#ifdef _DEBUG
+	if ( !fmsg ) fmsg = stderr;
+
+	if ( level <= debuglevel )
+		fprintf(fmsg, "\r%s: %s\n", level==LOGDEBUG?"DEBUG":(level==LOGERROR?
+"ERROR":(level==LOGWARNING?"WARNING":(level==LOGCRIT?"CRIT":"INFO"))), str);
+
+#ifdef _DEBUG
 	fflush(fmsg);
-	#endif
+#endif
 }
 
-void LogHex(const char *data, unsigned long len)
+void LogHex(int level, const char *data, unsigned long len)
 {
-	for(unsigned long i=0; i<len; i++) {
+	unsigned long i;
+	if ( level > debuglevel )
+		return;
+	for(i=0; i<len; i++) {
 		LogPrintf("%02X ", (unsigned char)data[i]);
 	}
 	LogPrintf("\n");
@@ -73,14 +90,16 @@ void LogHex(const char *data, unsigned l
 
 void LogHexString(const char *data, unsigned long len)
 {
-        for(unsigned long i=0; i<len; i++) {
-                LogPrintf("%02X ", (unsigned char)data[i]);
-        }
-        LogPrintf("\n");
+	unsigned long i;
+	if ( debuglevel==LOGCRIT )
+		return;
+	for(i=0; i<len; i++) {
+		LogPrintf("%02X ", (unsigned char)data[i]);
+	}
+	LogPrintf("\n");
 
-        for(unsigned long i=0; i<len; i++) {
-                LogPrintf("%c", (unsigned char)data[i]);
-        }
-        LogPrintf("\n");
+	for(i=0; i<len; i++) {
+		LogPrintf("%c", (unsigned char)data[i]);
+	}
+	LogPrintf("\n");
 }
-

Modified: log.h
==============================================================================
--- log.h	Sun Oct 11 06:33:52 2009	(r8)
+++ log.h	Fri Oct 30 02:26:19 2009	(r9)
@@ -23,6 +23,10 @@
 
 #include <stdio.h>
 
+#ifdef __cplusplus
+extern "C" {
+#endif
+// Enable this to get full debugging output
 //#define _DEBUG
 #define CRYPTO
 
@@ -30,16 +34,20 @@
 #undef NODEBUG
 #endif
 
-#define LOGDEBUG        0
+#define LOGCRIT         0
 #define LOGERROR        1
 #define LOGWARNING	2
 #define LOGINFO		3
+#define LOGDEBUG		4
+#define LOGALL		5
 
 void LogSetOutput(FILE *file);
 void LogPrintf(const char *format, ...);
 void Log(int level, const char *format, ...);
-void LogHex(const char *data, unsigned long len);
+void LogHex(int level, const char *data, unsigned long len);
 void LogHexString(const char *data, unsigned long len);
 
+#ifdef __cplusplus
+}
+#endif
 #endif
-

Copied and modified: parseurl.c (from r8, parseurl.cpp)
==============================================================================
--- parseurl.cpp	Sun Oct 11 06:33:52 2009	(r8, copy source)
+++ parseurl.c	Fri Oct 30 02:26:19 2009	(r9)
@@ -22,11 +22,18 @@
 #include <string.h>
 
 #include <assert.h>
+#include <ctype.h>
 
 #include "log.h"
 #include "parseurl.h"
 
-#include "rtmp.h"
+#define RTMP_PROTOCOL_UNDEFINED	-1
+#define RTMP_PROTOCOL_RTMP      0
+#define RTMP_PROTOCOL_RTMPT     1 // not yet supported
+#define RTMP_PROTOCOL_RTMPS     2 // not yet supported
+#define RTMP_PROTOCOL_RTMPE     3 // not yet supported
+#define RTMP_PROTOCOL_RTMPTE    4 // not yet supported
+#define RTMP_PROTOCOL_RTMFP     5 // not yet supported
 
 char *str2lower(char *str, int len)
 {
@@ -94,7 +101,7 @@ int hex2bin(char *str, char **hex)
 	return ret;
 }
 
-bool ParseUrl(char *url, int *protocol, char **host, unsigned int *port, char **playpath, char **app)
+int ParseUrl(char *url, int *protocol, char **host, unsigned int *port, char **playpath, char **app)
 {
 	assert(url != 0 && protocol != 0 && host != 0 && port != 0 && playpath != 0 && app != 0);
 
@@ -112,7 +119,7 @@ bool ParseUrl(char *url, int *protocol, 
 	if(p == 0) {
 		Log(LOGWARNING, "RTMP URL: No :// in url!");
 		free(lw);
-		return false;
+		return 0;
 	}
 
 	if(len == 4 && strncmp(lw, "rtmp", 4)==0)
@@ -143,7 +150,7 @@ parsehost:
 	// check for sudden death
 	if(*p==0) {
 		Log(LOGWARNING, "No hostname in URL!");		
-		return false;
+		return 0;
 	}
 
 	int iEnd   = strlen(p);
@@ -199,7 +206,7 @@ parsehost:
 
 	if(*p != '/') {
 		Log(LOGWARNING, "No application or playpath in URL!");
-		return true;
+		return 1;
 	}
 	p++; iEnd--;
 
@@ -253,7 +260,7 @@ parsehost:
 	int iPlaypathPos = -1;
 	int iPlaypathLen = -1;
 
-	bool bAddMP4 = false; // used to add at the end mp4: in front of the playpath
+	int bAddMP4 = 0; // used to add at the end mp4: in front of the playpath
 
 	// here filter out semicolon added parameters, e.g. slist=bla...;abc=def
 	//if((temp=strstr(p, ";"))!=0)
@@ -277,7 +284,7 @@ parsehost:
 			// filter .flv from playpath specified with slashes: rtmp://host/app/path.flv
 			if(iPlaypathLen >=4) {
 				if(strncmp(&p[iPlaypathPos+iPlaypathLen-4], ".f4v", 4)==0 || strncmp(&p[iPlaypathPos+iPlaypathLen-4], ".mp4", 4)==0) {
-					bAddMP4 = true;
+					bAddMP4 = 1;
 				} else if(strncmp(&p[iPlaypathPos+iPlaypathLen-4], ".flv", 4)==0) {
 					iPlaypathLen-=4;
 				}
@@ -304,6 +311,6 @@ parsehost:
 		Log(LOGWARNING, "No playpath in URL!");
 	}
 
-        return true;
+        return 1;
 }
 

Modified: parseurl.h
==============================================================================
--- parseurl.h	Sun Oct 11 06:33:52 2009	(r8)
+++ parseurl.h	Fri Oct 30 02:26:19 2009	(r9)
@@ -20,8 +20,14 @@
  *
  */
 
+#ifdef __cplusplus
+extern "C" {
+#endif
 int hex2bin(char *str, char **hex);
-bool ParseUrl(char *url, int *protocol, char **host, unsigned int *port, char **playpath, char **app);
+int ParseUrl(char *url, int *protocol, char **host, unsigned int *port, char **playpath, char **app);
+#ifdef __cplusplus
+}
+#endif
 
 #endif
 

Modified: rtmp.cpp
==============================================================================
--- rtmp.cpp	Sun Oct 11 06:33:52 2009	(r8)
+++ rtmp.cpp	Fri Oct 30 02:26:19 2009	(r9)
@@ -24,6 +24,10 @@
 #include <stdlib.h>
 #include <string.h>
 
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/tcp.h>
+
 #include <assert.h>
 
 #ifdef WIN32
@@ -124,6 +128,7 @@ bool CRTMP::Connect(
 	char *swfSHA256Hash,
 	uint32_t swfSize,
 	char *flashVer, 
+	char *subscribepath, 
 	double dTime,
 	bool bLiveStream,
 	long int timeout
@@ -146,6 +151,8 @@ bool CRTMP::Connect(
   	Log(LOGDEBUG, "app      : %s", app);
   if(auth)
   	Log(LOGDEBUG, "auth     : %s", auth);
+  if(subscribepath)
+  	Log(LOGDEBUG, "subscribepath : %s", subscribepath);
   if(flashVer)
   	Log(LOGDEBUG, "flashVer : %s", flashVer);
   if(dTime > 0)
@@ -158,7 +165,7 @@ bool CRTMP::Connect(
 	Link.SWFHash = swfSHA256Hash;
 	Link.SWFSize = swfSize;
   	Log(LOGDEBUG, "SWFSHA256:");
-  	LogHex(Link.SWFHash, 32);
+  	LogHex(LOGDEBUG, Link.SWFHash, 32);
 	Log(LOGDEBUG, "SWFSize  : %lu", Link.SWFSize);
   } else {
   	Link.SWFHash = NULL;
@@ -171,6 +178,7 @@ bool CRTMP::Connect(
   Link.app = app;
   Link.auth = auth;
   Link.flashVer = flashVer;
+  Link.subscribepath = subscribepath;
   Link.seekTime = dTime;
   Link.bLiveStream = bLiveStream;
   Link.timeout = timeout;
@@ -241,12 +249,14 @@ bool CRTMP::Connect(
     return false;
   }
 
+  int on = 1;
+  setsockopt(m_socket, IPPROTO_TCP, TCP_NODELAY, &on, sizeof(on));
   return true;
 }
 
-bool CRTMP::GetNextMediaPacket(RTMPPacket &packet)
+int CRTMP::GetNextMediaPacket(RTMPPacket &packet)
 {
-  bool bHasMediaPacket = false;
+  int bHasMediaPacket = 0;
   while (!bHasMediaPacket && IsConnected() && ReadPacket(packet))
   {
     if (!packet.IsReady())
@@ -289,14 +299,14 @@ bool CRTMP::GetNextMediaPacket(RTMPPacke
         // audio data
         //Log(LOGDEBUG, "%s, received: audio %lu bytes", __FUNCTION__, packet.m_nBodySize);
         HandleAudio(packet);
-        bHasMediaPacket = true;
+        bHasMediaPacket = 1;
         break;
 
       case 0x09:
         // video data
         //Log(LOGDEBUG, "%s, received: video %lu bytes", __FUNCTION__, packet.m_nBodySize);
         HandleVideo(packet);
-        bHasMediaPacket = true;
+        bHasMediaPacket = 1;
         break;
 
       case 0x0F: // flex stream send
@@ -322,14 +332,15 @@ bool CRTMP::GetNextMediaPacket(RTMPPacke
 
         obj.Dump();*/
 
-	HandleInvoke(packet.m_body+1, packet.m_nBodySize-1);
+	if ( HandleInvoke(packet.m_body+1, packet.m_nBodySize-1) == 1 )
+	  bHasMediaPacket = 2;
 	break;
       }
       case 0x12:
         // metadata (notify)
         Log(LOGDEBUG, "%s, received: notify %lu bytes", __FUNCTION__, packet.m_nBodySize);
-        HandleMetadata(packet.m_body, packet.m_nBodySize);
-        bHasMediaPacket = true;
+        if ( HandleMetadata(packet.m_body, packet.m_nBodySize) )
+          bHasMediaPacket = 1;
         break;
 
       case 0x13:
@@ -341,7 +352,8 @@ bool CRTMP::GetNextMediaPacket(RTMPPacke
 	Log(LOGDEBUG, "%s, received: invoke %lu bytes", __FUNCTION__, packet.m_nBodySize);
         //LogHex(packet.m_body, packet.m_nBodySize);
 
-	HandleInvoke(packet.m_body, packet.m_nBodySize);
+	if ( HandleInvoke(packet.m_body, packet.m_nBodySize) == 1 )
+		bHasMediaPacket = 2;
         break;
 
       case 0x16:
@@ -364,13 +376,13 @@ bool CRTMP::GetNextMediaPacket(RTMPPacke
 	
         // FLV tag(s)
         //Log(LOGDEBUG, "%s, received: FLV tag(s) %lu bytes", __FUNCTION__, packet.m_nBodySize);
-        bHasMediaPacket = true;
+        bHasMediaPacket = 1;
         break;
       }
       default:
         Log(LOGDEBUG, "%s, unknown packet type received: 0x%02x", __FUNCTION__, packet.m_packetType);
 	#ifdef _DEBUG
-	LogHex(packet.m_body, packet.m_nBodySize);
+	LogHex(LOGDEBUG, packet.m_body, packet.m_nBodySize);
 	#endif
     }
 
@@ -402,11 +414,12 @@ int CRTMP::ReadN(char *buffer, int n)
   while (n > 0)
   {
     int nBytes = 0;
-// todo, test this code:
-/*
     if(m_nBufferSize == 0)
-    	FillBuffer();
-    int nRead = ((n<m_nBufferSize)?n:m_nBufferSize;
+		if (!FillBuffer()) {
+			Close();
+			return 0;
+		}
+    int nRead = ((n<m_nBufferSize)?n:m_nBufferSize);
     if(nRead > 0) {
     	memcpy(ptr, m_pBufferStart, nRead);
 	m_pBufferStart += nRead;
@@ -415,29 +428,16 @@ int CRTMP::ReadN(char *buffer, int n)
 	m_nBytesIn += nRead;
 	if(m_nBytesIn > m_nBytesInSent + (600*1024)) // report every 600K
 		SendBytesReceived();
-    }//*/
+    }
+
 //again:
-    nBytes = recv(m_socket, ptr, n, 0);
+//    nBytes = recv(m_socket, ptr, n, 0);
 
     //Log(LOGDEBUG, "%s: %d bytes\n", __FUNCTION__, nBytes);
 #ifdef _DEBUG
         fwrite(ptr, 1, nBytes, netstackdump_read);
 #endif
 
-    if(m_bPlaying) {
-        m_nBytesIn += nBytes;
-	if (m_nBytesIn > m_nBytesInSent + (600*1024) ) // report every 600K
-                  SendBytesReceived();
-    }
- 
-    if (nBytes == -1)
-    {
-      Log(LOGERROR, "%s, RTMP recv error %d", __FUNCTION__, GetSockError());
-      //goto again;
-      Close();
-      return false;
-    }
-    
     if (nBytes == 0)
     {
       Log(LOGDEBUG, "%s, RTMP socket closed by server", __FUNCTION__);
@@ -540,7 +540,7 @@ bool CRTMP::SendConnectPacket()
   
   enc += EncodeBoolean(enc, "fpad", false);
   enc += EncodeNumber(enc, "capabilities", 15.0);
-  enc += EncodeNumber(enc, "audioCodecs", 1639.0);
+  enc += EncodeNumber(enc, "audioCodecs", 3191.0);
   enc += EncodeNumber(enc, "videoCodecs", 252.0);
   enc += EncodeNumber(enc, "videoFunction", 1.0);
   if(Link.pageUrl)
@@ -575,7 +575,7 @@ bool CRTMP::SendBGHasStream(double dId, 
   packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
   packet.m_packetType = 0x14; // INVOKE
 
-  packet.AllocPacket(256); // should be enough
+  packet.AllocPacket(1024); // should be enough
   char *enc = packet.m_body;
   enc += EncodeString(enc, "bgHasStream");
   enc += EncodeNumber(enc, dId);
@@ -608,7 +608,48 @@ bool CRTMP::SendCreateStream(double dStr
   return SendRTMP(packet);
 }
 
-bool CRTMP::SendPause()
+bool CRTMP::SendFCSubscribe(char *subscribepath)
+{
+  RTMPPacket packet;
+  packet.m_nChannel = 0x03;   // control channel (invoke)
+  packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
+  packet.m_packetType = 0x14; // INVOKE
+
+  packet.AllocPacket(256); // should be enough
+  Log(LOGDEBUG, "FCSubscribe: %s", subscribepath);
+  char *enc = packet.m_body;
+  enc += EncodeString(enc, "FCSubscribe");
+  enc += EncodeNumber(enc, 0);
+  *enc = 0x05; // NULL
+  enc++;
+  enc += EncodeString(enc, subscribepath);
+
+  packet.m_nBodySize = enc - packet.m_body;
+
+  return SendRTMP(packet);
+}
+
+bool CRTMP::SendDeleteStream(double dStreamId)
+{
+  RTMPPacket packet;
+  packet.m_nChannel = 0x03;   // control channel (invoke)
+  packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
+  packet.m_packetType = 0x14; // INVOKE
+
+  packet.AllocPacket(256); // should be enough
+  char *enc = packet.m_body;
+  enc += EncodeString(enc, "deleteStream");
+  enc += EncodeNumber(enc, 0.0);
+  *enc = 0x05; // NULL
+  enc++;
+  enc += EncodeNumber(enc, dStreamId);
+
+  packet.m_nBodySize = enc - packet.m_body;
+
+  return SendRTMP(packet);
+}
+
+bool CRTMP::SendPause(bool DoPause, double dTime)
 {
   RTMPPacket packet;
   packet.m_nChannel = 0x08;   // video channel 
@@ -621,8 +662,8 @@ bool CRTMP::SendPause()
   enc += EncodeNumber(enc, 0);
   *enc = 0x05; // NULL
   enc++;
-  enc += EncodeBoolean(enc, true);
-  enc += EncodeNumber(enc, 0);
+  enc += EncodeBoolean(enc, DoPause);
+  enc += EncodeNumber(enc, (double)dTime/1000);
 
   packet.m_nBodySize = enc - packet.m_body;
 
@@ -729,7 +770,7 @@ bool CRTMP::SendPlay()
   packet.m_packetType = 0x14; // INVOKE
   packet.m_nInfoField2 = m_stream_id; //0x01000000;
 
-  packet.AllocPacket(256); // should be enough
+  packet.AllocPacket(1024); // should be enough
   char *enc = packet.m_body;
   enc += EncodeString(enc, "play");
   enc += EncodeNumber(enc, 0.0); // stream id??
@@ -802,7 +843,7 @@ bool CRTMP::SendPing(short nType, unsign
     #ifdef CRYPTO
     memcpy(buf, Link.SWFVerificationResponse, 42);
     Log(LOGDEBUG, "Sending SWFVerification response: ");
-    LogHex(packet.m_body, packet.m_nBodySize);
+    LogHex(LOGDEBUG, packet.m_body, packet.m_nBodySize);
     #endif
   } else {
     if (nSize > 2)
@@ -815,12 +856,12 @@ bool CRTMP::SendPing(short nType, unsign
   return SendRTMP(packet);
 }
 
-void CRTMP::HandleInvoke(const char *body, unsigned int nBodySize)
+int CRTMP::HandleInvoke(const char *body, unsigned int nBodySize)
 {
   if (body[0] != 0x02) // make sure it is a string method name we start with
   {
     Log(LOGWARNING, "%s, Sanity failed. no string method in invoke packet", __FUNCTION__);
-    return;
+    return 0;
   }
 
   RTMP_LIB::AMFObject obj;
@@ -828,28 +869,36 @@ void CRTMP::HandleInvoke(const char *bod
   if (nRes < 0)
   { 
     Log(LOGERROR, "%s, error decoding invoke packet", __FUNCTION__);
-    return;
+    return 0;
   }
 
   obj.Dump();
   std::string method = obj.GetProperty(0).GetString();
   Log(LOGDEBUG, "%s, server invoking <%s>", __FUNCTION__, method.c_str());
 
-  if (method == "_result")
+#define CSCMP(a,b)	(a.size() == (sizeof(b)-1)) && !strcmp(a.c_str(),b)
+
+  if (CSCMP(method, "_result"))
   {
     std::string methodInvoked = m_methodCalls[0];
     m_methodCalls.erase(m_methodCalls.begin());
 
     Log(LOGDEBUG, "%s, received result for method call <%s>", __FUNCTION__, methodInvoked.c_str());
   
-    if (methodInvoked == "connect")
+    if (CSCMP(methodInvoked,"connect"))
     {
       SendServerBW();
       SendPing(3, 0, 300);
 
       SendCreateStream(2.0);
+
+      // Send the FCSubscribe if live stream or if subscribepath is set
+      if (Link.subscribepath)
+        SendFCSubscribe(Link.subscribepath);
+      else if (Link.bLiveStream)
+        SendFCSubscribe(Link.playpath);
     }
-    else if (methodInvoked == "createStream")
+    else if (CSCMP(methodInvoked,"createStream"))
     {
       m_stream_id = (int)obj.GetProperty(3).GetNumber();
 
@@ -862,28 +911,39 @@ void CRTMP::HandleInvoke(const char *bod
 	
       SendPing(3, 1, m_nBufferMS);
     }
-    else if (methodInvoked == "play")
+    else if (CSCMP(methodInvoked,"play"))
     {
+	  SendPlay();
     }
   }
-  else if (method == "onBWDone")
+  else if (CSCMP(method,"onBWDone"))
   {
-    //SendCheckBW();
+	if(nBodySize < 25)
+		SendCheckBW();
+	else
+	{
+		/* Dunno why we get this bogus request */
+		;
+	}
   }
-  else if (method == "_onbwcheck")
+  else if (CSCMP(method,"onFCSubscribe"))
+  {
+    // SendOnFCSubscribe();
+  }
+  else if (CSCMP(method,"_onbwcheck"))
   {
     SendCheckBWResult();
   }
-  else if (method == "_error")
+  else if (CSCMP(method,"_error"))
   {
     Log(LOGERROR, "rtmp server sent error");
   }
-  else if (method == "close")
+  else if (CSCMP(method,"close"))
   {
     Log(LOGERROR, "rtmp server requested close");
     Close();
   }
-  else if (method == "onStatus")
+  else if (CSCMP(method,"onStatus"))
   {
     std::string code  = obj.GetProperty(3).GetObject().GetProperty("code").GetString();
     std::string level = obj.GetProperty(3).GetObject().GetProperty("level").GetString();
@@ -891,12 +951,16 @@ void CRTMP::HandleInvoke(const char *bod
     Log(LOGDEBUG, "%s, onStatus: %s", __FUNCTION__, code.c_str() );
     if (code == "NetStream.Failed"
     ||  code == "NetStream.Play.Failed"
-    ||  code == "NetStream.Play.Stop"
     ||  code == "NetStream.Play.StreamNotFound"
     ||  code == "NetConnection.Connect.InvalidApp")
       Close();
 
-    //if (code == "NetStream.Play.Complete")
+    // Return 1 if this is a Play.Complete or Play.Stop
+    if (code == "NetStream.Play.Complete"
+    ||  code == "NetStream.Play.Stop") {
+      Close();
+      return 1;
+    }
 
     /*if(Link.seekTime > 0) {
     	if(code == "NetStream.Seek.Notify") { // seeked successfully, can play now!
@@ -910,6 +974,7 @@ void CRTMP::HandleInvoke(const char *bod
   {
 
   }
+  return 0;
 }
 
 //int pnum=0;
@@ -934,7 +999,51 @@ bool CRTMP::FindFirstMatchingProperty(AM
 	return false;
 }
 
-void CRTMP::HandleMetadata(char *body, unsigned int len)
+bool CRTMP::DumpMetaData(AMFObject &obj)
+{
+        std::string name;
+        AMFObjectProperty prop;
+	for (int n=0; n<obj.GetPropertyCount(); n++) {
+		AMFObjectProperty prop = obj.GetProperty(n);
+		if ( prop.GetType() != AMF_OBJECT ) {
+			char str[256]="";
+			switch( prop.GetType() )
+			{
+				case AMF_NUMBER:
+					if ( (double)int(prop.GetNumber()) == prop.GetNumber() )
+						snprintf(str, 255, "%.0f", prop.GetNumber() );
+					else
+						snprintf(str, 255, "%.2f", prop.GetNumber() );
+					break;
+				case AMF_BOOLEAN:
+					snprintf(str, 255, "%s", prop.GetNumber() == 1.?"TRUE":"FALSE");
+					break;
+				case AMF_STRING:
+					snprintf(str, 255, "%s", prop.GetString().c_str());
+					break;
+				case AMF_DATE:
+					snprintf(str, 255, "timestamp:%.2f", prop.GetNumber() );
+					break;
+				default:
+					snprintf(str, 255, "INVALID TYPE 0x%02x", (unsigned char)prop.GetType() );
+			}
+			if ( prop.GetPropName() != "" ) {
+				// chomp
+				if ( strlen(str) >= 1 && str[strlen(str)-1 ] == '\n')
+					str[strlen(str)-1] = '\0';
+				LogPrintf("  %-22s%s\n", prop.GetPropName().c_str(), str );
+			}
+		} else {
+			if ( prop.GetPropName() != "" )
+				LogPrintf("%s:\n", prop.GetPropName().c_str() );
+			AMFObject next = prop.GetObject();
+			DumpMetaData(next);
+		}
+	}
+	return false;
+}
+
+bool CRTMP::HandleMetadata(char *body, unsigned int len)
 {
 	/*Log(LOGDEBUG,"Parsing meta data: %d @0x%08X", packet.m_nBodySize, packet.m_body);
 	LogHex(packet.m_body, packet.m_nBodySize);
@@ -956,7 +1065,7 @@ void CRTMP::HandleMetadata(char *body, u
 	int nRes = obj.Decode(body, len);
 	if(nRes < 0) {
 		Log(LOGERROR, "%s, error decoding meta data packet", __FUNCTION__);
-		return;
+		return false;
 	}
 
 	obj.Dump();
@@ -964,10 +1073,18 @@ void CRTMP::HandleMetadata(char *body, u
 
 	if(metastring == "onMetaData") {
 		AMFObjectProperty prop;
+		// Show metadata
+		LogPrintf("\r%s\n", "Metadata:                  " );
+		DumpMetaData(obj);
 		if(FindFirstMatchingProperty(obj, "duration", prop)) {
 			m_fDuration = prop.GetNumber();
-			Log(LOGDEBUG, "Set duration: %f", m_fDuration);
+			//Log(LOGDEBUG, "Set duration: %.2f", m_fDuration);
 		}
+		return true;
+	}
+	else
+	{
+		return false;
 	}
 }
 
@@ -1018,7 +1135,7 @@ void CRTMP::HandlePing(const RTMPPacket 
 bool CRTMP::ReadPacket(RTMPPacket &packet)
 {
   char type;
-  if (ReadN(&type,1) != 1)
+  if (ReadN(&type,1) == 0)
   {
     Log(LOGERROR, "%s, failed to read RTMP packet header", __FUNCTION__);
     return false;
@@ -1026,6 +1143,28 @@ bool CRTMP::ReadPacket(RTMPPacket &packe
 
   packet.m_headerType = (type & 0xc0) >> 6;
   packet.m_nChannel = (type & 0x3f);
+  if ( packet.m_nChannel == 0 )
+  {
+	if (ReadN(&type,1) != 1)
+	{
+	  Log(LOGERROR, "%s, failed to read RTMP packet header 2nd byte", __FUNCTION__);
+	  return false;
+	} 
+	packet.m_nChannel = (unsigned)type;
+	packet.m_nChannel += 64;
+  } else if ( packet.m_nChannel == 1 )
+  {
+    char t[2];
+	int tmp;
+    if (ReadN(t,2) != 2)
+	{
+	  Log(LOGERROR, "%s, failed to read RTMP packet header 3nd byte", __FUNCTION__);
+	  return false;
+	} 
+	tmp = (((unsigned)t[1])<<8) + (unsigned)t[0];
+	packet.m_nChannel = tmp + 64;
+    Log(LOGDEBUG, "%s, m_nChannel: %0x", __FUNCTION__, packet.m_nChannel);
+  }
 
   int nSize = packetSize[packet.m_headerType];
   
@@ -1034,7 +1173,8 @@ bool CRTMP::ReadPacket(RTMPPacket &packe
 
   if (nSize < RTMP_LARGE_HEADER_SIZE) { // using values from the last message of this channel
     packet.FreePacketHeader(); // test whether this avoids memory leak
-    packet = m_vecChannelsIn[packet.m_nChannel];
+	if (m_vecChannelsIn[packet.m_nChannel])
+		packet = *m_vecChannelsIn[packet.m_nChannel];
   }
   
   nSize--;
@@ -1085,7 +1225,9 @@ bool CRTMP::ReadPacket(RTMPPacket &packe
   packet.m_nBytesRead += nChunk;
 
   // keep the packet as ref for other packets on this channel
-  m_vecChannelsIn[packet.m_nChannel] = packet;
+  if (!m_vecChannelsIn[packet.m_nChannel])
+  	m_vecChannelsIn[packet.m_nChannel] = new RTMPPacket;
+  *m_vecChannelsIn[packet.m_nChannel] = packet;
 
   if (packet.IsReady())
   {
@@ -1099,9 +1241,9 @@ bool CRTMP::ReadPacket(RTMPPacket &packe
  
     // reset the data from the stored packet. we keep the header since we may use it later if a new packet for this channel
     // arrives and requests to re-use some info (small packet header)
-    m_vecChannelsIn[packet.m_nChannel].m_body = NULL;
-    m_vecChannelsIn[packet.m_nChannel].m_nBytesRead = 0;
-    m_vecChannelsIn[packet.m_nChannel].m_hasAbsTimestamp = false; // can only be false if we reuse header
+    m_vecChannelsIn[packet.m_nChannel]->m_body = NULL;
+    m_vecChannelsIn[packet.m_nChannel]->m_nBytesRead = 0;
+    m_vecChannelsIn[packet.m_nChannel]->m_hasAbsTimestamp = false; // can only be false if we reuse header
   }
   else
     packet.m_body = NULL; // so it wont be erased on "free"
@@ -1111,26 +1253,27 @@ bool CRTMP::ReadPacket(RTMPPacket &packe
 
 short  CRTMP::ReadInt16(const char *data)
 {
+  unsigned char *c = (unsigned char *)data;
   short val;
-  memcpy(&val,data,sizeof(short));
-  return ntohs(val);
+  val = (c[0] << 8) | c[1];
+  return val;
 }
 
 int  CRTMP::ReadInt24(const char *data)
 {
-  char tmp[4] = {0};
-  memcpy(tmp+1, data, 3);
+  unsigned char *c = (unsigned char *)data;
   int val;
-  memcpy(&val, tmp, sizeof(int));
-  return ntohl(val);
+  val = (data[0] << 16) | (c[1] << 8) | c[2];
+  return val;
 }
 
 // big-endian 32bit integer
 int  CRTMP::ReadInt32(const char *data)
 {
+  unsigned char *c = (unsigned char *)data;
   int val;
-  memcpy(&val, data, sizeof(int));
-  return ntohl(val);
+  val = (c[0] << 24) | (c[1] << 16) | (c[2] << 8) | c[3];
+  return val;
 }
 
 std::string CRTMP::ReadString(const char *data)
@@ -1169,25 +1312,26 @@ int CRTMP::EncodeString(char *output, co
 
 int CRTMP::EncodeInt16(char *output, short nVal)
 {
-  nVal = htons(nVal);
-  memcpy(output, &nVal, sizeof(short));
+  output[1] = nVal & 0xff;
+  output[0] = nVal >> 8;
   return sizeof(short);
 }
 
 int CRTMP::EncodeInt24(char *output, int nVal)
 {
-  nVal = htonl(nVal);
-  char *ptr = (char *)&nVal;
-  ptr++;
-  memcpy(output, ptr, 3);
+  output[2] = nVal & 0xff;
+  output[1] = nVal >> 8;
+  output[0] = nVal >> 16;
   return 3;
 }
 
 // big-endian 32bit integer
 int CRTMP::EncodeInt32(char *output, int nVal)
 {
-  nVal = htonl(nVal);
-  memcpy(output, &nVal, sizeof(int));
+  output[3] = nVal & 0xff;
+  output[2] = nVal >> 8;
+  output[1] = nVal >> 16;
+  output[0] = nVal >> 24;
   return sizeof(int);
 }
 
@@ -1327,14 +1471,14 @@ bool CRTMP::HandShake(bool FP9HandShake)
 
 bool CRTMP::SendRTMP(RTMPPacket &packet)
 {
-  const RTMPPacket &prevPacket = m_vecChannelsOut[packet.m_nChannel];
-  if (packet.m_headerType != RTMP_PACKET_SIZE_LARGE)
+  const RTMPPacket *prevPacket = m_vecChannelsOut[packet.m_nChannel];
+  if (prevPacket && packet.m_headerType != RTMP_PACKET_SIZE_LARGE)
   {
     // compress a bit by using the prev packet's attributes
-    if (prevPacket.m_nBodySize == packet.m_nBodySize && packet.m_headerType == RTMP_PACKET_SIZE_MEDIUM) 
+    if (prevPacket->m_nBodySize == packet.m_nBodySize && packet.m_headerType == RTMP_PACKET_SIZE_MEDIUM) 
       packet.m_headerType = RTMP_PACKET_SIZE_SMALL;
 
-    if (prevPacket.m_nInfoField2 == packet.m_nInfoField2 && packet.m_headerType == RTMP_PACKET_SIZE_SMALL)
+    if (prevPacket->m_nInfoField2 == packet.m_nInfoField2 && packet.m_headerType == RTMP_PACKET_SIZE_SMALL)
       packet.m_headerType = RTMP_PACKET_SIZE_MINIMUM;
       
   }
@@ -1360,6 +1504,10 @@ bool CRTMP::SendRTMP(RTMPPacket &packet)
   if (nSize > 8)
     EncodeInt32LE(header+8, packet.m_nInfoField2);
 
+#if 0
+  int on=1;
+  setsockopt(m_socket, IPPROTO_TCP, TCP_CORK, &on, sizeof(on));
+#endif
   if (!WriteN(header, nSize))
   {
     Log(LOGWARNING, "%s: couldn't send rtmp header", __FUNCTION__);
@@ -1387,13 +1535,21 @@ bool CRTMP::SendRTMP(RTMPPacket &packet)
       if (!WriteN(&sep, 1))
         return false;  
     }
+#if 0
+    if (on) {
+		on = 0;
+	  setsockopt(m_socket, IPPROTO_TCP, TCP_CORK, &on, sizeof(on));
+	}
+#endif
   }
 
   if (packet.m_packetType == 0x14) // we invoked a remote method, keep it in call queue till result arrives
     m_methodCalls.push_back(ReadString(packet.m_body + 1));
 
-  m_vecChannelsOut[packet.m_nChannel] = packet;
-  m_vecChannelsOut[packet.m_nChannel].m_body = NULL;
+  if (!m_vecChannelsOut[packet.m_nChannel])
+    m_vecChannelsOut[packet.m_nChannel] = new RTMPPacket;
+  *m_vecChannelsOut[packet.m_nChannel] = packet;
+  m_vecChannelsOut[packet.m_nChannel]->m_body = NULL;
   return true;
 }
 
@@ -1408,12 +1564,16 @@ void CRTMP::Close()
   m_nBytesIn = 0;
   m_nBytesInSent = 0;
 
-  for (int i=0; i<64; i++)
+  for (int i=0; i<65600; i++)
   {
-    m_vecChannelsIn[i].Reset();
-    m_vecChannelsIn[i].m_nChannel = i;
-    m_vecChannelsOut[i].Reset();
-    m_vecChannelsOut[i].m_nChannel = i;
+    if (m_vecChannelsIn[i]) {
+	  delete m_vecChannelsIn[i];
+	  m_vecChannelsIn[i] = NULL;
+	}
+	if (m_vecChannelsOut[i]) {
+	  delete m_vecChannelsOut[i];
+	  m_vecChannelsOut[i] = NULL;
+	}
   }
 
   m_bPlaying = false;

Modified: rtmp.h
==============================================================================
--- rtmp.h	Sun Oct 11 06:33:52 2009	(r8)
+++ rtmp.h	Fri Oct 30 02:26:19 2009	(r9)
@@ -84,6 +84,7 @@ typedef struct
 	char *SWFHash;
 	uint32_t SWFSize;
 	char *flashVer;
+	char *subscribepath;
 
 	double seekTime;
 	bool bLiveStream;
@@ -123,6 +124,7 @@ class CRTMP
 	char *swfSHA256Hash,
 	uint32_t swfSize,
 	char *flashVer, 
+	char *subscribepath, 
       	double dTime,
 	bool bLiveStream,
 	long int timeout=300);
@@ -130,7 +132,7 @@ class CRTMP
       bool IsConnected(); 
       double GetDuration();
 
-      bool GetNextMediaPacket(RTMPPacket &packet);
+      int GetNextMediaPacket(RTMPPacket &packet);
 
       void Close();
 
@@ -147,7 +149,9 @@ class CRTMP
       static std::string ReadString(const char *data);
       static bool ReadBool(const char *data);
       static double ReadNumber(const char *data);
+	  bool SendPause(bool DoPause, double dTime);
 
+	  static bool DumpMetaData(AMFObject &obj);
       static bool FindFirstMatchingProperty(AMFObject &obj, std::string name, AMFObjectProperty &p);
 
     protected:
@@ -161,13 +165,14 @@ class CRTMP
       bool SendPing(short nType, unsigned int nObject, unsigned int nTime = 0);
       bool SendBGHasStream(double dId, char *playpath);
       bool SendCreateStream(double dStreamId);
+      bool SendDeleteStream(double dStreamId);
+      bool SendFCSubscribe(char *subscribepath);
       bool SendPlay();
-      bool SendPause();
       bool SendSeek(double dTime);
       bool SendBytesReceived();
 
-      void HandleInvoke(const char *body, unsigned int nBodySize);
-      void HandleMetadata(char *body, unsigned int len);
+      int HandleInvoke(const char *body, unsigned int nBodySize);
+      bool HandleMetadata(char *body, unsigned int len);
       void HandleChangeChunkSize(const RTMPPacket &packet);
       void HandleAudio(const RTMPPacket &packet);
       void HandleVideo(const RTMPPacket &packet);
@@ -184,6 +189,7 @@ class CRTMP
       bool WriteN(const char *buffer, int n);
 
       bool FillBuffer();
+	  void FlushBuffer();
 
       int  m_socket;
       int  m_chunkSize;
@@ -205,14 +211,12 @@ class CRTMP
       char *m_pBuffer;      // data read from socket
       char *m_pBufferStart; // pointer into m_pBuffer of next byte to process
       int  m_nBufferSize;   // number of unprocessed bytes in buffer
-      RTMPPacket m_vecChannelsIn[64];
-      RTMPPacket m_vecChannelsOut[64];
-      int  m_channelTimestamp[64]; // abs timestamp of last packet
+      RTMPPacket *m_vecChannelsIn[65600];
+      RTMPPacket *m_vecChannelsOut[65600];
+      int  m_channelTimestamp[65600]; // abs timestamp of last packet
 
       double m_fDuration; // duration of stream in seconds
   };
 };
 
 #endif
-
-

Modified: rtmpdump.cpp
==============================================================================
--- rtmpdump.cpp	Sun Oct 11 06:33:52 2009	(r8)
+++ rtmpdump.cpp	Fri Oct 30 02:26:19 2009	(r9)
@@ -34,6 +34,8 @@
 #include "AMFObject.h"
 #include "parseurl.h"
 
+int debuglevel = 1;
+
 using namespace RTMP_LIB;
 
 #define RTMPDUMP_VERSION	"v1.6"
@@ -120,9 +122,11 @@ int WriteStream(
 	static bool bFoundFlvKeyframe = false;
 
 	uint32_t prevTagSize = 0;
+	int rtnGetNextMediaPacket = 0;
 	RTMPPacket packet;
 
-	if(rtmp->GetNextMediaPacket(packet))
+	rtnGetNextMediaPacket = rtmp->GetNextMediaPacket(packet);
+	if(rtnGetNextMediaPacket)
 	{
 		char *packetBody	= packet.m_body;
 		unsigned int nPacketLen	= packet.m_nBodySize;
@@ -158,7 +162,7 @@ int WriteStream(
 					std::string metastring = metaObj.GetProperty(0).GetString();
 
                                 	if(metastring == "onMetaData") {
-						// comapre
+						// compare
 						if((nMetaHeaderSize != nPacketLen) || 
 						   (memcmp(metaHeader, packetBody, nMetaHeaderSize) != 0)) {
 							return -2;
@@ -398,6 +402,12 @@ stopKeyframeSearch:
 		if(tsm)
 			*tsm = nTimeStamp;
 
+                // Return 0 if this was completed nicely with invoke message Play.Stop or Play.Complete
+                if (rtnGetNextMediaPacket == 2) {
+                        Log(LOGDEBUG, "Got Play.Complete or Play.Stop from server. Assuming stream is complete");
+                        return 0;
+                }
+
 		return size;
 	}
 
@@ -450,11 +460,14 @@ int main(int argc, char **argv)
 
 	char *hostname = 0;
 	char *playpath = 0;
+	char *subscribepath = 0;
 	int port = -1;
 	int protocol = RTMP_PROTOCOL_UNDEFINED;
 	bool bLiveStream = false; // is it a live stream? then we can't seek/resume
 
-	long int timeout = 300; // timeout connection afte 300 seconds
+	long int timeout = 300; // timeout connection after 300 seconds
+	uint32_t dStartOffset = 0; // seek position in non-live mode
+	uint32_t dStopOffset = 0;
 
 	char *rtmpurl = 0;
 	char *swfUrl = 0;
@@ -468,7 +481,20 @@ int main(int argc, char **argv)
 
 	char *flvFile = 0;
 
-	char DEFAULT_FLASH_VER[]  = "LNX 9,0,124,0";
+	char DEFAULT_FLASH_VER[]  = "LNX 10,0,22,87";
+	
+	signal(SIGINT, sigIntHandler);
+
+	/* sleep(30); */
+
+	// Check for --quiet option before printing any output
+	int index = 0;
+       	while (index < argc)
+	{
+		if ( strcmp( argv[index], "--quiet")==0 || strcmp( argv[index], "-q")==0 )
+			debuglevel = LOGCRIT;
+		index++;
+	}
 
  	LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION);
 	LogPrintf("(c) 2009 Andrej Stepanchuk, license: GPL\n\n");
@@ -495,12 +521,16 @@ int main(int argc, char **argv)
 		{"timeout", 1, NULL, 'm'},
 		{"buffer",  1, NULL, 'b'},
 		{"skip",    1, NULL, 'k'},
+		{"subscribe",1,NULL, 'd'},
+		{"start",   1, NULL, 'A'},
+		{"stop",    1, NULL, 'B'},
+		{"debug",   0, NULL, 'z'},
+		{"quiet",   0, NULL, 'q'},
+		{"verbose", 0, NULL, 'V'},
 		{0,0,0,0}
 	};
 
-	signal(SIGINT, sigIntHandler);
-
-	while((opt = getopt_long(argc, argv, "hver:s:t:p:a:f:o:u:n:c:l:y:m:k:w:x:", longopts, NULL)) != -1) {
+	while((opt = getopt_long(argc, argv, "hVveqzr:s:t:p:a:f:o:u:n:c:l:y:m:k:d:A:B:w:x:", longopts, NULL)) != -1) {
 		switch(opt) {
 			case 'h':
 				LogPrintf("\nThis program dumps the media content streamed over rtmp.\n\n");
@@ -518,14 +548,20 @@ int main(int argc, char **argv)
 				LogPrintf("--swfsize|-x num        Size of the decompressed SWF file, required for SWFVerification\n");
 				LogPrintf("--auth|-u string        Authentication string to be appended to the connect string\n");
 				LogPrintf("--flashVer|-f string    Flash version string (default: \"%s\")\n", DEFAULT_FLASH_VER);
-				LogPrintf("--live|-v               Save a live stream, no --resume (seeking) of live strems possible\n");
+				LogPrintf("--live|-v               Save a live stream, no --resume (seeking) of live streams possible\n");
+				LogPrintf("--subscribe|-d string   Stream name to subscribe to (otherwise defaults to playpath if live is specifed)\n");
 				LogPrintf("--flv|-o string         FLV output file name, if the file name is - print stream to stdout\n");
 				LogPrintf("--resume|-e             Resume a partial RTMP download\n");
 				LogPrintf("--timeout|-m num        Timeout connection num seconds (default: %lu)\n", timeout);
+				LogPrintf("--start|-A num          Start at num seconds into stream (not valid when using --live)\n");
+				LogPrintf("--stop|-B num           Stop at num seconds into stream\n");
 				LogPrintf("--buffer|-b             Buffer time in milliseconds (default: %lu), this option makes only sense in stdout mode (-o -)\n", 
 					bufferTime);
 				LogPrintf("--skip|-k num           Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: %d)\n\n",
 					nSkipKeyFrames);
+				LogPrintf("--quiet|-q              Supresses all command output.\n");
+				LogPrintf("--verbose|-X            Verbose command output.\n");
+				LogPrintf("--debug|-z              Debug level command output.\n");
 				LogPrintf("If you don't pass parameters for swfUrl, pageUrl, app or auth these propertiews will not be included in the connect ");
 				LogPrintf("packet.\n\n");
 				return RD_SUCCESS;
@@ -571,6 +607,9 @@ int main(int argc, char **argv)
 			case 'v':
 				bLiveStream = true; // no seeking or resuming possible!
 				break;
+			case 'd':
+				subscribepath = optarg;
+				break;
 			case 'n':
 				hostname = optarg;
 				break;
@@ -643,6 +682,23 @@ int main(int argc, char **argv)
 			case 'm':
 				timeout = atoi(optarg);
 				break;
+			case 'A':
+				dStartOffset = int(atof(optarg)*1000.0);
+                                //printf("dStartOffset = %d\n", dStartOffset);
+				break;
+			case 'B':
+				dStopOffset = int(atof(optarg)*1000.0);
+                                //printf("dStartOffset = %d\n", dStartOffset);
+				break;
+			case 'q':
+				debuglevel = LOGCRIT;
+				break;
+			case 'V':
+				debuglevel = LOGDEBUG;
+				break;
+			case 'z':
+				debuglevel = LOGALL;
+				break;
 			default:
 				LogPrintf("unknown option: %c\n", opt);
 				break;
@@ -721,7 +777,8 @@ int main(int argc, char **argv)
 
 	int bufferSize = 1024*1024;
 	char *buffer = (char *)malloc(bufferSize);
-        int nRead = 0;
+	int nRead = 0, count = 0;
+
 
 	memset(buffer, 0, bufferSize);
 
@@ -756,7 +813,7 @@ int main(int argc, char **argv)
 				goto clean;
 			}
 			if(buffer[0] != 'F' || buffer[1] != 'L' || buffer[2] != 'V' || buffer[3] != 0x01) {
-				Log(LOGERROR, "Inavlid FLV file!");
+				Log(LOGERROR, "Invalid FLV file!");
 				nStatus = RD_FAILED;
                                 goto clean;
 			}
@@ -783,7 +840,7 @@ int main(int argc, char **argv)
 				Log(LOGWARNING, "First prevTagSize is not zero: prevTagSize = 0x%08X", prevTagSize);
 			}
 
-			// go through the file to find the mata data!
+			// go through the file to find the meta data!
 			uint32_t pos = dataOffset+4;
 			bool bFoundMetaHeader = false;
 
@@ -969,9 +1026,9 @@ start:
 			file = stdout;
 		else
 		{
-			file = fopen(flvFile, "wb");
+			file = fopen(flvFile, "w");
 			if(file == 0) {
-                        	LogPrintf("Failed to open file!\n");
+                        	LogPrintf("Failed to open file! %s\n", flvFile);
                         	return RD_FAILED;
                 	}
 		}
@@ -984,17 +1041,28 @@ start:
 
 	LogPrintf("Connecting ...\n");
 
+	// User defined seek offset
+	if (dStartOffset > 0) {
+        	if (bLiveStream)
+                	Log(LOGWARNING, "Can't seek in a live stream, ignoring --seek option");
+                // Don't need the offset if resuming an existing file
+                else if (bResume)
+                        Log(LOGDEBUG, "Can't seek a resumed stream, ignoring --seek option");
+                else
+                        dSeek += dStartOffset;
+        }
+
 	//{ // here we decrease the seek time by 10ms to make sure the server starts with the next keyframe
 	//double dFindSeek = dSeek;
 
 	//if(!bAudioOnly && dFindSeek >= 10.0)
 	//	dFindSeek-=10.0;
 
-	if (!rtmp->Connect(protocol, hostname, port, playpath, tcUrl, swfUrl, pageUrl, app, auth, swfHash, swfSize, flashVer, dSeek, bLiveStream, timeout)) {
+	if (!rtmp->Connect(protocol, hostname, port, playpath, tcUrl, swfUrl, pageUrl, app, auth, swfHash, swfSize, flashVer, subscribepath, dSeek, bLiveStream, timeout)) {
 		LogPrintf("Failed to connect!\n");
 		return RD_FAILED;
 	}
-	LogPrintf("Connected...\n\n");
+	Log(LOGINFO, "Connected...");
 	//}
 	
 	/*#ifdef _DEBUG
@@ -1007,13 +1075,16 @@ start:
 	}
 
 	// print initial status
-	LogPrintf("Starting download at ");
-	if(duration > 0) {
+	// Workaround to exit with 0 if the file is fully (> 99.9%) downloaded
+	if( duration > 0 && (double)timestamp >= (double)duration*999.0 ) {
+                LogPrintf("Already Completed at: TS=%.1f Duration=%.1f\n", (double)timestamp, (double)duration);
+                goto clean;
+        } else if(duration > 0) {
 		percent = ((double)timestamp) / (duration*1000.0)*100.0;
                 percent = round(percent*10.0)/10.0;
-                LogPrintf("%.3f KB (%.1f%%)\n", (double)size/1024.0, percent);
+                LogPrintf("Starting download at %.3f kB (%.1f%%)\n", (double)size/1024.0, percent);
         } else {
-                LogPrintf("%.3f KB\n", (double)size/1024.0);
+                LogPrintf("Starting download at %.3f kB\n", (double)size/1024.0);
         }
 
 	// write FLV header if not resuming
@@ -1032,11 +1103,12 @@ start:
 			goto clean;
 		}
 	}
-
 	do
 	{
 		nRead = WriteStream(rtmp, &buffer, bufferSize, &timestamp, bResume, dSeek, metaHeader, nMetaHeaderSize, initialFrame, initialFrameType, nInitialFrameSize, &dataType);
 
+#define PRINT_CHUNK	32768
+
 		//LogPrintf("nRead: %d\n", nRead);
 		if(nRead > 0) {
 			if(fwrite(buffer, sizeof(unsigned char), nRead, file) != (size_t)nRead) {
@@ -1045,6 +1117,7 @@ start:
                                 goto clean;
                         }
 			size += nRead;
+			count += nRead;
 	
 			//LogPrintf("write %dbytes (%.1f KB)\n", nRead, nRead/1024.0);
 			if(duration <= 0) // if duration unknown try to get it from the stream (onMetaData)
@@ -1061,38 +1134,73 @@ start:
 				}
 				percent = ((double)timestamp) / (duration*1000.0)*100.0;
 				percent = round(percent*10.0)/10.0;
-				LogPrintf("\r%.3f KB (%.1f%%)", (double)size/1024.0, percent);
+				if (count > PRINT_CHUNK && debuglevel >= LOGINFO) {
+					LogPrintf("\r%.3f KB (%.1f%%)", (double)size/1024.0, percent);
+					count = 0;
+				}
 			} else {
-				LogPrintf("\r%.3f KB", (double)size/1024.0);
+				if (count > PRINT_CHUNK && debuglevel >= LOGINFO) {
+					LogPrintf("\r%.3f KB", (double)size/1024.0);
+					count = 0;
+				}
 			}
 		}
 		#ifdef _DEBUG
 		else { Log(LOGDEBUG, "zero read!"); }
 		#endif
 
+		// Force clean close if a specified stop offset is reached
+		if (dStopOffset && timestamp >= dStopOffset) {
+		        LogPrintf("\nStop offset has been reached at %.2f seconds\n", (double)dStopOffset/1000.0);
+		        nRead = 0;
+		        rtmp->Close();
+		}
+
 	} while(!bCtrlC && nRead > -1 && rtmp->IsConnected());
 
+	// finalize header by writing the correct dataType (video, audio, video+audio)
+	if(!bResume && dataType != 0x5 && !bStdoutMode) {
+		Log(LOGDEBUG, "Writing data type: %02X", dataType);
+		fseek(file, 4, SEEK_SET);
+		fwrite(&dataType, sizeof(unsigned char), 1, file);
+	}
+
 	if(bResume && nRead == -2) {
-		LogPrintf("Couldn't resume FLV file, try --skip %d\n\n", nSkipKeyFrames+1);
+		LogPrintf("\rCouldn't resume FLV file, try --skip %d\n\n", nSkipKeyFrames+1);
 		nStatus = RD_FAILED;
 		goto clean;
 	}
+	
+	// If duration is available then assume the download is complete if > 99.9%
+	if (bLiveStream == false) {
+		if (duration > 0 && percent > 99.9) {
+			LogPrintf("\nDownload complete\n");
+			nStatus = RD_SUCCESS;
+			goto clean;
+		//} else if ( bCtrlC || nRead != (-1) ) {
+		} else {
+			LogPrintf("\nDownload may be incomplete (downloaded about %.2f%%), try --resume\n", percent);
+			nStatus = RD_INCOMPLETE;
+			goto clean;
+		}
+	}
 
-	// finalize header by writing the correct dataType (video, audio, video+audio)
-	if(!bResume && dataType != 0x5 && !bStdoutMode) {
-		//Log(LOGDEBUG, "Writing data type: %02X", dataType);
-		fseek(file, 4, SEEK_SET);
-		fwrite(&dataType, sizeof(unsigned char), 1, file);
+	// If nRead is zero then assume complete
+	if(nRead == 0) {
+	        LogPrintf("\nDownload complete\n");
+	        nStatus = RD_SUCCESS;
+	        goto clean;
 	}
-	if((duration > 0 && percent < 99.9) || bCtrlC || nRead != (-1)) {
-		Log(LOGWARNING, "Download may be incomplete (downloaded about %.1f%%), try --resume!", percent);
+	
+	// Ensure we have a non-zero exit code where WriteStream has failed
+	if (nRead < 0)
 		nStatus = RD_INCOMPLETE;
-	}
 
+        //Log(LOGDEBUG, "nStatus: %d, nRead: %d", nStatus, nRead);
 clean:
-	LogPrintf("Closing connection... ");
+	LogPrintf("\rClosing connection.\n");
 	rtmp->Close();
-	LogPrintf("done!\n\n");
+	//LogPrintf("done!\n\n");
 
 	if(file != 0)
 		fclose(file);

Modified: rtmppacket.cpp
==============================================================================
--- rtmppacket.cpp	Sun Oct 11 06:33:52 2009	(r8)
+++ rtmppacket.cpp	Fri Oct 30 02:26:19 2009	(r9)
@@ -21,6 +21,7 @@
  */
 
 #include <string.h>
+#include <stdlib.h>
 
 #include "rtmppacket.h"
 #include "log.h"
@@ -52,10 +53,9 @@ void RTMPPacket::Reset()
 
 bool RTMPPacket::AllocPacket(int nSize)
 {
-  m_body = new char[nSize];
+  m_body = (char *)calloc(1, nSize);
   if (!m_body)
     return false;
-  memset(m_body,0,nSize);
   m_nBytesRead = 0;
   return true;
 }
@@ -69,7 +69,7 @@ void RTMPPacket::FreePacket()
 void RTMPPacket::FreePacketHeader()
 {
   if (m_body)
-    delete [] m_body;
+    free(m_body);
   m_body = NULL;
 }
 

Modified: rtmppacket.h
==============================================================================
--- rtmppacket.h	Sun Oct 11 06:33:52 2009	(r8)
+++ rtmppacket.h	Fri Oct 30 02:26:19 2009	(r9)
@@ -23,6 +23,7 @@
  */
 
 #include <string>
+#include <stdint.h>
 
 #define RTMP_PACKET_TYPE_AUDIO 0x08
 #define RTMP_PACKET_TYPE_VIDEO 0x09
@@ -56,7 +57,7 @@ namespace RTMP_LIB
 
       BYTE	m_headerType;
       BYTE	m_packetType;
-      BYTE	m_nChannel;
+      int	m_nChannel;
       int32_t	m_nInfoField1; // 3 first bytes
       int32_t	m_nInfoField2; // last 4 bytes in a long header, absolute timestamp for long headers, relative timestamp for short headers 
       bool      m_hasAbsTimestamp; // timestamp absolute or relative?

Modified: streams.cpp
==============================================================================
--- streams.cpp	Sun Oct 11 06:33:52 2009	(r8)
+++ streams.cpp	Fri Oct 30 02:26:19 2009	(r9)
@@ -36,9 +36,11 @@
 #include "AMFObject.h"
 #include "parseurl.h"
 
+int debuglevel = 1;
+
 using namespace RTMP_LIB;
 
-#define RTMPDUMP_STREAMS_VERSION	"v1.2"
+#define RTMPDUMP_STREAMS_VERSION	"v1.4"
 
 #define RD_SUCCESS		0
 #define RD_FAILED		1
@@ -108,7 +110,10 @@ typedef struct
 	char *swfHash;
 	uint32_t swfSize;
 	char *flashVer;
+	char *subscribepath;
 
+	uint32_t dStartOffset;
+	uint32_t dStopOffset;
 	uint32_t nTimeStamp;
 } RTMP_REQUEST;
 
@@ -119,7 +124,7 @@ RTMP_REQUEST defaultRTMPRequest;
 
 bool ParseOption(char opt, char *arg, RTMP_REQUEST *req);
 
-char DEFAULT_FLASH_VER[]  = "LNX 9,0,124,0";
+char DEFAULT_FLASH_VER[]  = "LNX 10,0,22,87";
 
 #ifdef _DEBUG
 uint32_t debugTS = 0;
@@ -242,9 +247,11 @@ int WriteStream(
 	)
 {
 	uint32_t prevTagSize = 0;
+	int rtnGetNextMediaPacket = 0;
 	RTMPPacket packet;
 
-	if(rtmp->GetNextMediaPacket(packet))
+	rtnGetNextMediaPacket = rtmp->GetNextMediaPacket(packet);
+	if(rtnGetNextMediaPacket)
 	{
 		char *packetBody	= packet.m_body;
 		unsigned int nPacketLen	= packet.m_nBodySize;
@@ -359,6 +366,12 @@ int WriteStream(
 			//ptr += 4;
 		}
 
+                // Return 0 if this was completed nicely with invoke message Play.Stop or Play.Complete
+                if (rtnGetNextMediaPacket == 2) {
+                        Log(LOGDEBUG, "Got Play.Complete or Play.Stop from server. Assuming stream is complete");
+                        return 0;
+                }
+
 		return size;
 	}
 
@@ -597,8 +610,16 @@ void processTCPrequest
 	// send the packets
 	buffer = (char *)calloc(PACKET_SIZE,1);
 
+	// User defined seek offset
+	if (req.dStartOffset > 0) {
+		if (req.bLiveStream)
+			Log(LOGWARNING, "Can't seek in a live stream, ignoring --seek option");
+		else
+			dSeek += req.dStartOffset;
+	}
+
         if(dSeek != 0) {
-                LogPrintf("Continuing at TS: %d ms\n", req.nTimeStamp);
+                LogPrintf("Starting at TS: %d ms\n", req.nTimeStamp);
         }
 
         Log(LOGDEBUG, "Setting buffer time to: %dms", req.bufferTime);
@@ -618,6 +639,7 @@ void processTCPrequest
 			req.swfHash, 
 			req.swfSize, 
 			req.flashVer, 
+			req.subscribepath, 
 			dSeek, 
 			req.bLiveStream, 
 			req.timeout)) {
@@ -670,14 +692,22 @@ void processTCPrequest
                         	if(duration > 0) {
                                 	percent = ((double)(dSeek+req.nTimeStamp)) / (duration*1000.0)*100.0;
                                 	percent = round(percent*10.0)/10.0;
-                                	LogPrintf("\r%.3f KB (%.1f%%)", (double)size/1024.0, percent);
+                                	LogPrintf("\r%.3f KB / %.2f sec (%.1f%%)", (double)size/1024.0, (double)(req.nTimeStamp)/1000.0,  percent);
                         	} else {
-                                	LogPrintf("\r%.3f KB", (double)size/1024.0);
+                                	LogPrintf("\r%.3f KB / %.2f sec", (double)size/1024.0, (double)(req.nTimeStamp)/1000.0);
                         	}
                 	}
                 	#ifdef _DEBUG
                 	else { Log(LOGDEBUG, "zero read!"); }
                 	#endif	
+
+				// Force clean close if a specified stop offset is reached
+				if (req.dStopOffset && req.nTimeStamp >= req.dStopOffset) {
+					LogPrintf("\nStop offset has been reached at %.2f seconds\n", (double)req.dStopOffset/1000.0);
+					nRead = 0;
+					rtmp->Close();
+				}
+
 		} while(server->state == STREAMING_IN_PROGRESS && nRead > -1 && rtmp->IsConnected() && nWritten >= 0);
 	}
 cleanup:
@@ -839,6 +869,9 @@ bool ParseOption(char opt, char *arg, RT
                         case 'v':
                                 req->bLiveStream = true; // no seeking or resuming possible!
                                 break;
+						case 'd':
+								req->subscribepath = optarg;
+								break;
                         case 'n':
                                 req->hostname = arg;
                                 break;
@@ -906,6 +939,23 @@ bool ParseOption(char opt, char *arg, RT
 			case 'm':
                                 req->timeout = atoi(arg);
                                 break;
+			case 'A':
+				req->dStartOffset = atoi(arg)*1000;
+                                //printf("dStartOffset = %d\n", dStartOffset);
+				break;
+			case 'B':
+				req->dStopOffset = atoi(arg)*1000;
+                                //printf("dStartOffset = %d\n", dStartOffset);
+				break;
+			case 'q':
+				debuglevel = LOGCRIT;
+				break;
+			case 'V':
+				debuglevel = LOGDEBUG;
+				break;
+			case 'z':
+				debuglevel = LOGALL;
+				break;
                         default:
                                 LogPrintf("unknown option: %c, arg: %s\n", opt, arg);
                                 break;
@@ -959,14 +1009,20 @@ int main(int argc, char **argv)
 		{"timeout", 1, NULL, 'm'},
 		{"buffer",  1, NULL, 'b'},
 		//{"skip",    1, NULL, 'k'},
-		{"device",  1, NULL, 'd'},
+		{"device",  1, NULL, 'D'},
 		{"sport",   1, NULL, 'g'},
+		{"subscribe",1,NULL, 'd'},
+		{"start",   1, NULL, 'A'},
+		{"stop",    1, NULL, 'B'},
+		{"debug",   0, NULL, 'z'},
+		{"quiet",   0, NULL, 'q'},
+		{"verbose", 0, NULL, 'V'},
 		{0,0,0,0}
 	};
 
 	signal(SIGINT, sigIntHandler);
 
-	while((opt = getopt_long(argc, argv, "hvr:s:t:p:a:f:u:n:c:l:y:m:d:g:w:x:", longopts, NULL)) != -1) {
+	while((opt = getopt_long(argc, argv, "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:g:w:x:", longopts, NULL)) != -1) {
 		switch(opt) {
 			case 'h':
 				LogPrintf("\nThis program dumps the media content streamed over rtmp.\n\n");
@@ -984,19 +1040,25 @@ int main(int argc, char **argv)
 				LogPrintf("--swfsize|-x num        Size of the decompressed SWF file, required for SWFVerification\n");
 				LogPrintf("--auth|-u string        Authentication string to be appended to the connect string\n");
 				LogPrintf("--flashVer|-f string    Flash version string (default: \"%s\")\n", DEFAULT_FLASH_VER);
-				LogPrintf("--live|-v               Save a live stream, no --resume (seeking) of live strems possible\n");
+				LogPrintf("--live|-v               Get a live stream, no --resume (seeking) of live strems possible\n");
+				LogPrintf("--subscribe|-d string   Stream name to subscribe to (otherwise defaults to playpath if live is specifed)\n");
 				LogPrintf("--timeout|-m num        Timeout connection num seconds (default: %lu)\n", defaultRTMPRequest.timeout);
+				LogPrintf("--start|-A num          Start at num seconds into stream (not valid when using --live)\n");
+				LogPrintf("--stop|-B num           Stop at num seconds into stream\n");
 				LogPrintf("--buffer|-b             Buffer time in milliseconds (default: %lu)\n\n", 
 					defaultRTMPRequest.bufferTime);
 
-				LogPrintf("--device|-d             Streaming device ip address (default: %s)\n", DEFAULT_HTTP_STREAMING_DEVICE);
+				LogPrintf("--device|-D             Streaming device ip address (default: %s)\n", DEFAULT_HTTP_STREAMING_DEVICE);
 				LogPrintf("--sport|-g              Streaming port (default: %d)\n\n", nHttpStreamingPort);
+				LogPrintf("--quiet|-q              Supresses all command output.\n");
+				LogPrintf("--verbose|-x            Verbose command output.\n");
+				LogPrintf("--debug|-z              Debug level command output.\n");
 				LogPrintf("If you don't pass parameters for swfUrl, pageUrl, app or auth these propertiews will not be included in the connect ");
 				LogPrintf("packet.\n\n");
 				return RD_SUCCESS;
 				break;
 			// streaming server specific options
-			case 'd':
+			case 'D':
 				if(inet_addr(optarg) == INADDR_NONE) {
 					Log(LOGERROR, "Invalid binding address (requested address %s), ignoring", optarg);
 				} else {


More information about the rtmpdump mailing list