void sigIntHandler(int sig) { RTMP_ctrlC = TRUE; RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig); if (rtmpServer) stopStreaming(rtmpServer); signal(SIGINT, SIG_DFL); }
static void rtmp_rvod_stop_notify(void* ctx) { if (!ctx) { return; } RTMP_LogPrintf("rtmp stopeed\n"); t_rtmp_vod_ctx *c = ctx; c->stopeed = 1; }
void sigIntHandler(int sig) { RTMP_ctrlC = TRUE; RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig); // ignore all these signals now and let the connection close signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGQUIT, SIG_IGN); }
TFTYPE controlServerThread(void *unused) { char ich; while (1) { ich = getchar(); switch (ich) { case 'q': RTMP_LogPrintf("Exiting\n"); stopStreaming(rtmpServer); exit(0); break; default: RTMP_LogPrintf("Unknown command \'%c\', ignoring\n", ich); } } TFRET(); }
void sigIntHandler(int sig) { //xjzhang, 屏蔽这些signal,设置RTMP_ctrlC为TRUE从而进入正常退出流程; RTMP_ctrlC = TRUE; RTMP_LogPrintf("Caught signal: %d, cleaning up, just a second...\n", sig); // ignore all these signals now and let the connection close signal(SIGINT, SIG_IGN); signal(SIGTERM, SIG_IGN); #ifndef WIN32 signal(SIGHUP, SIG_IGN); signal(SIGPIPE, SIG_IGN); signal(SIGQUIT, SIG_IGN); #endif }
static void rtmp_rvod_pause_notify(void* ctx, int paused, double ts) { RTMP_LogPrintf("rtmp pause %d ts %lf\n", paused, ts); if (!ctx) { return; } t_rtmp_vod_ctx *c = ctx; c->paused = paused; if (paused) { c->start_timestamp = ts; } else { c->start_timestamp = ts; c->epoch = RTMP_GetTime(); } }
int main(int argc, char **argv) { int nStatus = RD_SUCCESS; // http streaming server char DEFAULT_HTTP_STREAMING_DEVICE[] = "0.0.0.0"; // 0.0.0.0 is any device char *rtmpStreamingDevice = DEFAULT_HTTP_STREAMING_DEVICE; // streaming device, default 0.0.0.0 int nRtmpStreamingPort = 1935; // port RTMP_LogPrintf("RTMP Server %s\n", RTMPDUMP_VERSION); RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n"); RTMP_debuglevel = RTMP_LOGINFO; if (argc > 1 && !strcmp(argv[1], "-z")) RTMP_debuglevel = RTMP_LOGALL; // init request memset(&defaultRTMPRequest, 0, sizeof(RTMP_REQUEST)); defaultRTMPRequest.rtmpport = -1; defaultRTMPRequest.protocol = RTMP_PROTOCOL_UNDEFINED; defaultRTMPRequest.bLiveStream = FALSE; // is it a live stream? then we can't seek/resume defaultRTMPRequest.timeout = 300; // timeout connection afte 300 seconds defaultRTMPRequest.bufferTime = 20 * 1000; signal(SIGINT, sigIntHandler); #ifndef WIN32 signal(SIGPIPE, SIG_IGN); #endif #ifdef _DEBUG netstackdump = fopen("netstackdump", "wb"); netstackdump_read = fopen("netstackdump_read", "wb"); #endif InitSockets(); // start text UI ThreadCreate(controlServerThread, 0); // start http streaming if ((rtmpServer = startStreaming(rtmpStreamingDevice, nRtmpStreamingPort)) == 0) { RTMP_Log(RTMP_LOGERROR, "Failed to start RTMP server, exiting!"); return RD_FAILED; } RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice, nRtmpStreamingPort); while (rtmpServer->state != STREAMING_STOPPED) { sleep(1); } RTMP_Log(RTMP_LOGDEBUG, "Done, exiting..."); CleanupSockets(); #ifdef _DEBUG if (netstackdump != 0) fclose(netstackdump); if (netstackdump_read != 0) fclose(netstackdump_read); #endif return nStatus; }
int main(int argc, char **argv) { int nStatus = RD_SUCCESS; // http streaming server char DEFAULT_HTTP_STREAMING_DEVICE[] = "0.0.0.0"; // 0.0.0.0 is any device char *httpStreamingDevice = DEFAULT_HTTP_STREAMING_DEVICE; // streaming device, default 0.0.0.0 int nHttpStreamingPort = 80; // port int opt; struct option longopts[] = { {"help", 0, NULL, 'h'}, {"host", 1, NULL, 'n'}, {"port", 1, NULL, 'c'}, {"socks", 1, NULL, 'S'}, {"protocol", 1, NULL, 'l'}, {"playpath", 1, NULL, 'y'}, {"rtmp", 1, NULL, 'r'}, {"swfUrl", 1, NULL, 's'}, {"tcUrl", 1, NULL, 't'}, {"pageUrl", 1, NULL, 'p'}, {"app", 1, NULL, 'a'}, #ifdef CRYPTO {"swfhash", 1, NULL, 'w'}, {"swfsize", 1, NULL, 'x'}, {"swfVfy", 1, NULL, 'W'}, {"swfAge", 1, NULL, 'X'}, #endif {"auth", 1, NULL, 'u'}, {"conn", 1, NULL, 'C'}, {"flashVer", 1, NULL, 'f'}, {"live", 0, NULL, 'v'}, //{"flv", 1, NULL, 'o'}, //{"resume", 0, NULL, 'e'}, {"timeout", 1, NULL, 'm'}, {"buffer", 1, NULL, 'b'}, //{"skip", 1, NULL, 'k'}, {"device", 1, NULL, 'D'}, {"sport", 1, NULL, 'g'}, {"subscribe", 1, NULL, 'd'}, {"start", 1, NULL, 'A'}, {"stop", 1, NULL, 'B'}, {"token", 1, NULL, 'T'}, {"debug", 0, NULL, 'z'}, {"quiet", 0, NULL, 'q'}, {"verbose", 0, NULL, 'V'}, {0, 0, 0, 0} }; RTMP_LogPrintf("HTTP-RTMP Stream Gateway %s\n", RTMPDUMP_VERSION); RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n"); // init request memset(&defaultRTMPRequest, 0, sizeof(RTMP_REQUEST)); defaultRTMPRequest.rtmpport = -1; defaultRTMPRequest.protocol = RTMP_PROTOCOL_UNDEFINED; defaultRTMPRequest.bLiveStream = FALSE; // is it a live stream? then we can't seek/resume defaultRTMPRequest.timeout = 120; // timeout connection after 120 seconds defaultRTMPRequest.bufferTime = 20 * 1000; defaultRTMPRequest.swfAge = 30; signal(SIGINT, sigIntHandler); #ifndef WIN32 signal(SIGPIPE, SIG_IGN); #endif InitSockets(); while ((opt = getopt_long(argc, argv, "hvqVzr:s:t:p:a:f:u:n:c:l:y:m:d:D:A:B:T:g:w:x:W:X:S:", longopts, NULL)) != -1) { switch (opt) { case 'h': RTMP_LogPrintf ("\nThis program serves media content streamed from RTMP onto HTTP.\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 in 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 Get 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 specified)\n"); RTMP_LogPrintf ("--timeout|-m num Timeout connection num seconds (default: %lu)\n", defaultRTMPRequest.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 ("--buffer|-b Buffer time in milliseconds (default: %lu)\n\n", defaultRTMPRequest.bufferTime); RTMP_LogPrintf ("--device|-D Streaming device ip address (default: %s)\n", DEFAULT_HTTP_STREAMING_DEVICE); RTMP_LogPrintf ("--sport|-g Streaming port (default: %d)\n\n", nHttpStreamingPort); 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"); return RD_SUCCESS; break; // streaming server specific options case 'D': if (inet_addr(optarg) == INADDR_NONE) { RTMP_Log(RTMP_LOGERROR, "Invalid binding address (requested address %s), ignoring", optarg); } else { httpStreamingDevice = optarg; } break; case 'g': { int port = atoi(optarg); if (port < 0 || port > 65535) { RTMP_Log(RTMP_LOGERROR, "Streaming port out of range (requested port %d), ignoring\n", port); } else { nHttpStreamingPort = port; } break; } default: //RTMP_LogPrintf("unknown option: %c\n", opt); if (!ParseOption(opt, optarg, &defaultRTMPRequest)) return RD_FAILED; break; } } #ifdef _DEBUG netstackdump = fopen("netstackdump", "wb"); netstackdump_read = fopen("netstackdump_read", "wb"); #endif // start text UI ThreadCreate(controlServerThread, 0); // start http streaming if ((httpServer = startStreaming(httpStreamingDevice, nHttpStreamingPort)) == 0) { RTMP_Log(RTMP_LOGERROR, "Failed to start HTTP server, exiting!"); return RD_FAILED; } RTMP_LogPrintf("Streaming on http://%s:%d\n", httpStreamingDevice, nHttpStreamingPort); while (httpServer->state != STREAMING_STOPPED) { sleep(1); } RTMP_Log(RTMP_LOGDEBUG, "Done, exiting..."); CleanupSockets(); #ifdef _DEBUG if (netstackdump != 0) fclose(netstackdump); if (netstackdump_read != 0) fclose(netstackdump_read); #endif return nStatus; }
void usage(char *prog) { 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 ("--url|-i url URL with options included (e.g. rtmp://host[:port]/path swfUrl=url tcUrl=url)\n"); RTMP_LogPrintf ("--rtmp|-r url URL (e.g. rtmp://host[:port]/path)\n"); RTMP_LogPrintf ("--live|-v Save a live stream, no --resume (seeking) of live streams possible\n"); RTMP_LogPrintf ("--flv|-o string FLV output file name, if the file name is - print stream to stdout\n"); RTMP_LogPrintf ("--timeout|-m num Timeout connection num seconds (default: %u)\n", DEF_TIMEOUT); RTMP_LogPrintf ("--quiet|-q Suppresses all command output.\n"); RTMP_LogPrintf("--verbose|-V Verbose 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 main(int argc, char* argv[]) { InitSockets(); double duration = -1; int nRead; //is live stream ? bool bLiveStream = TRUE; int bufsize = 1024 * 1024 * 10; char *buf = (char*)malloc(bufsize); memset(buf, 0, bufsize); long countbufsize = 0; FILE *fp = fopen("receive.flv", "wb"); if (!fp) { RTMP_LogPrintf("Open File Error.\n"); CleanupSockets(); return -1; } /* set log level */ RTMP_LogLevel loglvl = RTMP_LOGDEBUG; RTMP_LogSetLevel(loglvl); RTMP *rtmp = RTMP_Alloc(); RTMP_Init(rtmp); //set connection timeout,default 30s rtmp->Link.timeout = 10; char* rtmpFilePath = "d:\\rtmp.raw"; rtmp->m_pRTMPFile = fopen(rtmpFilePath,"rb"); if (!rtmp->m_pRTMPFile) { RTMP_LogPrintf("Failed to open File :%s\n", rtmpFilePath); return FALSE; } // HKS's live URL if(!RTMP_SetupURL(rtmp, "rtmp://live.hkstv.hk.lxdns.com/live/hks")) { RTMP_Log(RTMP_LOGERROR, "SetupURL Err\n"); RTMP_Free(rtmp); CleanupSockets(); return -1; } if (bLiveStream) { rtmp->Link.lFlags |= RTMP_LF_LIVE; } //1hour RTMP_SetBufferMS(rtmp, 3600 * 1000); if(!RTMP_Connect(rtmp, NULL)) { RTMP_Log(RTMP_LOGERROR, "Connect Err\n"); RTMP_Free(rtmp); CleanupSockets(); return -1; } if(!RTMP_ConnectStream(rtmp, 0)) { RTMP_Log(RTMP_LOGERROR, "ConnectStream Err\n"); RTMP_Close(rtmp); RTMP_Free(rtmp); CleanupSockets(); return -1; } while(nRead = RTMP_Read(rtmp, buf, bufsize)) { fwrite(buf, 1, nRead, fp); countbufsize += nRead; RTMP_LogPrintf("Receive: %5dByte, Total: %5.2fkB\n", nRead, countbufsize * 1.0 / 1024); } if(fp) fclose(fp); if(fpPcap) fclose(fpPcap); if (rtmp->m_pRTMPFile) { fclose(rtmp->m_pRTMPFile); } if(buf) { free(buf); } if(rtmp) { RTMP_Close(rtmp); RTMP_Free(rtmp); CleanupSockets(); rtmp = NULL; } return 0; }
// Return values: true (option parsing ok) // false (option not parsed/invalid) int ParseOption(char opt, char *arg, RTMP_REQUEST * req) { AVal parsedHost, parsedPlaypath, parsedApp; unsigned int parsedPort = 0; int parsedProtocol = RTMP_PROTOCOL_UNDEFINED; switch (opt) { #ifdef CRYPTO case 'w': { int res = hex2bin(arg, &req->swfHash.av_val); if (!res || res != RTMP_SWF_HASHLEN) { req->swfHash.av_val = NULL; RTMP_Log(RTMP_LOGWARNING, "Couldn't parse swf hash hex string, not hexstring or not %d bytes, ignoring!", RTMP_SWF_HASHLEN); } req->swfHash.av_len = RTMP_SWF_HASHLEN; break; } case 'x': { int size = atoi(arg); if (size <= 0) { RTMP_Log(RTMP_LOGERROR, "SWF Size must be at least 1, ignoring\n"); } else { req->swfSize = size; } break; } case 'W': { STR2AVAL(req->swfUrl, arg); req->swfVfy = 1; } break; case 'X': { int num = atoi(arg); if (num < 0) { RTMP_Log(RTMP_LOGERROR, "SWF Age must be non-negative, ignoring\n"); } else { req->swfAge = num; } break; } #endif case 'b': { int32_t bt = atol(arg); if (bt < 0) { RTMP_Log(RTMP_LOGERROR, "Buffer time must be greater than zero, ignoring the specified value %d!", bt); } else { req->bufferTime = bt; } break; } case 'v': req->bLiveStream = TRUE; // no seeking or resuming possible! break; case 'd': STR2AVAL(req->subscribepath, arg); break; case 'n': STR2AVAL(req->hostname, arg); break; case 'c': req->rtmpport = atoi(arg); break; case 'l': { int protocol = atoi(arg); if (protocol < RTMP_PROTOCOL_RTMP || protocol > RTMP_PROTOCOL_RTMPTS) { RTMP_Log(RTMP_LOGERROR, "Unknown protocol specified: %d, using default", protocol); return FALSE; } else { req->protocol = protocol; } break; } case 'y': STR2AVAL(req->playpath, arg); break; case 'r': { req->rtmpurl = arg; if (!RTMP_ParseURL (req->rtmpurl, &parsedProtocol, &parsedHost, &parsedPort, &parsedPlaypath, &parsedApp)) { RTMP_Log(RTMP_LOGWARNING, "Couldn't parse the specified url (%s)!", arg); } else { if (!req->hostname.av_len) req->hostname = parsedHost; if (req->rtmpport == -1) req->rtmpport = parsedPort; if (req->playpath.av_len == 0 && parsedPlaypath.av_len) { req->playpath = parsedPlaypath; } if (req->protocol == RTMP_PROTOCOL_UNDEFINED) req->protocol = parsedProtocol; if (req->app.av_len == 0 && parsedApp.av_len) { req->app = parsedApp; } } break; } case 's': STR2AVAL(req->swfUrl, arg); break; case 't': STR2AVAL(req->tcUrl, arg); break; case 'p': STR2AVAL(req->pageUrl, arg); break; case 'a': STR2AVAL(req->app, arg); break; case 'f': STR2AVAL(req->flashVer, arg); break; case 'u': STR2AVAL(req->auth, arg); break; case 'C': parseAMF(&req->extras, optarg, &req->edepth); break; case 'm': req->timeout = atoi(arg); break; case 'A': req->dStartOffset = (int)(atof(arg) * 1000.0); //printf("dStartOffset = %d\n", dStartOffset); break; case 'B': req->dStopOffset = (int)(atof(arg) * 1000.0); //printf("dStartOffset = %d\n", dStartOffset); break; case 'T': STR2AVAL(req->token, arg); break; case 'S': STR2AVAL(req->sockshost, arg); case 'q': RTMP_debuglevel = RTMP_LOGCRIT; break; case 'V': RTMP_debuglevel = RTMP_LOGDEBUG; break; case 'z': RTMP_debuglevel = RTMP_LOGALL; break; default: RTMP_LogPrintf("unknown option: %c, arg: %s\n", opt, arg); return FALSE; } return TRUE; }
void usage(char *prog) { 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 ("--url|-i url URL with options included (e.g. rtmp://host[:port]/path swfUrl=url tcUrl=url)\n"); RTMP_LogPrintf ("--rtmp|-r url URL (e.g. rtmp://host[:port]/path)\n"); RTMP_LogPrintf ("--host|-n hostname Overrides the hostname in the rtmp url\n"); 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 num Overrides the protocol in the rtmp url (0 - RTMP, 2 - RTMPE)\n"); RTMP_LogPrintf ("--playpath|-y path Overrides the playpath parsed from rtmp url\n"); RTMP_LogPrintf ("--playlist|-Y Set playlist before playing\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 ("--realtime|-R Don't attempt to speed up download via the Pause/Unpause BUFX hack\n"); RTMP_LogPrintf ("--flv|-o string FLV output file name, if the file name is - print stream to stdout\n"); RTMP_LogPrintf ("--resume|-e Resume a partial RTMP download\n"); RTMP_LogPrintf ("--timeout|-m num Timeout connection num seconds (default: %u)\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 ("--jtv|-j JSON Authentication token for Justin.tv legacy servers\n"); RTMP_LogPrintf ("--hashes|-# Display progress with hashes, not with the byte counter\n"); RTMP_LogPrintf ("--buffer|-b Buffer time in milliseconds (default: %u)\n", DEF_BUFTIME); RTMP_LogPrintf ("--skip|-k num Skip num keyframes when looking for last keyframe to resume from. Useful if resume fails (default: %d)\n\n", 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"); }
void QRtmp::run() { bool first = true; int retries = 0; if(!m_destFile.fileName().isEmpty()) if(!m_destFile.open(QIODevice::WriteOnly)) { setError(QString("Can't open %1 for writing").arg(m_destFile.fileName())); return; } while(!m_stop) { RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", m_bufferTime); RTMP_SetBufferMS(m_rtmp, m_bufferTime); if(first) { first = false; RTMP_LogPrintf("Connecting ...\n"); if(!RTMP_Connect(m_rtmp, NULL)) { setError("RTMP_Connect failed"); break; } RTMP_Log(RTMP_LOGINFO, "Connected..."); // User defined seek offset if(dStartOffset > 0) { // Don't need the start offset if resuming an existing file if(m_bResume) { RTMP_Log(RTMP_LOGWARNING, "Can't seek a resumed stream, ignoring --start option"); dStartOffset = 0; } else dSeek = dStartOffset; } // Calculate the length of the stream to still play if(dStopOffset > 0) { // Quit if start seek is past required stop offset if(dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); break; } } if(!RTMP_ConnectStream(m_rtmp, dSeek)) { setError("RTMP_ConnectStream failed"); break; } } else { if(retries) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if(!RTMP_IsTimedout(m_rtmp)) setError("RTMP_IsTimedout failed"); else setError("RTMP_IsTimedout RD_INCOMPLETE"); break; } RTMP_Log(RTMP_LOGINFO, "Connection timed out, trying to resume.\n\n"); /* Did we already try pausing, and it still didn't work? */ if(m_rtmp->m_pausing == 3) { /* Only one try at reconnecting... */ retries = 1; dSeek = m_rtmp->m_pauseStamp; if(dStopOffset > 0) { if(dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); break; } } if(!RTMP_ReconnectStream(m_rtmp, dSeek)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if(!RTMP_IsTimedout(m_rtmp)) setError("RTMP_IsTimedout failed"); else setError("RTMP_IsTimedout RD_INCOMPLETE"); break; } } else if(!RTMP_ToggleStream(m_rtmp)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if(!RTMP_IsTimedout(m_rtmp)) setError("RTMP_IsTimedout failed"); else setError("RTMP_IsTimedout RD_INCOMPLETE"); break; } m_bResume = true; } int nStatus = download(); if(nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(m_rtmp) || m_bLiveStream) break; } RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n"); RTMP_Close(m_rtmp); if(m_destFile.isOpen()) m_destFile.close(); setStreamIsRunning(false); }
void doServe(STREAMING_SERVER * server, // server socket and state (our listening socket) int sockfd // client connection socket ) { server->state = STREAMING_IN_PROGRESS; RTMP rtmp = { 0 }; /* our session with the real client */ RTMPPacket packet = { 0 }; // timeout for http requests fd_set fds; struct timeval tv; memset(&tv, 0, sizeof(struct timeval)); tv.tv_sec = 5; FD_ZERO(&fds); FD_SET(sockfd, &fds); if (select(sockfd + 1, &fds, NULL, NULL, &tv) <= 0) { RTMP_Log(RTMP_LOGERROR, "Request timeout/select failed, ignoring request"); goto quit; } else { RTMP_Init(&rtmp); rtmp.m_sb.sb_socket = sockfd; if (!RTMP_Serve(&rtmp)) { RTMP_Log(RTMP_LOGERROR, "Handshake failed"); goto cleanup; } } server->arglen = 0; while (RTMP_IsConnected(&rtmp) && RTMP_ReadPacket(&rtmp, &packet)) { if (!RTMPPacket_IsReady(&packet)) continue; ServePacket(server, &rtmp, &packet); RTMPPacket_Free(&packet); } cleanup: RTMP_LogPrintf("Closing connection... "); RTMP_Close(&rtmp); /* Should probably be done by RTMP_Close() ... */ rtmp.Link.playpath.av_val = NULL; rtmp.Link.tcUrl.av_val = NULL; rtmp.Link.swfUrl.av_val = NULL; rtmp.Link.pageUrl.av_val = NULL; rtmp.Link.app.av_val = NULL; rtmp.Link.flashVer.av_val = NULL; RTMP_LogPrintf("done!\n\n"); quit: if (server->state == STREAMING_IN_PROGRESS) server->state = STREAMING_ACCEPTING; return; }
int main(int argc, char **argv) { extern char *optarg; int nStatus = RD_SUCCESS; int bStdoutMode = TRUE; // if true print the stream directly to stdout, messages go to stderr int bLiveStream = TRUE; // is it a live stream? then we can't seek/resume long int timeout = DEF_TIMEOUT; // timeout connection after 120 seconds RTMP rtmp = { 0 }; AVal fullUrl = { 0, 0 }; RTMP_debuglevel = RTMP_LOGINFO; char *flvFile = 0; signal(SIGINT, sigIntHandler); signal(SIGTERM, sigIntHandler); signal(SIGHUP, sigIntHandler); signal(SIGPIPE, sigIntHandler); signal(SIGQUIT, sigIntHandler); RTMP_LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION); RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n"); int opt; struct option longopts[] = { {"help", 0, NULL, 'h'}, {"url", 1, NULL, 'i'}, {"rtmp", 1, NULL, 'r'}, {"live", 0, NULL, 'v'}, {"timeout", 1, NULL, 'm'}, {"hashes", 0, NULL, '#'}, {"quiet", 0, NULL, 'q'}, {"verbose", 0, NULL, 'V'}, {0, 0, 0, 0} }; while ((opt = getopt_long(argc, argv, "hVvqzRr:s:t:i:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#j:", longopts, NULL)) != -1) { switch (opt) { case 'h': usage(argv[0]); return RD_SUCCESS; case 'v': bLiveStream = TRUE; // no seeking or resuming possible! break; case 'i': STR2AVAL(fullUrl, optarg); break; case 'o': flvFile = optarg; if (strcmp(flvFile, "-")) bStdoutMode = FALSE; break; case 'm': timeout = atoi(optarg); break; case 'q': RTMP_debuglevel = RTMP_LOGCRIT; break; case 'V': RTMP_debuglevel = RTMP_LOGDEBUG; break; case 'z': RTMP_debuglevel = RTMP_LOGALL; break; default: RTMP_LogPrintf("unknown option: %c\n", opt); usage(argv[0]); return RD_FAILED; break; } } if (flvFile == 0) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified an output file (-o filename), using stdout"); bStdoutMode = TRUE; } if (!file) { if (bStdoutMode) { file = stdout; SET_BINMODE(file); } else { file = fopen(flvFile, "w+b"); if (file == 0) { RTMP_LogPrintf("Failed to open file! %s\n", flvFile); return RD_FAILED; } } } //////////////////////////////////////////////////////////////////////////////////////// RTMP_Init(&rtmp);//初始化RTMP参数 //指定了-i 参数,直接设置URL if (RTMP_SetupURL(&rtmp, fullUrl.av_val) == FALSE) { RTMP_Log(RTMP_LOGERROR, "Couldn't parse URL: %s", fullUrl.av_val); return RD_FAILED; } rtmp.Link.timeout = timeout ; /* Try to keep the stream moving if it pauses on us */ if (!bLiveStream ) rtmp.Link.lFlags |= RTMP_LF_BUFX; else { rtmp.Link.lFlags |= RTMP_LF_LIVE ; } printf("aaaaa:%d\n", rtmp.Link.lFlags ) ; while (!RTMP_ctrlC) { RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", DEF_BUFTIME); RTMP_SetBufferMS(&rtmp, 2000);//告诉服务器帮我缓存多久 RTMP_LogPrintf("Connecting ...\n"); if (!RTMP_Connect(&rtmp, NULL)) {//建立连接,发送"connect" nStatus = RD_NO_CONNECT; break; } RTMP_Log(RTMP_LOGINFO, "Connected..."); //处理服务端返回的各种控制消息包,比如收到connect的result后就进行createStream,以及发送play(test)消息 if (!RTMP_ConnectStream(&rtmp, 0)) {//一旦返回,表示服务端开始发送数据了.可以play了 nStatus = RD_FAILED; break; } nStatus = Download(&rtmp, file, bStdoutMode, bLiveStream ); if (nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(&rtmp) || bLiveStream) break; } RTMP_LogPrintf("Download complete\n"); //clean: RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n"); RTMP_Close(&rtmp); if (file != 0) fclose(file); return nStatus; }
int start_sample_rtmp_server(int argc, char **argv) { int nStatus = RD_SUCCESS; int i; // http streaming server char DEFAULT_HTTP_STREAMING_DEVICE[] = "0.0.0.0"; // 0.0.0.0 is any device char *rtmpStreamingDevice = DEFAULT_HTTP_STREAMING_DEVICE; // streaming device, default 0.0.0.0 int nRtmpStreamingPort = 1935; // port char *cert = NULL, *key = NULL; RTMP_LogPrintf("RTMP Server %s\n", "AVRemoteControl Build 1.0"); RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu; license: GPL\n\n"); RTMP_debuglevel = RTMP_LOGINFO; for (i = 1; i < argc; i++) { if (!strcmp(argv[i], "-z")) RTMP_debuglevel = RTMP_LOGALL; else if (!strcmp(argv[i], "-c") && i + 1 < argc) cert = argv[++i]; else if (!strcmp(argv[i], "-k") && i + 1 < argc) key = argv[++i]; } if (cert && key) sslCtx = RTMP_TLS_AllocServerContext(cert, key); // init request memset(&defaultRTMPRequest, 0, sizeof(RTMP_REQUEST)); defaultRTMPRequest.rtmpport = -1; defaultRTMPRequest.protocol = RTMP_PROTOCOL_UNDEFINED; defaultRTMPRequest.bLiveStream = true; // is it a live stream? then we can't seek/resume defaultRTMPRequest.timeout = 500; // timeout connection after 300 seconds defaultRTMPRequest.bufferTime = 20 * 1000; signal(SIGINT, sigIntHandler); #ifndef WIN32 signal(SIGPIPE, SIG_IGN); #endif #ifdef _DEBUG netstackdump = fopen("netstackdump", "wb"); netstackdump_read = fopen("netstackdump_read", "wb"); #endif InitSockets(); // start text UI //ThreadCreate(controlServerThread, 0); std::thread theThread(controlServerThread); // start http streaming if ((rtmpServer = startStreaming(rtmpStreamingDevice, nRtmpStreamingPort)) == 0) { RTMP_Log(RTMP_LOGERROR, "Failed to start RTMP server, exiting!"); return RD_FAILED; } RTMP_LogPrintf("Streaming on rtmp://%s:%d\n", rtmpStreamingDevice, nRtmpStreamingPort); while (rtmpServer->state != STREAMING_STOPPED) { sleep(1); } RTMP_Log(RTMP_LOGDEBUG, "Done, exiting..."); if (sslCtx) RTMP_TLS_FreeServerContext(sslCtx); CleanupSockets(); #ifdef _DEBUG if (netstackdump != 0) fclose(netstackdump); if (netstackdump_read != 0) fclose(netstackdump_read); #endif return nStatus; }
int do_rtmp(int port,int protocol,char* playpath_arg,char* host,char* swfVfy_arg,char* tcUrl_arg,char* app_arg,char* pageUrl_arg,char* flashVer_arg,char* conn,char* outfile) { int nStatus = RD_SUCCESS; double percent = 0; double duration = 0.0; int nSkipKeyFrames = DEF_SKIPFRM; // skip this number of keyframes when resuming int bOverrideBufferTime = FALSE; // if the user specifies a buffer time override this is true int bResume = FALSE; // true in resume mode uint32_t dSeek = 0; // seek position in resume mode, 0 otherwise 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 char *metaHeader = 0; uint32_t nMetaHeaderSize = 0; // video keyframe for matching char *initialFrame = 0; uint32_t nInitialFrameSize = 0; int initialFrameType = 0; // tye: audio or video AVal hostname = { 0, 0 }; AVal playpath = { 0, 0 }; AVal subscribepath = { 0, 0 }; AVal usherToken = { 0, 0 }; //Justin.tv auth token int retries = 0; int bLiveStream = FALSE; // is it a live stream? then we can't seek/resume int bHashes = FALSE; // display byte counters not hashes by default long int timeout = DEF_TIMEOUT; // timeout connection after 120 seconds uint32_t dStopOffset = 0; RTMP rtmp; AVal swfUrl = { 0, 0 }; AVal tcUrl = { 0, 0 }; AVal pageUrl = { 0, 0 }; AVal app = { 0, 0 }; AVal auth = { 0, 0 }; AVal swfHash = { 0, 0 }; uint32_t swfSize = 0; AVal flashVer = { 0, 0 }; AVal sockshost = { 0, 0 }; #ifdef CRYPTO int swfAge = 30; /* 30 days for SWF cache by default */ int swfVfy = 0; unsigned char hash[RTMP_SWF_HASHLEN]; #endif char *flvFile = 0; signal(SIGINT, sigIntHandler); signal(SIGTERM, sigIntHandler); signal(SIGHUP, sigIntHandler); signal(SIGPIPE, sigIntHandler); signal(SIGQUIT, sigIntHandler); RTMP_debuglevel = RTMP_LOGINFO; RTMP_Init(&rtmp); STR2AVAL(&swfUrl,swfVfy_arg); swfVfy = 1; if (host) { STR2AVAL(&hostname, host); } if (playpath_arg) { STR2AVAL(&playpath, playpath_arg); } if (tcUrl_arg) { STR2AVAL(&tcUrl, tcUrl_arg); } if (pageUrl_arg) { STR2AVAL(&pageUrl, pageUrl_arg); } if (app_arg) { STR2AVAL(&app, app_arg); } if (flashVer_arg) { STR2AVAL(&flashVer, flashVer_arg); } if (conn) { AVal av; STR2AVAL(&av, conn); if (!RTMP_SetOpt(&rtmp, &av_conn, &av)) { RTMP_Log(RTMP_LOGERROR, "Invalid AMF parameter: %s", conn); return RD_FAILED; } } flvFile = outfile; file = fopen(flvFile, "w+b"); if (file == 0) { RTMP_LogPrintf("Failed to open file! %s\n", flvFile); return RD_FAILED; } if (port == 0) { if (protocol & RTMP_FEATURE_SSL) port = 443; else if (protocol & RTMP_FEATURE_HTTP) port = 80; else port = 1935; } #ifdef CRYPTO if (swfVfy) { if (RTMP_HashSWF(swfUrl.av_val, &swfSize, hash, swfAge) == 0) { swfHash.av_val = (char *)hash; swfHash.av_len = RTMP_SWF_HASHLEN; } } if (swfHash.av_len == 0 && swfSize > 0) { RTMP_Log(RTMP_LOGWARNING, "Ignoring SWF size, supply also the hash with --swfhash"); swfSize = 0; } if (swfHash.av_len != 0 && swfSize == 0) { RTMP_Log(RTMP_LOGWARNING, "Ignoring SWF hash, supply also the swf size with --swfsize"); swfHash.av_len = 0; swfHash.av_val = NULL; } #endif RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath, &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout); /* Try to keep the stream moving if it pauses on us */ if (!bLiveStream && !(protocol & RTMP_FEATURE_HTTP)) rtmp.Link.lFlags |= RTMP_LF_BUFX; int first = 1; while (!RTMP_ctrlC) { RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", bufferTime); RTMP_SetBufferMS(&rtmp, bufferTime); if (first) { first = 0; RTMP_LogPrintf("Connecting ...\n"); if (!RTMP_Connect(&rtmp, NULL)) { nStatus = RD_NO_CONNECT; break; } RTMP_Log(RTMP_LOGINFO, "Connected..."); if (!RTMP_ConnectStream(&rtmp, dSeek)) { nStatus = RD_FAILED; break; } } else { nInitialFrameSize = 0; if (retries) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } RTMP_Log(RTMP_LOGINFO, "Connection timed out, trying to resume.\n\n"); /* Did we already try pausing, and it still didn't work? */ if (rtmp.m_pausing == 3) { /* Only one try at reconnecting... */ retries = 1; dSeek = rtmp.m_pauseStamp; if (dStopOffset > 0) { if (dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); nStatus = RD_SUCCESS; break; } } if (!RTMP_ReconnectStream(&rtmp, dSeek)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } } else if (!RTMP_ToggleStream(&rtmp)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } bResume = TRUE; } nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume, metaHeader, nMetaHeaderSize, initialFrame, initialFrameType, nInitialFrameSize, nSkipKeyFrames, bLiveStream, bHashes, bOverrideBufferTime, bufferTime, &percent); free(initialFrame); initialFrame = NULL; /* If we succeeded, we're done. */ if (nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(&rtmp) || bLiveStream) break; } if (nStatus == RD_SUCCESS) { RTMP_LogPrintf("Download complete\n"); } else if (nStatus == RD_INCOMPLETE) { RTMP_LogPrintf ("Download may be incomplete (downloaded about %.2f%%), try resuming\n", percent); } RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n"); RTMP_Close(&rtmp); if (file != 0) fclose(file); return nStatus; }
int Download(RTMP * rtmp, FILE * file, int bStdoutMode, int bLiveStream) { int32_t now, lastUpdate; int bufferSize = 64 * 1024; char *buffer; int nRead = 0; off_t size = ftello(file); rtmp->m_read.timestamp = 0; if (rtmp->m_read.timestamp) { RTMP_Log(RTMP_LOGDEBUG, "Continuing at TS: %d ms\n", rtmp->m_read.timestamp); } if (bLiveStream) { RTMP_LogPrintf("Starting Live Stream\n"); } else { RTMP_LogPrintf("Starting download at: %.3f kB\n", (double) size / 1024.0); } rtmp->m_read.initialFrameType = 0; rtmp->m_read.nResumeTS = 0; rtmp->m_read.metaHeader = 0; rtmp->m_read.initialFrame = 0; rtmp->m_read.nMetaHeaderSize = 0; rtmp->m_read.nInitialFrameSize = 0; buffer = (char *) malloc(bufferSize); unsigned long lasttimestamp = 0 ; unsigned long streamlasttimestamp = 0 ; now = RTMP_GetTime(); lastUpdate = now - 1000; do { nRead = RTMP_Read(rtmp, buffer, bufferSize); //RTMP_LogPrintf("nRead: %d\n", nRead); if (nRead > 0) { if (fwrite(buffer, sizeof(unsigned char), nRead, file) != (size_t) nRead) { RTMP_Log(RTMP_LOGERROR, "%s: Failed writing, exiting!", __FUNCTION__); free(buffer); return RD_FAILED; } size += nRead; ///////log struct timeval tnow ; gettimeofday(&tnow, NULL); unsigned long t2_ms = tnow.tv_sec * 1000 + tnow.tv_usec/1000; if( lasttimestamp == 0 ) { streamlasttimestamp = rtmp->m_read.timestamp ;//这个是音频流里面的时间戳。这个算出来的就是音频的码率 lasttimestamp = t2_ms ; //这个是我们收到数据的时间戳,这个算出来的是音频的实际接收码率 continue ; } float tmprate = (double)8*nRead/1024/( (double)(t2_ms-lasttimestamp)/1000) ; float streamtmprate = (double)8*nRead/1024/( (double)(rtmp->m_read.timestamp - streamlasttimestamp)/1000) ; KULV_LOG("RTMP_Read,ms,nread:%d\trecv timediff:%dms,receive rate:%.2fkbps\tstream timediff:%dms,stream rate:%.1fkbps.", nRead, t2_ms-lasttimestamp, tmprate, rtmp->m_read.timestamp - streamlasttimestamp, streamtmprate ); lasttimestamp = t2_ms ; streamlasttimestamp = rtmp->m_read.timestamp ; now = RTMP_GetTime(); if (abs(now - lastUpdate) > 200) { RTMP_LogStatus("\r%.3f kB / %.2f sec. recv speed:%.2f kbps. stream rate:%.2f kbps.", (double) size / 1024.0, (double) (rtmp->m_read.timestamp) / 1000.0, tmprate, streamtmprate); lastUpdate = now; } } else { RTMP_Log(RTMP_LOGDEBUG, "zero read!"); if (rtmp->m_read.status == RTMP_READ_EOF) break; } } while (!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp) && !RTMP_IsTimedout(rtmp)); free(buffer); if (nRead < 0) nRead = rtmp->m_read.status; RTMP_Log(RTMP_LOGDEBUG, "RTMP_Read returned: %d", nRead); if (nRead == -3) return RD_SUCCESS; if ( RTMP_ctrlC || nRead < 0 || RTMP_IsTimedout(rtmp)) { return RD_INCOMPLETE; } return RD_SUCCESS; }
int Download(RTMP * rtmp, // connected RTMP object FILE * file, uint32_t dSeek, uint32_t dStopOffset, double duration, int bResume, char *metaHeader, uint32_t nMetaHeaderSize, char *initialFrame, int initialFrameType, uint32_t nInitialFrameSize, int nSkipKeyFrames, int bStdoutMode, int bLiveStream, int bRealtimeStream, int bHashes, int bOverrideBufferTime, uint32_t bufferTime, double *percent) // percentage downloaded [out] { int32_t now, lastUpdate; int bufferSize = 64 * 1024; char *buffer = (char *) malloc(bufferSize); int nRead = 0; off_t size = ftello(file); unsigned long lastPercent = 0; rtmp->m_read.timestamp = dSeek; *percent = 0.0; if (rtmp->m_read.timestamp) { RTMP_Log(RTMP_LOGDEBUG, "Continuing at TS: %d ms\n", rtmp->m_read.timestamp); } if (bLiveStream) { RTMP_LogPrintf("Starting Live Stream\n"); } else { // print initial status // Workaround to exit with 0 if the file is fully (> 99.9%) downloaded if (duration > 0) { if ((double) rtmp->m_read.timestamp >= (double) duration * 999.0) { RTMP_LogPrintf("Already Completed at: %.3f sec Duration=%.3f sec\n", (double) rtmp->m_read.timestamp / 1000.0, (double) duration / 1000.0); return RD_SUCCESS; } else { *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0; *percent = ((double) (int) (*percent * 10.0)) / 10.0; RTMP_LogPrintf("%s download at: %.3f kB / %.3f sec (%.1f%%)\n", bResume ? "Resuming" : "Starting", (double) size / 1024.0, (double) rtmp->m_read.timestamp / 1000.0, *percent); } } else { RTMP_LogPrintf("%s download at: %.3f kB\n", bResume ? "Resuming" : "Starting", (double) size / 1024.0); } if (bRealtimeStream) RTMP_LogPrintf(" in approximately realtime (disabled BUFX speedup hack)\n"); } 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; rtmp->m_read.initialFrameType = initialFrameType; rtmp->m_read.nResumeTS = dSeek; rtmp->m_read.metaHeader = metaHeader; rtmp->m_read.initialFrame = initialFrame; rtmp->m_read.nMetaHeaderSize = nMetaHeaderSize; rtmp->m_read.nInitialFrameSize = nInitialFrameSize; buffer = (char *) malloc(bufferSize); now = RTMP_GetTime(); lastUpdate = now - 1000; do { nRead = RTMP_Read(rtmp, buffer, bufferSize); //RTMP_LogPrintf("nRead: %d\n", nRead); if (nRead > 0) { if (fwrite(buffer, sizeof(unsigned char), nRead, file) != (size_t) nRead) { RTMP_Log(RTMP_LOGERROR, "%s: Failed writing, exiting!", __FUNCTION__); free(buffer); return RD_FAILED; } size += nRead; //RTMP_LogPrintf("write %dbytes (%.1f kB)\n", nRead, nRead/1024.0); if (duration <= 0) // if duration unknown try to get it from the stream (onMetaData) duration = RTMP_GetDuration(rtmp); if (duration > 0) { // make sure we claim to have enough buffer time! if (!bOverrideBufferTime && bufferTime < (duration * 1000.0)) { bufferTime = (uint32_t) (duration * 1000.0) + 5000; // extra 5sec to make sure we've got enough RTMP_Log(RTMP_LOGDEBUG, "Detected that buffer time is less than duration, resetting to: %dms", bufferTime); RTMP_SetBufferMS(rtmp, bufferTime); RTMP_UpdateBufferMS(rtmp); } *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0; *percent = ((double) (int) (*percent * 10.0)) / 10.0; if (bHashes) { if (lastPercent + 1 <= *percent) { RTMP_LogStatus("#"); lastPercent = (unsigned long) *percent; } } else { now = RTMP_GetTime(); if (abs(now - lastUpdate) > 200) { RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)", (double) size / 1024.0, (double) (rtmp->m_read.timestamp) / 1000.0, *percent); lastUpdate = now; } } } else { now = RTMP_GetTime(); if (abs(now - lastUpdate) > 200) { if (bHashes) RTMP_LogStatus("#"); else RTMP_LogStatus("\r%.3f kB / %.2f sec", (double) size / 1024.0, (double) (rtmp->m_read.timestamp) / 1000.0); lastUpdate = now; } } } else { #ifdef _DEBUG RTMP_Log(RTMP_LOGDEBUG, "zero read!"); #endif if (rtmp->m_read.status == RTMP_READ_EOF) break; } } while (!RTMP_ctrlC && nRead > -1 && RTMP_IsConnected(rtmp) && !RTMP_IsTimedout(rtmp)); free(buffer); if (nRead < 0) nRead = rtmp->m_read.status; /* Final status update */ if (!bHashes) { if (duration > 0) { *percent = ((double) rtmp->m_read.timestamp) / (duration * 1000.0) * 100.0; *percent = ((double) (int) (*percent * 10.0)) / 10.0; RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)", (double) size / 1024.0, (double) (rtmp->m_read.timestamp) / 1000.0, *percent); } else { RTMP_LogStatus("\r%.3f kB / %.2f sec", (double) size / 1024.0, (double) (rtmp->m_read.timestamp) / 1000.0); } } RTMP_Log(RTMP_LOGDEBUG, "RTMP_Read returned: %d", nRead); if (bResume && nRead == -2) { RTMP_LogPrintf("Couldn't resume FLV file, try --skip %d\n\n", nSkipKeyFrames + 1); return RD_FAILED; } if (nRead == -3) return RD_SUCCESS; if ((duration > 0 && *percent < 99.9) || RTMP_ctrlC || nRead < 0 || RTMP_IsTimedout(rtmp)) { return RD_INCOMPLETE; } return RD_SUCCESS; }
//Publish using RTMP_Write() int publish_using_write(){ uint32_t start_time=0; uint32_t now_time=0; uint32_t pre_frame_time=0; uint32_t lasttime=0; int bNextIsKey=0; char* pFileBuf=NULL; //read from tag header uint32_t type=0; uint32_t datalength=0; uint32_t timestamp=0; RTMP *rtmp=NULL; FILE*fp=NULL; fp=fopen("cuc_ieschool.flv","rb"); if (!fp){ RTMP_LogPrintf("Open File Error.\n"); // CleanupSockets(); return -1; } /* set log level */ //RTMP_LogLevel loglvl=RTMP_LOGDEBUG; //RTMP_LogSetLevel(loglvl); // if (!InitSockets()){ // RTMP_LogPrintf("Init Socket Err\n"); // return -1; // } rtmp=RTMP_Alloc(); RTMP_Init(rtmp); //set connection timeout,default 30s rtmp->Link.timeout=5; if(!RTMP_SetupURL(rtmp,"rtmp://localhost/publishlive/livestream")) { RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n"); RTMP_Free(rtmp); // CleanupSockets(); return -1; } RTMP_EnableWrite(rtmp); //1hour RTMP_SetBufferMS(rtmp, 3600*1000); if (!RTMP_Connect(rtmp,NULL)){ RTMP_Log(RTMP_LOGERROR,"Connect Err\n"); RTMP_Free(rtmp); // CleanupSockets(); return -1; } if (!RTMP_ConnectStream(rtmp,0)){ RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n"); RTMP_Close(rtmp); RTMP_Free(rtmp); //CleanupSockets(); return -1; } printf("Start to send data ...\n"); //jump over FLV Header fseek(fp,9,SEEK_SET); //jump over previousTagSizen fseek(fp,4,SEEK_CUR); start_time=RTMP_GetTime(); while(1) { if((((now_time=RTMP_GetTime())-start_time) <(pre_frame_time)) && bNextIsKey){ //wait for 1 sec if the send process is too fast //this mechanism is not very good,need some improvement if(pre_frame_time>lasttime){ RTMP_LogPrintf("TimeStamp:%8lu ms\n", pre_frame_time); lasttime=pre_frame_time; } sleep(1); continue; } //jump over type fseek(fp,1,SEEK_CUR); if(!ReadU24(&datalength,fp)) break; if(!ReadTime(×tamp,fp)) break; //jump back fseek(fp,-8,SEEK_CUR); pFileBuf=(char*)malloc(11+datalength+4); memset(pFileBuf,0,11+datalength+4); if(fread(pFileBuf,1,11+datalength+4,fp)!=(11+datalength+4)) break; pre_frame_time=timestamp; if (!RTMP_IsConnected(rtmp)){ RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n"); break; } if (!RTMP_Write(rtmp,pFileBuf,11+datalength+4)){ RTMP_Log(RTMP_LOGERROR,"Rtmp Write Error\n"); break; } free(pFileBuf); pFileBuf=NULL; if(!PeekU8(&type,fp)) break; if(type==0x09){ if(fseek(fp,11,SEEK_CUR)!=0) break; if(!PeekU8(&type,fp)){ break; } if(type==0x17) bNextIsKey=1; else bNextIsKey=0; fseek(fp,-11,SEEK_CUR); } } RTMP_LogPrintf("\nSend Data Over\n"); if(fp) fclose(fp); if (rtmp!=NULL){ RTMP_Close(rtmp); RTMP_Free(rtmp); rtmp=NULL; } if(pFileBuf){ free(pFileBuf); pFileBuf=NULL; } //CleanupSockets(); return 0; }
int main(int argc, char **argv) { extern char *optarg; int nStatus = RD_SUCCESS; double percent = 0; double duration = 0.0; int nSkipKeyFrames = DEF_SKIPFRM; // skip this number of keyframes when resuming int bOverrideBufferTime = FALSE; // if the user specifies a buffer time override this is true int bStdoutMode = TRUE; // if true print the stream directly to stdout, messages go to stderr int bResume = FALSE; // true in resume mode uint32_t dSeek = 0; // seek position in resume mode, 0 otherwise 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 char *metaHeader = 0; uint32_t nMetaHeaderSize = 0; // video keyframe for matching char *initialFrame = 0; uint32_t nInitialFrameSize = 0; int initialFrameType = 0; // tye: audio or video AVal hostname = { 0, 0 }; AVal playpath = { 0, 0 }; AVal subscribepath = { 0, 0 }; AVal usherToken = { 0, 0 }; //Justin.tv auth token int port = -1; int protocol = RTMP_PROTOCOL_UNDEFINED; int retries = 0; int bLiveStream = FALSE; // is it a live stream? then we can't seek/resume int bRealtimeStream = FALSE; // If true, disable the BUFX hack (be patient) int bHashes = FALSE; // display byte counters not hashes by default long int timeout = DEF_TIMEOUT; // timeout connection after 120 seconds uint32_t dStartOffset = 0; // seek position in non-live mode uint32_t dStopOffset = 0; RTMP rtmp = { 0 }; FILE *pLogFile; AVal fullUrl = { 0, 0 }; AVal swfUrl = { 0, 0 }; AVal tcUrl = { 0, 0 }; AVal pageUrl = { 0, 0 }; AVal app = { 0, 0 }; AVal auth = { 0, 0 }; AVal swfHash = { 0, 0 }; uint32_t swfSize = 0; AVal flashVer = { 0, 0 }; AVal sockshost = { 0, 0 }; #ifdef CRYPTO int swfAge = 30; /* 30 days for SWF cache by default */ int swfVfy = 0; unsigned char hash[RTMP_SWF_HASHLEN]; #endif char *flvFile = 0; signal(SIGINT, sigIntHandler); signal(SIGTERM, sigIntHandler); #ifndef WIN32 signal(SIGHUP, sigIntHandler); signal(SIGPIPE, sigIntHandler); signal(SIGQUIT, sigIntHandler); #endif RTMP_debuglevel = RTMP_LOGALL; //pLogFile = fopen("log.txt", "w"); //RTMP_LogSetOutput(pLogFile); // 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) RTMP_debuglevel = RTMP_LOGCRIT; index++; } #define RTMPDUMP_VERSION "2.4" RTMP_LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION); RTMP_LogPrintf ("(c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n"); if (!InitSockets()) { RTMP_Log(RTMP_LOGERROR, "Couldn't load sockets support on your platform, exiting!"); return RD_FAILED; } /* sleep(30); */ RTMP_Init(&rtmp); int opt; /* struct option longopts[] = { {"help", 0, NULL, 'h'}, {"host", 1, NULL, 'n'}, {"port", 1, NULL, 'c'}, {"socks", 1, NULL, 'S'}, {"protocol", 1, NULL, 'l'}, {"playpath", 1, NULL, 'y'}, {"playlist", 0, NULL, 'Y'}, {"rtmp", 1, NULL, 'r'}, {"swfUrl", 1, NULL, 's'}, {"tcUrl", 1, NULL, 't'}, {"pageUrl", 1, NULL, 'p'}, {"app", 1, NULL, 'a'}, {"auth", 1, NULL, 'u'}, {"conn", 1, NULL, 'C'}, #ifdef CRYPTO {"swfhash", 1, NULL, 'w'}, {"swfsize", 1, NULL, 'x'}, {"swfVfy", 1, NULL, 'W'}, {"swfAge", 1, NULL, 'X'}, #endif {"flashVer", 1, NULL, 'f'}, {"live", 0, NULL, 'v'}, {"flv", 1, NULL, 'o'}, {"resume", 0, NULL, 'e'}, {"timeout", 1, NULL, 'm'}, {"buffer", 1, NULL, 'b'}, {"skip", 1, NULL, 'k'}, {"subscribe", 1, NULL, 'd'}, {"start", 1, NULL, 'A'}, {"stop", 1, NULL, 'B'}, {"token", 1, NULL, 'T'}, {"hashes", 0, NULL, '#'}, {"debug", 0, NULL, 'z'}, {"quiet", 0, NULL, 'q'}, {"verbose", 0, NULL, 'V'}, {0, 0, 0, 0} };*/ while ((opt = getopt/*_long*/(argc, argv, "hVveqzr:s:t:p:a:b:f:o:u:C:n:c:l:y:Ym:k:d:A:B:T:w:x:W:X:S:#"/*, longopts, NULL*/)) != -1) { switch (opt) { case 'h': usage(argv[0]); return RD_SUCCESS; #ifdef CRYPTO case 'w': { int res = hex2bin(optarg, &swfHash.av_val); if (res != RTMP_SWF_HASHLEN) { swfHash.av_val = NULL; RTMP_Log(RTMP_LOGWARNING, "Couldn't parse swf hash hex string, not hexstring or not %d bytes, ignoring!", RTMP_SWF_HASHLEN); } swfHash.av_len = RTMP_SWF_HASHLEN; break; } case 'x': { int size = atoi(optarg); if (size <= 0) { RTMP_Log(RTMP_LOGERROR, "SWF Size must be at least 1, ignoring\n"); } else { swfSize = size; } break; } case 'W': STR2AVAL(swfUrl, optarg); swfVfy = 1; break; case 'X': { int num = atoi(optarg); if (num < 0) { RTMP_Log(RTMP_LOGERROR, "SWF Age must be non-negative, ignoring\n"); } else { swfAge = num; } } break; #endif case 'k': nSkipKeyFrames = atoi(optarg); if (nSkipKeyFrames < 0) { RTMP_Log(RTMP_LOGERROR, "Number of keyframes skipped must be greater or equal zero, using zero!"); nSkipKeyFrames = 0; } else { RTMP_Log(RTMP_LOGDEBUG, "Number of skipped key frames for resume: %d", nSkipKeyFrames); } break; case 'b': { int32_t bt = atol(optarg); if (bt < 0) { RTMP_Log(RTMP_LOGERROR, "Buffer time must be greater than zero, ignoring the specified value %d!", bt); } else { bufferTime = bt; bOverrideBufferTime = TRUE; } break; } case 'v': bLiveStream = TRUE; // no seeking or resuming possible! break; case 'R': bRealtimeStream = TRUE; // seeking and resuming is still possible break; case 'd': STR2AVAL(subscribepath, optarg); break; case 'n': STR2AVAL(hostname, optarg); break; case 'c': port = atoi(optarg); break; case 'l': protocol = atoi(optarg); if (protocol < RTMP_PROTOCOL_RTMP || protocol > RTMP_PROTOCOL_RTMPTS) { RTMP_Log(RTMP_LOGERROR, "Unknown protocol specified: %d", protocol); return RD_FAILED; } break; case 'y': STR2AVAL(playpath, optarg); break; case 'Y': RTMP_SetOpt(&rtmp, &av_playlist, (AVal *)&av_true); break; case 'r': { AVal parsedHost, parsedApp, parsedPlaypath; unsigned int parsedPort = 0; int parsedProtocol = RTMP_PROTOCOL_UNDEFINED; if (!RTMP_ParseURL (optarg, &parsedProtocol, &parsedHost, &parsedPort, &parsedPlaypath, &parsedApp)) { RTMP_Log(RTMP_LOGWARNING, "Couldn't parse the specified url (%s)!", optarg); } else { if (!hostname.av_len) hostname = parsedHost; if (port == -1) port = parsedPort; if (playpath.av_len == 0 && parsedPlaypath.av_len) { playpath = parsedPlaypath; } if (protocol == RTMP_PROTOCOL_UNDEFINED) protocol = parsedProtocol; if (app.av_len == 0 && parsedApp.av_len) { app = parsedApp; } } break; } case 'i': STR2AVAL(fullUrl, optarg); break; case 's': STR2AVAL(swfUrl, optarg); break; case 't': STR2AVAL(tcUrl, optarg); break; case 'p': STR2AVAL(pageUrl, optarg); break; case 'a': STR2AVAL(app, optarg); break; case 'f': STR2AVAL(flashVer, optarg); break; case 'o': flvFile = optarg; if (strcmp(flvFile, "-")) bStdoutMode = FALSE; break; case 'e': bResume = TRUE; break; case 'u': STR2AVAL(auth, optarg); 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; case 'A': dStartOffset = (int) (atof(optarg) * 1000.0); break; case 'B': dStopOffset = (int) (atof(optarg) * 1000.0); break; case 'T': { AVal token; STR2AVAL(token, optarg); RTMP_SetOpt(&rtmp, &av_token, &token); } break; case '#': bHashes = TRUE; break; case 'q': RTMP_debuglevel = RTMP_LOGCRIT; break; case 'V': RTMP_debuglevel = RTMP_LOGDEBUG; break; case 'z': RTMP_debuglevel = RTMP_LOGALL; break; case 'S': STR2AVAL(sockshost, optarg); break; case 'j': STR2AVAL(usherToken, optarg); break; default: RTMP_LogPrintf("unknown option: %c\n", opt); usage(argv[0]); return RD_FAILED; break; } } if (!hostname.av_len && !fullUrl.av_len) { RTMP_Log(RTMP_LOGERROR, "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname"); return RD_FAILED; } if (playpath.av_len == 0 && !fullUrl.av_len) { RTMP_Log(RTMP_LOGERROR, "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath"); return RD_FAILED; } if (protocol == RTMP_PROTOCOL_UNDEFINED && !fullUrl.av_len) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP"); protocol = RTMP_PROTOCOL_RTMP; } if (port == -1 && !fullUrl.av_len) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a port (--port) or rtmp url (-r), using default port 1935"); port = 0; } if (port == 0 && !fullUrl.av_len) { if (protocol & RTMP_FEATURE_SSL) port = 443; else if (protocol & RTMP_FEATURE_HTTP) port = 80; else port = 1935; } if (flvFile == 0) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified an output file (-o filename), using stdout"); bStdoutMode = TRUE; } if (bStdoutMode && bResume) { RTMP_Log(RTMP_LOGWARNING, "Can't resume in stdout mode, ignoring --resume option"); bResume = FALSE; } if (bLiveStream && bResume) { RTMP_Log(RTMP_LOGWARNING, "Can't resume live stream, ignoring --resume option"); bResume = FALSE; } #ifdef CRYPTO if (swfVfy) { if (RTMP_HashSWF(swfUrl.av_val, (unsigned int *)&swfSize, hash, swfAge) == 0) { swfHash.av_val = (char *)hash; swfHash.av_len = RTMP_SWF_HASHLEN; } } if (swfHash.av_len == 0 && swfSize > 0) { RTMP_Log(RTMP_LOGWARNING, "Ignoring SWF size, supply also the hash with --swfhash"); swfSize = 0; } if (swfHash.av_len != 0 && swfSize == 0) { RTMP_Log(RTMP_LOGWARNING, "Ignoring SWF hash, supply also the swf size with --swfsize"); swfHash.av_len = 0; swfHash.av_val = NULL; } #endif if (tcUrl.av_len == 0) { tcUrl.av_len = strlen(RTMPProtocolStringsLower[protocol]) + hostname.av_len + app.av_len + sizeof("://:65535/"); tcUrl.av_val = (char *) malloc(tcUrl.av_len); if (!tcUrl.av_val) return RD_FAILED; tcUrl.av_len = snprintf(tcUrl.av_val, tcUrl.av_len, "%s://%.*s:%d/%.*s", RTMPProtocolStringsLower[protocol], hostname.av_len, hostname.av_val, port, app.av_len, app.av_val); } int first = 1; // User defined seek offset if (dStartOffset > 0) { // Live stream if (bLiveStream) { RTMP_Log(RTMP_LOGWARNING, "Can't seek in a live stream, ignoring --start option"); dStartOffset = 0; } } if (!fullUrl.av_len) { RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath, &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, &flashVer, &subscribepath, &usherToken, dSeek, dStopOffset, bLiveStream, timeout); } else { if (RTMP_SetupURL(&rtmp, fullUrl.av_val) == FALSE) { RTMP_Log(RTMP_LOGERROR, "Couldn't parse URL: %s", fullUrl.av_val); return RD_FAILED; } } /* Try to keep the stream moving if it pauses on us */ if (!bLiveStream && !bRealtimeStream && !(protocol & RTMP_FEATURE_HTTP)) rtmp.Link.lFlags |= RTMP_LF_BUFX; 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) if (bResume) { nStatus = OpenResumeFile(flvFile, &file, &size, &metaHeader, &nMetaHeaderSize, &duration); if (nStatus == RD_FAILED) goto clean; if (!file) { // file does not exist, so go back into normal mode bResume = FALSE; // we are back in fresh file mode (otherwise finalizing file won't be done) } else { nStatus = GetLastKeyframe(file, nSkipKeyFrames, &dSeek, &initialFrame, &initialFrameType, &nInitialFrameSize); if (nStatus == RD_FAILED) { RTMP_Log(RTMP_LOGDEBUG, "Failed to get last keyframe."); goto clean; } if (dSeek == 0) { RTMP_Log(RTMP_LOGDEBUG, "Last keyframe is first frame in stream, switching from resume to normal mode!"); bResume = FALSE; } } } if (!file) { if (bStdoutMode) { file = stdout; SET_BINMODE(file); } else { file = fopen(flvFile, "w+b"); if (file == 0) { RTMP_LogPrintf("Failed to open file! %s\n", flvFile); return RD_FAILED; } } } #ifdef _DEBUG netstackdump = fopen("netstackdump", "wb"); netstackdump_read = fopen("netstackdump_read", "wb"); #endif while (!RTMP_ctrlC) { RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", bufferTime); RTMP_SetBufferMS(&rtmp, bufferTime); if (first) { first = 0; RTMP_LogPrintf("Connecting ...\n"); if (!RTMP_Connect(&rtmp, NULL)) { nStatus = RD_NO_CONNECT; break; } RTMP_Log(RTMP_LOGINFO, "Connected..."); // User defined seek offset if (dStartOffset > 0) { // Don't need the start offset if resuming an existing file if (bResume) { RTMP_Log(RTMP_LOGWARNING, "Can't seek a resumed stream, ignoring --start option"); dStartOffset = 0; } else { dSeek = dStartOffset; } } // Calculate the length of the stream to still play if (dStopOffset > 0) { // Quit if start seek is past required stop offset if (dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); nStatus = RD_SUCCESS; break; } } if (!RTMP_ConnectStream(&rtmp, dSeek)) { nStatus = RD_FAILED; break; } } else { nInitialFrameSize = 0; if (retries) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } RTMP_Log(RTMP_LOGINFO, "Connection timed out, trying to resume.\n\n"); /* Did we already try pausing, and it still didn't work? */ if (rtmp.m_pausing == 3) { /* Only one try at reconnecting... */ retries = 1; dSeek = rtmp.m_pauseStamp; if (dStopOffset > 0) { if (dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed\n"); nStatus = RD_SUCCESS; break; } } if (!RTMP_ReconnectStream(&rtmp, dSeek)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } } else if (!RTMP_ToggleStream(&rtmp)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream\n\n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } bResume = TRUE; } nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume, metaHeader, nMetaHeaderSize, initialFrame, initialFrameType, nInitialFrameSize, nSkipKeyFrames, bStdoutMode, bLiveStream, bRealtimeStream, bHashes, bOverrideBufferTime, bufferTime, &percent); free(initialFrame); initialFrame = NULL; /* If we succeeded, we're done. */ if (nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(&rtmp) || bLiveStream) break; } if (nStatus == RD_SUCCESS) { RTMP_LogPrintf("Download complete\n"); } else if (nStatus == RD_INCOMPLETE) { RTMP_LogPrintf ("Download may be incomplete (downloaded about %.2f%%), try resuming\n", percent); } clean: RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n"); RTMP_Close(&rtmp); if (file != 0) fclose(file); CleanupSockets(); #ifdef _DEBUG if (netstackdump != 0) fclose(netstackdump); if (netstackdump_read != 0) fclose(netstackdump_read); #endif fclose(pLogFile); return nStatus; }
//Publish using RTMP_SendPacket() int publish_using_packet(){ RTMP *rtmp=NULL; RTMPPacket *packet=NULL; uint32_t start_time=0; uint32_t now_time=0; //the timestamp of the previous frame long pre_frame_time=0; long lasttime=0; int bNextIsKey=1; uint32_t preTagsize=0; //packet attributes uint32_t type=0; uint32_t datalength=0; uint32_t timestamp=0; uint32_t streamid=0; FILE*fp=NULL; fp=fopen("../live.flv","rb"); if (!fp){ RTMP_LogPrintf("Open File Error.\n"); // CleanupSockets(); return -1; } /* set log level */ //RTMP_LogLevel loglvl=RTMP_LOGDEBUG; //RTMP_LogSetLevel(loglvl); // if (!InitSockets()){ // RTMP_LogPrintf("Init Socket Err\n"); // return -1; // } rtmp=RTMP_Alloc(); RTMP_Init(rtmp); //set connection timeout,default 30s rtmp->Link.timeout=5; if(!RTMP_SetupURL(rtmp,"rtmp://101.251.251.93:1935/myapp/mystream")) { RTMP_Log(RTMP_LOGERROR,"SetupURL Err\n"); RTMP_Free(rtmp); // CleanupSockets(); return -1; } //if unable,the AMF command would be 'play' instead of 'publish' RTMP_EnableWrite(rtmp); if (!RTMP_Connect(rtmp,NULL)){ RTMP_Log(RTMP_LOGERROR,"Connect Err\n"); RTMP_Free(rtmp); // CleanupSockets(); return -1; } if (!RTMP_ConnectStream(rtmp,0)){ RTMP_Log(RTMP_LOGERROR,"ConnectStream Err\n"); RTMP_Close(rtmp); RTMP_Free(rtmp); // CleanupSockets(); return -1; } packet=(RTMPPacket*)malloc(sizeof(RTMPPacket)); RTMPPacket_Alloc(packet,1024*64); RTMPPacket_Reset(packet); packet->m_hasAbsTimestamp = 0; packet->m_nChannel = 0x04; packet->m_nInfoField2 = rtmp->m_stream_id; RTMP_LogPrintf("Start to send data ...\n"); //jump over FLV Header fseek(fp,9,SEEK_SET); //jump over previousTagSizen fseek(fp,4,SEEK_CUR); start_time=RTMP_GetTime(); while(1) { if((((now_time=RTMP_GetTime())-start_time) <(pre_frame_time)) && bNextIsKey){ //wait for 1 sec if the send process is too fast //this mechanism is not very good,need some improvement if(pre_frame_time>lasttime){ RTMP_LogPrintf("TimeStamp:%8lu ms\n",pre_frame_time); lasttime=pre_frame_time; } sleep(1); continue; } //not quite the same as FLV spec if(!ReadU8(&type,fp)) break; if(!ReadU24(&datalength,fp)) break; if(!ReadTime(×tamp,fp)) break; if(!ReadU24(&streamid,fp)) break; if (type!=0x08&&type!=0x09){ //jump over non_audio and non_video frame, //jump over next previousTagSizen at the same time fseek(fp,datalength+4,SEEK_CUR); continue; } if(fread(packet->m_body,1,datalength,fp)!=datalength) break; packet->m_headerType = RTMP_PACKET_SIZE_LARGE; packet->m_nTimeStamp = timestamp; packet->m_packetType = type; packet->m_nBodySize = datalength; pre_frame_time=timestamp; if (!RTMP_IsConnected(rtmp)){ RTMP_Log(RTMP_LOGERROR,"rtmp is not connect\n"); break; } if (!RTMP_SendPacket(rtmp,packet,0)){ RTMP_Log(RTMP_LOGERROR,"Send Error\n"); break; } if(!ReadU32(&preTagsize,fp)) break; if(!PeekU8(&type,fp)) break; if(type==0x09){ if(fseek(fp,11,SEEK_CUR)!=0) break; if(!PeekU8(&type,fp)){ break; } if(type==0x17) bNextIsKey=1; else bNextIsKey=0; fseek(fp,-11,SEEK_CUR); } } RTMP_LogPrintf("\nSend Data Over\n"); if(fp) fclose(fp); if (rtmp!=NULL){ RTMP_Close(rtmp); RTMP_Free(rtmp); rtmp=NULL; } if (packet!=NULL){ RTMPPacket_Free(packet); free(packet); packet=NULL; } // CleanupSockets(); return 0; }
int QRtmp::download() { qint32 now, lastUpdate; int bufferSize = 64 * 1024; char *buffer = (char *) malloc(bufferSize); int nRead = 0; off_t size = 0; m_rtmp->m_read.timestamp = dSeek; m_percent = 0.0; if(m_rtmp->m_read.timestamp) RTMP_Log(RTMP_LOGDEBUG, "Continuing at TS: %d ms\n", m_rtmp->m_read.timestamp); if(m_bLiveStream) RTMP_LogPrintf("Starting Live Stream\n"); else { // print initial status // Workaround to exit with 0 if the file is fully (> 99.9%) downloaded if(m_duration > 0) { if((double) m_rtmp->m_read.timestamp >= (double) m_duration * 999.0) { RTMP_LogPrintf("Already Completed at: %.3f sec Duration=%.3f sec\n", (double) m_rtmp->m_read.timestamp / 1000.0, (double) m_duration / 1000.0); return RD_SUCCESS; } else { m_percent = ((double) m_rtmp->m_read.timestamp) / (m_duration * 1000.0) * 100.0; m_percent = ((double) (int) (m_percent * 10.0)) / 10.0; RTMP_LogPrintf("%s download at: %.3f kB / %.3f sec (%.1f%%)\n", m_bResume ? "Resuming" : "Starting", (double) size / 1024.0, (double) m_rtmp->m_read.timestamp / 1000.0, m_percent); } } else { RTMP_LogPrintf("%s download at: %.3f kB\n", m_bResume ? "Resuming" : "Starting", (double) size / 1024.0); } } if(dStopOffset > 0) RTMP_LogPrintf("For duration: %.3f sec\n", (double) (dStopOffset - dSeek) / 1000.0); m_rtmp->m_read.nResumeTS = dSeek; now = RTMP_GetTime(); lastUpdate = now - 1000; do { nRead = RTMP_Read(m_rtmp, buffer, bufferSize); //RTMP_LogPrintf("nRead: %d\n", nRead); if(nRead > 0) { if(m_destFile.isOpen()) { if(m_destFile.write(buffer, nRead) != nRead) { setError(QString("Can't write to %1 - %2").arg(m_destFile.fileName()).arg(m_destFile.errorString())); m_stop = true; } } else emit readData(QByteArray(buffer, nRead)); m_destFile.flush(); size += nRead; setStreamIsRunning(true); //RTMP_LogPrintf("write %dbytes (%.1f kB)\n", nRead, nRead/1024.0); if(m_duration <= 0) // if duration unknown try to get it from the stream (onMetaData) m_duration = RTMP_GetDuration(m_rtmp); if(m_duration > 0) { // make sure we claim to have enough buffer time! if(!m_bOverrideBufferTime && m_bufferTime < (m_duration * 1000.0)) { m_bufferTime = (quint32) (m_duration * 1000.0) + 5000; // extra 5sec to make sure we've got enough RTMP_Log(RTMP_LOGDEBUG, "Detected that buffer time is less than duration, resetting to: %dms", m_bufferTime); RTMP_SetBufferMS(m_rtmp, m_bufferTime); RTMP_UpdateBufferMS(m_rtmp); } m_percent = ((double) m_rtmp->m_read.timestamp) / (m_duration * 1000.0) * 100.0; m_percent = ((double) (int) (m_percent * 10.0)) / 10.0; now = RTMP_GetTime(); if(abs(now - lastUpdate) > 200) { RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)", (double) size / 1024.0, (double) (m_rtmp->m_read.timestamp) / 1000.0, m_percent); lastUpdate = now; } } else { now = RTMP_GetTime(); if(abs(now - lastUpdate) > 200) { RTMP_LogStatus("\r%.3f kB / %.2f sec", (double) size / 1024.0, (double) (m_rtmp->m_read.timestamp) / 1000.0); lastUpdate = now; } } } }while (!m_stop && nRead > -1 && RTMP_IsConnected(m_rtmp) && !RTMP_IsTimedout(m_rtmp)); free(buffer); if (nRead < 0) nRead = m_rtmp->m_read.status; /* Final status update */ if(m_duration > 0) { m_percent = ((double) m_rtmp->m_read.timestamp) / (m_duration * 1000.0) * 100.0; m_percent = ((double) (int) (m_percent * 10.0)) / 10.0; RTMP_LogStatus("\r%.3f kB / %.2f sec (%.1f%%)", (double) size / 1024.0, (double) (m_rtmp->m_read.timestamp) / 1000.0, m_percent); } else { RTMP_LogStatus("\r%.3f kB / %.2f sec", (double) size / 1024.0, (double) (m_rtmp->m_read.timestamp) / 1000.0); } RTMP_Log(RTMP_LOGDEBUG, "RTMP_Read returned: %d", nRead); if(m_bResume && nRead == -2) { RTMP_LogPrintf("Couldn't resume FLV file, try --skip %d\n\n", m_nSkipKeyFrames + 1); return RD_FAILED; } if(nRead == -3) return RD_SUCCESS; if((m_duration > 0 && m_percent < 99.9) || m_stop || nRead < 0 || RTMP_IsTimedout(m_rtmp)) { return RD_INCOMPLETE; } return RD_SUCCESS; }
void processTCPrequest(STREAMING_SERVER * server, // server socket and state (our listening socket) int sockfd // client connection socket ) { char buf[512] = { 0 }; // answer buffer char header[2048] = { 0 }; // request header char *filename = NULL; // GET request: file name //512 not enuf char *buffer = NULL; // stream buffer char *ptr = NULL; // header pointer int len; size_t nRead = 0; RTMP rtmp = { 0 }; uint32_t dSeek = 0; // can be used to start from a later point in the stream // reset RTMP options to defaults specified upon invokation of streams RTMP_REQUEST req; char srvhead[] = "\r\nServer: HTTP-RTMP Stream Server " RTMPDUMP_VERSION "\r\n"; // timeout for http requests fd_set fds; struct timeval tv; char *status = "404 Not Found"; server->state = STREAMING_IN_PROGRESS; memcpy(&req, &defaultRTMPRequest, sizeof(RTMP_REQUEST)); memset(&tv, 0, sizeof(struct timeval)); tv.tv_sec = 5; // go through request lines //do { FD_ZERO(&fds); FD_SET(sockfd, &fds); if (select(sockfd + 1, &fds, NULL, NULL, &tv) <= 0) { RTMP_Log(RTMP_LOGERROR, "Request timeout/select failed, ignoring request"); goto quit; } else { nRead = recv(sockfd, header, 2047, 0); header[2047] = '\0'; RTMP_Log(RTMP_LOGDEBUG, "%s: header: %s", __FUNCTION__, header); if (strstr(header, "Range: bytes=") != 0) { // TODO check range starts from 0 and asking till the end. RTMP_LogPrintf("%s, Range request not supported\n", __FUNCTION__); len = sprintf(buf, "HTTP/1.0 416 Requested Range Not Satisfiable%s\r\n", srvhead); send(sockfd, buf, len, 0); goto quit; } if (strncmp(header, "GET", 3) == 0 && nRead > 4) { char *p = filename; filename = header + 4; // filter " HTTP/..." from end of request while (*p != '\0') { if (*p == ' ') { *p = '\0'; break; } p++; } } } //} while(!isHTTPRequestEOF(header, nRead)); // if we got a filename from the GET method if (filename != NULL) { RTMP_Log(RTMP_LOGDEBUG, "%s: Request header: %s", __FUNCTION__, filename); if (filename[0] == '/') { // if its not empty, is it /? ptr = filename + 1; // parse parameters if (*ptr == '?') { int len; ptr++; len = strlen(ptr); while (len >= 2) { // get position of the next '&' char *temp; char ich = *ptr; unsigned int nArgLen; char *arg; ptr++; if (*ptr != '=') goto filenotfound; // long parameters not (yet) supported ptr++; len -= 2; nArgLen = len; if ((temp = strstr(ptr, "&")) != 0) { nArgLen = temp - ptr; } arg = (char *) malloc((nArgLen + 1) * sizeof(char)); memcpy(arg, ptr, nArgLen * sizeof(char)); arg[nArgLen] = '\0'; //RTMP_Log(RTMP_LOGDEBUG, "%s: unescaping parameter: %s", __FUNCTION__, arg); http_unescape(arg); RTMP_Log(RTMP_LOGDEBUG, "%s: parameter: %c, arg: %s", __FUNCTION__, ich, arg); ptr += nArgLen + 1; len -= nArgLen + 1; if (!ParseOption(ich, arg, &req)) { status = "400 unknown option"; goto filenotfound; } } } } else { goto filenotfound; } } else { RTMP_LogPrintf("%s: No request header received/unsupported method\n", __FUNCTION__); } // do necessary checks right here to make sure the combined request of default values and GET parameters is correct if (!req.hostname.av_len) { RTMP_Log(RTMP_LOGERROR, "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname"); status = "400 Missing Hostname"; goto filenotfound; } if (req.playpath.av_len == 0) { RTMP_Log(RTMP_LOGERROR, "You must specify a playpath (--playpath) or url (-r \"rtmp://host[:port]/playpath\") containing a playpath"); status = "400 Missing Playpath"; goto filenotfound;; } if (req.protocol == RTMP_PROTOCOL_UNDEFINED) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP"); req.protocol = RTMP_PROTOCOL_RTMP; } if (req.rtmpport == -1) { RTMP_Log(RTMP_LOGWARNING, "You haven't specified a port (--port) or rtmp url (-r), using default port"); req.rtmpport = 0; } if (req.rtmpport == 0) { if (req.protocol & RTMP_FEATURE_SSL) req.rtmpport = 443; else if (req.protocol & RTMP_FEATURE_HTTP) req.rtmpport = 80; else req.rtmpport = 1935; } if (req.tcUrl.av_len == 0) { char str[512] = { 0 }; req.tcUrl.av_len = snprintf(str, 511, "%s://%.*s:%d/%.*s", RTMPProtocolStringsLower[req.protocol], req.hostname.av_len, req.hostname.av_val, req.rtmpport, req.app.av_len, req.app.av_val); req.tcUrl.av_val = (char *) malloc(req.tcUrl.av_len + 1); strcpy(req.tcUrl.av_val, str); } if (req.swfVfy) { #ifdef CRYPTO if (RTMP_HashSWF(req.swfUrl.av_val, &req.swfSize, req.hash, req.swfAge) == 0) { req.swfHash.av_val = (char *)req.hash; req.swfHash.av_len = RTMP_SWF_HASHLEN; } #endif } // after validation of the http request send response header len = sprintf(buf, "HTTP/1.0 200 OK%sContent-Type: video/flv\r\n\r\n", srvhead); send(sockfd, buf, len, 0); // send the packets buffer = (char *) calloc(PACKET_SIZE, 1); // User defined seek offset if (req.dStartOffset > 0) { if (req.bLiveStream) RTMP_Log(RTMP_LOGWARNING, "Can't seek in a live stream, ignoring --seek option"); else dSeek += req.dStartOffset; } if (dSeek != 0) { RTMP_LogPrintf("Starting at TS: %d ms\n", 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, req.dStopOffset, req.bLiveStream, req.timeout); /* backward compatibility, we always sent this as true before */ if (req.auth.av_len) rtmp.Link.lFlags |= RTMP_LF_AUTH; rtmp.Link.extras = req.extras; rtmp.Link.token = req.token; rtmp.m_read.timestamp = dSeek; RTMP_LogPrintf("Connecting ... port: %d, app: %s\n", req.rtmpport, req.app); if (!RTMP_Connect(&rtmp, NULL)) { RTMP_LogPrintf("%s, failed to connect!\n", __FUNCTION__); } else { unsigned long size = 0; double percent = 0; double duration = 0.0; int nWritten = 0; int nRead = 0; do { nRead = RTMP_Read(&rtmp, buffer, PACKET_SIZE); if (nRead > 0) { if ((nWritten = send(sockfd, buffer, nRead, 0)) < 0) { RTMP_Log(RTMP_LOGERROR, "%s, sending failed, error: %d", __FUNCTION__, GetSockError()); goto cleanup; // we are in STREAMING_IN_PROGRESS, so we'll go to STREAMING_ACCEPTING } size += nRead; //RTMP_LogPrintf("write %dbytes (%.1f KB)\n", nRead, nRead/1024.0); if (duration <= 0) // if duration unknown try to get it from the stream (onMetaData) duration = RTMP_GetDuration(&rtmp); if (duration > 0) { percent = ((double) (dSeek + rtmp.m_read.timestamp)) / (duration * 1000.0) * 100.0; percent = ((double) (int) (percent * 10.0)) / 10.0; RTMP_LogStatus("\r%.3f KB / %.2f sec (%.1f%%)", (double) size / 1024.0, (double) (rtmp.m_read.timestamp) / 1000.0, percent); } else { RTMP_LogStatus("\r%.3f KB / %.2f sec", (double) size / 1024.0, (double) (rtmp.m_read.timestamp) / 1000.0); } } #ifdef _DEBUG else { RTMP_Log(RTMP_LOGDEBUG, "zero read!"); } #endif } while (server->state == STREAMING_IN_PROGRESS && nRead > -1 && RTMP_IsConnected(&rtmp) && nWritten >= 0); } cleanup: RTMP_LogPrintf("Closing connection... "); RTMP_Close(&rtmp); RTMP_LogPrintf("done!\n\n"); quit: if (buffer) { free(buffer); buffer = NULL; } if (sockfd) closesocket(sockfd); if (server->state == STREAMING_IN_PROGRESS) server->state = STREAMING_ACCEPTING; return; filenotfound: RTMP_LogPrintf("%s, %s, %s\n", __FUNCTION__, status, filename); len = sprintf(buf, "HTTP/1.0 %s%s\r\n", status, srvhead); send(sockfd, buf, len, 0); goto quit; }