基于live555实现简单的rtsp client

记录下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

typedef void(CALLBACK* CALLBACK_FUN)(int, int, int);

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)
    
  
  CALLBACK_FUN m_CallbackFun;
  unsigned m_iDataSize;
  void SetCallback(CALLBACK_FUN fun);
  BYTE *GetData(int *piDatasize);
 

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;
    /*
    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;
	
    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);
  
   m_iDataSize = frameSize;
   if(m_CallbackFun)
   {
	m_CallbackFun(0, 0, 0);
   }
  
 
  /*
  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;
  }
  *
  
  if (fPerFrameFileNameBuffer != NULL) {
    if (fOutFid != NULL) { fclose(fOutFid); fOutFid = NULL; }
  }

  // Then try getting the next frame:
  continuePlaying();
}

 

在FileSink.cpp中实现两个函数:

				 void FileSink::SetCallback(CALLBACK_FUN fun)
				 {
					 m_CallbackFun = (CALLBACK_FUN)fun;
				 }

				 BYTE* FileSink::GetData(int *piDatasize)
				 {
					 *piDatasize = m_iDataSize;
					 return fBuffer;
				 }


3、修改H264VideoFileSink.cpp

H264VideoFileSink*
H264VideoFileSink::createNew(UsageEnvironment& env, char const* fileName,
			     char const* sPropParameterSetsStr,
			     unsigned bufferSize, Boolean oneFilePerFrame) {
  do {
    FILE* fid;
    char const* perFrameFileNamePrefix;
/*
    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;

    return new H264VideoFileSink(env, fid, sPropParameterSetsStr, bufferSize, perFrameFileNamePrefix);
  } while (0);

  return NULL;
}


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

在文件中添加:

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¼°ÆäSize
		printf("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);
		}
	}
}

找到unsigned fileSinkBufferSize,改为

unsigned fileSinkBufferSize = 3*1024*1024;

 

::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);
	  /*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*/
	} 
if (createOggFileSink) {
	fileSink = OggFileSink
	  ::createNew(*env, outFileName,
		      subsession->rtpTimestampFrequency(), subsession->fmtp_config());
      } else if (fileSink == NULL) {
	// Normal case:
		  /*added 20140703*/
		  g_ExtraSPSLen = 0;
		  /*end added*/
	fileSink = FileSink::createNew(*env, outFileName,
				       fileSinkBufferSize, oneFilePerFrame);
      }

 

::setupStreams()

  delete setupIter;
  setupIter = NULL;
  if (!madeProgress) shutdown();

 

::shutdown()

  if (shutdownImmediately) continueAfterTEARDOWN(NULL, 0, NULL);
  areAlreadyShuttingDown = False; 


::continueAfterTEARDOWN()

  // Adios...
//  exit(shutdownExitCode);

 

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
    点赞
  • 4
    评论
  • 0
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

相关推荐
©️2020 CSDN 皮肤主题: 技术黑板 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、C币套餐、付费专栏及课程。

余额充值