RTMPdump(libRTMP) 源代码分析系列文章:

RTMPdump (libRTMP) 源代码分析3: AMF编码

RTMPdump (libRTMP) 源代码分析6: 建立一个流媒体连接  (NetStream部分 1)

RTMPdump (libRTMP) 源代码分析9: 接收消息 (Message)  (接收视音频数据)

函数调用结构图

 

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

 

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

  

  •     }  
  •       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! %s\n", 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 Completed\n");  
  •           nStatus = RD_SUCCESS;  
  •           break;  
  •         }  
  •         }  
  •       //创建流(Stream)(发送connect命令消息后处理传来的数据)  
  •       itoa(rtmp.m_inChunkSize,temp,10);  
  •       rtmp.dlg->AppendB_R_Info("输入Chunk大小",temp);  
  •       itoa(rtmp.m_outChunkSize,temp,10);  
  •       rtmp.dlg->AppendB_R_Info("输出Chunk大小",temp);  
  •       itoa(rtmp.m_stream_id,temp,10);  
  •       rtmp.dlg->AppendB_R_Info("Stream ID",temp);  
  •       itoa(rtmp.m_nBufferMS,temp,10);  
  •       rtmp.dlg->AppendB_R_Info("Buffer时长(ms)",temp);  
  •       itoa(rtmp.m_nServerBW,temp,10);  
  •       rtmp.dlg->AppendB_R_Info("ServerBW",temp);  
  •       itoa(rtmp.m_nClientBW,temp,10);  
  •       rtmp.dlg->AppendB_R_Info("ClientBW",temp);  
  •       itoa((int)rtmp.m_fEncoding,temp,10);  
  •       rtmp.dlg->AppendB_R_Info("命令消息编码方法",temp);  
  •       itoa((int)rtmp.m_fDuration,temp,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 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;  
  •     }  
  •     //-----------------  
  •       
  •     //-----------------  
  •     rtmp.dlg->AppendCInfo("开始将媒体数据写入文件");  
  •     //-----------------  
  •       //下载,写入文件  
  •       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.dlg->AppendCInfo("写入文件完成");  
  •         //-----------------  
  •       RTMP_LogPrintf("Download complete\n");  
  •     }  
  •     //没下载完的时候  
  •   else if (nStatus == RD_INCOMPLETE)  
  •     {  
  •         //-----------------  
  •         rtmp.dlg->AppendCInfo("写入文件可能不完整");  
  •         //-----------------  
  •       RTMP_LogPrintf  
  •     ("Download may be incomplete (downloaded about %.2f%%), try resuming\n",  
  •      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;  
  • }