RTMPdump 源代码分析 1: main()函数
注:此前写了一些列的分析RTMPdump(libRTMP)源代码的文章,在此列一个列表: =============================== 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; }
//?初始化?sockets int InitSockets() { #ifdef?WIN32 ??WORD?version; ??WSADATA?wsaData; ??version?=?MAKEWORD(1,?1); ??return?(WSAStartup(version,?&wsaData)?==?0); #else ??return?TRUE; #endif }
inline?void CleanupSockets() { #ifdef?WIN32 ??WSACleanup(); #endif }
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 (编辑:李大同) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |