用live555做本地視訊採集轉發,附原始碼
在分別做了基於live555與Darwin兩種開源伺服器的轉發伺服器後,不得不說Darwin確實在架構以及效能方面較live555略勝一籌,不過沒關係,以live555的更新速度,作者的負責,相信在客戶端開發以及ipC等方面會給大家帶來不少幫助,不羅嗦,今天要給大家帶來的是基於live555的本地視訊實時採集與轉發的介紹(有程式碼噢~).。
在對live555做二次開發時,最好的方式就是儘量多地去繼承自live555,而不要去改動開原始碼本身,尤其是一開始接觸live555,這樣在live555官方升級後,我們再對原生代碼進行升級時,就可以比較少地去考慮自己對live555的修改了,省時省力,效果還不錯。
不論是做遠端採集轉發還是本地採集轉發,我們首先要做的就是繼承live555中的OnDemandServerMediaSubsession類來實現自己需求的OnDemand類,按照類名Subsession,表示的只是一種型別媒體的會話,如果有多種型別媒體需要轉發(比如音訊、視訊),那麼就需要實現多種OnDemandServerMediaSubsession的繼承,來個性化對不同媒體的轉發,那麼今天我們只對H264視訊進行本地採集和轉發,我們實現的類命名為:H264LiveVideoServerMediaSubsession,主要重寫的方法有
private: // redefined virtual functions virtual FramedSource* createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate); virtual RTPSink* createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* inputSource); protected: virtual char const* sdpLines();
其中 CreateNewRTPSink類似於H264VideoFileServerMediaSubsession直接返回H264VideoRTPSink物件就行了
RTPSink* H264LiveVideoServerMediaSubsession::createNewRTPSink(Groupsock* rtpGroupsock, unsigned char rtpPayloadTypeIfDynamic, FramedSource* /*inputSource*/) { return H264VideoRTPSink::createNew(envir(), rtpGroupsock, 96, 0, "H264"); }
關鍵部分就是createNewStreamSource函式,建立自定義的Source來採集視訊,提供H264VideoRTPSink基類MultiFramedRTPSink通過packFrame()呼叫fSource->getNextFrame(...),一次獲取一個完整幀進行轉發。那麼我們先實現的就是這個Source:
同樣,我們實現的自定義source類繼承自H264VideoStreamFramer,重寫virtual void doGetNextFrame();方法實現本地採集資料的獲取:
void MyH264VideoStreamFramer::doGetNextFrame()
{
TNAL* pNal = NULL;//TNAL自定義的儲存單幀資料的結構體
unsigned char* pOrgImg;
//獲取NAL,如果m_pNalArray還有未取完的,先發送完,如果傳送完了,從pH264Enc中獲取最新資料幀,存入m_pNalArray連結串列
if((m_pNalArray != NULL) && (m_iCurNal < m_iCurNalNum))
{
pNal = &m_pNalArray[m_iCurNal];//m_pNalArray儲存TNAL的連結串列,儲存本地採集的資料鏈表
}
else
{
m_pH264Enc->CleanNAL(m_pNalArray, m_iCurNalNum);//清空m_pNalArray連結串列
m_iCurNal = 0;
pOrgImg = m_pCamera->QueryFrame();
gettimeofday(&fPresentationTime, NULL);//同一幀的NAL具有相同的時間戳
m_pH264Enc->Encode(pOrgImg, m_pNalArray, m_iCurNalNum);
pNal = &m_pNalArray[m_iCurNal];
}
m_iCurNal++;
unsigned char* realData = pNal->data;//轉發的資料指標
unsigned int realLen = pNal->size;//轉發的資料長度
if(realLen < fMaxSize)
{
memcpy(fTo, realData, realLen);//複製到fTo中,fTo為轉發的中轉地址
}
else
{
memcpy(fTo, realData, fMaxSize);
fNumTruncatedBytes = realLen - fMaxSize;
}
fDurationInMicroseconds = 40000;//控制播放速度
//gettimeofday(&fPresentationTime, NULL);
fFrameSize = realLen;
afterGetting(this); //通知RTPSink,資料獲取完成
}
那麼再回到H264LiveVideoServerMediaSubsession類中,CreateNewSource返回的為MyH264VideoStreamFramer物件
return MyH264VideoStreamFramer::createNew(envir(), NULL);
於是整個live555從source到sink的連線流程就通了,那麼為什麼要重寫sdpLines函式呢?這裡只是一種簡單形式的轉發實現,其sdp資訊並未真實構造,所以就寫成了固定的格式,大家也可以按照自己的方式去重寫
char const* H264LiveVideoServerMediaSubsession::sdpLines()
{
return fSDPLines =
"m=video 0 RTP/AVP 96\r\n"
"c=IN IP4 0.0.0.0\r\n"
"b=AS:96\r\n"
"a=rtpmap:96 H264/90000\r\n"
"a=fmtp:96 packetization-mode=1;profile-level-id=000000;sprop-parameter-sets=H264\r\n"
"a=control:track1\r\n";
}
至於本地Camera視訊的採集以及H264 Encode,因人而異,對不同的裝置也有不同的樣式,附上的程式碼中實現的是windows本地的camera YUV視訊採集以及264編碼,感謝分享!
------------------------------------------------------------
本文轉自www.easydarwin.org,更多開源流媒體解決方案,請關注我們的微信:EasyDarwin