From: hyc Date: Sat, 27 Mar 2010 05:35:04 +0000 (+0000) Subject: Option restructuring, allow most options to be passed along X-Git-Tag: v2.4~140 X-Git-Url: https://granicus.if.org/sourcecode?a=commitdiff_plain;h=18f3bf620a97bde394421e27e002b6e41e9ca700;p=rtmpdump Option restructuring, allow most options to be passed along with the RTMP URL, clean up git-svn-id: svn://svn.mplayerhq.hu/rtmpdump/trunk@405 400ebc74-4327-4243-bc38-086b20814532 --- diff --git a/librtmp/Makefile b/librtmp/Makefile index 8e037b5..7de910e 100644 --- a/librtmp/Makefile +++ b/librtmp/Makefile @@ -32,7 +32,7 @@ log.o: log.c log.h Makefile rtmp.o: rtmp.c rtmp.h rtmp_sys.h handshake.h dh.h log.h amf.h Makefile amf.o: amf.c amf.h bytes.h log.h Makefile hashswf.o: hashswf.c http.h rtmp.h rtmp_sys.h Makefile -parseurl.o: parseurl.c rtmp_sys.h log.h Makefile +parseurl.o: parseurl.c rtmp.h rtmp_sys.h log.h Makefile librtmp.pc: librtmp.pc.in Makefile sed -e "s;@prefix@;$(prefix);" -e "s;@VERSION@;$(VERSION);" \ diff --git a/librtmp/handshake.h b/librtmp/handshake.h index e01d286..249a6fb 100644 --- a/librtmp/handshake.h +++ b/librtmp/handshake.h @@ -346,7 +346,7 @@ HandShake(RTMP * r, bool FP9HandShake) char type; getoff *getdh, *getdig; - if (encrypted || r->Link.SWFHash.av_len) + if (encrypted || r->Link.SWFSize) FP9HandShake = true; else FP9HandShake = false; @@ -504,7 +504,7 @@ HandShake(RTMP * r, bool FP9HandShake) dhposServer); /* generate SWFVerification token (SHA256 HMAC hash of decompressed SWF, key are the last 32 bytes of the server handshake) */ - if (r->Link.SWFHash.av_len) + if (r->Link.SWFSize) { const char swfVerify[] = { 0x01, 0x01 }; char *vend = r->Link.SWFVerificationResponse+sizeof(r->Link.SWFVerificationResponse); @@ -512,7 +512,7 @@ HandShake(RTMP * r, bool FP9HandShake) memcpy(r->Link.SWFVerificationResponse, swfVerify, 2); AMF_EncodeInt32(&r->Link.SWFVerificationResponse[2], vend, r->Link.SWFSize); AMF_EncodeInt32(&r->Link.SWFVerificationResponse[6], vend, r->Link.SWFSize); - HMACsha256(r->Link.SWFHash.av_val, SHA256_DIGEST_LENGTH, + HMACsha256(r->Link.SWFHash, SHA256_DIGEST_LENGTH, &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH], SHA256_DIGEST_LENGTH, &r->Link.SWFVerificationResponse[10]); } @@ -866,7 +866,7 @@ SHandShake(RTMP * r) dhposClient); /* generate SWFVerification token (SHA256 HMAC hash of decompressed SWF, key are the last 32 bytes of the server handshake) */ - if (r->Link.SWFHash.av_len) + if (r->Link.SWFSize) { const char swfVerify[] = { 0x01, 0x01 }; char *vend = r->Link.SWFVerificationResponse+sizeof(r->Link.SWFVerificationResponse); @@ -874,7 +874,7 @@ SHandShake(RTMP * r) memcpy(r->Link.SWFVerificationResponse, swfVerify, 2); AMF_EncodeInt32(&r->Link.SWFVerificationResponse[2], vend, r->Link.SWFSize); AMF_EncodeInt32(&r->Link.SWFVerificationResponse[6], vend, r->Link.SWFSize); - HMACsha256(r->Link.SWFHash.av_val, SHA256_DIGEST_LENGTH, + HMACsha256(r->Link.SWFHash, SHA256_DIGEST_LENGTH, &serversig[RTMP_SIG_SIZE - SHA256_DIGEST_LENGTH], SHA256_DIGEST_LENGTH, &r->Link.SWFVerificationResponse[10]); } diff --git a/librtmp/rtmp.c b/librtmp/rtmp.c index 19f90d8..f24c3be 100644 --- a/librtmp/rtmp.c +++ b/librtmp/rtmp.c @@ -276,7 +276,8 @@ RTMP_UpdateBufferMS(RTMP *r) #else #define OSS "GNU" #endif -static const char DEFAULT_FLASH_VER[] = OSS " 10,0,32,18"; +#define DEF_VERSTR OSS " 10,0,32,18" +static const char DEFAULT_FLASH_VER[] = DEF_VERSTR; const AVal RTMP_DefaultFlashVer = { (char *)DEFAULT_FLASH_VER, sizeof(DEFAULT_FLASH_VER) - 1 }; @@ -296,8 +297,8 @@ RTMP_SetupStream(RTMP *r, uint32_t swfSize, AVal *flashVer, AVal *subscribepath, - double dTime, - uint32_t dLength, bool bLiveStream, long int timeout) + double dStart, + double dStop, bool bLiveStream, long int timeout) { RTMP_Log(RTMP_LOGDEBUG, "Protocol : %s", RTMPProtocolStrings[protocol&7]); RTMP_Log(RTMP_LOGDEBUG, "Hostname : %.*s", host->av_len, host->av_val); @@ -318,10 +319,10 @@ RTMP_SetupStream(RTMP *r, RTMP_Log(RTMP_LOGDEBUG, "subscribepath : %s", subscribepath->av_val); if (flashVer && flashVer->av_val) RTMP_Log(RTMP_LOGDEBUG, "flashVer : %s", flashVer->av_val); - if (dTime > 0) - RTMP_Log(RTMP_LOGDEBUG, "SeekTime : %.3f sec", (double)dTime / 1000.0); - if (dLength > 0) - RTMP_Log(RTMP_LOGDEBUG, "playLength : %.3f sec", (double)dLength / 1000.0); + if (dStart > 0) + RTMP_Log(RTMP_LOGDEBUG, "StartTime : %.3f sec", dStart); + if (dStop > 0) + RTMP_Log(RTMP_LOGDEBUG, "StopTime : %.3f sec", dStop); RTMP_Log(RTMP_LOGDEBUG, "live : %s", bLiveStream ? "yes" : "no"); RTMP_Log(RTMP_LOGDEBUG, "timeout : %d sec", timeout); @@ -329,16 +330,14 @@ RTMP_SetupStream(RTMP *r, #ifdef CRYPTO if (swfSHA256Hash != NULL && swfSize > 0) { - r->Link.SWFHash = *swfSHA256Hash; + memcpy(r->Link.SWFHash, swfSHA256Hash->av_val, sizeof(r->Link.SWFHash)); r->Link.SWFSize = swfSize; RTMP_Log(RTMP_LOGDEBUG, "SWFSHA256:"); - RTMP_LogHex(RTMP_LOGDEBUG, r->Link.SWFHash.av_val, 32); + RTMP_LogHex(RTMP_LOGDEBUG, r->Link.SWFHash, sizeof(r->Link.SWFHash)); RTMP_Log(RTMP_LOGDEBUG, "SWFSize : %lu", r->Link.SWFSize); } else { - r->Link.SWFHash.av_len = 0; - r->Link.SWFHash.av_val = NULL; r->Link.SWFSize = 0; } #endif @@ -373,15 +372,18 @@ RTMP_SetupStream(RTMP *r, if (app && app->av_len) r->Link.app = *app; if (auth && auth->av_len) - r->Link.auth = *auth; + { + r->Link.auth = *auth; + r->Link.authflag = true; + } if (flashVer && flashVer->av_len) r->Link.flashVer = *flashVer; else r->Link.flashVer = RTMP_DefaultFlashVer; if (subscribepath && subscribepath->av_len) r->Link.subscribepath = *subscribepath; - r->Link.seekTime = dTime; - r->Link.length = dLength; + r->Link.seekTime = dStart; + r->Link.stopTime = dStop; r->Link.bLiveStream = bLiveStream; r->Link.timeout = timeout; @@ -401,6 +403,251 @@ RTMP_SetupStream(RTMP *r, } } +enum { OPT_STR=0, OPT_INT, OPT_BOOL, OPT_CONN }; +static const char *optinfo[] = { + "string", "integer", "boolean", "AMF" }; + +#define OFF(x) offsetof(struct RTMP,x) + +static struct urlopt { + AVal name; + off_t off; + int otype; + char *use; +} options[] = { + { AVC("socks"), OFF(Link.sockshost), OPT_STR, + "Use the specified SOCKS proxy" }, + { AVC("app"), OFF(Link.app), OPT_STR, + "Name of target app on server" }, + { AVC("tcUrl"), OFF(Link.tcUrl), OPT_STR, + "URL to played stream" }, + { AVC("pageUrl"), OFF(Link.pageUrl), OPT_STR, + "URL of played media's web page" }, + { AVC("swfUrl"), OFF(Link.swfUrl), OPT_STR, + "URL to player SWF file" }, + { AVC("flashver"), OFF(Link.flashVer), OPT_STR, + "Flash version string (default " DEF_VERSTR ")" }, + { AVC("conn"), OFF(Link.extras), OPT_CONN, + "Append arbitrary AMF data to Connect message" }, + { AVC("playpath"), OFF(Link.playpath), OPT_STR, + "Path to target media on server" }, + { AVC("live"), OFF(Link.bLiveStream), OPT_BOOL, + "Stream is live, no seeking possible" }, + { AVC("subscribe"), OFF(Link.subscribepath), OPT_STR, + "Stream to subscribe to" }, + { AVC("token"), OFF(Link.token), OPT_STR, + "Key for SecureToken response" }, + { AVC("swfVfy"), OFF(Link.swfVfy), OPT_BOOL, + "Perform SWF Verification" }, + { AVC("swfAge"), OFF(Link.swfAge), OPT_INT, + "Number of days to use cached SWF hash" }, + { AVC("start"), OFF(Link.seekTime), OPT_INT, + "Stream start position in milliseconds" }, + { AVC("stop"), OFF(Link.stopTime), OPT_INT, + "Stream stop position in milliseconds" }, + { AVC("buffer"), OFF(m_nBufferMS), OPT_INT, + "Buffer time in milliseconds" }, + { AVC("timeout"), OFF(Link.timeout), OPT_INT, + "Session timeout in seconds" }, + { {NULL,0}, 0, 0} +}; + +static const AVal truth[] = { + AVC("1"), + AVC("on"), + AVC("yes"), + AVC("true"), + {0,0} +}; + +static void RTMP_OptUsage() +{ + int i; + + RTMP_LogPrintf("Valid RTMP options are:\n"); + for (i=0; options[i].name.av_len; i++) { + RTMP_LogPrintf("%10s %-7s %s\n", options[i].name.av_val, + optinfo[options[i].otype], options[i].use); + } +} + +static int +parseAMF(AMFObject *obj, AVal *av, int *depth) +{ + AMFObjectProperty prop = {{0,0}}; + int i; + char *p, *arg = av->av_val; + + if (arg[1] == ':') + { + p = (char *)arg+2; + switch(arg[0]) + { + case 'B': + prop.p_type = AMF_BOOLEAN; + prop.p_vu.p_number = atoi(p); + break; + case 'S': + prop.p_type = AMF_STRING; + prop.p_vu.p_aval.av_val = p; + prop.p_vu.p_aval.av_len = av->av_len - (p-arg); + break; + case 'N': + prop.p_type = AMF_NUMBER; + prop.p_vu.p_number = strtod(p, NULL); + break; + case 'Z': + prop.p_type = AMF_NULL; + break; + case 'O': + i = atoi(p); + if (i) + { + prop.p_type = AMF_OBJECT; + } + else + { + (*depth)--; + return 0; + } + break; + default: + return -1; + } + } + else if (arg[2] == ':' && arg[0] == 'N') + { + p = strchr(arg+3, ':'); + if (!p || !*depth) + return -1; + prop.p_name.av_val = (char *)arg+3; + prop.p_name.av_len = p - (arg+3); + + p++; + switch(arg[1]) + { + case 'B': + prop.p_type = AMF_BOOLEAN; + prop.p_vu.p_number = atoi(p); + break; + case 'S': + prop.p_type = AMF_STRING; + prop.p_vu.p_aval.av_val = p; + prop.p_vu.p_aval.av_len = av->av_len - (p-arg); + break; + case 'N': + prop.p_type = AMF_NUMBER; + prop.p_vu.p_number = strtod(p, NULL); + break; + case 'O': + prop.p_type = AMF_OBJECT; + break; + default: + return -1; + } + } + else + return -1; + + if (*depth) + { + AMFObject *o2; + for (i=0; i<*depth; i++) + { + o2 = &obj->o_props[obj->o_num-1].p_vu.p_object; + obj = o2; + } + } + AMF_AddProp(obj, &prop); + if (prop.p_type == AMF_OBJECT) + (*depth)++; + return 0; +} + +bool RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg) +{ + int i; + void *v; + + for (i=0; options[i].name.av_len; i++) { + if (opt->av_len != options[i].name.av_len) continue; + if (strcasecmp(opt->av_val, options[i].name.av_val)) continue; + v = (char *)r + options[i].off; + switch(options[i].otype) { + case OPT_STR: { + AVal *aptr = v; + *aptr = *arg; } + break; + case OPT_INT: { + long l = strtol(arg->av_val, NULL, 0); + *(int *)v = l; } + break; + case OPT_BOOL: { + int j; + bool b = false; + for (j=0; truth[j].av_len; j++) { + if (arg->av_len != truth[j].av_len) continue; + if (strcasecmp(arg->av_val, truth[j].av_val)) continue; + b = true; break; } + *(bool *)v = b; + } + break; + case OPT_CONN: + if (parseAMF(&r->Link.extras, arg, &r->Link.edepth)) + return false; + break; + } + break; + } + if (!options[i].name.av_len) { + RTMP_Log(RTMP_LOGERROR, "Unknown option %s", opt->av_val); + RTMP_OptUsage(); + return false; + } + + return true; +} + +bool RTMP_SetupURL(RTMP *r, char *url) +{ + AVal opt, arg; + char *p1, *p2, *ptr = strchr(url, ' '); + bool ret; + unsigned int port = 0; + + while (ptr) { + *ptr++ = '\0'; + p1 = ptr; + p2 = strchr(p1, '='); + if (!p2) + break; + opt.av_val = p1; + opt.av_len = p2 - p1; + *p2++ = '\0'; + arg.av_val = p2; + ptr = strchr(p2, ' '); + if (ptr) { + *ptr = '\0'; + arg.av_len = ptr - p2; + } else { + arg.av_len = strlen(p2); + } + ret = RTMP_SetOpt(r, &opt, &arg); + if (!ret) + return ret; + } + ret = RTMP_ParseURL(url, &r->Link.protocol, &r->Link.hostname, + &port, &r->Link.playpath0, &r->Link.app); + if (!ret) + return ret; + r->Link.port = port; + r->Link.playpath = r->Link.playpath0; + if (r->Link.swfVfy && r->Link.swfUrl.av_len) + RTMP_HashSWF(r->Link.swfUrl.av_val, &r->Link.SWFSize, + (unsigned char *)r->Link.SWFHash, r->Link.swfAge); + return true; +} + static bool add_addr_info(struct sockaddr_in *service, AVal *host, int port) { @@ -606,15 +853,12 @@ SocksNegotiate(RTMP *r) } bool -RTMP_ConnectStream(RTMP *r, double seekTime, uint32_t dLength) +RTMP_ConnectStream(RTMP *r, double seekTime) { RTMPPacket packet = { 0 }; if (seekTime >= -2.0) r->Link.seekTime = seekTime; - if (dLength >= 0) - r->Link.length = dLength; - r->m_mediaChannel = 0; while (!r->m_bPlaying && RTMP_IsConnected(r) && RTMP_ReadPacket(r, &packet)) @@ -641,16 +885,13 @@ RTMP_ConnectStream(RTMP *r, double seekTime, uint32_t dLength) } bool -RTMP_ReconnectStream(RTMP *r, int bufferTime, double seekTime, - uint32_t dLength) +RTMP_ReconnectStream(RTMP *r, double seekTime) { RTMP_DeleteStream(r); RTMP_SendCreateStream(r); - RTMP_SetBufferMS(r, bufferTime); - - return RTMP_ConnectStream(r, seekTime, dLength); + return RTMP_ConnectStream(r, seekTime); } bool @@ -681,6 +922,7 @@ RTMP_DeleteStream(RTMP *r) r->m_bPlaying = false; SendDeleteStream(r, r->m_stream_id); + r->m_stream_id = -1; } int @@ -1607,8 +1849,8 @@ SendPlay(RTMP *r) enc = AMF_EncodeNumber(enc, pend, ++r->m_numInvokes); *enc++ = AMF_NULL; - RTMP_Log(RTMP_LOGDEBUG, "%s, seekTime=%.2f, dLength=%d, sending play: %s", - __FUNCTION__, r->Link.seekTime, r->Link.length, + RTMP_Log(RTMP_LOGDEBUG, "%s, seekTime=%.2f, stopTime=%.2f, sending play: %s", + __FUNCTION__, r->Link.seekTime, r->Link.stopTime, r->Link.playpath.av_val); enc = AMF_EncodeString(enc, pend, &r->Link.playpath); if (!enc) @@ -1637,9 +1879,9 @@ SendPlay(RTMP *r) // 0: plays a frame 'start' ms away from the beginning // >0: plays a live or recoded stream for 'len' milliseconds //enc += EncodeNumber(enc, -1.0); // len - if (r->Link.length) + if (r->Link.stopTime) { - enc = AMF_EncodeNumber(enc, pend, r->Link.length); // len + enc = AMF_EncodeNumber(enc, pend, r->Link.stopTime - r->Link.seekTime); if (!enc) return false; } @@ -2230,7 +2472,7 @@ HandleCtrl(RTMP *r, const RTMPPacket *packet) //RTMP_LogHex(packet.m_body, packet.m_nBodySize); // respond with HMAC SHA256 of decompressed SWF, key is the 30byte player key, also the last 30 bytes of the server handshake are applied - if (r->Link.SWFHash.av_len) + if (r->Link.SWFSize) { RTMP_SendCtrl(r, 0x1B, 0, 0); } @@ -2866,6 +3108,9 @@ RTMP_Close(RTMP *r) r->m_resplen = 0; r->m_unackd = 0; + free(r->Link.playpath0.av_val); + r->Link.playpath0.av_val = NULL; + #ifdef CRYPTO if (r->Link.dh) { diff --git a/librtmp/rtmp.h b/librtmp/rtmp.h index fac4ac2..90f858f 100644 --- a/librtmp/rtmp.h +++ b/librtmp/rtmp.h @@ -76,8 +76,6 @@ extern "C" #define RTMP_PACKET_SIZE_SMALL 2 #define RTMP_PACKET_SIZE_MINIMUM 3 - typedef unsigned char BYTE; - typedef struct RTMPChunk { int c_headerSize; @@ -88,9 +86,9 @@ extern "C" typedef struct RTMPPacket { - BYTE m_headerType; - BYTE m_packetType; - BYTE m_hasAbsTimestamp; // timestamp absolute or relative? + uint8_t m_headerType; + uint8_t m_packetType; + uint8_t m_hasAbsTimestamp; // timestamp absolute or relative? int m_nChannel; uint32_t m_nTimeStamp; // timestamp int32_t m_nInfoField2; // last 4 bytes in a long header @@ -120,10 +118,10 @@ extern "C" typedef struct RTMP_LNK { AVal hostname; - unsigned int port; - int protocol; + AVal sockshost; - AVal playpath; + AVal playpath0; /* parsed from URL */ + AVal playpath; /* passed in explicitly */ AVal tcUrl; AVal swfUrl; AVal pageUrl; @@ -132,26 +130,30 @@ extern "C" AVal flashVer; AVal subscribepath; AVal token; - AVal playpath0; AMFObject extras; + int edepth; + + int seekTime; + int stopTime; - double seekTime; - uint32_t length; bool authflag; bool bLiveStream; + bool swfVfy; + int swfAge; + int protocol; int timeout; // number of seconds before connection times out - AVal sockshost; unsigned short socksport; + unsigned short port; #ifdef CRYPTO void *dh; // for encryption void *rc4keyIn; void *rc4keyOut; - AVal SWFHash; uint32_t SWFSize; + char SWFHash[32]; char SWFVerificationResponse[42]; #endif } RTMP_LNK; @@ -236,10 +238,13 @@ extern "C" bool RTMP_ParseURL(const char *url, int *protocol, AVal *host, unsigned int *port, AVal *playpath, AVal *app); + void RTMP_ParsePlaypath(AVal *in, AVal *out); void RTMP_SetBufferMS(RTMP *r, int size); void RTMP_UpdateBufferMS(RTMP *r); + bool RTMP_SetOpt(RTMP *r, const AVal *opt, AVal *arg); + bool RTMP_SetupURL(RTMP *r, char *url); void RTMP_SetupStream(RTMP *r, int protocol, AVal *hostname, unsigned int port, @@ -254,8 +259,8 @@ extern "C" uint32_t swfSize, AVal *flashVer, AVal *subscribepath, - double dTime, - uint32_t dLength, bool bLiveStream, long int timeout); + double dStart, + double dStop, bool bLiveStream, long int timeout); bool RTMP_Connect(RTMP *r, RTMPPacket *cp); struct sockaddr; @@ -271,9 +276,8 @@ extern "C" double RTMP_GetDuration(RTMP *r); bool RTMP_ToggleStream(RTMP *r); - bool RTMP_ConnectStream(RTMP *r, double seekTime, uint32_t dLength); - bool RTMP_ReconnectStream(RTMP *r, int bufferTime, double seekTime, - uint32_t dLength); + bool RTMP_ConnectStream(RTMP *r, double seekTime); + bool RTMP_ReconnectStream(RTMP *r, double seekTime); void RTMP_DeleteStream(RTMP *r); int RTMP_GetNextMediaPacket(RTMP *r, RTMPPacket *packet); int RTMP_ClientPacket(RTMP *r, RTMPPacket *packet); diff --git a/rtmpdump.c b/rtmpdump.c index e464460..ead20df 100644 --- a/rtmpdump.c +++ b/rtmpdump.c @@ -46,6 +46,10 @@ #define RD_FAILED 1 #define RD_INCOMPLETE 2 +#define DEF_TIMEOUT 30 /* seconds */ +#define DEF_BUFTIME (10 * 60 * 60 * 1000) /* 10 hours default */ +#define DEF_SKIPFRM 0 + // starts sockets bool InitSockets() @@ -119,6 +123,8 @@ int hex2bin(char *str, char **hex) static const AVal av_onMetaData = AVC("onMetaData"); static const AVal av_duration = AVC("duration"); +static const AVal av_conn = AVC("conn"); +static const AVal av_token = AVC("token"); int OpenResumeFile(const char *flvFile, // file name [in] @@ -431,7 +437,7 @@ GetLastKeyframe(FILE * file, // output file [in] int Download(RTMP * rtmp, // connected RTMP object - FILE * file, uint32_t dSeek, uint32_t dLength, double duration, bool bResume, char *metaHeader, uint32_t nMetaHeaderSize, char *initialFrame, int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames, bool bStdoutMode, bool bLiveStream, bool bHashes, bool bOverrideBufferTime, uint32_t bufferTime, double *percent) // percentage downloaded [out] + FILE * file, uint32_t dSeek, uint32_t dStopOffset, double duration, bool bResume, char *metaHeader, uint32_t nMetaHeaderSize, char *initialFrame, int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames, bool bStdoutMode, bool bLiveStream, bool bHashes, bool bOverrideBufferTime, uint32_t bufferTime, double *percent) // percentage downloaded [out] { int32_t now, lastUpdate; int bufferSize = 64 * 1024; @@ -484,8 +490,8 @@ Download(RTMP * rtmp, // connected RTMP object } } - if (dLength > 0) - RTMP_LogPrintf("For duration: %.3f sec\n", (double) dLength / 1000.0); + if (dStopOffset > 0) + RTMP_LogPrintf("For duration: %.3f sec\n", (double) (dStopOffset - dSeek) / 1000.0); if (bResume && nInitialFrameSize > 0) rtmp->m_read.flags |= RTMP_READ_RESUME; @@ -620,95 +626,81 @@ Download(RTMP * rtmp, // connected RTMP object #define STR2AVAL(av,str) av.av_val = str; av.av_len = strlen(av.av_val) -int -parseAMF(AMFObject *obj, const char *arg, int *depth) +void usage(char *prog) { - AMFObjectProperty prop = {{0,0}}; - int i; - char *p; - - if (arg[1] == ':') - { - p = (char *)arg+2; - switch(arg[0]) - { - case 'B': - prop.p_type = AMF_BOOLEAN; - prop.p_vu.p_number = atoi(p); - break; - case 'S': - prop.p_type = AMF_STRING; - STR2AVAL(prop.p_vu.p_aval,p); - break; - case 'N': - prop.p_type = AMF_NUMBER; - prop.p_vu.p_number = strtod(p, NULL); - break; - case 'Z': - prop.p_type = AMF_NULL; - break; - case 'O': - i = atoi(p); - if (i) - { - prop.p_type = AMF_OBJECT; - } - else - { - (*depth)--; - return 0; - } - break; - default: - return -1; - } - } - else if (arg[2] == ':' && arg[0] == 'N') - { - p = strchr(arg+3, ':'); - if (!p || !*depth) - return -1; - prop.p_name.av_val = (char *)arg+3; - prop.p_name.av_len = p - (arg+3); - - p++; - switch(arg[1]) - { - case 'B': - prop.p_type = AMF_BOOLEAN; - prop.p_vu.p_number = atoi(p); - break; - case 'S': - prop.p_type = AMF_STRING; - STR2AVAL(prop.p_vu.p_aval,p); - break; - case 'N': - prop.p_type = AMF_NUMBER; - prop.p_vu.p_number = strtod(p, NULL); - break; - case 'O': - prop.p_type = AMF_OBJECT; - break; - default: - return -1; - } - } - else - return -1; - - if (*depth) - { - AMFObject *o2; - for (i=0; i<*depth; i++) - { - o2 = &obj->o_props[obj->o_num-1].p_vu.p_object; - obj = o2; - } - } - AMF_AddProp(obj, &prop); - if (prop.p_type == AMF_OBJECT) - (*depth)++; - return 0; + RTMP_LogPrintf + ("\n%s: This program dumps the media content streamed over RTMP.\n\n", prog); + RTMP_LogPrintf("--help|-h Prints this help screen.\n"); + RTMP_LogPrintf + ("--rtmp|-r url URL (e.g. rtmp://host[:port]/path)\n"); + RTMP_LogPrintf + ("--host|-n hostname Overrides the hostname in the rtmp url\n"); + RTMP_LogPrintf + ("--port|-c port Overrides the port in the rtmp url\n"); + RTMP_LogPrintf + ("--socks|-S host:port Use the specified SOCKS proxy\n"); + RTMP_LogPrintf + ("--protocol|-l Overrides the protocol in the rtmp url (0 - RTMP, 2 - RTMPE)\n"); + RTMP_LogPrintf + ("--playpath|-y Overrides the playpath parsed from rtmp url\n"); + RTMP_LogPrintf("--swfUrl|-s url URL to player swf file\n"); + RTMP_LogPrintf + ("--tcUrl|-t url URL to played stream (default: \"rtmp://host[:port]/app\")\n"); + RTMP_LogPrintf("--pageUrl|-p url Web URL of played programme\n"); + RTMP_LogPrintf("--app|-a app Name of target app on server\n"); +#ifdef CRYPTO + RTMP_LogPrintf + ("--swfhash|-w hexstring SHA256 hash of the decompressed SWF file (32 bytes)\n"); + RTMP_LogPrintf + ("--swfsize|-x num Size of the decompressed SWF file, required for SWFVerification\n"); + RTMP_LogPrintf + ("--swfVfy|-W url URL to player swf file, compute hash/size automatically\n"); + RTMP_LogPrintf + ("--swfAge|-X days Number of days to use cached SWF hash before refreshing\n"); +#endif + RTMP_LogPrintf + ("--auth|-u string Authentication string to be appended to the connect string\n"); + RTMP_LogPrintf + ("--conn|-C type:data Arbitrary AMF data to be appended to the connect string\n"); + RTMP_LogPrintf + (" B:boolean(0|1), S:string, N:number, O:object-flag(0|1),\n"); + RTMP_LogPrintf + (" Z:(null), NB:name:boolean, NS:name:string, NN:name:number\n"); + RTMP_LogPrintf + ("--flashVer|-f string Flash version string (default: \"%s\")\n", + RTMP_DefaultFlashVer.av_val); + RTMP_LogPrintf + ("--live|-v Save a live stream, no --resume (seeking) of live streams possible\n"); + RTMP_LogPrintf + ("--subscribe|-d string Stream name to subscribe to (otherwise defaults to playpath if live is specifed)\n"); + RTMP_LogPrintf + ("--flv|-o string FLV output file name, if the file name is - print stream to stdout\n"); + RTMP_LogPrintf + ("--resume|-e Resume a partial RTMP download\n"); + RTMP_LogPrintf + ("--timeout|-m num Timeout connection num seconds (default: %lu)\n", + DEF_TIMEOUT); + RTMP_LogPrintf + ("--start|-A num Start at num seconds into stream (not valid when using --live)\n"); + RTMP_LogPrintf + ("--stop|-B num Stop at num seconds into stream\n"); + RTMP_LogPrintf + ("--token|-T key Key for SecureToken response\n"); + RTMP_LogPrintf + ("--hashes|-# Display progress with hashes, not with the byte counter\n"); + RTMP_LogPrintf + ("--buffer|-b Buffer time in milliseconds (default: %lu)\n", + DEF_BUFTIME); + RTMP_LogPrintf + ("--skip|-k num Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: %d)\n\n", + DEF_SKIPFRM); + RTMP_LogPrintf + ("--quiet|-q Suppresses all command output.\n"); + RTMP_LogPrintf("--verbose|-V Verbose command output.\n"); + RTMP_LogPrintf("--debug|-z Debug level command output.\n"); + RTMP_LogPrintf + ("If you don't pass parameters for swfUrl, pageUrl, or auth these properties will not be included in the connect "); + RTMP_LogPrintf("packet.\n\n"); } int @@ -720,13 +712,13 @@ main(int argc, char **argv) double percent = 0; double duration = 0.0; - int nSkipKeyFrames = 0; // skip this number of keyframes when resuming + int nSkipKeyFrames = DEF_SKIPFRM; // skip this number of keyframes when resuming bool bOverrideBufferTime = false; // if the user specifies a buffer time override this is true bool bStdoutMode = true; // if true print the stream directly to stdout, messages go to stderr bool bResume = false; // true in resume mode uint32_t dSeek = 0; // seek position in resume mode, 0 otherwise - uint32_t bufferTime = 10 * 60 * 60 * 1000; // 10 hours as default + uint32_t bufferTime = DEF_BUFTIME; // meta header and initial frame for the resume mode (they are read from the file and compared with // the stream we are trying to continue @@ -747,12 +739,11 @@ main(int argc, char **argv) bool bLiveStream = false; // is it a live stream? then we can't seek/resume bool bHashes = false; // display byte counters not hashes by default - long int timeout = 120; // timeout connection after 120 seconds + long int timeout = DEF_TIMEOUT; // timeout connection after 120 seconds uint32_t dStartOffset = 0; // seek position in non-live mode uint32_t dStopOffset = 0; - uint32_t dLength = 0; // length to play from stream - calculated from seek position and dStopOffset + RTMP rtmp = { 0 }; - char *rtmpurl = 0; AVal swfUrl = { 0, 0 }; AVal tcUrl = { 0, 0 }; AVal pageUrl = { 0, 0 }; @@ -761,10 +752,7 @@ main(int argc, char **argv) AVal swfHash = { 0, 0 }; uint32_t swfSize = 0; AVal flashVer = { 0, 0 }; - AVal token = { 0, 0 }; AVal sockshost = { 0, 0 }; - AMFObject extras = {0}; - int edepth = 0; #ifdef CRYPTO int swfAge = 30; /* 30 days for SWF cache by default */ @@ -805,6 +793,8 @@ main(int argc, char **argv) /* sleep(30); */ + RTMP_Init(&rtmp); + int opt; struct option longopts[] = { {"help", 0, NULL, 'h'}, @@ -852,79 +842,7 @@ main(int argc, char **argv) switch (opt) { case 'h': - RTMP_LogPrintf - ("\nThis program dumps the media content streamed over RTMP.\n\n"); - RTMP_LogPrintf("--help|-h Prints this help screen.\n"); - RTMP_LogPrintf - ("--rtmp|-r url URL (e.g. rtmp://host[:port]/path)\n"); - RTMP_LogPrintf - ("--host|-n hostname Overrides the hostname in the rtmp url\n"); - RTMP_LogPrintf - ("--port|-c port Overrides the port in the rtmp url\n"); - RTMP_LogPrintf - ("--socks|-S host:port Use the specified SOCKS proxy\n"); - RTMP_LogPrintf - ("--protocol|-l Overrides the protocol in the rtmp url (0 - RTMP, 2 - RTMPE)\n"); - RTMP_LogPrintf - ("--playpath|-y Overrides the playpath parsed from rtmp url\n"); - RTMP_LogPrintf("--swfUrl|-s url URL to player swf file\n"); - RTMP_LogPrintf - ("--tcUrl|-t url URL to played stream (default: \"rtmp://host[:port]/app\")\n"); - RTMP_LogPrintf("--pageUrl|-p url Web URL of played programme\n"); - RTMP_LogPrintf("--app|-a app Name of target app on server\n"); -#ifdef CRYPTO - RTMP_LogPrintf - ("--swfhash|-w hexstring SHA256 hash of the decompressed SWF file (32 bytes)\n"); - RTMP_LogPrintf - ("--swfsize|-x num Size of the decompressed SWF file, required for SWFVerification\n"); - RTMP_LogPrintf - ("--swfVfy|-W url URL to player swf file, compute hash/size automatically\n"); - RTMP_LogPrintf - ("--swfAge|-X days Number of days to use cached SWF hash before refreshing\n"); -#endif - RTMP_LogPrintf - ("--auth|-u string Authentication string to be appended to the connect string\n"); - RTMP_LogPrintf - ("--conn|-C type:data Arbitrary AMF data to be appended to the connect string\n"); - RTMP_LogPrintf - (" B:boolean(0|1), S:string, N:number, O:object-flag(0|1),\n"); - RTMP_LogPrintf - (" Z:(null), NB:name:boolean, NS:name:string, NN:name:number\n"); - RTMP_LogPrintf - ("--flashVer|-f string Flash version string (default: \"%s\")\n", - RTMP_DefaultFlashVer.av_val); - RTMP_LogPrintf - ("--live|-v Save a live stream, no --resume (seeking) of live streams possible\n"); - RTMP_LogPrintf - ("--subscribe|-d string Stream name to subscribe to (otherwise defaults to playpath if live is specifed)\n"); - RTMP_LogPrintf - ("--flv|-o string FLV output file name, if the file name is - print stream to stdout\n"); - RTMP_LogPrintf - ("--resume|-e Resume a partial RTMP download\n"); - RTMP_LogPrintf - ("--timeout|-m num Timeout connection num seconds (default: %lu)\n", - timeout); - RTMP_LogPrintf - ("--start|-A num Start at num seconds into stream (not valid when using --live)\n"); - RTMP_LogPrintf - ("--stop|-B num Stop at num seconds into stream\n"); - RTMP_LogPrintf - ("--token|-T key Key for SecureToken response\n"); - RTMP_LogPrintf - ("--hashes|-# Display progress with hashes, not with the byte counter\n"); - RTMP_LogPrintf - ("--buffer|-b Buffer time in milliseconds (default: %lu), this option makes only sense in stdout mode (-o -)\n", - bufferTime); - RTMP_LogPrintf - ("--skip|-k num Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: %d)\n\n", - nSkipKeyFrames); - RTMP_LogPrintf - ("--quiet|-q Suppresses all command output.\n"); - RTMP_LogPrintf("--verbose|-V Verbose command output.\n"); - RTMP_LogPrintf("--debug|-z Debug level command output.\n"); - RTMP_LogPrintf - ("If you don't pass parameters for swfUrl, pageUrl, or auth these properties will not be included in the connect "); - RTMP_LogPrintf("packet.\n\n"); + usage(argv[0]); return RD_SUCCESS; #ifdef CRYPTO case 'w': @@ -1014,8 +932,7 @@ main(int argc, char **argv) break; case 'l': protocol = atoi(optarg); - if (protocol != RTMP_PROTOCOL_RTMP - && protocol != RTMP_PROTOCOL_RTMPE) + if (protocol < RTMP_PROTOCOL_RTMP || protocol > RTMP_PROTOCOL_RTMPS) { RTMP_Log(RTMP_LOGERROR, "Unknown protocol specified: %d", protocol); return RD_FAILED; @@ -1030,9 +947,8 @@ main(int argc, char **argv) unsigned int parsedPort = 0; int parsedProtocol = RTMP_PROTOCOL_UNDEFINED; - rtmpurl = optarg; if (!RTMP_ParseURL - (rtmpurl, &parsedProtocol, &parsedHost, &parsedPort, + (optarg, &parsedProtocol, &parsedHost, &parsedPort, &parsedPlaypath, &parsedApp)) { RTMP_Log(RTMP_LOGWARNING, "Couldn't parse the specified url (%s)!", @@ -1084,13 +1000,16 @@ main(int argc, char **argv) case 'u': STR2AVAL(auth, optarg); break; - case 'C': - if (parseAMF(&extras, optarg, &edepth)) - { - RTMP_Log(RTMP_LOGERROR, "Invalid AMF parameter: %s", optarg); - return RD_FAILED; - } - break; + case 'C': { + AVal av; + STR2AVAL(av, optarg); + if (!RTMP_SetOpt(&rtmp, &av_conn, &av)) + { + RTMP_Log(RTMP_LOGERROR, "Invalid AMF parameter: %s", optarg); + return RD_FAILED; + } + } + break; case 'm': timeout = atoi(optarg); break; @@ -1100,8 +1019,11 @@ main(int argc, char **argv) case 'B': dStopOffset = (int) (atof(optarg) * 1000.0); break; - case 'T': + case 'T': { + AVal token; STR2AVAL(token, optarg); + RTMP_SetOpt(&rtmp, &av_token, &token); + } break; case '#': bHashes = true; @@ -1120,6 +1042,7 @@ main(int argc, char **argv) break; default: RTMP_LogPrintf("unknown option: %c\n", opt); + usage(argv[0]); return RD_FAILED; break; } @@ -1231,18 +1154,10 @@ main(int argc, char **argv) } } - RTMP rtmp = { 0 }; - RTMP_Init(&rtmp); RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath, &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, - &flashVer, &subscribepath, dSeek, 0, bLiveStream, timeout); + &flashVer, &subscribepath, dSeek, dStopOffset, bLiveStream, timeout); - /* backward compatibility, we always sent this as true before */ - if (auth.av_len) - rtmp.Link.authflag = true; - - rtmp.Link.extras = extras; - rtmp.Link.token = token; off_t size = 0; // ok, we have to get the timestamp of the last keyframe (only keyframes are seekable) / last audio frame (audio only streams) @@ -1339,10 +1254,8 @@ main(int argc, char **argv) // Calculate the length of the stream to still play if (dStopOffset > 0) { - dLength = dStopOffset - dSeek; - // Quit if start seek is past required stop offset - if (dLength <= 0) + if (dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); nStatus = RD_SUCCESS; @@ -1350,7 +1263,7 @@ main(int argc, char **argv) } } - if (!RTMP_ConnectStream(&rtmp, dSeek, dLength)) + if (!RTMP_ConnectStream(&rtmp, dSeek)) { nStatus = RD_FAILED; break; @@ -1378,15 +1291,14 @@ main(int argc, char **argv) dSeek = rtmp.m_pauseStamp; if (dStopOffset > 0) { - dLength = dStopOffset - dSeek; - if (dLength <= 0) + if (dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); nStatus = RD_SUCCESS; break; } } - if (!RTMP_ReconnectStream(&rtmp, bufferTime, dSeek, dLength)) + if (!RTMP_ReconnectStream(&rtmp, dSeek)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) @@ -1408,7 +1320,7 @@ main(int argc, char **argv) bResume = true; } - nStatus = Download(&rtmp, file, dSeek, dLength, duration, bResume, + nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume, metaHeader, nMetaHeaderSize, initialFrame, initialFrameType, nInitialFrameSize, nSkipKeyFrames, bStdoutMode, bLiveStream, bHashes, diff --git a/rtmpgw.c b/rtmpgw.c index 012c9b4..11a8477 100644 --- a/rtmpgw.c +++ b/rtmpgw.c @@ -343,7 +343,6 @@ void processTCPrequest(STREAMING_SERVER * server, // server socket and state (ou RTMP rtmp = { 0 }; uint32_t dSeek = 0; // can be used to start from a later point in the stream - int32_t dLength = -1; // reset RTMP options to defaults specified upon invokation of streams RTMP_REQUEST req; @@ -548,16 +547,11 @@ void processTCPrequest(STREAMING_SERVER * server, // server socket and state (ou RTMP_LogPrintf("Starting at TS: %d ms\n", dSeek); } - if (req.dStopOffset > 0) - { - dLength = req.dStopOffset - dSeek; - } - RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", req.bufferTime); RTMP_Init(&rtmp); RTMP_SetBufferMS(&rtmp, req.bufferTime); RTMP_SetupStream(&rtmp, req.protocol, &req.hostname, req.rtmpport, &req.sockshost, - &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, dSeek, dLength, + &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, dSeek, req.dStopOffset, req.bLiveStream, req.timeout); /* backward compatibility, we always sent this as true before */ if (req.auth.av_len) @@ -937,11 +931,11 @@ ParseOption(char opt, char *arg, RTMP_REQUEST * req) req->timeout = atoi(arg); break; case 'A': - req->dStartOffset = atoi(arg) * 1000; + req->dStartOffset = (int)(atof(arg) * 1000.0); //printf("dStartOffset = %d\n", dStartOffset); break; case 'B': - req->dStopOffset = atoi(arg) * 1000; + req->dStopOffset = (int)(atof(arg) * 1000.0); //printf("dStartOffset = %d\n", dStartOffset); break; case 'T': diff --git a/rtmpsuck.c b/rtmpsuck.c index 066f346..dcebac0 100644 --- a/rtmpsuck.c +++ b/rtmpsuck.c @@ -95,9 +95,6 @@ typedef struct Flist *f_head, *f_tail; Flist *f_cur; -#ifdef CRYPTO - unsigned char hash[HASHLEN]; -#endif } STREAMING_SERVER; STREAMING_SERVER *rtmpServer = 0; // server structure pointer @@ -215,11 +212,9 @@ ServeInvoke(STREAMING_SERVER *server, int which, RTMPPacket *pack, const char *b else if (AVMATCH(&pname, &av_swfUrl)) { #ifdef CRYPTO - if (pval.av_val && RTMP_HashSWF(pval.av_val, &server->rc.Link.SWFSize, server->hash, 30) == 0) - { - server->rc.Link.SWFHash.av_val = (char *)server->hash; - server->rc.Link.SWFHash.av_len = HASHLEN; - } + if (pval.av_val) + RTMP_HashSWF(pval.av_val, &server->rc.Link.SWFSize, + (unsigned char *)server->rc.Link.SWFHash, 30); #endif server->rc.Link.swfUrl = pval; pval.av_val = NULL; @@ -907,16 +902,16 @@ void doServe(STREAMING_SERVER * server, // server socket and state (our listenin short nType = AMF_DecodeInt16(pc.m_body); /* SWFverification */ if (nType == 0x1a) - #ifdef CRYPTO - if (server->rc.Link.SWFHash.av_len) +#ifdef CRYPTO + if (server->rc.Link.SWFSize) { RTMP_SendCtrl(&server->rc, 0x1b, 0, 0); sendit = 0; } - #else +#else /* The session will certainly fail right after this */ RTMP_Log(RTMP_LOGERROR, "%s, server requested SWF verification, need CRYPTO support! ", __FUNCTION__); - #endif +#endif } else if (server->f_cur && ( pc.m_packetType == 0x08 || @@ -974,9 +969,6 @@ cleanup: server->rc.Link.app.av_val = NULL; server->rc.Link.auth.av_val = NULL; server->rc.Link.flashVer.av_val = NULL; -#ifdef CRYPTO - server->rc.Link.SWFHash.av_val = NULL; -#endif RTMP_LogPrintf("done!\n\n"); quit: