加入收藏 | 设为首页 | 会员中心 | 我要投稿 李大同 (https://www.lidatong.com.cn/)- 科技、建站、经验、云计算、5G、大数据,站长网!
当前位置: 首页 > 百科 > 正文

RTMPdump 源代码分析 1: main()函数

发布时间:2020-12-15 07:01:52 所属栏目:百科 来源:网络整理
导读:注:此前写了一些列的分析RTMPdump(libRTMP)源代码的文章,在此列一个列表: RTMPdump 源代码分析 1: main()函数 RTMPDump(libRTMP)源代码分析 2:解析RTMP地址——RTMP_ParseURL() RTMPdump(libRTMP) 源代码分析 3: AMF编码 RTMPdump(libRTMP)源

注:此前写了一些列的分析RTMPdump(libRTMP)源代码的文章,在此列一个列表:
RTMPdump 源代码分析 1: main()函数
RTMPDump(libRTMP)源代码分析 2:解析RTMP地址——RTMP_ParseURL()
RTMPdump(libRTMP) 源代码分析 3: AMF编码
RTMPdump(libRTMP)源代码分析 4: 连接第一步——握手(Hand Shake)
RTMPdump(libRTMP) 源代码分析 5: 建立一个流媒体连接 (NetConnection部分)
RTMPdump(libRTMP) 源代码分析 6: 建立一个流媒体连接 (NetStream部分 1)
RTMPdump(libRTMP) 源代码分析 7: 建立一个流媒体连接 (NetStream部分 2)
RTMPdump(libRTMP) 源代码分析 8: 发送消息(Message)
RTMPdump(libRTMP) 源代码分析 9: 接收消息(Message)(接收视音频数据)
RTMPdump(libRTMP) 源代码分析 10: 处理各种消息(Message)

===============================

rtmpdump 是一个用来处理 RTMP 流媒体的工具包,支持 rtmp://,rtmpt://,rtmpe://,rtmpte://,and rtmps:// 等。之前在学习RTMP协议的时候,发现没有讲它源代码的,只好自己分析,现在打算把自己学习的成果写出来,可能结果不一定都对,先暂且记录一下。


使用RTMPdump下载一个流媒体的大致流程是这样的:

RTMP_Init();//初始化结构体
InitSockets();//初始化Socket
RTMP_ParseURL();//解析输入URL
RTMP_SetupStream();//一些设置
fopen();//打开文件,准备写入
RTMP_Connect();//建立NetConnection
RTMP_ConnectStream()//建立NetStream
Download();//下载函数
RTMP_Close();//关闭连接
fclose();//关闭文件
CleanupSockets();//清理Socket


其中Download()主要是使用RTMP_Read()进行下载的。

注:可以参考:RTMP流媒体播放过程

下面贴上自己注释的RTMPDump源代码。注意以下几点:

1.此RTMPDump已经被移植进VC 2010 的 MFC的工程,所以main()函数已经被改名为rtmpdump(),而且参数也改了,传进来一个MFC窗口的句柄。不过功能没怎么改(控制台程序移植到MFC以后,main()就不是程序的入口了,所以main()名字改成什么是无所谓的)

2.里面有很多提取信息的代码形如:rtmp.dlg->AppendCInfo("开始初始化Socket...");这些代码是我为了获取RTMP信息而自己加的,并不影响程序的执行。

int?rtmpdump(LPVOID?lpParam,int?argc,char?**argv)
{
	
??extern?char?*optarg;
??//一定要设置,否则只能运行一次
??extern?int?optind;
??optind=0;
??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?};
??int?port?=?-1;
??int?protocol?=?RTMP_PROTOCOL_UNDEFINED;
??int?retries?=?0;
??int?bLiveStream?=?FALSE;	//?是直播流吗??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;

??signal(SIGINT,?sigIntHandler);
??signal(SIGTERM,?sigIntHandler);
#ifndef?WIN32
??signal(SIGHUP,?sigIntHandler);
??signal(SIGPIPE,?sigIntHandler);
??signal(SIGQUIT,?sigIntHandler);
#endif

??RTMP_debuglevel?=?RTMP_LOGINFO;

??//首先搜寻“?--quiet”选项
??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?"1.0"
??RTMP_LogPrintf("RTMP流媒体下载?%sn",?RTMPDUMP_VERSION);
??RTMP_LogPrintf
????("2012?雷霄骅?中国传媒大学/信息工程学院/通信与信息系统/数字电视技术n");
??//RTMP_LogPrintf("输入?-h?获取命令选项n");
????RTMP_Init(&rtmp);
	//句柄-----------------------------
	rtmp.dlg=(CSpecialPRTMPDlg?*)lpParam;
	//---------------------------------
	//----------------------
	rtmp.dlg->AppendCInfo("开始初始化Socket...");
	//-----------------------------
	if?(!InitSockets())
	{
		//----------------------
		rtmp.dlg->AppendCInfo("初始化Socket失败!");
		//-----------------------------
		RTMP_Log(RTMP_LOGERROR,"Couldn't?load?sockets?support?on?your?platform,?exiting!");
		return?RD_FAILED;
	}
??//----------------------
??rtmp.dlg->AppendCInfo("成功初始化Socket");
??//-----------------------------
??/*?sleep(30);?*/



??int?opt;
/*??struct?option?longopts[]?=?{
????{"help",?0,?NULL,?'h'},????{"host",?1,?'n'},????{"port",?'c'},????{"socks",?'S'},????{"protocol",?'l'},????{"playpath",?'y'},????{"playlist",?'Y'},????{"rtmp",?'r'},????{"swfUrl",?'s'},????{"tcUrl",?'t'},????{"pageUrl",?'p'},????{"app",?'a'},????{"auth",?'u'},????{"conn",?'C'},#ifdef?CRYPTO
????{"swfhash",?'w'},????{"swfsize",?'x'},????{"swfVfy",?'W'},????{"swfAge",?'X'},#endif
????{"flashVer",?'f'},????{"live",?'v'},????{"flv",?'o'},????{"resume",?'e'},????{"timeout",?'m'},????{"buffer",?'b'},????{"skip",?'k'},????{"subscribe",?'d'},????{"start",?'A'},????{"stop",?'B'},????{"token",?'T'},????{"hashes",?'#'},????{"debug",?'z'},????{"quiet",?'q'},????{"verbose",?'V'},????{0,?0}
??};*/
??//分析命令行参数,注意用法。
??//选项都是一个字母,后面有冒号的代表该选项还有相关参数
??//一直循环直到获取所有的opt
??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,?ignoringn");
	??????}
	????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,?ignoringn");
	??????}
	????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':
		//----------------
		rtmp.dlg->AppendCInfo("该RTMP的URL是一个直播流");
		//----------------
	??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;
	??//路径参数-r
	case?'r':
	??{
	????AVal?parsedHost,?parsedApp,?parsedPlaypath;
	????unsigned?int?parsedPort?=?0;
	????int?parsedProtocol?=?RTMP_PROTOCOL_UNDEFINED;
		//解析URL。注optarg指向参数(URL)
		RTMP_LogPrintf("RTMP?URL?:?%sn",optarg);
		//----------------
		rtmp.dlg->AppendCInfo("解析RTMP的URL...");
		//----------------
	????if?(!RTMP_ParseURL
		(optarg,?&parsedProtocol,?&parsedHost,?&parsedPort,?&parsedPlaypath,?&parsedApp))
	??????{
			//----------------
			rtmp.dlg->AppendCInfo("解析RTMP的URL失败!");
			//----------------
		RTMP_Log(RTMP_LOGWARNING,?"无法解析?url?(%s)!",????optarg);
	??????}
	????else
	??????{
			//----------------
			rtmp.dlg->AppendCInfo("解析RTMP的URL成功");
			//----------------
		//把解析出来的数据赋值
		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':
	??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:?%cn",?opt);
	??usage(argv[0]);
	??return?RD_FAILED;
	??break;
	}
????}

