基于live555实现简单的rtsp client

来源:互联网 发布:linux 显示进程 编辑:程序博客网 时间:2024/05/17 08:00

记录下live555 rtsp client的简单编译实现过程。

参考资料:http://www.cnblogs.com/skyseraph/archive/2012/04/11/2442840.html

                    http://blog.csdn.net/xy365/article/details/20715937

live555下载地址:http://www.live555.com/liveMedia/public/

开发工具:VS2010


首先编译live555

1、建立空白解决方案


2、解决方案上右击,添加--新建项目,分别建立名为BasicUsageEnvironment、groupsock、liveMedia、UsageEnvironment的win32项目,选择类型为静态库,去掉预编译头选项


3、分别添加相应文件夹下cpp文件

4、添加头文件。选择视图--属性管理器,找一个项目,双击Microsoft.Cpp.Win32.user



在VC++目录--包含目录下添加头文件目录


(从父级或项目默认设置继承不可去掉,否则出错)

这样添加头文件,所有的解决方案下的工程都能使用。

5、在方案目录下建立lib文件夹,设置项目--属性--常规--输出目录。

6、重新生成解决方案

查看lib目录,生成lib文件,编译成功。

=================================================================================================================================

下面开始进行基于live555的rtsp client

1、建立一个VC++空项目,从testProgs目录下拷贝playComman.cpp .hh openRTSP.cpp并添加到项目

2、属性--配置属性--VC++目录,包含目录添加上步添加的4个文件夹中的include,库目录添加上步生成的lib的目录

3、属性--配置属性--链接器--输入,附加依赖项


编译下,没什么问题,烦人的配置过程已经结束,下面就开始愉快地改代码了

1、回到live555解决方案,打开liveMedia工程,修改FileSink.hh

#ifndef _FILE_SINK_HH#define _FILE_SINK_HH#ifndef _MEDIA_SINK_HH#include "MediaSink.hh"#endif<span style="color:#ff0000;">typedef void(CALLBACK* CALLBACK_FUN)(int, int, int);</span>class FileSink: public MediaSink {public:  static FileSink* createNew(UsageEnvironment& env, char const* fileName,     unsigned bufferSize = 20000,     Boolean oneFilePerFrame = False);  // "bufferSize" should be at least as large as the largest expected  //   input frame.  // "oneFilePerFrame" - if True - specifies that each input frame will  //   be written to a separate file (using the presentation time as a  //   file name suffix).  The default behavior ("oneFilePerFrame" == False)  //   is to output all incoming data into a single file.  virtual void addData(unsigned char const* data, unsigned dataSize,       struct timeval presentationTime);  // (Available in case a client wants to add extra data to the output file)        <span style="color:#ff0000;">CALLBACK_FUN m_CallbackFun;  unsigned m_iDataSize;  void SetCallback(CALLBACK_FUN fun);  BYTE *GetData(int *piDatasize);</span> protected:  FileSink(UsageEnvironment& env, FILE* fid, unsigned bufferSize,   char const* perFrameFileNamePrefix);      // called only by createNew()  virtual ~FileSink();protected: // redefined virtual functions:  virtual Boolean continuePlaying();protected:  static void afterGettingFrame(void* clientData, unsigned frameSize,unsigned numTruncatedBytes,struct timeval presentationTime,unsigned durationInMicroseconds);  virtual void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime);  FILE* fOutFid;  unsigned char* fBuffer;  unsigned fBufferSize;  char* fPerFrameFileNamePrefix; // used if "oneFilePerFrame" is True  char* fPerFrameFileNameBuffer; // used if "oneFilePerFrame" is True  struct timeval fPrevPresentationTime;  unsigned fSamePresentationTimeCounter;};#endif
2、修改FileSink.cpp

::createNew()

FileSink* FileSink::createNew(UsageEnvironment& env, char const* fileName,      unsigned bufferSize, Boolean oneFilePerFrame) {  do {    FILE* fid;    char const* perFrameFileNamePrefix;<span style="color:#ff0000;">/*    if (oneFilePerFrame) {      // Create the fid for each frame      fid = NULL;      perFrameFileNamePrefix = fileName;    } else {      // Normal case: create the fid once      fid = OpenOutputFile(env, fileName);      if (fid == NULL) break;      perFrameFileNamePrefix = NULL;    }*/fid = NULL;perFrameFileNamePrefix = NULL;</span>    return new FileSink(env, fid, bufferSize, perFrameFileNamePrefix);  } while (0);  return NULL;}


