LIVE555再学习 -- testRTSPClient 实例


转载地址:http://blog.csdn.net/qq_29350001/article/details/78051767

上一篇文章简单看了一遍 testRTSPClient  的源码,接下来举几个应用实例加深一下。

首先什么都不做修改,先执行一遍,看一下。

一、执行 testRTSPClient  



特么,上面的东西我没看明白。。。 a=、b=、c=等等这是什么?

还有我看别人分析的好像用到什么网络抓包工具,我不知道是什么工具,可能是 Wireshark ,我也不晓得怎么用。

参看: live555源代码分析

二、核心代码

再就是做移植的时候主要更改的地方


上述函数中会返回每帧数据长度 frameSize,fReceiveBuffer 是缓存指针;
DUMMY_SINK_RECEIVE_BUFFER_SIZE宏值默认是100000,若是高清视频超过1080p需要设置为300000。
一般为了取流我们必须在此触发个回调函数将frameSize,fReceiveBuffer以参数化形式送出。
fSubsession为媒体信息,主要提供通道名称、RTP负载类型、RTP时间戳、序列等信息,很重要的信息,可用于区分音、视频。
DummySink为数据池,用于处理摄像机发送过来的视频流。

三、实例(1)

参看:live555 使用testRTSPClient 接收h264记录

接收 h264 文件记录

