生活随笔
收集整理的這篇文章主要介紹了
通过live555实现H264 RTSP直播(Windows版)
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
為何標明“Windows版”,因為firehood大神已經實現了linux版:通過live555實現H264 RTSP直播
相關文章:
【1】Win7(Windows 7)下用VS2013(Visual Studio 2013)編譯live555
【2】RTSP協議分析
【3】windows命名管道
一.基礎
live555的學習基本上都是從E:\live555\testProgs中的testOnDemandRTSPServer.cpp示例開始的,這個例子實現了一個最簡單的RTSP服務器。文件名中的“OnDemand”意思是:依指令行事,也就是說只有當客戶端通過URL主動訪問并發送相關指令時,該RTSP服務器才會將文件流化并推送到客戶端。這個例子是基于RTP單播的,關于單播可以參考:Qt調用jrtplib實現單播、多播和廣播
通過testOnDemandRTSPServer.cpp可以學習一個RTSP服務器的搭建步驟。這里新建一個名為h264LiveMediaServer的Win32控制臺工程,新建并添加h264LiveMediaServer.cpp,然后將testOnDemandRTSPServer.cpp拷貝到h264LiveMediaServer.cpp,接著做少量修改,只保留與H.264會話相關的部分,如下所示:
[cpp] view plain
copy #include?"liveMedia.hh"??#include?"BasicUsageEnvironment.hh"????UsageEnvironment*?env;????????Boolean?reuseFirstSource?=?False;??????static?void?announceStream(RTSPServer*?rtspServer,?ServerMediaSession*?sms,??????char?const*?streamName,?char?const*?inputFileName);?????int?main(int?argc,?char**?argv)???{????????????TaskScheduler*?scheduler?=?BasicTaskScheduler::createNew();??????env?=?BasicUsageEnvironment::createNew(*scheduler);????????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?\"h264LiveMediaServer\"";??????????????char?const*?streamName?=?"h264ESVideoTest";????????????????????????char?const*?inputFileName?=?"480320.264";?????????????????????????ServerMediaSession*?sms=?ServerMediaSession::createNew(*env,?streamName,?streamName,descriptionString);??????????????????????????????sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(*env,?inputFileName,?reuseFirstSource));????????????rtspServer->addServerMediaSession(sms);????????????announceStream(rtspServer,?sms,?streamName,?inputFileName);??????????????????if?(rtspServer->setUpTunnelingOverHTTP(80)?||?rtspServer->setUpTunnelingOverHTTP(8000)?||?rtspServer->setUpTunnelingOverHTTP(8080))???????{??????????*env?<<?"\n(We?use?port?"?<<?rtspServer->httpServerPortNum()?<<?"?for?optional?RTSP-over-HTTP?tunneling.)\n";??????}??????else???????{??????????*env?<<?"\n(RTSP-over-HTTP?tunneling?is?not?available.)\n";??????}????????????env->taskScheduler().doEventLoop();????????return?0;???}????static?void?announceStream(RTSPServer*?rtspServer,?ServerMediaSession*?sms,??????char?const*?streamName,?char?const*?inputFileName)?{??????char*?url?=?rtspServer->rtspURL(sms);??????UsageEnvironment&?env?=?rtspServer->envir();??????env?<<?"\n\""?<<?streamName?<<?"\"?stream,?from?the?file?\""??????????<<?inputFileName?<<?"\"\n";??????env?<<?"Play?this?stream?using?the?URL?\""?<<?url?<<?"\"\n";??????delete[]?url;??}??
如何測試可參考【1】,測試結果如下所示:
二.實現
在通過live555實現H264 RTSP直播中,博主是通過FIFO隊列實現的,FIFO隊列實際上是Linux下的命名管道,而Windows下也有命名管道,因此在Windows中的流程圖如下所示:
關于Windows命名管道詳見【3】。
這里不使用命名管道來實現,而是直接讀取本地H264文件,分解成StartCode+NALU內存塊,然后拷貝到Live555 Server。這樣一來,就很容易改成命名管道的形式,命名管道的客戶端只需讀取本地H264文件,分解成StartCode(0x000001或0x00000001)+NALU內存塊,并寫入管道,命名管道服務器端(在Live555 Server中)讀取管道數據,并拷貝到Live555 Server。
通過“基礎”中的分析可以得出,想實現自定義服務器,需要將sms->addSubsession(H264VideoFileServerMediaSubsession::createNew(*env, inputFileName,reuseFirstSource)),中的H264VideoFileServerMediaSubsession替換成自己的子會話。H264VideoFileServerMediaSubsession類在其createNewStreamSource(unsigned /*clientSessionId*/, unsigned& estBitrate)函數中調用了ByteStreamFileSource::createNew(envir(), fFileName),而frame的獲取正是在ByteStreamFileSource類中的doGetNextFrame()函數中實現的。因此,這里需要繼承H264VideoFileServerMediaSubsession和ByteStreamFileSource類,并重寫其中的createNewStreamSource和doGetNextFrame函數。
代碼如下所示:
h264LiveFramedSource.hh
[cpp] view plain
copy #ifndef?_H264LIVEFRAMEDSOURCE_HH??#define?_H264LIVEFRAMEDSOURCE_HH??????#include?<ByteStreamFileSource.hh>??????class?H264LiveFramedSource?:?public?ByteStreamFileSource??{??public:??????static?H264LiveFramedSource*?createNew(UsageEnvironment&?env,?unsigned?preferredFrameSize?=?0,?unsigned?playTimePerFrame?=?0);??????protected:??????H264LiveFramedSource(UsageEnvironment&?env,?unsigned?preferredFrameSize,?unsigned?playTimePerFrame);??????~H264LiveFramedSource();??????private:????????????virtual?void?doGetNextFrame();??};????#endif??
h264LiveFramedSource.cpp
[cpp] view plain
copy #include?"h264LiveFramedSource.hh"??#include?"GroupsockHelper.hh"??#include?"spsdecode.h"????int?findStartCode(unsigned?char?*buf,?int?zeros_in_startcode)??{??????int?info;??????int?i;????????info?=?1;??????for?(i?=?0;?i?<?zeros_in_startcode;?i++)??????if?(buf[i]?!=?0)??????????info?=?0;????????if?(buf[i]?!=?1)??????????info?=?0;??????return?info;??}????int?getNextNalu(FILE*?inpf,?unsigned?char*?buf)??{??????int?pos?=?0;??????int?startCodeFound?=?0;??????int?info2?=?0;??????int?info3?=?0;????????while?(!feof(inpf)?&&?(buf[pos++]?=?fgetc(inpf))?==?0);????????while?(!startCodeFound)??????{??????????if?(feof(inpf))??????????{??????????????return?pos?-?1;??????????}??????????buf[pos++]?=?fgetc(inpf);??????????info3?=?findStartCode(&buf[pos?-?4],?3);??????????startCodeFound=(info3?==?1);??????????if?(info3?!=?1)??????????????info2?=?findStartCode(&buf[pos?-?3],?2);???????????startCodeFound?=?(info2?==?1?||?info3?==?1);??????}??????if?(info2)??????{??????????fseek(inpf,?-3,?SEEK_CUR);??????????return?pos?-?3;??????}??????if?(info3)??????{??????????fseek(inpf,?-4,?SEEK_CUR);??????????return?pos?-?4;??????}??}????FILE?*?inpf;??unsigned?char*?inBuf;??int?inLen;??int?nFrameRate;??H264LiveFramedSource::H264LiveFramedSource(UsageEnvironment&?env,?unsigned?preferredFrameSize,?unsigned?playTimePerFrame)??:?ByteStreamFileSource(env,?0,?preferredFrameSize,?playTimePerFrame)??{??????char?*fname?=?"480320.264";??????inpf?=?NULL;??????inpf?=?fopen(fname,?"rb");??????inBuf?=?(unsigned?char*)calloc(1024?*?100,?sizeof(char));??????inLen?=?0;??????inLen?=?getNextNalu(inpf,?inBuf);????????????unsigned?int?nSpsLen?=?inLen?-?4;??????unsigned?char?*pSps?=?(unsigned?char*)malloc(nSpsLen);??????memcpy(pSps,?inBuf?+?4,?nSpsLen);??????????????int?width?=?0,?height?=?0,?fps?=?0;????????h264_decode_sps(pSps,?nSpsLen,?width,?height,?fps);????????nFrameRate?=?0;??????if?(fps)??????????nFrameRate?=?fps;??????else??????????nFrameRate?=?25;??}????H264LiveFramedSource*?H264LiveFramedSource::createNew(UsageEnvironment&?env,?unsigned?preferredFrameSize,?unsigned?playTimePerFrame)??{??????H264LiveFramedSource*?newSource?=?new?H264LiveFramedSource(env,?preferredFrameSize,?playTimePerFrame);??????return?newSource;??}????H264LiveFramedSource::~H264LiveFramedSource()??{??????free(inBuf);??????fclose(inpf);??}????????????????????????????????????????????void?H264LiveFramedSource::doGetNextFrame()??{??????fFrameSize?=?inLen;??????if?(fFrameSize?>?fMaxSize)??????{??????????fNumTruncatedBytes?=?fFrameSize?-?fMaxSize;??????????fFrameSize?=?fMaxSize;??????}??????else??????{??????????fNumTruncatedBytes?=?0;??????}??????memmove(fTo,?inBuf,?fFrameSize);????????inLen?=?0;??????inLen?=?getNextNalu(inpf,?inBuf);??????gettimeofday(&fPresentationTime,?NULL);??????fDurationInMicroseconds?=?1000000?/?nFrameRate;????????????nextTask()?=?envir().taskScheduler().scheduleDelayedTask(0,?(TaskFunc*)FramedSource::afterGetting,?this);??}??
h264LiveVideoServerMediaSubssion.hh
[cpp] view plain
copy #ifndef?_H264LIVEVIDEOSERVERMEDIASUBSSION_HH??#define?_H264LIVEVIDEOSERVERMEDIASUBSSION_HH??#include?"H264VideoFileServerMediaSubsession.hh"????class?H264LiveVideoServerMediaSubssion?:?public?H264VideoFileServerMediaSubsession?{????public:??????static?H264LiveVideoServerMediaSubssion*?createNew(UsageEnvironment&?env,?Boolean?reuseFirstSource);????protected:???????H264LiveVideoServerMediaSubssion(UsageEnvironment&?env,?Boolean?reuseFirstSource);??????~H264LiveVideoServerMediaSubssion();????protected:?????????????FramedSource*?createNewStreamSource(unsigned?clientSessionId,?unsigned&?estBitrate);??};????#endif??
h264LiveVideoServerMediaSubssion.cpp
[cpp] view plain
copy #include?"h264LiveVideoServerMediaSubssion.hh"??#include?"h264LiveFramedSource.hh"??#include?"H264VideoStreamFramer.hh"????H264LiveVideoServerMediaSubssion*?H264LiveVideoServerMediaSubssion::createNew(UsageEnvironment&?env,?Boolean?reuseFirstSource)??{??????return?new?H264LiveVideoServerMediaSubssion(env,?reuseFirstSource);??}????H264LiveVideoServerMediaSubssion::H264LiveVideoServerMediaSubssion(UsageEnvironment&?env,?Boolean?reuseFirstSource)??:?H264VideoFileServerMediaSubsession(env,?0,?reuseFirstSource)??{????}????H264LiveVideoServerMediaSubssion::~H264LiveVideoServerMediaSubssion()??{??}????FramedSource*?H264LiveVideoServerMediaSubssion::createNewStreamSource(unsigned?clientSessionId,?unsigned&?estBitrate)??{????????????estBitrate?=?1000;?????????????H264LiveFramedSource*?liveSource?=?H264LiveFramedSource::createNew(envir());??????if?(liveSource?==?NULL)??????{??????????return?NULL;??????}??????????????return?H264VideoStreamFramer::createNew(envir(),?liveSource);??}??
還需在h264LiveMediaServer.cpp中做相應的修改
[cpp] view plain
copy #include?"liveMedia.hh"??#include?"BasicUsageEnvironment.hh"??#include?"h264LiveVideoServerMediaSubssion.hh"????UsageEnvironment*?env;????????Boolean?reuseFirstSource?=?True;??????static?void?announceStream(RTSPServer*?rtspServer,?ServerMediaSession*?sms,?char?const*?streamName);?????int?main(int?argc,?char**?argv)???{????????????TaskScheduler*?scheduler?=?BasicTaskScheduler::createNew();??????env?=?BasicUsageEnvironment::createNew(*scheduler);??????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?\"h264LiveMediaServer\"";??????????????char?const*?streamName?=?"h264ESVideoTest";??????????????????????????ServerMediaSession*?sms=?ServerMediaSession::createNew(*env,?streamName,?streamName?,descriptionString);??????????????sms->addSubsession(H264LiveVideoServerMediaSubssion::createNew(*env,?reuseFirstSource));??????????????rtspServer->addServerMediaSession(sms);??????????????announceStream(rtspServer,?sms,?streamName);??????????????????env->taskScheduler().doEventLoop();????????return?0;???}????static?void?announceStream(RTSPServer*?rtspServer,?ServerMediaSession*?sms,char?const*?streamName)???{??????char*?url?=?rtspServer->rtspURL(sms);??????UsageEnvironment&?env?=?rtspServer->envir();??????env?<<?"\n\""?<<?streamName?<<?"\"?stream\"\n";??????env?<<?"Play?this?stream?using?the?URL?\""?<<?url?<<?"\"\n";??????delete[]?url;??}??
關于spsdecode.h,詳見: H.264(H264)解碼SPS獲取分辨率和幀率
三.測試
參考鏈接:http://blog.csdn.net/firehood_/article/details/16844397
總結
以上是生活随笔為你收集整理的通过live555实现H264 RTSP直播(Windows版)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。