1. 程式人生 > >用live555做本地視訊採集轉發,附原始碼

用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