本人在一家创业公司工作一年,这里对Easydarwin流媒体的使用有一定的心德,这里写出来跟大家学习,希望大家不要笑话我!!!
我在这个公司最后的工作中,有一个按需推流的需求,开始我对这方面不是很懂,到处问人,在Easydarwin团队的babosa指导,看了他们的解决方案http://blog.csdn.net/ss00_2012/article/details/51441753,奈何需求不一样,未能完全实现。因为我们只用了Easydarwin的一部分东西
之后我老大说可以在rtsp连接握手的时候能进行操作。所以我进行一系列的学习,最终找到了添加的位置。
Eesydarwin的流媒体服务器重 最重要的是
QTSSReflectorModule 这个模块,
触发,EasyDarwin为每一路推流转发维护一个ReflectorSession对象,该对象的fNumOutputs属性用来指示当前拉流客户端的数量。当客户端停止拉流时会调用QTSSReflectorModule::DestroySession()->QTSSReflectorModule::RemoveOutput()->ReflectorSession::RemoveOutput(),其中ReflectorSession::RemoveOutput();
但是我们观察DestroySession() 这个函数;在ReflectorSession 维护的对象中有一个
GetNumOutputs
()函数,得到客户端的连接数,这个连接数ReflectorSession对象自己会维护一个,所以但
GetNumOutputs
()<=1的时候我们就通知推流器停止推流
QTSS_Error DestroySession(QTSS_ClientSessionClosing_Params* inParams)
{
RTPSessionOutput** theOutput = NULL;
ReflectorOutput* outputPtr = NULL;
ReflectorSession* theSession = NULL;
OSMutexLocker locker (sSessionMap->GetMutex());
UInt32 theLen = sizeof(theSession);
QTSS_Error theErr = QTSS_GetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &theSession, &theLen);
//qtss_printf("QTSSReflectorModule.cpp:DestroySession sClientBroadcastSessionAttr=%"_U32BITARG_" theSession=%"_U32BITARG_" err=%"_S32BITARG_" \n",(UInt32)sClientBroadcastSessionAttr, (UInt32)theSession,theErr);
if (theSession != NULL) //
这里是推流器
{
ReflectorSession* deletedSession = NULL;
theErr = QTSS_SetValue(inParams->inClientSession, sClientBroadcastSessionAttr, 0, &deletedSession, sizeof(deletedSession));
SourceInfo* theSoureInfo = theSession->GetSourceInfo();
if (theSoureInfo == NULL)
return QTSS_NoErr;
UInt32 numStreams = theSession->GetNumStreams();
SourceInfo::StreamInfo* theStreamInfo = NULL;
for (UInt32 index = 0; index < numStreams; index++)
{ theStreamInfo = theSoureInfo->GetStreamInfo(index);
if (theStreamInfo != NULL)
theStreamInfo->fSetupToReceive = false;
}
Bool16 killClients = false; // the pref as the default
UInt32 theLen = sizeof(killClients);
(void) QTSS_GetValue(inParams->inClientSession, sKillClientsEnabledAttr, 0, &killClients, &theLen);
//qtss_printf("QTSSReflectorModule.cpp:DestroySession broadcaster theSession=%"_U32BITARG_"\n", (UInt32) theSession);
theSession->RemoveSessionFromOutput(inParams->inClientSession);
if(sHLSOutputEnabled)
theSession->StopHLSSession();
printf("---停止推流----\n");
RemoveOutput(NULL, theSession, killClients);
}
else//这里是客户端
{
theLen = 0;
theErr = QTSS_GetValuePtr(inParams->inClientSession, sOutputAttr, 0, (void**)&theOutput, &theLen);
if ((theErr != QTSS_NoErr) || (theLen != sizeof(RTPSessionOutput*)) || (theOutput == NULL) || (*theOutput == NULL))
return QTSS_RequestFailed;
theSession = (*theOutput)->GetReflectorSession();
int num= theSession->GetNumOutputs();
printf("theSession->GetNumOutputs()-----:%d\n",theSession->GetNumOutputs());
//判断引用数是否为0.
if(num<=1)
{
QTSServerInterface::GetServer()->CastToCenterServerPushStram(theSession->GetSessionName(),1);
// theSession->RemoveSessionFromOutput(theSession);
}
if (theOutput != NULL)
outputPtr = (ReflectorOutput*) *theOutput;
if (outputPtr != NULL)
{
RemoveOutput(outputPtr, theSession, false);
RTPSessionOutput* theOutput = NULL;
(void)QTSS_SetValue(inParams->inClientSession, sOutputAttr, 0, &theOutput, sizeof(theOutput));
}
}
return QTSS_NoErr;
}
当播放器播放一串rtsp地址就是向流媒体请求,rtsp 的握手就开始了
enum
{
qtssDescribeMethod = 0,
qtssSetupMethod = 1,
qtssTeardownMethod = 2,
qtssNumVIPMethods = 3,
qtssPlayMethod = 3,
qtssPauseMethod = 4,
qtssOptionsMethod = 5,
qtssAnnounceMethod = 6,
qtssGetParameterMethod = 7,
qtssSetParameterMethod = 8,
qtssRedirectMethod = 9,
qtssRecordMethod = 10,
qtssNumMethods = 11,
qtssIllegalMethod = 11
};
typedef UInt32 QTSS_RTSPMethod;
这是表示连接状态的枚举,而播放器发送给流媒体
流媒体进行恢复ProcessRTSPRequest
()->DoDescribe();
播放器发送给流媒体服务器的都是一个theFilepath
./movies/9800010.sdp 所以通过设备代码我们就能找到设备
”
所以我们就能在DoDescribe
();这个函数里发信息给推流器,让推流开启推流
QTSS_Error DoDescribe(QTSS_StandardRTSP_Params* inParams)
{
char *theFilepath = NULL;
ReflectorSession* theSession = DoSessionSetup(inParams, qtssRTSPReqFilePath, false, NULL, &theFilepath );
OSCharArrayDeleter tempFilePath(theFilepath);
//通知中服务器推流
QTSServerInterface::GetServer()->CastToCenterServerPushStram(theFilepath,0);
if (theSession == NULL)
return QTSS_RequestFailed;
....
return QTSS_NoErr;
}
由于我们没有使用Easydarwin的网络模块,是自己一个udp网络库 要在QTSServerInterface.h这里添加自己网络管理模块回调
//函数指针
typedef void(*OnCastCenterServerPushStream)(void* mediaMng, char* sdpName,int type);
class QTSServerInterface : public QTSSDictionary
{
public:
//Initialize must be called right off the bat to initialize dictionary resources
static void Initialize();
//
// CONSTRUCTOR / DESTRUCTOR
QTSServerInterface();
virtual ~QTSServerInterface() {}
//通知中心服务器进行推流操作
virtual void CastToCenterServerPushStram(char* steamName,int type){}
public:
//流媒体管理句柄
void* mediaManager;
//通知回调
OnCastCenterServerPushStream CallFunc;
};
而QTSServer这和模块继承了QTSServerInterface ,所以我们要把我们加的方法实现
class QTSServer : public QTSServerInterface
{
public:
QTSServer()
{
}
virtual ~QTSServer();
//
// Initialize
//
// This function starts the server. If it returns true, the server has
// started up sucessfully. If it returns false, a fatal error occurred
// while attempting to start the server.
//
// This function *must* be called before the server creates any threads,
// because one of its actions is to change the server to the right UID / GID.
// Threads will only inherit these if they are created afterwards.
Bool16 Initialize(XMLPrefsParser* inPrefsSource, PrefsSource* inMessagesSource,
UInt16 inPortOverride, Bool16 createListeners,const char*inAbsolutePath);
//
// InitModules
//
// Initialize *does not* do much of the module initialization tasks. This
// function may be called after the server has created threads, but the
// server must not be in a state where it can do real work. In other words,
// call this function right after calling Initialize.
void InitModules(QTSS_ServerState inEndState);
//通知中心服务进行推流关流操作
void CastToCenterServerPushStram(char* steamName,int type);
........................................................
};
//实现让中心服务器推流操作
void QTSServer::CastToCenterServerPushStram(char* steamName,int type){
//设置0为请求推流播放 1为请求推流播放停止
if(mediaManager!=0 && CallFunc!=0){
CallFunc(mediaManager,steamName,type);
}
}
上面的想通了,后面主要是这个数据的回调问题,我开始是自己写的一个数据传输的类,但是在这里面编译不过了,因为Easydarwin里面重构new,最后终于知道用回调。这个按需推流是很low,因为并不是我要播放的时候就马上推流,可以你第一次播放rtsp 才向流媒体申请,流媒体才发送信息给推流器让推流器推流。所以想过并不理想,但是功能是有的。
thanks Easydarwin,还有流媒体大神云上鱼的指点。
![]()
![]()