Example #1
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
	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 = TRUE;	// 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 };

	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 };


	char *flvFile = 0;

	signal(SIGINT, sigIntHandler);
	signal(SIGTERM, sigIntHandler);
	signal(SIGHUP, sigIntHandler);
	signal(SIGPIPE, sigIntHandler);
	signal(SIGQUIT, sigIntHandler);

	RTMP_debuglevel = RTMP_LOGINFO;

	// 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++;
	}

	RTMP_LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION);
	RTMP_LogPrintf("(c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n");

	RTMP_Init(&rtmp);//初始化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'},
		{"url", 1, NULL, 'i'},
		{"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'},
		{"flashVer", 1, NULL, 'f'},
		{"live", 0, NULL, 'v'},
		{"realtime", 0, NULL, 'R'},
		{"flv", 1, NULL, 'o'},
		{"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'},
		{"jtv", 1, NULL, 'j'},
		{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 '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 '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 (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
	{//指定了-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;
		}
	}

	/* Try to keep the stream moving if it pauses on us */
	if (!bLiveStream && !bRealtimeStream && !(protocol & RTMP_FEATURE_HTTP))
		rtmp.Link.lFlags |= RTMP_LF_BUFX;
	if( bLiveStream ){
		rtmp.Link.lFlags |= RTMP_LF_LIVE ;
	}

	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
				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;
			}
		}

		nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, 
				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);


#ifdef _DEBUG
	if (netstackdump != 0)
		fclose(netstackdump);
	if (netstackdump_read != 0)
		fclose(netstackdump_read);
#endif
	return nStatus;
}
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;
}
Example #3
0
int Download(RTMP * rtmp, 
		FILE * file, uint32_t dSeek, uint32_t dStopOffset, double duration, 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;
	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",
						"Starting",
						(double) size / 1024.0, (double) rtmp->m_read.timestamp / 1000.0,
						*percent);
			}
		}
		else
		{
			RTMP_LogPrintf("%s download at: %.3f kB\n",
					"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);

	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 (nRead == -3)
		return RD_SUCCESS;

	if ((duration > 0 && *percent < 99.9) || RTMP_ctrlC || nRead < 0
			|| RTMP_IsTimedout(rtmp))
	{
		return RD_INCOMPLETE;
	}

	return RD_SUCCESS;
}
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;
}
Example #5
0
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;

    char srvhead[] = "\r\nServer: HTTP-RTMP Stream Server " RTMPDUMP_VERSION "\r\n";

    char *status = "404 Not Found";

    server->state = STREAMING_IN_PROGRESS;

    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;
    memcpy(&req, &defaultRTMPRequest, sizeof(RTMP_REQUEST));

    // timeout for http requests
    fd_set fds;
    struct timeval tv;

    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)
        {
            filename = header + 4;

            // filter " HTTP/..." from end of request
            char *p = filename;
            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 == '?')
            {
                ptr++;
                int len = strlen(ptr);

                while (len >= 2)
                {
                    char ich = *ptr;
                    ptr++;
                    if (*ptr != '=')
                        goto filenotfound;	// long parameters not (yet) supported

                    ptr++;
                    len -= 2;

                    // get position of the next '&'
                    char *temp;

                    unsigned int nArgLen = len;
                    if ((temp = strstr(ptr, "&")) != 0)
                    {
                        nArgLen = temp - ptr;
                    }

                    char *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 && !req.fullUrl.av_len)
    {
        RTMP_Log(RTMP_LOGERROR,
                 "You must specify a hostname (--host) or url (-r \"rtmp://host[:port]/playpath\") containing a hostname");
        status = "400 Missing Hostname";
        goto filenotfound;
    }
    if (req.playpath.av_len == 0 && !req.fullUrl.av_len)
    {
        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 && !req.fullUrl.av_len)
    {
        RTMP_Log(RTMP_LOGWARNING,
                 "You haven't specified a protocol (--protocol) or rtmp url (-r), using default protocol RTMP");
        req.protocol = RTMP_PROTOCOL_RTMP;
    }
    if (req.rtmpport == -1 && !req.fullUrl.av_len)
    {
        RTMP_Log(RTMP_LOGWARNING,
                 "You haven't specified a port (--port) or rtmp url (-r), using default port");
        req.rtmpport = 0;
    }
    if (req.rtmpport == 0 && !req.fullUrl.av_len)
    {
        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);
    if (!req.fullUrl.av_len)
    {
        RTMP_SetupStream(&rtmp, req.protocol, &req.hostname, req.rtmpport, &req.sockshost,
                         &req.playpath, &req.tcUrl, &req.swfUrl, &req.pageUrl, &req.app, &req.auth, &req.swfHash, req.swfSize, &req.flashVer, &req.subscribepath, &req.usherToken, dSeek, req.dStopOffset,
                         req.bLiveStream, req.timeout);
    }
    else
    {
        if (RTMP_SetupURL(&rtmp, req.fullUrl.av_val) == FALSE)
        {
            RTMP_Log(RTMP_LOGERROR, "Couldn't parse URL: %s", req.fullUrl.av_val);
            return;
        }
    }
    /* backward compatibility, we always sent this as true before */
    if (req.auth.av_len)
        rtmp.Link.lFlags |= RTMP_LF_AUTH;

    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.av_val);
    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;
}
Example #6
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;//xjzhang,默认的bufferTime为10小时;

    // 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 };
    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 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 };

    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;
    //xjzhang, 屏蔽这些signal,进入正常退出流程;
    signal(SIGINT, sigIntHandler);
    signal(SIGTERM, sigIntHandler);
