对live555封装的比较好的一个类,网上找到的,觉得不错,给大家共享,也不记得从哪里下载的了,版权归原作者!
这个类的主要特点是可以创建多个客户端,连接多路码流;我们可以参考他的设计,在sink中将码流回调出来,在外部解码;
文件如下:
#pragma once #include "BasicUsageEnvironment\BasicUsageEnvironment.hh" #include "groupsock\GroupsockHelper.hh" #include "liveMedia\liveMedia.hh" #include <iostream> #include <list> #include <map> struct YHRTSPClient { TaskScheduler* pTaskScheduler; UsageEnvironment* pEnv; RTSPClient* pClient; MediaSession *pMediaSession; MediaSubsessionIterator *iter; Boolean bMadeProgress; unsigned fileSinkBufferSize; unsigned socketInputBufferSize; Boolean bStreamUsingTCP; Authenticator* pAuthenticator; char m_cEventLoop; YHRTSPClient() { pClient = NULL; pMediaSession = NULL; iter = NULL; bMadeProgress = False; fileSinkBufferSize = 100000; socketInputBufferSize = 524288; bStreamUsingTCP = False; pAuthenticator = NULL; m_cEventLoop = 0; } YHRTSPClient(RTSPClient *client) { pClient = client; pMediaSession = NULL; iter = NULL; bMadeProgress = False; fileSinkBufferSize = 100000; socketInputBufferSize = 524288; bStreamUsingTCP = False; pAuthenticator = NULL; m_cEventLoop = 0; } }; class CYHMediaClient { public: static CYHMediaClient* GetInstance(); CYHMediaClient(void); ~CYHMediaClient(void); public: BOOL CreateRTPClient(LONG lID, const char *chServerURL); //BOOL StartStreaming(char *chWatchVariable = NULL); void StopStreaming(int nClientID); void GetSDPDescription(RTSPClient* pRTSPClient, RTSPClient::responseHandler* afterFunc); void SetupStreams(RTSPClient* pRTSPClient); void SetupSubsession(RTSPClient* pRTSPClient,MediaSubsession* subsession, Boolean streamUsingTCP, RTSPClient::responseHandler* afterFunc); void StartPlayingSession(RTSPClient* pRTSPClient,MediaSession* session, double start, double end, float scale, RTSPClient::responseHandler* afterFunc); void TearDownSession(RTSPClient* pRTSPClient,MediaSession* session, RTSPClient::responseHandler* afterFunc); void SetFileSinkAndSocket(YHRTSPClient *pYHClient,unsigned fileSinkBufferSize,unsigned socketInputBufferSize); void SetStreamUsingTCP(YHRTSPClient* pYHClient, Boolean bStreamUsingTCP){pYHClient->bStreamUsingTCP = bStreamUsingTCP;}; UsageEnvironment *GetEnvironment(RTSPClient* pRTSPClient) { UsageEnvironment *pEnv = NULL; if (NULL == pRTSPClient) { return pEnv; } YHRTSPClient *pYHClient = GetYHRTSPClient(pRTSPClient); if (NULL != pYHClient) { pEnv = pYHClient->pEnv; } return pEnv; }; private: void GetOptions(RTSPClient* pRTSPClient, RTSPClient::responseHandler* afterFunc); void Shutdown(RTSPClient *pClient = NULL); YHRTSPClient* GetYHRTSPClient(RTSPClient *pClient); void EraseYHRTSPClient(RTSPClient *pClient); LONG GetRTSPClientID(RTSPClient* pClient); void CloseMediaSinks(RTSPClient *pClient); static void ContinueAfterOptions(RTSPClient* pClient, int resultCode, char* resultString); static void ContinueAfterDescribe(RTSPClient* pClient, int resultCode, char* resultString); static void ContinueAfterSetup(RTSPClient* pClient, int resultCode, char* resultString); static void ContinueAfterPlay(RTSPClient* pClient, int resultCode, char* resultString); static void SubsessionAfterPlaying(void* clientData); static void SubsessionByeHandler(void* clientData); static void ContinueAfterTearDown(RTSPClient* pClient, int resultCode, char* resultString) ; public: std::list<YHRTSPClient*> m_listRTSPClient; std::map<int, RTSPClient*> m_mapClientID; private: static CYHMediaClient* pYHMediaClient; //TaskScheduler* pTaskScheduler; //UsageEnvironment* pEnv; }; 源文件如下:
<pre class="cpp" name="code">//#include "stdafx.h" #include "YHMediaClient.h" #include "WinCriticalSection.h" #include "MetMediaSink.h" //#include "YHWMPlayDemoDlg.h" char const* clientProtocolName = "RTSP"; CYHMediaClient* CYHMediaClient::pYHMediaClient = NULL; WinCriticalSection g_cs; CYHMediaClient* CYHMediaClient::GetInstance() { Mutex mutex(g_cs); if (CYHMediaClient::pYHMediaClient == NULL) { pYHMediaClient = new CYHMediaClient; } return pYHMediaClient; } CYHMediaClient::CYHMediaClient(void) { } CYHMediaClient::~CYHMediaClient(void) { m_listRTSPClient.empty(); } BOOL CYHMediaClient::CreateRTPClient(LONG lID, const char *chServerURL) { g_cs.Enter(); TaskScheduler *pTaskScheduler = BasicTaskScheduler::createNew(); UsageEnvironment *pEnv = BasicUsageEnvironment::createNew(*pTaskScheduler); RTSPClient* pRTSPClient = (RTSPClient*)RTSPClient::createNew(*pEnv, chServerURL, 0, NULL, 0); if (NULL == pRTSPClient) { *pEnv << "Failed to create" << clientProtocolName << " client: " << pEnv->getResultMsg() << "\n"; return FALSE; } m_mapClientID.insert(std::make_pair(lID, pRTSPClient)); YHRTSPClient *stucClient = new YHRTSPClient(pRTSPClient); stucClient->pTaskScheduler = pTaskScheduler; stucClient->pEnv = pEnv; CYHMediaClient::GetInstance()->m_listRTSPClient.push_back(stucClient); GetOptions(pRTSPClient, ContinueAfterOptions); g_cs.Leave(); stucClient->pEnv->taskScheduler().doEventLoop(&stucClient->m_cEventLoop); if (pTaskScheduler) { delete pTaskScheduler; pTaskScheduler = NULL; } if(stucClient) { delete stucClient; stucClient = NULL; } return TRUE; } void CYHMediaClient::StopStreaming(int nClientID) { Mutex mutex(g_cs); std::map<int, RTSPClient*>::iterator iter = m_mapClientID.find(nClientID); if (iter != m_mapClientID.end()) { RTSPClient* pRTSPClient = iter->second; Shutdown(pRTSPClient); EraseYHRTSPClient(pRTSPClient); m_mapClientID.erase(iter); } } void CYHMediaClient::Shutdown(RTSPClient *pClient) { YHRTSPClient* pYHRTSPClient = GetYHRTSPClient(pClient); if (pYHRTSPClient != NULL) { if (pYHRTSPClient->pMediaSession != NULL) { TearDownSession(pClient,pYHRTSPClient->pMediaSession, ContinueAfterTearDown); } else { ContinueAfterTearDown(NULL, 0, NULL); } CYHMediaClient::GetInstance()->CloseMediaSinks(pClient); if (pYHRTSPClient->pMediaSession) { Medium::close(pYHRTSPClient->pMediaSession); pYHRTSPClient->pMediaSession = NULL; } // Finally, shut down our client: if (pClient) { Medium::close(pClient); pClient = NULL; } pYHRTSPClient->m_cEventLoop = 1; } } void CYHMediaClient::GetOptions(RTSPClient* pRTSPClient, RTSPClient::responseHandler* afterFunc) { Mutex mutex(g_cs); if (pRTSPClient) { YHRTSPClient *pYHClient = CYHMediaClient::GetInstance()->GetYHRTSPClient(pRTSPClient); pRTSPClient->sendOptionsCommand(afterFunc, pYHClient->pAuthenticator); } } void CYHMediaClient::GetSDPDescription(RTSPClient* pRTSPClient,RTSPClient::responseHandler* afterFunc) { Mutex mutex(g_cs); if (pRTSPClient) { YHRTSPClient *pYHClient = CYHMediaClient::GetInstance()->GetYHRTSPClient(pRTSPClient); pRTSPClient->sendDescribeCommand(afterFunc, pYHClient->pAuthenticator); } } void CYHMediaClient::SetupSubsession(RTSPClient* pRTSPClient,MediaSubsession* subsession, Boolean streamUsingTCP, RTSPClient::responseHandler* afterFunc) { Mutex mutex(g_cs); Boolean forceMulticastOnUnspecified = False; if (pRTSPClient) { YHRTSPClient *pYHClient = CYHMediaClient::GetInstance()->GetYHRTSPClient(pRTSPClient); pRTSPClient->sendSetupCommand(*subsession, afterFunc, False, streamUsingTCP, forceMulticastOnUnspecified, pYHClient->pAuthenticator); } } void CYHMediaClient::StartPlayingSession(RTSPClient* pRTSPClient,MediaSession* session, double start, double end, float scale, RTSPClient::responseHandler* afterFunc) { //Mutex mutex(g_cs); if (pRTSPClient) { YHRTSPClient *pYHClient = CYHMediaClient::GetInstance()->GetYHRTSPClient(pRTSPClient); pRTSPClient->sendPlayCommand(*session, afterFunc, start, end, scale, pYHClient->pAuthenticator); } } void CYHMediaClient::TearDownSession(RTSPClient* pRTSPClient,MediaSession* session, RTSPClient::responseHandler* afterFunc) { Mutex mutex(g_cs); if (pRTSPClient) { YHRTSPClient *pYHClient = CYHMediaClient::GetInstance()->GetYHRTSPClient(pRTSPClient); pRTSPClient->sendTeardownCommand(*session, afterFunc, pYHClient->pAuthenticator); } } void CYHMediaClient::ContinueAfterOptions(RTSPClient* pClient, int resultCode, char* resultString) { Mutex mutex(g_cs); if(CYHMediaClient::GetInstance() != NULL) { if (resultCode != 0) { *(CYHMediaClient::GetInstance()->GetEnvironment(pClient)) << clientProtocolName << " \"OPTIONS\" request failed: " << resultString << "\n"; return; } else { *(CYHMediaClient::GetInstance()->GetEnvironment(pClient)) << clientProtocolName << " \"OPTIONS\" request returned: " << resultString << "\n"; } delete[] resultString; CYHMediaClient::GetInstance()->GetSDPDescription(pClient, ContinueAfterDescribe); } return; } void CYHMediaClient::ContinueAfterDescribe(RTSPClient* pClient, int resultCode, char* resultString) { Mutex mutex(g_cs); if (CYHMediaClient::GetInstance() != NULL) { if (resultCode != 0) { CYHMediaClient::GetInstance()->Shutdown(); return; } char* sdpDescription = resultString; // Create a media session object from this SDP description: MediaSession *pMediaSession = MediaSession::createNew(*(CYHMediaClient::GetInstance()->GetEnvironment(pClient)), sdpDescription); delete[] sdpDescription; if (pMediaSession == NULL) { *(CYHMediaClient::GetInstance()->GetEnvironment(pClient)) << "Failed to create a MediaSession object from the SDP description: " << CYHMediaClient::GetInstance()->GetEnvironment(pClient)->getResultMsg() << "\n"; CYHMediaClient::GetInstance()->Shutdown(pClient); return; } else if (!pMediaSession->hasSubsessions()) { *(CYHMediaClient::GetInstance()->GetEnvironment(pClient)) << "This session has no media subsessions (i.e., \"m=\" lines)\n"; CYHMediaClient::GetInstance()->Shutdown(pClient); return; } // Then, setup the "RTPSource"s for the session: MediaSubsessionIterator iter(*pMediaSession); MediaSubsession *subsession; YHRTSPClient *pYHClient = CYHMediaClient::GetInstance()->GetYHRTSPClient(pClient); pYHClient->bMadeProgress = False; pYHClient->pMediaSession = pMediaSession; while ((subsession = iter.next()) != NULL) { if (!subsession->initiate()) { *(CYHMediaClient::GetInstance()->GetEnvironment(pClient)) << "Unable to create receiver for \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession: " << CYHMediaClient::GetInstance()->GetEnvironment(pClient)->getResultMsg() << "\n"; } else { *(CYHMediaClient::GetInstance()->GetEnvironment(pClient)) << "Created receiver for \"" << subsession->mediumName() << "/" << subsession->codecName() << "\" subsession (client ports " << subsession->clientPortNum() << "-" << subsession->clientPortNum()+1 << ")\n"; pYHClient->bMadeProgress = True; if (subsession->rtpSource() != NULL) { // Because we're saving the incoming data, rather than playing // it in real time, allow an especially large time threshold // (1 second) for reordering misordered incoming packets: unsigned const thresh = 500000; // 0.5 second subsession->rtpSource()->setPacketReorderingThresholdTime(thresh); // Set the RTP source's OS socket buffer size as appropriate - either if we were explicitly asked (using -B), // or if the desired FileSink buffer size happens to be larger than the current OS socket buffer size. // (The latter case is a heuristic, on the assumption that if the user asked for a large FileSink buffer size, // then the input data rate may be large enough to justify increasing the OS socket buffer size also.) int socketNum = subsession->rtpSource()->RTPgs()->socketNum(); unsigned curBufferSize = getReceiveBufferSize(*CYHMediaClient::GetInstance()->GetEnvironment(pClient), socketNum); if (pYHClient->socketInputBufferSize > 0 || pYHClient->fileSinkBufferSize > curBufferSize) { unsigned newBufferSize = pYHClient->socketInputBufferSize > 0 ? pYHClient->socketInputBufferSize : pYHClient->fileSinkBufferSize; newBufferSize = setReceiveBufferTo(*CYHMediaClient::GetInstance()->GetEnvironment(pClient), socketNum, newBufferSize); } } } } if (!pYHClient->bMadeProgress) { CYHMediaClient::GetInstance()->Shutdown(pClient); return; } // Perform additional 'setup' on each subsession, before playing them: CYHMediaClient::GetInstance()->SetupStreams(pClient); } } void CYHMediaClient::SetupStreams(RTSPClient* pRTSPClient) { Mutex mutex(g_cs); YHRTSPClient *struClient = GetYHRTSPClient(pRTSPClient); if (struClient->iter == NULL) struClient->iter = new MediaSubsessionIterator(*(struClient->pMediaSession)); MediaSubsession *subsession = NULL; while ((subsession = struClient->iter->next()) != NULL) { // We have another subsession left to set up: if (subsession->clientPortNum() == 0) continue; // port # was not set if (pRTSPClient != NULL) { SetupSubsession(pRTSPClient, subsession, /*struClient->bStreamUsingTCP*/True, ContinueAfterSetup); } return; } // We're done setting up subsessions. if (!struClient->bMadeProgress) { Shutdown(pRTSPClient); return; } // Create and start "FileSink"s for each subsession: struClient->bMadeProgress = False; MediaSubsessionIterator iter(*(struClient->pMediaSession)); while ((subsession = iter.next()) != NULL) { if (subsession->readSource() == NULL) continue; // was not initiated // Create an output file for each desired stream: CMetMediaSink* pMediaSink; unsigned int requestedBufferSize = 524288; pMediaSink = CMetMediaSink::createNew(*struClient->pEnv, struClient->socketInputBufferSize); pMediaSink->SetMediaSession(struClient->pMediaSession); pMediaSink->SetPlayHandle(GetRTSPClientID(pRTSPClient)); subsession->sink = pMediaSink; if (subsession->sink == NULL) { *struClient->pEnv << "Failed to create MediaSink for \"" << "MediaClient"<< "\": " << struClient->pEnv->getResultMsg() << "\n"; } else { subsession->sink->startPlaying(*(subsession->readSource()),SubsessionAfterPlaying,subsession); // Also set a handler to be called if a RTCP "BYE" arrives // for this subsession: if (subsession->rtcpInstance() != NULL) { subsession->rtcpInstance()->setByeHandler(SubsessionByeHandler, subsession); } struClient->bMadeProgress = True; } } if (!struClient->bMadeProgress) { Shutdown(pRTSPClient); return; } double duration = 0; YHRTSPClient* pYHRTSPClient = GetYHRTSPClient(pRTSPClient); duration = pYHRTSPClient->pMediaSession->playEndTime(); if (duration < 0) duration = 0.0; double dwEnd = duration; StartPlayingSession(pRTSPClient,pYHRTSPClient->pMediaSession, 0, dwEnd, 1.0, ContinueAfterPlay); } void CYHMediaClient::ContinueAfterSetup(RTSPClient* pClient, int resultCode, char* resultString) { Mutex mutex(g_cs); if (pClient == NULL) { return; } if (CYHMediaClient::GetInstance() != NULL) { if (resultCode == 0) { YHRTSPClient *pYHClient = CYHMediaClient::GetInstance()->GetYHRTSPClient(pClient); pYHClient->bMadeProgress = True; } // Set up the next subsession, if any: CYHMediaClient::GetInstance()->SetupStreams(pClient); } } void CYHMediaClient::ContinueAfterPlay(RTSPClient* pClient, int resultCode, char* resultString) { Mutex mutex(g_cs); if (CYHMediaClient::GetInstance() != NULL) { if (resultCode != 0) { *(CYHMediaClient::GetInstance()->GetEnvironment(pClient)) << "Failed to start playing session: " << resultString << "\n"; CYHMediaClient::GetInstance()->Shutdown(pClient); return; } else { *(CYHMediaClient::GetInstance()->GetEnvironment(pClient)) << "Started playing session\n"; } char const* actionString ="Receiving streamed data..."; *(CYHMediaClient::GetInstance()->GetEnvironment(pClient)) << actionString << "...\n"; } } void CYHMediaClient::SubsessionAfterPlaying(void* clientData) { Mutex mutex(g_cs); MediaSubsession* subsession = (MediaSubsession*)clientData; Medium::close(subsession->sink); subsession->sink = NULL; } void CYHMediaClient::SubsessionByeHandler(void* clientData) { Mutex mutex(g_cs); MediaSubsession* subsession = (MediaSubsession*)clientData; if (subsession != NULL) { // Act now as if the subsession had closed: SubsessionAfterPlaying(subsession); } } void CYHMediaClient::ContinueAfterTearDown(RTSPClient* pClient, int resultCode, char* resultString) { /* Mutex mutex(g_cs); // Now that we've stopped any more incoming data from arriving, close our output files: if (CYHMediaClient::GetInstance()!= NULL) { CYHMediaClient::GetInstance()->CloseMediaSinks(pClient); YHRTSPClient *struClient = CYHMediaClient::GetInstance()->GetYHRTSPClient(pClient); if (struClient->pMediaSession) { Medium::close(struClient->pMediaSession); } // Finally, shut down our client: if (struClient->pAuthenticator) { delete struClient->pAuthenticator; } Medium::close(pClient); struClient->m_cEventLoop = 1; }*/ } void CYHMediaClient::CloseMediaSinks(RTSPClient *pClient) { YHRTSPClient* pYHRTSPClient = GetYHRTSPClient(pClient); if (pYHRTSPClient->pMediaSession == NULL) return; MediaSubsessionIterator iter(*pYHRTSPClient->pMediaSession); MediaSubsession* subsession; while ((subsession = iter.next()) != NULL) { Medium::close(subsession->sink); subsession->sink = NULL; } } YHRTSPClient* CYHMediaClient::GetYHRTSPClient(RTSPClient *pClient) { if (m_listRTSPClient.size() == 0) { return NULL; } std::list<YHRTSPClient*>::iterator iter; for (iter = m_listRTSPClient.begin(); iter != m_listRTSPClient.end(); ++iter) { if (pClient == (*iter)->pClient) { return *iter; } else continue; } return NULL; } void CYHMediaClient::EraseYHRTSPClient(RTSPClient *pClient) { if (m_listRTSPClient.size() == 0) { return ; } std::list<YHRTSPClient*>::iterator iter; for (iter = m_listRTSPClient.begin(); iter != m_listRTSPClient.end(); ++iter) { if (pClient == (*iter)->pClient) { break; } else continue; } m_listRTSPClient.erase(iter); } LONG CYHMediaClient::GetRTSPClientID(RTSPClient* pClient) { std::map<int, RTSPClient*>::iterator iter = m_mapClientID.begin(); int nClientID = -1; for (; iter != m_mapClientID.end(); ++iter) { if (pClient == iter->second) { nClientID = iter->first; } } return nClientID; } void CYHMediaClient::SetFileSinkAndSocket(YHRTSPClient *pYHClient,unsigned fileSinkBufferSize,unsigned socketInputBufferSize) { if (NULL == pYHClient) { return; } pYHClient->fileSinkBufferSize = fileSinkBufferSize; pYHClient->socketInputBufferSize = socketInputBufferSize; return; }