Darwin是Apple公司的开源视频服务器,支持通过编写Module来进行扩展,从而支持新的文件格式。本文给出如何编写支持新的文件格式的Module。
Darwin的Module必须实现两个函数,其中一个在服务器加载Module的时候被调用,这个函数的名字定义为ModuleName_Main,其中ModuleName是模块的名字,模块的文件名也是这个名字。这个函数的定义通常是下面的样子:
QTSS_Error ModuleName_Main(void* inPrivateArgs)
{
return _stublibrary_main(inPrivateArgs, ModuleNameDispatch);
}
函数中标为红色的部分的ModuleNameDispatch是Module中必须定义的另一个函数的名字,这个函数通常会是下面的样子:
QTSS_Error ModuleNameDispatch (QTSS_Role inRole, QTSS_RoleParamPtr inParamBlock)
{
switch (inRole) {
case QTSS_Register_Role:
return Register (&inParamBlock->regParams);
case QTSS_Initialize_Role:
return Initialize (&inParamBlock->initParams);
case QTSS_RTSPPreProcessor_Role:
return ProcessRTSPRequest (&inParamBlock->rtspRequestParams);
case QTSS_RTPSendPackets_Role:
return SendPackets(&inParamBlock->rtpSendPacketsParams);
case QTSS_ClientSessionClosing_Role:
return DestroySession (&inParamBlock->clientSessionClosingParams);
}
return QTSS_NoErr;
}
在解释这段代码之前需要对几个概念进行解释,首先是Role。Darwin视频服务器把需要处理的任务进行分类,当需要处理某一类型的任务时,服务器就依次调用对应的Module,这些被调用的Module是注册了与处理该类任务对应的Role的Module。
Register Role是每个Module都必须实现的,且不需要注册,在这个Role中Module通过调用函数QTSS_AddRole注册所支持的其它Role。如下是典型的对应于Register Role的处理的实现,这里注册了三个Role,分别是QTSS_Initialize_Role、QTSS_RTSPPreProcessor_Role和QTSS_ClientSessionClosing_Role。
QTSS_Error Register (QTSS_Register_Params* inParams)
{
(void)QTSS_AddRole(QTSS_Initialize_Role);
(void)QTSS_AddRole(QTSS_RTSPPreProcessor_Role);
(void)QTSS_AddRole(QTSS_ClientSessionClosing_Role);
}
QTSS_RTSPPreProcessor_Role这个Role对应RTSP协议处理,可以参见《Darwin中RTSP协议的实现》了解Darwin视频服务器对于RTSP协议处理的细节,如下是一个典型的对应于QTSS_RTSPPreProcessor_Role的处理的实现。
QTSS_Error ProcessRTSPRequest(QTSS_StandardRTSP_Params* inParamBlock)
{
QTSS_RTSPMethod* theMethod = NULL;
UInt32 theMethodLen = 0;
if ((QTSS_GetValuePtr(inParamBlock->inRTSPRequest,
qtssRTSPReqMethod,
0,
(void**)&theMethod,
&theMethodLen) != QTSS_NoErr) ||
(theMethodLen != sizeof(QTSS_RTSPMethod))) {
return QTSS_RequestFailed;
}
QTSS_Error err = QTSS_NoErr;
switch (*theMethod) {
case qtssDescribeMethod:
err = DoDescribe(inParamBlock);
break;
case qtssSetupMethod:
err = DoSetup(inParamBlock);
break;
case qtssPlayMethod:
err = DoPlay(inParamBlock);
break;
case qtssTeardownMethod:
(void)QTSS_Teardown(inParamBlock->inClientSession);
(void)QTSS_SendStandardRTSPResponse(inParamBlock->inRTSPRequest, inParamBlock->inClientSession, 0);
break;
case qtssPauseMethod:
err = DoPause (inParamBlock);
break;
case qtssGetParameterMethod:
err = DoGetParameterMethod (inParamBlock);
break;
default:
break;
}
if (err != QTSS_NoErr)
(void)QTSS_Teardown(inParamBlock->inClientSession);
return QTSS_NoErr;
}
理解这段代码需要理解Darwin中Object的概念,可以参照《Dictionary数据类型在Darwin视频服务器中的使用》,或者Darwin的文档《QuickTime Streaming Server Modules Programming Guide》。
每个RTSPRequest有一个名字为qtssRequestMethod的属性,其值与RTSP协议的请求方法对应,入Describe,Setup等。在与QTSS_RTSPPreProcessor_Role对应的处理中,对Describe等方法分别调用对应的函数进行处理。
注册了RTSP Prerequest Role或者RTSP Request Role的模块必须实RTP Send Packets Role,模块通过该Role向发出请求的客户端发送流媒体数据。当需要向请求客户端发送流媒体数据的时候,注册了RTSP Prerequest Role或者RTSP Request Role的模块调用QTSS_Play,然后服务器就会通过RTP Send Packets Role调用该模块。注册了RTSP Prerequest Role或者RTSP Request Role的模块默认具有RTP Send Packets Role,不需要进行注册。
对应于RTP Send Packets Role的处理函数要实现的就是把流媒体数据按照实际播放的速度进行发送。这里的关键就是对发送的速度的计算和把控。具体的实现是通过计算流媒体包的发送时间,得出如下数据结构中的具体数据,要发送的数据包、发送时间等。
typedef struct
{
void* packetData;
QTSS_TimeVal packetTransmitTime;
QTSS_TimeVal suggestedWakeupTime;
} QTSS_PacketStruct;
模块循环调用QTSS_Write发送准备好的数据,当发送的数据由于其发送时间比当前时间的差值大于一定的值,则从循环中跳出,并从处理RTP Send Packets Role的函数中返回到服务器。服务器会根据当前受阻的数据包的发送时间再次调用函数发送数据。
参考资料
- QuickTime Streaming Server Modules Programming Guide。
转至于http://blog.sina.com.cn/s/blog_6a4c492f0100pffv.html
http://blog.sina.com.cn/dqzhangp