[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, ×tamp, 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