#ifndef WIN32
    signal(SIGHUP, sigIntHandler);
    signal(SIGPIPE, sigIntHandler);
    signal(SIGQUIT, sigIntHandler);
#endif

    RTMP_debuglevel = RTMP_LOGINFO;

    // 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.3b"
    RTMP_LogPrintf("RTMPDump %s\n", RTMPDUMP_VERSION);
    RTMP_LogPrintf
    ("(c) 2010 Andrej Stepanchuk, Howard Chu, The Flvstreamer Team; license: GPL\n");

    //xjzhang, 只对Window版本,因为window对socket操作需要先用WSAStartup()初始化;
    if (!InitSockets())
    {
        RTMP_Log(RTMP_LOGERROR,
                 "Couldn't load sockets support on your platform, exiting!");
        return RD_FAILED;
    }

    /* sleep(30); */

    //xjzhang,初始化rtmp结构体;
    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}
      };*/

    //xjzhang, 从命令行参数中获取配置选项;
    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 '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'://xjzhang,根据地址获取协议,主机,端口,播放路径等参数;
        {
            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 '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': //xjzhang, 指定要输出的flv文件;
            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;
        default:
            RTMP_LogPrintf("unknown option: %c\n", opt);
            usage(argv[0]);
            return RD_FAILED;
            break;
        }
    }

    //xjzhang,对选项进行合理性检查;
    if (!hostname.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)
    {
        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)
    {
        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)
    {
        RTMP_Log(RTMP_LOGWARNING,
                 "You haven't specified a port (--port) or rtmp url (-r), using default port 1935");
        port = 0;
    }
    if (port == 0)
    {
        if (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)
    {
        char str[512] = { 0 };

        tcUrl.av_len = snprintf(str, 511, "%s://%.*s:%d/%.*s",
                                RTMPProtocolStringsLower[protocol], hostname.av_len,
                                hostname.av_val, port, app.av_len, app.av_val);
        tcUrl.av_val = (char *) malloc(tcUrl.av_len + 1);
        strcpy(tcUrl.av_val, str);
    }

    int first = 1;

    // 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;
        }
    }

	//xjzhang,将获取到的参数(协议,主机,端口,代理,播放路径等)传递给LibRTMP,对stream进行设置;
    RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath,
                     &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize,
                     &flashVer, &subscribepath, 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;

    off_t size = 0;

	//xjzhang, resume 参数对直播流无效,所以对直播流bResume为false;
	// 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
        {
			//xjzhang,打开指定的输出文件;
            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

	//xjzhnag,进入download的循环中;
    while (!RTMP_ctrlC)
    {
        RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", bufferTime);
        RTMP_SetBufferMS(&rtmp, bufferTime);

		//xjzhang,第一次进入该循环中需要先建立stream连接;
        if (first)
        {
            first = 0;
            RTMP_LogPrintf("Connecting ...\n");

			//xjzhang,根据之前设置的连接参数建立连接;
            if (!RTMP_Connect(&rtmp, NULL))
            {
                nStatus = RD_FAILED;
                break;
            }

            RTMP_Log(RTMP_LOGINFO, "Connected...");

			//xjzhang,根据参数设置下载起点,对直播流忽略此设置;
			// 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;
                }
            }

			//xjzhang,根据参数设置下载终点,对直播流忽略此设置;
            // 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;
                }
            }

			//xjzhang, 已经发出connet命令,从网络收取server发来的chunk消息,等待server回应”play start“
            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;
        }

		//xjzhang, 协商连接过程都已完成,对端开始发流,本端开始dump;
        nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume,
                           metaHeader, nMetaHeaderSize, initialFrame,
                           initialFrameType, nInitialFrameSize,
                           nSkipKeyFrames, bStdoutMode, bLiveStream, bHashes,
                           bOverrideBufferTime, bufferTime, &percent);
        free(initialFrame);
        initialFrame = NULL;

        /* If we succeeded, we're done.
         */
        if (nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(&rtmp) || bLiveStream)
            break;
    }//xjzhang, break while{};

    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:
	//xjzhang, 关闭RTMP连接,关闭输出文件;
    RTMP_Log(RTMP_LOGDEBUG, "Closing connection.\n");
    RTMP_Close(&rtmp);

    if (file != 0)
        fclose(file);

	//xjzhang, 只对Window平台,调用WSACleanup()清理socket;
    CleanupSockets();

#ifdef _DEBUG
    if (netstackdump != 0)
        fclose(netstackdump);
    if (netstackdump_read != 0)
        fclose(netstackdump_read);
#endif
    return nStatus;
}
Example #7
0
//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(&timestamp,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;
}