为了正常的体验网站,请在浏览器设置里面开启Javascript功能!

DSS源码分析汇总精华

2013-10-27 50页 doc 1MB 243阅读

用户头像

is_412617

暂无简介

举报
DSS源码分析汇总精华Darwin 5.5.5 代码分析文档 ---sinkin(jinzhcheng@tom.com) 2007.10 DarwinStreamServer是苹果公司开发的流媒体视频服务器。我们分析的代码基于版本5.5.5。 一、DSS启动、初始化流程 我们从Server.tproj/main.cpp入手,分析DSS加载module及和module交互的流程。 (1)、DSS在fork子进程后调用StartServer启动服务 调用OS、OSThread、Socket、SocketUtils、QTSSDi...
DSS源码分析汇总精华
Darwin 5.5.5 代码分析文档 ---sinkin(jinzhcheng@tom.com) 2007.10 DarwinStreamServer是苹果公司开发的流媒体视频服务器。我们分析的代码基于版本5.5.5。 一、DSS启动、初始化流程 我们从Server.tproj/main.cpp入手,分析DSS加载module及和module交互的流程。 (1)、DSS在fork子进程后调用StartServer启动服务 调用OS、OSThread、Socket、SocketUtils、QTSSDictionaryMap、 QTSServerInterface、QTSServer等类的Initialize数进行初始化。 (2)、select_startevents函数 initialize the select() implementation of the event queue. (3)、QTSServer::Initialize函数 继续调用QTSSModule、QTSServerPres、QTSSMessages、RTSPRequestInterface、 RTSPSessionInterface、RTPSessionInterface、RTPStream、RTSPSession、 QTSSFile、QTSSUserProfile等类的Initialize函数,进行dictionary的初始化。 加载了第一个模块QTSSErrorLogModule。 this->SetDefaultIPAddr() // set default IP addr & DNS name // begin listening,注意这里传的是false参数。 this->CreateListeners(false,fSrvrPrefs,inPortOverride) (4)、TaskThreadPool::AddThreads(numThreads) // numThreads为 1 到这里,第一个线程创建、运行、被添加到线程池里。 在startBroadcastRTSPSession函数里,又调用AddThreads函数在线程池里添加了一个线程。 (5)、TimeoutTask::Initialize() Start up the server's global tasks, and start listening. The timeoutTask mechanism is task based, we therefore must do this after adding task threads. This be done before starting the sockets and server tasks. sThread = NEW TimeoutTaskThread(); sThread->signal(Task::kStartEvent); 创建一个TimeoutTaskThread类对象,实际上这个类的名字容易产生混淆,它并不是一个线程类,而 是一个基于Task类的任务类。 因为前面已经在线程池里添加了一个任务线程,所以在这里调用signal的时候,就会找到这个线程, 并把事件加入到这个线程的任务队列里,等待被处理。(这时,刚才创建的线程应该也在 TaskThread::Entry函数里等待事件的发生) (6)、IdleTask::Initialize() // 创建并启动空闲任务线程 sIdleThread = NEW IdleTaskThread(); sIdleThread->Start(); (7)、Socket::StartThread() // 启动Socket类的sEventThread类所对应的线程。sEventThread类在Socket::Initialize函数里创建 // 到目前为止,这已是第三个启动的线程,分别是任务线程、空闲任务线程、事务线程。 (8)、OSThread::Sleep(1000) 这里的Sleep是调用usleep来实现,为什么这里要睡眠1s???是为了等待线程的启动??? (9)、sServer->InitModules(inInitialState) 初始化并加载一些模块。共加载了 QTSSHomeDirectoryModule、QTSSRefMovieModule、QTSSFileModule、QTSSReflectorModule、 QTSSRelayModule、QTSSAccessLogModule、QTSSFlowControlModule、QTSSPosixFileSysModule、 QTSSAdminModule、QTSSMP3StreamingModule、QTSSAccessModule这些模块。 fSrvrPrefs = new QTSServerPrefs(sPrefsSource, true); ... ... fSrvrM7essages = new QTSSMessages(sMessagesSource); QTSSModuleUtils::Initialize(fSrvrMessages, this, QTSServerInterface::GetErrorLogStream()); ... ... Add Reread Preferences Service. // 对于那些支持initial role的模块,通过它们的CallDispatch函数来调用具体的initial函数。 this->DoInitRole(); (10)、sServer->StartTasks() 创建RTCPTask、RTPStatsUpdaterTask Start listening,因为TCPListenerSocket是EventContext的继承类,所以这里实际上调用的是 EventContext::RequestEvent(). (11)、sServer->SetupUDPSockets() udp sockets are set up after the rtcp task is instantiated。 针对系统的每一个ip地址,都创建并绑定一个socket端口对(分别用于RTP data发送和RTCP data接 收),并申请对这两个socket端口的监听。 注意调用CreateUDPSocketPair函数传进去的Port参数为0,所以在通过Darwin播放静态多媒体文件 时,不论是同一个媒体文件的音频、视频流还是同时播放的多个媒体文件,都是这两个socket端口 来完成RTCP、RTP数据的处理。 (12)、CleanPid(true); WritePid(!inDontFork); doneStartingUp = true; 在/var/run下写pid文件 (13)、sServer->SwitchPersonality() switch to run user and group ID 执行setgid、setuid函数 (14)、RunServer() 由一个大循环构成: while((theServerState != qtssShuttingDownState) && (theServerState != qtssFatalErrorState)) { OSThread::Sleep(1000); if(sStatusUpdateInterval) // 周期性更新状态,当带有-D参数时,为3。 { 通过PrintHeader打印标题行 RTP-Conns RTSP-Conns HTTP-Conns kBits/Sec Pkts/Sec RTP-Playing AvgDelay CurMaxDelay MaxDelay AvgQuality NumThinned Time RTP-Conns RTSP-Conns HTTP-Conns kBits/Sec Pkts/Sec TotConn TotBytes TotPktsLost Time 通过DebugLevel_1、PrintStatus打印每字段的值。 每个字段对应的变量分别为: RTP-Conns: fNumRTPSessions; RTSP-Conns: fNumRTPSessions; HTTP-Conns: fNumRTSPHTTPSessions; kBits/Sec: fCurrentRTPBandwidthInBits; Pkts/Sec: fRTPPacketsPerSecond; RTP-Playing: fNumRTPPlayingSessions; // sLastDebugPacket即为上次的fTotalRTPPackets。 AvgDelay: fTotalLate/(fTotalRTPPackets – sLastDebugPackets) CurMaxDelay: fCurrentMaxLate; MaxDelay: fMaxLate; // sLastDebugTotalQuality即为上次的fTotalQuality。 AvgQuality: (fTotalQuality-sLastDebugTotalQuality)/(fTotalRTPPackets–sLastDebugPackets) NumThinned: fNumThinned; TotConn: fTotalRTPSessions; TotBytes: fTotalRTPBytes; TotPktsLost: fTotalRTPPacketsLost 如果接收到SigInt或者SigTerm,则终止sServer。 } 二、几大部分 (一)、模块 · DSS利用模块来响应请求和处理任务。DSS的模块分为三种类型: (1)、Content-Managing Modules The content-managing modules manage RTSP requests and responses related to media sources, such as a file or a broadcast. Each module is responsible for interpreting the client's request, reading and parsing their supported files or network source, and responding with RTSP and RTP. In some cases, such as the mp3 streaming module, the module uses HTTP. The content-managing modules are QTSSFileModule, QTSSReflectorModule, QTSSRelayModule, and QTSSMP3StreamingModule. (2)、Server-Support Modules The server-support modules performserver data gathering and logging functions. The server- support modules are QTSSErrorLogModule, QTSSAccessLogModule, QTSSWebStatsModule, QTSSWebDebugModule, QTSSAdminModule, and QTSSPOSIXFileSystemModule. (3)、Access Control Modules The access control modules provide authentication and authorization functions as well as URL path manipulation. The access control modules are QTSSAccessModule, QTSSHomeDirectoryModule, QTSSHttpFileModule, and QTSSSpamDefenseModule. · 几个相关类的定义 一、QTSSModule类 各个Module类均以QTSSModule类为基类。 (1)、构建函数 QTSSModule::QTSSModule(char* inName, char* inPath) : QTSSDictionary(QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kModuleDictIndex)), ... ... { 如果模块以库文件的形式保存在disk上,利用OSCodeFragment类来处理 另外,QTSSModule本身是QTSSDictionary的继承类,它还有一个QTSSDictionary类对象的指 针成员 调用SetVal、SetValue进行一些属性的设置 } · 模块加载过程 一、以加载QTSSErrorLogModule为例 在创建一个QTSSModule类对象后,调用该类的SetupModule成员函数,并调用AddModule函数和调用QTSServer的BuildModuleRoleArrays函数。 注意:DSS还提供了一个OSCodeFragment类,来处理以库文件的形式保存在disk的模块。 (1)SetupModule成员函数 传给SetupModule成员函数的两个形参分别是QTSS_CallbacksPtr(函数指针)、 QTSS_MainEntryPointPtr(具体模块的进入函数指针,对于QTSSErrorLogModule类,这个函数是QTSSErrorLogModule_Main) 调用QTSSErrorLogModule_Main HYPERLINK "http://localhost/lxr/http/ident?v=DarwinStreamingSrvr5.5.5-Source;i=_stublibrary_main" _stublibrary_main(inPrivateArgs, QTSSErrorLogModuleDispatch) HYPERLINK "http://localhost/lxr/http/ident?v=DarwinStreamingSrvr5.5.5-Source;i=sCallbacks" sCallbacks = theArgs->inCallbacks; sErrorLogStream = theArgs->inErrorLogStream; // Send requested information back to the server theArgs->outStubLibraryVersion = QTSS_API_VERSION; theArgs->outDispatchFunction = inDispatchFunc; 注:这里的sCallbacks、sErrorLogStream分别是在QTSS_Private.cpp里定义的静 态全局量。theArgs->inCallbacks为SetupModule的实参(即QTSServer类的 sCallbacks成员),而inDispatchFunc为具体模块传进来的实参 QTSSErrorLogModuleDispatch。 将fDispatchFunc设置为thePrivateArgs.outDispatchFunction,fDispatchFunc为QTSSModule的私有类成员。这样DSS可以通过具体模块的基类QTSSModule的这个私有成员来让具体模块进行分发处理。 调用QTSServerInterface::LogError函数进行log功能。 (2)AddModule函数 传给AddModule函数的实参为要加载的具体模块。 Prepare to invoke the module's Register role. Setup the Register param block QTSS_ModuleState theModuleState; theModuleState.curModule = inModule; theModuleState.curRole = QTSS_Register_Role; theModuleState.curTask = NULL; OSThread::SetMainThreadData(&theModuleState); inModule->CallDispatch(QTSS_Register_Role, &theRegParams); (fDispatchFunc)(inRole, inParams); 对于QTSSErrorLogModule模块即为QTSSErrorLogModuleDispatch函数,该函数根据不同的 Role调用模块具体的处理函数,对于QTSS_Register_Role调用Register函数。Register函 数通过调用QTSS_AddRole来告知DSS模块支持的Role。 // Update the module name to reflect what was returned from the register role inModule->SetValue(qtssModName, 0, theRegParams.regParams.outModuleName, ::strlen(theRegParams.regParams.outModuleName), false); // Give the module object a prefs dictionary. Instance attributes are allowed for these objects. QTSSPrefs* thePrefs = NEW QTSSPrefs( sPrefsSource, inModule->GetValue(qtssModName), QTSSDictionaryMap::GetMap(QTSSDictionaryMap::kModulePrefsDictIndex), true); thePrefs->RereadPreferences(); inModule->SetPrefsDict(thePrefs); // Add this module to the array of module (dictionaries) UInt32 theNumModules = this->GetNumValues(qtssSvrModuleObjects); QTSS_Error theErr = this->SetValue(qtssSvrModuleObjects, theNumModules, &inModule, sizeof(QTSSModule*), QTSSDictionary::kDontObeyReadOnly); // Add this module to the module queue sModuleQueue.EnQueue(inModule->GetQueueElem()); (3)BuildModuleRoleArrays函数 根据QTSSModule的fRoleArray数组的Role支持情况,在QTSServerInterface::sNumModulesInRole里做记录,并在sModuleArray数组里保存相关Role的所有QTSSModule指针。 这样,QTSServer就可以知道目前有哪些模块支持哪些Role。 到此,一个Module已经添加到QTSServer里。 二、以加载QTSSReflectorModule为例 在创建一个QTSSModule类对象后,调用该类的SetupModule成员函数,并调用AddModule函数和调用QTSServer的BuildModuleRoleArrays函数。 QTSSModule* theReflectorModule = new QTSSModule("QTSSReflectorModule"); (void)theReflectorModule->SetupModule(&sCallbacks, &QTSSReflectorModule_Main); 调用 QTSSReflectorModule_Main函数, 将fDispatchFunc设置为 thePrivateArgs.outDispatchFunction(QTSSReflectorModuleDispatch),fDispatchFunc为 QTSSModule的私有类成员。这样DSS可以通过具体模块的基类QTSSModule的这个私有成员来 让具体模块进行分发处理。 调用QTSServerInterface::LogError函数进行log功能 for(UInt32 x=0;xCallDispatch(QTSS_ErrorLog_Role,&theParams); // 因为只有QTSSErrorLogModule注册kErrorLogRole,实际上会调用这个类的 // LogError函数。 If this is a fatal error, 调用SetValue set the proper attribute in the RTSPServer dictionary。 (void)AddModule(theReflectorModule); 后面同上面所说的QTSSErrorLogModule加载情况。 · 一些模块的介绍 一、QTSSRTPFileModule Content source module that uses the QTFileLib to serve Hinted QuickTime files to clients. 支持TSS_Initialize_Role、QTSS_RTSPPreProcessor_Role、QTSS_ClientSessionClosing_Role、 QTSS_RereadPrefs_Role。Dispatch函数里也提供了QTSS_RTPSendPackets_Role的处理,直接通过模块来调用这个Role的处理。 在initialize函数里,通过SetupSupportedMethods登记支持的方法:DESCRIBE, SETUP, PLAY, PAUSE, and TEARDOWN。 !!这个模块并没有被加载!! (1)、ProcessRTSPRequest QTSS_RTSPPreProcessor_Role的处理函数。 根据请求内容,分别做出处理: Describe: DoDescribe(inParams); Setup: DoSetup(inParams); Play: DoPlay(inParams); Teardown: // Tell the server that this session should be killed, and send a TEARDOWN response QTSS_Teardown(...); QTSS_SendStandardRTSPResponse(); Pause: QTSS_Pause(); QTSS_SendStandardRTSPResponse(); (2)、DoDescribe Check and see if this is a request we should handle. We handle all requests with URLs that end in a '.rtp' Get the FileSession for this DESCRIBE, if any. if(theFile != NULL) { // There is already a file for this session. This can happen if there are multiple // DESCRIBES, or a DESCRIBE has been issued with a Session ID, or some such thing. if ( !theFullPath.Equal( *theFile->fFile.GetMoviePath() ) ) { delete theFile; theFile = NULL; QTSS_SetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, &theFile, sizeof(theFile)); } } if(theFile == NULL) { // 创建FileSession类对象,调用theFile->fFile.Initialize(theFullPath, 8); CreateRTPFileSession(inParamBlock, theFullPath, &theFile); QTSS_SetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, &theFile, sizeof(theFile)); } 二、QTSSFileModule模块 Content source module that uses the QTFileLib to serve Hinted QuickTime files to clients. 注册了QTSS_Initialize_Role、QTSS_RTSPRequest_Role、QTSS_ClientSessionClosing_Role、 QTSS_RereadPrefs_Role的处理。在Dispatch函数里还添加了QTSS_RTPSendPackets_Role的处理。 在initialize函数里,通过SetupSupportedMethods登记支持的方法:DESCRIBE, SETUP, PLAY, PAUSE, and TEARDOWN。 (1)、ProcessRTSPRequest() QTSS_RTSPRequest_Role的处理函数。 根据请求内容,分别做出处理: Describe: DoDescribe(inParams); Setup: DoSetup(inParams); Play: DoPlay(inParams); Teardown: // Tell the server that this session should be killed, and send a TEARDOWN response QTSS_Teardown(...); QTSS_SendStandardRTSPResponse(); Pause: QTSS_Pause(); QTSS_SendStandardRTSPResponse(); (2)、DoDescribe() if(isSDP(inParamBlock)) // sdp file { ... ... QTSS_GetValuePtr(inParamBlock->inRTSPRequest, qtssRTSPReqFilePath, 0,(void**)&pathStr.Ptr, &pathStr.Len); QTSSModuleUtils::SendErrorResponse(inParamBlock->inRTSPRequest,qtssClientNotFound,sNoSDPFileFoundErr,&pathStr); ... ... return err; } ... ... if (theFile != NULL) { // There is already a file for this session. This can happen if there are multiple // DESCRIBES, or a DESCRIBE has been issued with a Session ID, or some such thing. moviePath(theFile->fFile.GetMoviePath()); if(!requestPath.Equal(moviePath)) { DeleteFileSession(theFile); theFile = NULL; QTSS_SetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, &theFile, sizeof(theFile)); } } if (theFile == NULL) { // 创建FileSession类对象,调用theFile->fFile.Initialize(inPath,); // theFile->fFile为QTRTPFile类型的对象。 // 通过QTRTPFile、QTFile、QTSSFile等类对象来完成媒体文件的解析 CreateQTRTPFile(inParamBlock, thePath.GetObject(), &theFile); QTSS_SetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, &theFile, sizeof(theFile)); } // replace the sacred character we have trodden on in order to truncate the path. // 文件名添加.sdp后缀 thePath.GetObject()[thePathLen – sSDPSuffix.Len] = sSDPSuffix.Ptr[0]; if (sEnableMovieFileSDP) // 在配置文件里enable_movie_file_sdp为false,为什么也能在客 // 户端的播放器里访问sdp文件??? { // Check to see if there is an sdp file, if so, return that file instead of the // built-in sdp. QTSSModuleUtils::ReadEntireFile(thePath.GetObject(), &theSDPData); } 后续是sdp信息的处理(这里实现什么???)如果配置文件中的record_movie_file_sdp为 true,则会生成一个sdp文件。 // now parse the movie media sdp data. We need to do this in order to extract payload // information. The SDP parser object will not take responsibility of the memory (one // exception... see above) // 注意在前面已经调用了: // theSDPData.Ptr = theFile->fFile.GetSDPFile(&sdpLen); theSDPData.Len = sdpLen; // 从媒体文件里获取sdp信息。 theFile->fSDPSource.Parse(theSDPData.Ptr, theSDPData.Len); (3)、DoSetup() if (isSDP(inParamBlock)) { ... ... } QTSS_GetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, (void*)&theFile, &theLen); if((theErr != QTSS_NoErr) || (theLen != sizeof(FileSession*))) { ... ... } QTSS_GetValueAsString(inParamBlock->inRTSPRequest, qtssRTSPReqFileDigit, 0, &theDigitStr); // 比如客户端发送一个 //“SETUP rtsp://192.168.2.163:554/sample_100kbit.mp4/trackID=3 RTSP/1.0\r\n”请求,则 // theTrackID等于3。 Uint32 theTrackID = ::strtol(theDigitStr, NULL, 10); // 调用QTRTPFile::AddTrack theFile->fFile.AddTrack(theTrackID, true); // Before setting up this track, check to see if there is an If-Modified-Since date. If // there is, and the content hasn't been modified, then just return a 304 Not Modified 这里所起的作用是什么??? // Find the payload for this track ID(if applicable) 根据theFile->fSDPSource的streaminfo,设置thePayload、thePayloadType、bufferDelay // Create a new RTP stream // 实际上是调用RTPSession::AddStream() QTSS_AddRTPStream(inParamBlock->inClientSession, inParamBlock->inRTSPRequest, &newStream, 0); // Set the payload type, payload name & timescale of this track // Set the number of quality levels. Allow up to 6 调用QTSS_SetValue进行设置。 // Get the SSRC of this track // give the file some info it needs. 根据TrackID,将对应的trackEntry->SSRC设置为上面获得的SSRC,将trackEntry->Cookie1 Cookie2 分别设为newStream、thePayloadType。 theErr = QTSS_GetValuePtr(inParamBlock->inRTSPHeaders, qtssXRTPMetaInfoHeader, 0, (void**)&theHeader.Ptr, &theHeader.Len); if(theErr == QTSS_NoErr) { ... ... } // Our array has now been updated to reflect the fields requested by the client. send the // setup response 调用QTSS_AppendRTSPHeader QTSS_SendStandardRTSPResponse(inParamBlock->inRTSPRequest, newStream, 0); (4)、SetupCacheBuffers() 通过QTSS_GetValue获取playCount值。 // increments num buffers after initialization so do only once per session // Allocate函数以及OSFileSource::ReadFromCache/ReadFromDisk函数的分析待续 if (sEnableSharedBuffers && playCount == 1) (*theFile)->fFile.AllocateSharedBuffers(sSharedBufferUnitKSize, sSharedBufferInc, sSharedBufferUnitSize,sSharedBufferMaxUnits); if (sEnablePrivateBuffers) // reinitializes buffers to current location so do every time (*theFile)->fFile.AllocatePrivateBuffers(sSharedBufferUnitKSize, sPrivateBufferUnitSize, sPrivateBufferMaxUnits); playCount ++; QTSS_SetValue(inParamBlock->inClientSession, sFileSessionPlayCountAttrID, 0, &playCount, (5)、DoPlay() if (isSDP(inParamBlock)) { ... ... } QTSS_GetValue(inParamBlock->inClientSession, sFileSessionAttr, 0, (void*)&theFile, &theLen); SetupCacheBuffers(inParamBlock, theFile); 调用一系列的设置工作QTSS_SetValue。。。 // Tell the server to start playing this movie. We do want it to send RTCP SRs, but we // DON'T want it to write the RTP header // 调用RTPSession::Play QTSS_Play(inParamBlock->inClientSession, inParamBlock->inRTSPRequest, qtssPlayFlagsSendRTCP); 准备RTSP回复内容,并调用QTSS_SendStandardRTSPResponse (6)、SendPackets() QTSS_RTPSendPackets_Role的处理函数,在RTPSession::Run函数里会调用这个Role的注册函数。 theLastPacketTrack = (*theFile)->fFile.GetLastPacketTrack(); while (true) { if ((*theFile)->fPacketStruct.packetData == NULL) { // 寻找要传输的包,theTransmitTime为发送时间 theTransmitTime = (*theFile)->fFile.GetNextPacket( (char**)&(*theFile)->fPacketStruct.packetData, &(*theFile)->fNextPacketLen); // 刚找到的 theLastPacketTrack = (*theFile)->fFile.GetLastPacketTrack(); theStream = (QTSS_Object)theLastPacketTrack->Cookie1; // Check to see if we should stop playing now if (((*theFile)->fStopTime !=-1) && (theTransmitTime > (*theFile)->fStopTime)) if (((*theFile)->fStopTrackID !=0) && ((*theFile)->fStopTrackID == theLastPacketTrack->TrackID) && (theLastPacketTrack->HTCB->fCurrentPacketNumber > (*theFile)->fStopPN)) { // We should indeed stop playing ... ... inParams->outNextPacketTime = qtssDontCallSendPacketsAgain; ... ... return QTSS_NoErr; } // Find out what our play speed is. Send packets out at the specified rate, // and do so by altering the transmit time of the packet based on the // Speed rate. 调整theTransmitTime } // 如果还没有数据 if ((*theFile)->fPacketStruct.packetData == NULL) { inParams->outNextPacketTime = qtssDontCallSendPacketsAgain; return QTSS_NoERR; } // 发送数据 // If the stream is video, we need to make sure that QTRTPFile knows what quality level // we're at 设置quality level。 adjust the timestamp so it reflects paused time. // 调用RTPStream::Write函数。RTPStream::Write返回EAGAIN时,QTSS_Write返回QTSS_WouldBlock theErr = QTSS_Write(theStream, &(*theFile)->fPacketStruct, (*theFile)->fNextPacketLen,NULL,theFlags); // 设置inParams->outNextPacketTime,RTPSession::Run函数根据outNextPacketTime,决定返回值。 if ( theErr == QTSS_WouldBlock ) { // reset the packet time stamp so we adjust it again when we really do send it if (currentTimeStamp != pauseTimeStamp) SetPacketTimeStamp(currentTimeStamp, packetDataPtr); // In the case of a QTSS_WouldBlock error, the packetTransmitTime field of the // packet struct will be set to the time to wakeup, or -1 if not known. // If the time to wakeup is not given by the server, just give a fixed guess interval if ((*theFile)->fPacketStruct.suggestedWakeupTime == -1) inParams->outNextPacketTime = sFlowControlProbeInterval; else inParams->outNextPacketTime=(*theFile)->fPacketStruct.suggestedWakeupTime- inParams->inCurrentTime; } else { QTSS_SetValue(theStream, sRTPStreamLastSentPacketSeqNumAttrID, 0, &curSeqNum, sizeof(curSeqNum)); (*theFile)->fPacketStruct.packetData = NULL; } 三、QTSSPosixFileSysModule模块 支持QTSS_OpenFile_Role,实际上在该模块的Dispatch函数里同时支持QTSS_Advise_Role、 QTSS_ReadFile_Role、QTSS_CloseFile_Role、QTSS_RequestEventFile_Role的处理,这些Role是直接通过模块来调用的。 (1)、OpenFile // 创建OSFileSource对象,该类实现底层的文件操作。 theFileSource = NEW OSFileSource(inParams->inPath); 获取文件长度、修改时间等信息。 // Ad
/
本文档为【DSS源码分析汇总精华】,请使用软件OFFICE或WPS软件打开。作品中的文字与图均可以修改和编辑, 图片更改请在作品中右键图片并更换,文字修改请直接点击文字进行修改,也可以新增和删除文档中的内容。
[版权声明] 本站所有资料为用户分享产生,若发现您的权利被侵害,请联系客服邮件isharekefu@iask.cn,我们尽快处理。 本作品所展示的图片、画像、字体、音乐的版权可能需版权方额外授权,请谨慎使用。 网站提供的党政主题相关内容(国旗、国徽、党徽..)目的在于配合国家政策宣传,仅限个人学习分享使用,禁止用于任何广告和商用目的。

历史搜索

    清空历史搜索