为了更加深入理解rtmp协议,下面分析一个开源的rtmp工具:rtmpdump。下载地址:https://github.com/aajanki/rtmpdump
rtmpdump实际就是一个下载工具,它会把URL中指定的资源下载下来
1、初始化
2、解析URL
3、打开文件(把URL资源保存到本地的文件)
4、建立连接
5、建立流
6、开始下载
7、下载完成,关闭连接,清理资源
下面是简化过的main函数,只保留核心的几个函数
intmain(int argc, char **argv){ // *** 只保留核心代码 // rtmp初始化 RTMP_Init(&rtmp); // 参数解析 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:#j:J:", longopts, NULL)) != -1) { switch (opt) { case 'h': usage(argv[0]); return RD_SUCCESS; case 'r': { AVal parsedHost, parsedApp, parsedPlaypath; unsigned int parsedPort = 0; int parsedPRotocol = RTMP_PROTOCOL_UNDEFINED; // URL解析 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; } default: RTMP_LogPrintf("unknown option: %c/n", opt); usage(argv[0]); return RD_FAILED; break; } } // 启动流 RTMP_SetupStream(&rtmp, protocol, &hostname, port, &sockshost, &playpath, &tcUrl, &swfUrl, &pageUrl, &app, &auth, &swfHash, swfSize, &FlashVer, &subscribepath, &usherToken, &WeebToken, dSeek, dStopOffset, bLiveStream, timeout); 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; } } } while (!RTMP_ctrlC) { RTMP_Log(RTMP_LOGDEBUG, "Setting buffer time to: %dms", bufferTime); RTMP_SetBufferMS(&rtmp, bufferTime); if (first) { first = 0; RTMP_LogPrintf("Connecting .../n"); // 建立连接 if (!RTMP_Connect(&rtmp, NULL)) { nStatus = RD_NO_CONNECT; break; } RTMP_Log(RTMP_LOGINFO, "Connected..."); // User defined seek offset if (dStartOffset > 0) { // Don't need the start offset if resuming an existing file if (bResume) { RTMP_Log(RTMP_LOGWARNING, "Can't seek a resumed stream, ignoring --start option"); dStartOffset = 0; } else { dSeek = dStartOffset; } } // Calculate the length of the stream to still play if (dStopOffset > 0) { // Quit if start seek is past required stop offset if (dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed/n"); nStatus = RD_SUCCESS; break; } } // 建立流连接 if (!RTMP_ConnectStream(&rtmp, dSeek)) { nStatus = RD_FAILED; break; } } else { nInitialFrameSize = 0; if (retries) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream/n/n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } RTMP_Log(RTMP_LOGINFO, "Connection timed out, trying to resume./n/n"); /* Did we already try pausing, and it still didn't work? */ if (rtmp.m_pausing == 3) { /* Only one try at reconnecting... */ retries = 1; dSeek = rtmp.m_pauseStamp; if (dStopOffset > 0) { if (dStopOffset <= dSeek) { RTMP_LogPrintf("Already Completed/n"); nStatus = RD_SUCCESS; break; } } if (!RTMP_ReconnectStream(&rtmp, dSeek)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream/n/n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } } else if (!RTMP_ToggleStream(&rtmp)) { RTMP_Log(RTMP_LOGERROR, "Failed to resume the stream/n/n"); if (!RTMP_IsTimedout(&rtmp)) nStatus = RD_FAILED; else nStatus = RD_INCOMPLETE; break; } bResume = TRUE; } // 下载 nStatus = Download(&rtmp, file, dSeek, dStopOffset, duration, bResume, metaHeader, nMetaHeaderSize, initialFrame, initialFrameType, nInitialFrameSize, nSkipKeyFrames, bStdoutMode, bLiveStream, bHashes, bOverrideBufferTime, bufferTime, &percent); free(initialFrame); initialFrame = NULL; /* If we succeeded, we're done. */ if (nStatus != RD_INCOMPLETE || !RTMP_IsTimedout(&rtmp) || bLiveStream) break; } if (nStatus == RD_SUCCESS) { RTMP_LogPrintf("Download complete/n"); } else if (nStatus == RD_INCOMPLETE) { RTMP_LogPrintf ("Download may be incomplete (downloaded about %.2f%%), try resuming/n", percent); }clean: // 关闭,清理 RTMP_Log(RTMP_LOGDEBUG, "Closing connection./n"); RTMP_Close(&rtmp); if (file != 0) fclose(file); CleanupSockets();#ifdef _DEBUG if (netstackdump != 0) fclose(netstackdump); if (netstackdump_read != 0) fclose(netstackdump_read);#endif return nStatus;}
新闻热点
疑难解答