::afterGettingFrame()

void FileSink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes, struct timeval presentationTime) {  if (numTruncatedBytes > 0) {    envir() << "FileSink::afterGettingFrame(): The input frame data was too large for our buffer size ("    << fBufferSize << ").  "            << numTruncatedBytes << " bytes of trailing data was dropped!  Correct this by increasing the \"bufferSize\" parameter in the \"createNew()\" call to at least "            << fBufferSize + numTruncatedBytes << "\n";  }  addData(fBuffer, frameSize, presentationTime);  <span style="color:#ff0000;">   m_iDataSize = frameSize;   if(m_CallbackFun)   {m_CallbackFun(0, 0, 0);   }</span>  <span style="color:#ff0000;">   /*  if (fOutFid == NULL || fflush(fOutFid) == EOF) {    // The output file has closed.  Handle this the same way as if the input source had closed:    if (fSource != NULL) fSource->stopGettingFrames();    onSourceClosure();    return;  }  */</span>    if (fPerFrameFileNameBuffer != NULL) {    if (fOutFid != NULL) { fclose(fOutFid); fOutFid = NULL; }  }  // Then try getting the next frame:  continuePlaying();}


在FileSink.cpp中实现两个函数:
<span style="color:#ff0000;"> void FileSink::SetCallback(CALLBACK_FUN fun) { m_CallbackFun = (CALLBACK_FUN)fun; } BYTE* FileSink::GetData(int *piDatasize) { *piDatasize = m_iDataSize; return fBuffer; }</span>

3、修改H264VideoFileSink.cpp
H264VideoFileSink*H264VideoFileSink::createNew(UsageEnvironment& env, char const* fileName,     char const* sPropParameterSetsStr,     unsigned bufferSize, Boolean oneFilePerFrame) {  do {    FILE* fid;    char const* perFrameFileNamePrefix;<span style="color:#ff0000;">/*    if (oneFilePerFrame) {      // Create the fid for each frame      fid = NULL;      perFrameFileNamePrefix = fileName;    } else {      // Normal case: create the fid once      fid = OpenOutputFile(env, fileName);      if (fid == NULL) break;      perFrameFileNamePrefix = NULL;    }*/</span>fid = NULL;perFrameFileNamePrefix = NULL;    return new H264VideoFileSink(env, fid, sPropParameterSetsStr, bufferSize, perFrameFileNamePrefix);  } while (0);  return NULL;}

重新生成解决方案,回到rtsp client工程,修改playComman.cpp

在文件中添加:

<span style="color:#ff0000;">FileSink *g_fileSink;BYTE g_XVidBuffer[0xffffff];int g_XVidBufferLen = 0;BYTE g_ExtraSPS[1024];int g_ExtraSPSLen = 0;typedef void(CALLBACK* CALLBACK_FUN)(int, int, int);void CALLBACK ChannelCallback(int iParam1, int iParam2, int iEvent){if(g_fileSink){int iDataSize=0;BYTE *pData;pData = g_fileSink->GetData(&iDataSize);//´ËÌŽ¿ÉÒÔÈ¡µÃFrame¼°ÆäSizeprintf("len:%06d, data: %02x %02x %02x %02x %02x %02x %02x %02x \n",iDataSize,pData[0],pData[1],pData[2],pData[3],pData[4],pData[5],pData[6],pData[7]);if(iDataSize>=300){//frame ´óСÅДàmemcpy(g_XVidBuffer, g_ExtraSPS, g_ExtraSPSLen);memcpy(g_XVidBuffer+g_ExtraSPSLen,pData,iDataSize);//Œ¢Frame Ñ}Ñuµ½Global var ÒÔÀûááÀm³Ìʽ¿ÉÒÔ×xÈ¡g_XVidBufferLen = iDataSize+g_ExtraSPSLen;}else{char sTemp[MAX_PATH];sprintf(sTemp, "error   ==> len:%06d, data: %02x %02x %02x %02x %02x %02x %02x %02x \n",iDataSize,pData[0],pData[1],pData[2],pData[3],pData[4],pData[5],pData[6],pData[7]);OutputDebugString(sTemp);}}}</span>
找到unsigned fileSinkBufferSize,改为

<span style="color:#ff0000;">unsigned fileSinkBufferSize = 3*1024*1024;</span>


::createOutputFiles()

      FileSink* fileSink = NULL;      Boolean createOggFileSink = False; // by default      if (strcmp(subsession->mediumName(), "video") == 0) {if (strcmp(subsession->codecName(), "H264") == 0) {  // For H.264 video stream, we use a special sink that adds 'start codes',  // and (at the start) the SPS and PPS NAL units:  fileSink = H264VideoFileSink::createNew(*env, outFileName,  subsession->fmtp_spropparametersets(),  fileSinkBufferSize, oneFilePerFrame);<span style="color:#ff0000;">  /*added 20140703*/  g_fileSink = fileSink;  g_fileSink->SetCallback((CALLBACK_FUN)ChannelCallback);  unsigned int num=0;    SPropRecord * sps=parseSPropParameterSets(subsession->fmtp_spropparametersets(),num);  struct timeval timeNow;  gettimeofday(&timeNow, NULL);  g_ExtraSPSLen = 0;  unsigned char start_code[4] = {0x00, 0x00, 0x00, 0x01};    for(unsigned int i=0;i<num;i++){  memcpy(&g_ExtraSPS[g_ExtraSPSLen],start_code,4);  g_ExtraSPSLen+=4;  memcpy(&g_ExtraSPS[g_ExtraSPSLen],sps[i].sPropBytes,sps[i].sPropLength);  g_ExtraSPSLen+=sps[i].sPropLength;  }  memcpy(&g_ExtraSPS[g_ExtraSPSLen],start_code,4);  g_ExtraSPSLen+=4;  delete[] sps;    /*end added*/</span>} 
if (createOggFileSink) {fileSink = OggFileSink  ::createNew(*env, outFileName,      subsession->rtpTimestampFrequency(), subsession->fmtp_config());      } else if (fileSink == NULL) {// Normal case:<span style="color:#ff0000;">  /*added 20140703*/  g_ExtraSPSLen = 0;  /*end added*/</span>fileSink = FileSink::createNew(*env, outFileName,       fileSinkBufferSize, oneFilePerFrame);      }


::setupStreams()

  delete setupIter;<span style="color:#ff0000;">  setupIter = NULL;</span>  if (!madeProgress) shutdown();


::shutdown()

  if (shutdownImmediately) continueAfterTEARDOWN(NULL, 0, NULL);  <span style="color:#ff0000;">areAlreadyShuttingDown = False; </span>


::continueAfterTEARDOWN()

  // Adios...<span style="color:#ff0000;">//  exit(shutdownExitCode);</span>
 

OK,修改完毕。思路就是修改原来的FileSink,将原先保存为文件的代码改为得到码流使用自定义的回调函数来处理,实现得到实时码流的功能。

最后,在项目--属性--配置属性--调试 中,添加命令参数:rtsp://218.204.223.237:554/live/1/0547424F573B085C/gsfp90ef4k0a6iap.sdp 

运行



下面是几个rtsp测试地址:

rtsp://218.204.223.237:554/live/1/0547424F573B085C/gsfp90ef4k0a6iap.sdp

rtsp://116.199.127.68/huayu

rtsp://218.204.223.237:554/live/1/66251FC11353191F/e7ooqwcfbqjoo80j.sdp

rtsp://211.139.194.251:554/live/2/13E6330A31193128/5iLd2iNl5nQ2s8r8.sdp

rtsp://180.168.116.75:554/user=admin&password=&channel=1&stream=0.sdp 

rtsp://218.204.223.237:554/live/1/6D1E43167B3A7BDA/oby9efo80duh9bjf.sdp

(有几个不能用的,程序还需要再修改)







3 0
原创粉丝点击