编写Darwin的Module来支持新的流媒体文件格式

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。

编写模块就是根据要实现的功能,注册实现一系列的Role。为了编写一个支持特定封装格式的模块,如上代码所示,注册并实现了QTSS_Register_Role、QTSS_Initialize_Role、QTSS_RTSPPreProcessor_Role、QTSS_RTPSendPackets_Role、QTSS_ClientSessionClosing_Role等Role。接下来重点介绍QTSS_Register_Role、QTSS_RTSPPreProcessor_Role、QTSS_RTPSendPackets_Role。

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,不需要进行注册。

对应于QTSS_RTPSendPackets_Role的处理函数要实现的就是把流媒体数据按照实际播放的速度进行发送。这里的关键就是对发送速度的计算和把控。具体的实现是通过计算流媒体包的发送时间,得出如下数据结构中的具体数据,要发送的数据包、发送时间等。

typedef struct
{
       void*                                                     packetData;
       QTSS_TimeVal                         packetTransmitTime;
       QTSS_TimeVal                         suggestedWakeupTime;
} QTSS_PacketStruct;

模块循环调用QTSS_Write发送准备好的数据,当发送的数据由于其发送时间比当前时间的差值大于一定的值,则从循环中跳出,并从处理RTP Send Packets Role的函数中返回到服务器。服务器会根据当前受阻的数据包的发送时间再次调用函数发送数据。如下是实现QTSS_RTPSendPackets_Role的典型代码。

QTSS_Error SendPackets(QTSS_RTPSendPackets_Params* inParams)
{
    ...
    while (true) { // send packets loop
        if (fPacket.packetData == NULL) {
            theTransmitTime = GetNextPacket ();
            fPacket.packetTransmitTime = theTransmitTime;
            theReturn = QTSS_Write(theStream, &fPacket .....);
            if (theReturn == QTSS_WouldBlock) {
                return;
            else { //发送成功
                fPacket.packetData = NULL;
            }
        }
    }
}


参考资料

  1. QuickTime Streaming Server Modules Programming Guide。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值