??if?(!hostname.av_len)
????{
??????RTMP_Log(RTMP_LOGERROR,??"您必须指定?主机名(hostname)?(--host)?或?url?(-r?"rtmp://host[:port]/playpath")?包含?a?hostname");
??????return?RD_FAILED;
????}
??if?(playpath.av_len?==?0)
????{
??????RTMP_Log(RTMP_LOGERROR,??"您必须指定?播放路径(playpath)?(--playpath)?或?url?(-r?"rtmp://host[:port]/playpath")?包含?a?playpath");
??????return?RD_FAILED;
????}

??if?(protocol?==?RTMP_PROTOCOL_UNDEFINED)
????{
??????RTMP_Log(RTMP_LOGWARNING,??"您没有指定?协议(protocol)?(--protocol)?或?rtmp?url?(-r),?默认协议?RTMP");
??????protocol?=?RTMP_PROTOCOL_RTMP;
????}
??if?(port?==?-1)
????{
??????RTMP_Log(RTMP_LOGWARNING,??"您没有指定?端口(port)?(--port)?或?rtmp?url?(-r),?默认端口?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,??"请指定一个输出文件?(-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)
????{
??????//直播流
??????if?(bLiveStream)
	{
	??RTMP_Log(RTMP_LOGWARNING,??????"Can't?seek?in?a?live?stream,?ignoring?--start?option");
	??dStartOffset?=?0;
	}
????}
??//----------------
??rtmp.dlg->AppendCInfo("开始初始化RTMP连接的参数...");
??//----------------
??//设置
??RTMP_SetupStream(&rtmp,?protocol,?&hostname,?&sockshost,?&playpath,???&tcUrl,?&swfUrl,?&pageUrl,?&app,?&auth,?&swfHash,?swfSize,???&flashVer,?&subscribepath,?dSeek,?dStopOffset,?bLiveStream,?timeout);
??//此处设置参数-----------------
??rtmp.dlg->AppendCInfo("成功初始化RTMP连接的参数");
??//-----------------------------
??char?*temp=(char?*)malloc(MAX_URL_LENGTH);

??memcpy(temp,rtmp.Link.hostname.av_val,rtmp.Link.hostname.av_len);
??temp[rtmp.Link.hostname.av_len]='';
??rtmp.dlg->AppendB_R_L_Info("主机名",temp);

??itoa(rtmp.Link.port,temp,10);
??rtmp.dlg->AppendB_R_L_Info("端口号",temp);

??memcpy(temp,rtmp.Link.app.av_val,rtmp.Link.app.av_len);
??temp[rtmp.Link.app.av_len]='';
??rtmp.dlg->AppendB_R_L_Info("应用程序",rtmp.Link.playpath.av_val,rtmp.Link.playpath.av_len);
??temp[rtmp.Link.playpath.av_len]='';
??rtmp.dlg->AppendB_R_L_Info("路径",temp);


??//-----------------------------

??/*?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;

??//?ok,我们必须获得timestamp?of?the?last?keyframe?(only?keyframes?are?seekable)?/?last?audio?frame?(audio?only?streams)
??if?(bResume)
????{
	//打开文件,输出的文件(Resume)
??????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)
	{
	//直接输出到stdout
	??file?=?stdout;
	??SET_BINMODE(file);
	}
??????else
	{
	//打开一个文件
	//w+b?读写打开或建立一个二进制文件,允许读和写。
		//-----------------
		rtmp.dlg->AppendCInfo("创建输出文件...");
		//-----------------------------
	??file?=?fopen(flvFile,?"w+b");
	??if?(file?==?0)
	????{
			//-----------------
			rtmp.dlg->AppendCInfo("创建输出文件失败!");
			//-----------------------------
	??????RTMP_LogPrintf("Failed?to?open?file!?%sn",?flvFile);
	??????return?RD_FAILED;
	????}
	??rtmp.dlg->AppendCInfo("成功创建输出文件");
	}
????}

#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);
	??//设置Buffer时间
	??//-----------------
	??rtmp.dlg->AppendCInfo("设置缓冲(Buffer)的时间");
	??//-----------------------------
??????RTMP_SetBufferMS(&rtmp,?bufferTime);
	??//第一次执行
??????if?(first)
	{
	??first?=?0;
	??RTMP_LogPrintf("开始建立连接!n");
	??//-----------------
	??rtmp.dlg->AppendCInfo("开始建立连接(NetConnection)...");
	??//-----------------------------
	??//建立连接(Connect)
	??if?(!RTMP_Connect(&rtmp,?NULL))
	????{
			//-----------------
			rtmp.dlg->AppendCInfo("建立连接(NetConnection)失败!");
			//-----------------------------
	??????nStatus?=?RD_FAILED;
	??????break;
	????}
	??//-----------------
	??rtmp.dlg->AppendCInfo("成功建立连接(NetConnection)");
	??//-----------------------------
	??//RTMP_Log(RTMP_LOGINFO,?"已链接...");

	??//?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?Completedn");
		??nStatus?=?RD_SUCCESS;
		??break;
		}
	????}
	??//创建流(Stream)(发送connect命令消息后处理传来的数据)
	??itoa(rtmp.m_inChunkSize,10);
	??rtmp.dlg->AppendB_R_Info("输入Chunk大小",temp);
	??itoa(rtmp.m_outChunkSize,10);
	??rtmp.dlg->AppendB_R_Info("输出Chunk大小",temp);
	??itoa(rtmp.m_stream_id,10);
	??rtmp.dlg->AppendB_R_Info("Stream?ID",temp);
	??itoa(rtmp.m_nBufferMS,10);
	??rtmp.dlg->AppendB_R_Info("Buffer时长(ms)",temp);
	??itoa(rtmp.m_nServerBW,10);
	??rtmp.dlg->AppendB_R_Info("ServerBW",temp);
	??itoa(rtmp.m_nClientBW,10);
	??rtmp.dlg->AppendB_R_Info("ClientBW",temp);
	??itoa((int)rtmp.m_fEncoding,10);
	??rtmp.dlg->AppendB_R_Info("命令消息编码方法",temp);
	??itoa((int)rtmp.m_fDuration,10);
	??rtmp.dlg->AppendB_R_Info("时长(s)",temp);

	??rtmp.dlg->ShowBInfo();
	??free(temp);
	??//-----------------
	??rtmp.dlg->AppendCInfo("开始建立网络流(NetStream)");
	??//-----------------------------
	??if?(!RTMP_ConnectStream(&rtmp,?dSeek))
	????{
		//-----------------
		rtmp.dlg->AppendCInfo("建立网络流(NetStream)失败!");
		//-----------------
	??????nStatus?=?RD_FAILED;
	??????break;
	????}
	??//-----------------
	??rtmp.dlg->AppendCInfo("成功建立网络流(NetStream)!");
	??//-----------------
	}
??????else
	{
	??nInitialFrameSize?=?0;

??????????if?(retries)
????????????{
	??????RTMP_Log(RTMP_LOGERROR,?"Failed?to?resume?the?streamnn");
	??????if?(!RTMP_IsTimedout(&rtmp))
	????????nStatus?=?RD_FAILED;
	??????else
	????????nStatus?=?RD_INCOMPLETE;
	??????break;
????????????}
	??RTMP_Log(RTMP_LOGINFO,?"Connection?timed?out,?trying?to?resume.nn");
??????????/*?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?Completedn");
		??????nStatus?=?RD_SUCCESS;
		??????break;
????????????????????}
????????????????}
??????????????if?(!RTMP_ReconnectStream(&rtmp,?dSeek))
????????????????{
	??????????RTMP_Log(RTMP_LOGERROR,?"Failed?to?resume?the?streamnn");
	??????????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?streamnn");
	??????if?(!RTMP_IsTimedout(&rtmp))
		nStatus?=?RD_FAILED;
	??????else
		nStatus?=?RD_INCOMPLETE;
	??????break;
	????}
	??bResume?=?TRUE;
	}
	//-----------------
	
	//-----------------
	rtmp.dlg->AppendCInfo("开始将媒体数据写入文件");
	//-----------------
	??//下载,写入文件
??????nStatus?=?Download(&rtmp,?file,?duration,?bResume,?metaHeader,?nMetaHeaderSize,?initialFrame,?initialFrameType,?nInitialFrameSize,?bStdoutMode,?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.dlg->AppendCInfo("写入文件完成");
		//-----------------
??????RTMP_LogPrintf("Download?completen");
????}
	//没下载完的时候
??else?if?(nStatus?==?RD_INCOMPLETE)
????{
		//-----------------
		rtmp.dlg->AppendCInfo("写入文件可能不完整");
		//-----------------
??????RTMP_LogPrintf
	("Download?may?be?incomplete?(downloaded?about?%.2f%%),?try?resumingn",?percent);
????}
??//后续清理工作
clean:
??//-----------------
??rtmp.dlg->AppendCInfo("关闭连接");
??//-----------------
??RTMP_Log(RTMP_LOGDEBUG,?"Closing?connection.n");
??RTMP_Close(&rtmp);
??rtmp.dlg->AppendCInfo("关闭文件");
??if?(file?!=?0)
????fclose(file);
??rtmp.dlg->AppendCInfo("关闭Socket");
??CleanupSockets();

#ifdef?_DEBUG
??if?(netstackdump?!=?0)
????fclose(netstackdump);
??if?(netstackdump_read?!=?0)
????fclose(netstackdump_read);
#endif
??return?nStatus;
}


其中InitSocket()代码很简单,初始化了Socket,如下:

//?初始化?sockets
int
InitSockets()
{
#ifdef?WIN32
??WORD?version;
??WSADATA?wsaData;

??version?=?MAKEWORD(1,?1);
??return?(WSAStartup(version,?&wsaData)?==?0);
#else
??return?TRUE;
#endif
}


CleanupSockets()则更简单:

inline?void
CleanupSockets()
{
#ifdef?WIN32
??WSACleanup();
#endif
}


Download()函数则比较复杂:

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

??//long?ftell(FILE?*stream);
??//返回当前文件指针
??RTMP_LogPrintf("开始下载!n");
??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?msn",?rtmp->m_read.timestamp);
????}
??//是直播
??if?(bLiveStream)
????{
??????RTMP_LogPrintf("直播流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?secn",(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?kBn",????bResume???"Resuming"?:?"Starting",????(double)?size?/?1024.0);
	}
????}

??if?(dStopOffset?>?0)
????RTMP_LogPrintf("For?duration:?%.3f?secn",?(double)?(dStopOffset?-?dSeek)?/?1000.0);

??//各种设置参数到rtmp连接
??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;

??now?=?RTMP_GetTime();
??lastUpdate?=?now?-?1000;
??do
????{
	//从rtmp中把bufferSize(64k)个数据读入buffer
??????nRead?=?RTMP_Read(rtmp,?buffer,?bufferSize);
??????//RTMP_LogPrintf("nRead:?%dn",?nRead);
??????if?(nRead?>?0)
	{
	//函数:size_t?fwrite(const?void*?buffer,size_t?size,size_t?count,FILE*?stream);
	//向文件读入写入一个数据块。返回值:返回实际写入的数据块数目
	//(1)buffer:是一个指针,对fwrite来说,是要输出数据的地址。
	//(2)size:要写入内容的单字节数;?  
	//(3)count:要进行写入size字节的数据项的个数;?  
	//(4)stream:目标文件指针。?  
	//(5)返回实际写入的数据项个数count。
	//关键。把buffer里面的数据写成文件
		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/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;	//?再加5s以确保buffertime足够长

		??RTMP_Log(RTMP_LOGDEBUG,??????"Detected?that?buffer?time?is?less?than?duration,?resetting?to:?%dms",??????bufferTime);
		??//重设Buffer长度
		??RTMP_SetBufferMS(rtmp,?bufferTime);
		??//给服务器发送UserControl消息通知Buffer改变
		??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
		{
			//设置显示数据的更新间隔200ms
		??now?=?RTMP_GetTime();
		??if?(abs(now?-?lastUpdate)?>?200)
		????{
		??????RTMP_LogStatus("r%.3f?kB?/?%.2f?sec?(%.1f%%)",(double)?(rtmp->m_read.timestamp)?/?1000.0,?*percent);
		??????lastUpdate?=?now;
		????}
		}
	????}
	??else
	????{
		//现在距离开机的毫秒数
	??????now?=?RTMP_GetTime();
		??//每间隔200ms刷新一次数据
	??????if?(abs(now?-?lastUpdate)?>?200)
		{
		??if?(bHashes)
		????RTMP_LogStatus("#");
		??else
			//size为已写入文件的字节数
		????RTMP_LogStatus("r%.3f?kB?/?%.2f?sec",?(double)?size?/?1024.0,??????(double)?(rtmp->m_read.timestamp)?/?1000.0);
		??lastUpdate?=?now;
		}
	????}
	}
#ifdef?_DEBUG
??????else
	{
	??RTMP_Log(RTMP_LOGDEBUG,?"zero?read!");
	}
#endif

????}
??while?(!RTMP_ctrlC?&&?nRead?>?-1?&&?RTMP_IsConnected(rtmp)?&&?!RTMP_IsTimedout(rtmp));
??free(buffer);
??if?(nRead?<?0)
	//nRead是读取情况
????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)?(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?%dnn",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;
}


以上内容是我能理解到的rtmpdump.c里面的内容。


rtmpdump源代码(Linux):http://download.csdn.net/detail/leixiaohua1020/6376561

rtmpdump源代码(VC 2005 工程):http://download.csdn.net/detail/leixiaohua1020/6563163

(编辑:李大同)

【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容!

    推荐文章
      热点阅读