標籤:windows live555 h264即時資料流 rtsp 發送伺服器
文如其名,最近在做的項目要求利用RTSP協議轉寄處理完的H264視頻資料給上一層用戶端,環境是Windows的VS2013,於是就各種百度Google找代碼。結果在得到利用live555去做比較簡單的結論的同時也悲情地發現,網上別人貼出來的代碼基本都是Linux上面的。在修改了兩份來適用於Windows無效後,又一次陷入了百度Google的無盡搜尋中。Anyway,最後終於解決了,所以貼出代碼跟大家分享下,希望能給和我需求相似的童鞋一點啟發,也希望有高手指正其中的問題。
用live555進行RTSP的播放基本上是通過修改其給出來的播放本地檔案的DEMO來實現的。但由於其DEMO封裝的比較深,所以要直接修改他的fread處的代碼變成記憶體拷貝來實現即時傳輸會顯得比較彆扭。本文參考了網上的一些代碼,自訂了一個繼承自H264VideoFileServerMediaSubsession的類來來進行處理,同時定義了一個繼承自FramedSource的類來做記憶體的拷貝操作,該類亦是區別於讀本地檔案和即時資料流之緊要處。
一口氣雜七雜八說了好多,下面貼出代碼吧。如果覺得需要或者懶得自己搭建live555的環境亦可以在文中最後的連結中下載該工程(環境為VS2013),如果你的VS版本合適即可直接運行。
主檔案(程式入口)
#include "H264LiveVideoServerMediaSubssion.hh"#include "H264FramedLiveSource.hh"#include "liveMedia.hh"#include "BasicUsageEnvironment.hh"#define BUFSIZE 1024*200static void announceStream(RTSPServer* rtspServer, ServerMediaSession* sms,char const* streamName)//顯示RTSP串連資訊{char* url = rtspServer->rtspURL(sms);UsageEnvironment& env = rtspServer->envir();env <<streamName<< "\n";env << "Play this stream using the URL \"" << url << "\"\n";delete[] url;}int main(int argc, char** argv) {//設定環境UsageEnvironment* env;Boolean reuseFirstSource = False;//如果為“true”則其他接入的用戶端跟第一個用戶端看到一樣的視頻流,否則其他用戶端接入的時候將重新播放TaskScheduler* scheduler = BasicTaskScheduler::createNew();env = BasicUsageEnvironment::createNew(*scheduler);//建立RTSP伺服器UserAuthenticationDatabase* authDB = NULL;RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);if (rtspServer == NULL) {*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";exit(1);}char const* descriptionString= "Session streamed by \"testOnDemandRTSPServer\"";//類比即時資料流發送相關變數int datasize;//資料區長度unsigned char* databuf;//資料區指標databuf = (unsigned char*)malloc(1024*1024);bool dosent;//rtsp發送標誌位,為true則發送,否則退出//從檔案中拷貝1M資料到記憶體中作為即時網路傳輸記憶體類比,如果即時網路傳輸應該是雙線程結構,記得在這裡加上線程鎖//此外即時傳輸的資料拷貝應該是發生在H264FramedLiveSource檔案中,所以這裡只是自上往下的傳指標過去給它FILE *pf;fopen_s(&pf, "test.264", "rb");fread(databuf, 1, BUFSIZE, pf);datasize = BUFSIZE;dosent = true;fclose(pf);//上面的部分除了類比網路傳輸的部分外其他的基本跟live555提供的demo一樣,而下面則修改為網路傳輸的形式,為此重寫addSubsession的第一個參數相關檔案char const* streamName = "h264ESVideoTest";ServerMediaSession* sms = ServerMediaSession::createNew(*env, streamName, streamName,descriptionString);sms->addSubsession(H264LiveVideoServerMediaSubssion::createNew(*env, reuseFirstSource, &datasize, databuf,&dosent));//修改為自己實現的H264LiveVideoServerMediaSubssionrtspServer->addServerMediaSession(sms);announceStream(rtspServer, sms, streamName);//提示使用者輸入串連資訊env->taskScheduler().doEventLoop(); //迴圈等待串連free(databuf);//釋放掉記憶體return 0;}
自訂H264VideoFileServerMediaSubsession類
H264VideoFileServerMediaSubsession.hh
#ifndef _H264_LIVE_VIDEO_SERVER_MEDIA_SUBSESSION_HH#define _H264_LIVE_VIDEO_SERVER_MEDIA_SUBSESSION_HH#include "H264VideoFileServerMediaSubsession.hh"class H264LiveVideoServerMediaSubssion : public H264VideoFileServerMediaSubsession {public:static H264LiveVideoServerMediaSubssion* createNew(UsageEnvironment& env, Boolean reuseFirstSource, int *datasize, unsigned char* databuf, bool *dosent);protected: // we're a virtual base classH264LiveVideoServerMediaSubssion(UsageEnvironment& env, Boolean reuseFirstSource, int *datasize, unsigned char* databuf, bool *dosent);~H264LiveVideoServerMediaSubssion();protected: // redefined virtual functionsFramedSource* createNewStreamSource(unsigned clientSessionId,unsigned& estBitrate);public:char fFileName[100];int *Server_datasize;//資料區大小指標unsigned char* Server_databuf;//資料區指標bool *Server_dosent;//發送標示};#endif
H264VideoFileServerMediaSubsession.cpp
#include "H264LiveVideoServerMediaSubssion.hh"#include "H264FramedLiveSource.hh"#include "H264VideoStreamFramer.hh"H264LiveVideoServerMediaSubssion* H264LiveVideoServerMediaSubssion::createNew(UsageEnvironment& env, Boolean reuseFirstSource, int *datasize, unsigned char* databuf, bool *dosent){return new H264LiveVideoServerMediaSubssion(env, reuseFirstSource, datasize, databuf, dosent);}H264LiveVideoServerMediaSubssion::H264LiveVideoServerMediaSubssion(UsageEnvironment& env, Boolean reuseFirstSource, int *datasize, unsigned char* databuf, bool *dosent): H264VideoFileServerMediaSubsession(env, fFileName, reuseFirstSource)//H264VideoFileServerMediaSubsession不是我們需要修改的檔案, //但是我們又要用它來初始化我們的函數, //所以給個空數組進去即可{Server_datasize = datasize;//資料區大小指標Server_databuf = databuf;//資料區指標Server_dosent = dosent;//發送標示}H264LiveVideoServerMediaSubssion::~H264LiveVideoServerMediaSubssion(){}FramedSource* H264LiveVideoServerMediaSubssion::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate){/* Remain to do : assign estBitrate */estBitrate = 1000; // kbps, estimate//建立視頻源H264FramedLiveSource* liveSource = H264FramedLiveSource::createNew(envir(), Server_datasize, Server_databuf, Server_dosent);if (liveSource == NULL){return NULL;}// Create a framer for the Video Elementary Stream:return H264VideoStreamFramer::createNew(envir(), liveSource);}
自訂H264FramedLiveSource類
H264FramedLiveSource.hh
#ifndef _H264FRAMEDLIVESOURCE_HH#define _H264FRAMEDLIVESOURCE_HH#include <FramedSource.hh>class H264FramedLiveSource : public FramedSource{public:static H264FramedLiveSource* createNew(UsageEnvironment& env, int *datasize, unsigned char* databuf, bool *dosent, unsigned preferredFrameSize = 0, unsigned playTimePerFrame = 0);protected:H264FramedLiveSource(UsageEnvironment& env, int *datasize, unsigned char* databuf, bool *dosent, unsigned preferredFrameSize, unsigned playTimePerFrame);~H264FramedLiveSource();private:virtual void doGetNextFrame();int TransportData(unsigned char* to, unsigned maxSize);protected:int *Framed_datasize;//資料區大小指標unsigned char *Framed_databuf;//資料區指標bool *Framed_dosent;//發送標示int readbufsize;//記錄已讀取資料區大小int bufsizel;//記錄資料區大小};#endif
H264FramedLiveSource.cpp
#include "H264FramedLiveSource.hh"H264FramedLiveSource::H264FramedLiveSource(UsageEnvironment& env, int *datasize, unsigned char* databuf, bool *dosent, unsigned preferredFrameSize, unsigned playTimePerFrame): FramedSource(env){Framed_datasize = datasize;//資料區大小指標Framed_databuf = databuf;//資料區指標Framed_dosent = dosent;//發送標示}H264FramedLiveSource* H264FramedLiveSource::createNew(UsageEnvironment& env, int *datasize, unsigned char* databuf, bool *dosent, unsigned preferredFrameSize, unsigned playTimePerFrame){H264FramedLiveSource* newSource = new H264FramedLiveSource(env, datasize, databuf, dosent, preferredFrameSize, playTimePerFrame);return newSource;}H264FramedLiveSource::~H264FramedLiveSource(){}void H264FramedLiveSource::doGetNextFrame(){if (*Framed_dosent == true){*Framed_dosent = false;bufsizel = *Framed_datasize;readbufsize = 0;fFrameSize = fMaxSize;memcpy(fTo, Framed_databuf + readbufsize, fFrameSize);readbufsize += fFrameSize;}else{if (bufsizel - readbufsize>fMaxSize){fFrameSize = fMaxSize;memcpy(fTo, Framed_databuf + readbufsize, fFrameSize);readbufsize += fFrameSize;}else{memcpy(fTo, Framed_databuf + readbufsize, bufsizel - readbufsize);*Framed_dosent = true;}}nextTask() = envir().taskScheduler().scheduleDelayedTask(0,(TaskFunc*)FramedSource::afterGetting, this);//表示延遲0秒後再執行 afterGetting 函數return;}
工程:點擊開啟連結
Windows下利用live555實現H264即時資料流RTSP發送