[cpp]  view plain  copy
  1.  void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,    
  2.  struct timeval presentationTime, unsigned /*durationInMicroseconds*/) {    
  3.   // We've just received a frame of data.  (Optionally) print out information about it:    
  4. #ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME    
  5.   if (fStreamId != NULL) envir() << "Stream \"" << fStreamId << "\"; ";    
  6.   envir() << fSubsession.mediumName() << "/" << fSubsession.codecName() << ":\tReceived " << frameSize << " bytes";    
  7.   if (numTruncatedBytes > 0) envir() << " (with " << numTruncatedBytes << " bytes truncated)";    
  8.   char uSecsStr[6+1]; // used to output the 'microseconds' part of the presentation time    
  9.   sprintf(uSecsStr, "%06u", (unsigned)presentationTime.tv_usec);    
  10.   envir() << ".\tPresentation time: " << (unsigned)presentationTime.tv_sec << "." << uSecsStr;    
  11.   if (fSubsession.rtpSource() != NULL && !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {    
  12.     envir() << "!"// mark the debugging output to indicate that this presentation time is not RTCP-synchronized    
  13.   }    
  14.   envir() << "\n";    
  15. #endif    
  16.       
  17.   //todo one frame    
  18.   //save to file    
  19.   if(!strcmp(fSubsession.mediumName(), "video"))    
  20.   {    
  21.  if(firstFrame)    
  22.  {    
  23.  unsigned int num;    
  24.  SPropRecord *sps = parseSPropParameterSets(fSubsession.fmtp_spropparametersets(), num);    
  25.  // For H.264 video stream, we use a special sink that insert start_codes:    
  26.  struct timeval tv= {0,0};    
  27.  unsigned char start_code[4] = {0x00, 0x00, 0x00, 0x01};    
  28.  FILE *fp = fopen("test.264""a+b");    
  29.  if(fp)    
  30.  {    
  31.  fwrite(start_code, 4, 1, fp);    
  32.  fwrite(sps[0].sPropBytes, sps[0].sPropLength, 1, fp);    
  33.  fwrite(start_code, 4, 1, fp);    
  34.  fwrite(sps[1].sPropBytes, sps[1].sPropLength, 1, fp);    
  35.  fclose(fp);    
  36.  fp = NULL;    
  37.  }    
  38.     
  39.     
  40.  delete [] sps;    
  41.     
  42.     
  43.  firstFrame = False;    
  44.  }    
  45.     
  46.     
  47.  char *pbuf = (char *)fReceiveBuffer;    
  48.  char head[4] = {0x00, 0x00, 0x00, 0x01};    
  49.  FILE *fp = fopen("test.264""a+b");    
  50.  if(fp)    
  51.  {    
  52.  fwrite(head, 4, 1, fp);    
  53.  fwrite(fReceiveBuffer, frameSize, 1, fp);    
  54.  fclose(fp);    
  55.  fp = NULL;    
  56.  }    
  57.   }    
  58.     
  59.     
  60.   // Then continue, to request the next frame of data:    
  61.   continuePlaying();    
  62. }    

先理解关键知识点,不知道有没有误导,H264 的帧是以 NAL 单元的单位来传送的,一个 NAL 单元包含一帧(I帧 或 P帧 或 B帧),这三种类型的帧可以百度。所谓的 NAL 单元就是去掉 SPS、PPS 的视频帧, I 帧是关键帧,所有的解析都需要靠它,两个 I 帧之间被称为视频序列,I 帧头部需要加入 SPS 和 PPS,这两个之间需要 0x00000001 来分割, 0x00 0x00 0x00 0x01 + SPS的 Base64 解码形式 + 0x00 0x00 0x00 0x01 + PPS 的解码形式 + 0x00 0x00 0x00 0x01 视频帧(IDR帧) 这样组成的一个 buffer,FFMPEG 的 H264 解码器才能成功解码。

四、实例(2)

参看:【rtsp录制器】testRTSPClient+mp4v2录制mp4(h264+aac)

实现效果为: 
修改 testRTSPClient 来接收音视频流,并通过mp4v2 将音视频流录制为mp4(audio:aac,video:h264)

live555 和 MP4v2 我们之前都讲过的,这就简单了。

参看:mp4v2再学习 -- H264视频编码成MP4文件

参看:LIVE555再学习 -- Windows 下编译

因为我用的是vs2017,直接下载参看博客提供的工程会出现错误。

[cpp]  view plain  copy
  1. 1>------ 已启动生成: 项目: testRTSPClient, 配置: Debug Win32 ------  
  2. 1>TRACKER :  
  3. 1>C:\Program Files (x86)\MSBuild\Microsoft.Cpp\v4.0\Platforms\Win32\Microsoft.Cpp.Win32.Targets(153,5):  
  4.  error MSB6006: “CL.exe”已退出,代码为 5。  
  5. 1>已完成生成项目“testRTSPClient.vcxproj”的操作 - 失败。  
  6. ========== 生成: 成功 0 个,失败 1 个,最新 0 个,跳过 0 个 ==========  
而且需要的库文件也不是vs2017生成的,容易产生版本不兼容。

得了还是自己从新做一遍。

(1)新建项目

打开 VS;
文件->新建->项目->Win32控制台应用程序->选择空项目,点击完成。
注意,选择的位置最好不要有 空格或者汉字。

(2)拷贝文件

将 live555 四个库文件的include 和 lib 拷贝到一起。将MP4v2的include和lib拷贝到一起。

然后将参看博客工程里的 testRTSPClient 文件夹下的 .cpp 和 .h 文件拷贝到新建项目下。 

其中源码部分也要注意要和存放位置一致




根据实际需要修改你的位置

 


然后将 .h 添加到头文件刷选器下,将.cpp添加到源文件刷选器下


(3)配置属性

打开属性面板

解决方案资源管理器->右键单击项目->属性


头文件配置

配置属性->C/C++->常规->附加包含目录,输入
 ..\MP4Encoder\include;..\live555\BasicUsageEnvironment\include;..\live555\groupsock\include;..\live555\liveMedia\include;..\live555\UsageEnvironment\include


导入库配置

配置属性->链接器->常规->附加库目录,输入 ..\MP4Encoder\lib


配置属性->链接器->输入->附加依赖项,输入 libmp4v2.lib;

live555 四个库,用程序包含了,这里就不用再添加了。

[cpp]  view plain  copy
  1. #pragma comment (lib, "Ws2_32.lib")    
  2. #pragma comment(lib,"../live555/lib/BasicUsageEnvironment.lib")  
  3. #pragma comment(lib,"../live555/lib/liveMedia.lib")  
  4. #pragma comment(lib,"../live555/lib/groupsock.lib")  
  5. #pragma comment(lib,"../live555/lib/UsageEnvironment.lib")  



此刻再编译testRTSPClient,OK 编译成功,生成 testRTSPClient.exe。


(4)测试

打开live555服务器


执行 testRTSPClient.exe rtsp://192.168.2.xx/Titanic.mkv


最后生成 lsh.mp4,将其和 Titanic.mkv 对比。可发现比 Titanic.mkv 小了十几 KB。


使用 VLC 播放 lsh.mp4 ,没有声音. 


(5)项目工程

下载:testRTSPClient+mp4v2录制mp4工程文件

(6)总结

这个工程,源码部分,还好理解。核心源码

[cpp]  view plain  copy
  1. void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,  
  2. struct timeval presentationTime, unsigned /*durationInMicroseconds*/) {  
  3.     // We've just received a frame of data.  (Optionally) print out information about it:  
  4. #ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME  
  5.     if (fStreamId != NULL) envir() << "Stream \"" << fStreamId << "\"; ";  
  6.     envir() << fSubsession.mediumName() << "/" << fSubsession.codecName() << ":\tReceived " << frameSize << " bytes";  
  7.     if (numTruncatedBytes > 0) envir() << " (with " << numTruncatedBytes << " bytes truncated)";  
  8.     char uSecsStr[6+1]; // used to output the 'microseconds' part of the presentation time  
  9.     sprintf(uSecsStr, "%06u", (unsigned)presentationTime.tv_usec);  
  10.     envir() << ".\tPresentation time: " << (int)presentationTime.tv_sec << "." << uSecsStr;  
  11.     if (fSubsession.rtpSource() != NULL && !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {  
  12.         envir() << "!"// mark the debugging output to indicate that this presentation time is not RTCP-synchronized  
  13.     }  
  14. #ifdef DEBUG_PRINT_NPT  
  15.     envir() << "\tNPT: " << fSubsession.getNormalPlayTime(presentationTime);  
  16. #endif  
  17.     envir() << "\n";  
  18. #endif  
  19.   
  20.     //----------------------------------------------------------------------------------  
  21.     static int count = 1;  
  22.     //  
  23.     if(count++ == 6000){  
  24.         m_mp4Encoder->CloseMp4Encoder();  
  25.         MessageBoxA(NULL,"end","end",MB_OK);  
  26.     }  
  27.     //  
  28.     if ( 0==strcmp(fSubsession.mediumName(),"video") )       
  29.     {  
  30.         m_recvBuf[0] = 0x00;  
  31.         m_recvBuf[1] = 0x00;  
  32.         m_recvBuf[2] = 0x00;  
  33.         m_recvBuf[3] = 0x01;  
  34.         //  
  35.         if(m_isInitSpsPps)  
  36.         {  
  37.             m_isInitSpsPps = false;  
  38.             unsigned int num=0;    
  39.             SPropRecord * sps=parseSPropParameterSets(fSubsession.fmtp_spropparametersets(),num);  
  40.             //  
  41.             for(int i=0;i<2;i++){  
  42.                 memcpy(&m_recvBuf[4], sps[i].sPropBytes, sps[i].sPropLength);  
  43.                 m_mp4Encoder->Mp4VEncode(m_recvBuf,sps[i].sPropLength+4);  
  44.             }  
  45.             delete[] sps;    
  46.         }  
  47.         //  
  48.         memcpy(&m_recvBuf[4], fReceiveBuffer, frameSize);  
  49.         m_mp4Encoder->Mp4VEncode(m_recvBuf,frameSize+4);  
  50.     }  
  51.     if ( 0==strcmp(fSubsession.mediumName(),"audio") )  
  52.     {  
  53.         memcpy(m_recvBuf,fReceiveBuffer,frameSize);  
  54.         m_mp4Encoder->Mp4AEncode(m_recvBuf,frameSize);  
  55.     }  
  56.     //----------------------------------------------------------------------------------  
  57.     // Then continue, to request the next frame of data:  
  58.     continuePlaying();  
  59. }  

MP4v2部分的源码,我就不晓得了。跟我之前参看的 mp4v2再学习 -- H264视频编码成MP4文件 不太一样

然后,我又想一般我们都是 sensor 输出数据然后通过 RTSP 传输 VLC 播放,是这样的一套。

这个 testRTSPClient RTSP的客户端肯定是不对的。应该是 RTSP的服务器测。

所以,接下来我们讲下一个源码分析之 testH264VideoStreamer,官网介绍如下:

testH264VideoStreamer repeatedly reads from a H.264 Elementary Stream video file (named "test.264"), and streams it using RTP multicast. This program also has a built-in RTSP server.

  • Apple's "QuickTime Player" can be used to receive and play this audio stream. To use this, have the player open the session's "rtsp://" URL (which the program prints out as it starts streaming).
  • The Open Source "VLC" and "MPlayer" media players can also be used.

翻译一下:

testH264VideoStreamer 重复从 H.264 基本流视频文件(名为“test.264”)中读取,并使用 RTP 多播进行流式传输。 该程序还具有内置的 RTSP 服务器。
Apple 的“QuickTime 播放器”可用于接收和播放此音频流。 要使用它,让玩家打开会话的“rtsp://”URL(程序在开始流式传输时打印出来)。
开源“VLC”和“MPlayer”媒体播放器也可以使用。



  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值