live555实践

基本的原理在这里

可以看下加深认知

http://blog.csdn.net/rembass/article/details/17653273

 

1.live555的编译及测试(windows)

http://blog.csdn.net/hjl240/article/details/48159243

..UsageEnvironmentinclude
..liveMediainclude
..groupsockinclude
..BasicUsageEnvironmentinclude

 记住mediaServer项目也一样的配置,一样的添加头文件,一样的添加源码

按照这个文章的配置编译了一把出现如下错误

结果是如下

在各个项目预编译器里面添加_CRT_SECURE_NO_WARNINGS

依然有错误

添加_WINSOCK_DEPRECATED_NO_WARNINGS

int BitVector::get_expGolombSigned() {
  unsigned codeNum = get_expGolomb();

  if ((codeNum&1) == 0) { // even
    return -(codeNum/2);  //错误    C4146    一元负运算符应用于无符号类型,结果仍为无符号类型
  } else { // odd
    return (codeNum+1)/2;
  }
}

还有一个选择打开和关闭SDL检查的位置就是:项目属性->配置属性->C/C++->SDL检查,选否

编译正常通过,生产如下文件

此时编译mediaServer,会出现大量的链接错误

 在MediaServer的ive555MediaServer.cpp添加

#ifdef _DEBUG
    #pragma comment (lib, "../Debug/BasicUsageEnvironment.lib")  
    #pragma comment (lib, "../Debug/groupsock.lib")  
    #pragma comment (lib, "../Debug/liveMedia.lib")  
    #pragma comment (lib, "../Debug/UsageEnvironment.lib")
#else
    #pragma comment (lib, "../Release/BasicUsageEnvironment.lib")  
    #pragma comment (lib, "../Release/groupsock.lib")  
    #pragma comment (lib, "../Release/liveMedia.lib")  
    #pragma comment (lib, "../Release/UsageEnvironment.lib")
#endif

依然报错

1>------ 已启动生成: 项目: mediaServer, 配置: Debug Win32 ------
1>BasicUsageEnvironment.lib(BasicUsageEnvironment.obj) : error LNK2019: 无法解析的外部符号 _initializeWinsockIfNecessary,该符号在函数 "protected: __thiscall BasicUsageEnvironment::BasicUsageEnvironment(class TaskScheduler &)" (??0BasicUsageEnvironment@@IAE@AAVTaskScheduler@@@Z) 中被引用
1>groupsock.lib(GroupsockHelper.obj) : error LNK2001: 无法解析的外部符号 _initializeWinsockIfNecessary
1>groupsock.lib(NetAddress.obj) : error LNK2019: 无法解析的外部符号 _our_inet_addr,该符号在函数 "public: __thiscall NetAddressList::NetAddressList(char const *)" (??0NetAddressList@@QAE@PBD@Z) 中被引用
1>groupsock.lib(GroupsockHelper.obj) : error LNK2001: 无法解析的外部符号 _our_inet_addr
1>groupsock.lib(GroupsockHelper.obj) : error LNK2019: 无法解析的外部符号 _our_srandom,该符号在函数 "unsigned int __cdecl ourIPAddress(class UsageEnvironment &)" (?ourIPAddress@@YAIAAVUsageEnvironment@@@Z) 中被引用
1>groupsock.lib(GroupsockHelper.obj) : error LNK2019: 无法解析的外部符号 _our_random,该符号在函数 "unsigned int __cdecl chooseRandomIPv4SSMAddress(class UsageEnvironment &)" (?chooseRandomIPv4SSMAddress@@YAIAAVUsageEnvironment@@@Z) 中被引用
1>liveMedia.lib(ProxyServerMediaSession.obj) : error LNK2001: 无法解析的外部符号 _our_random
1>liveMedia.lib(RTCP.obj) : error LNK2001: 无法解析的外部符号 _our_random
1>liveMedia.lib(RTPSink.obj) : error LNK2001: 无法解析的外部符号 _our_random
1>liveMedia.lib(GenericMediaServer.obj) : error LNK2019: 无法解析的外部符号 _our_random32,该符号在函数 "protected: class GenericMediaServer::ClientSession * __thiscall GenericMediaServer::createNewClientSessionWithId(void)" (?createNewClientSessionWithId@GenericMediaServer@@IAEPAVClientSession@1@XZ) 中被引用
1>liveMedia.lib(RTPSink.obj) : error LNK2001: 无法解析的外部符号 _our_random32
1>liveMedia.lib(RTPSource.obj) : error LNK2001: 无法解析的外部符号 _our_random32
1>G:opencodelive555liveDebugmediaServer.exe : fatal error LNK1120: 5 个无法解析的外部命令
========== 生成: 成功 0 个,失败 1 个,最新 4 个,跳过 0 个 ==========

开始只导入了C++文件和h文件,groupsock下有inet.c,liveMedia下有rtcp_from_spec.c,必须添加到工程,切记切记!

编译通过

运行一把

只支持提示的格式,其他的格式不支持的,截一个播放片段的页面,后面的就不截了!

2.应用验证

很多人在网上找应用,找不到参考的列子,要吗就是例子乱七八糟,根本不知道如何运行,或者一堆错,浪费时间,还没有得到应用的使用方法

实际上官方给了不少列子的http://www.live555.com/liveMedia/#testProgs   这些例子都包含在下载的源码里面

作为RTSP client它提供了两个例子testRTSPClientopenRTSP

作为RTSP server提供了一个例子testOnDemandRTSPServer

运行一把,提示使用方法

日志

G:opencodelive555liveDebug>testProgs.exe rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream
Created new TCP socket 128 for connection
Connecting to 192.168.1.68, port 554 on socket 128...
...remote connection opened
Sending request: DESCRIBE rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream RTSP/1.0
CSeq: 2
User-Agent: testProgs.exe (LIVE555 Streaming Media v2017.07.18)
Accept: application/sdp


Received 952 new bytes of response data.
Received a complete DESCRIBE response:



RTSP/1.0 200 OK
CSeq: 2
Content-Type: application/sdp
Content-Base: rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/
Content-Length: 793

v=0
o=- 1504884932064984 1504884932064984 IN IP4 192.168.1.68
s=Media Presentation
e=NONE
b=AS:5100
t=0 0
a=control:rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:5000
a=recvonly
a=x-dimensions:1920,1080
a=control:rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/trackID=1
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z00AKpWoHgCJ+WEAAAf9AAFfkAQ=,aO48gA==
m=audio 0 RTP/AVP 8
c=IN IP4 0.0.0.0
b=AS:50
a=recvonly
a=control:rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/trackID=2
a=rtpmap:8 PCMA/8000
a=Media_header:MEDIAINFO=494D4B48010200000400000111710110401F000000FA000000000000000000000000000000000000;
a=appversion:1.0

[URL:"rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"]: Got a SDP description:
v=0
o=- 1504884932064984 1504884932064984 IN IP4 192.168.1.68
s=Media Presentation
e=NONE
b=AS:5100
t=0 0
a=control:rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:5000
a=recvonly
a=x-dimensions:1920,1080
a=control:rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/trackID=1
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z00AKpWoHgCJ+WEAAAf9AAFfkAQ=,aO48gA==
m=audio 0 RTP/AVP 8
c=IN IP4 0.0.0.0
b=AS:50
a=recvonly
a=control:rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/trackID=2
a=rtpmap:8 PCMA/8000
a=Media_header:MEDIAINFO=494D4B48010200000400000111710110401F000000FA000000000000000000000000000000000000;
a=appversion:1.0

[URL:"rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"]: Initiated the "video/H264" subsession (client ports 60736-60737)
Sending request: SETUP rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/trackID=1 RTSP/1.0
CSeq: 3
User-Agent: testProgs.exe (LIVE555 Streaming Media v2017.07.18)
Transport: RTP/AVP;unicast;client_port=60736-60737


Received 204 new bytes of response data.
Received a complete SETUP response:
RTSP/1.0 200 OK
CSeq: 3
Session:       1072111189;timeout=60
Transport: RTP/AVP;unicast;client_port=60736-60737;server_port=8370-8371;ssrc=7812147b;mode="play"
Date:  Fri, Sep 08 2017 15:35:32 GMT


[URL:"rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"]: Set up the "video/H264" subsession (client ports 60736-60737)
[URL:"rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"]: Created a data sink for the "video/H264" subsession
[URL:"rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"]: Initiated the "audio/PCMA" subsession (client ports 60738-60739)
Sending request: SETUP rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/trackID=2 RTSP/1.0
CSeq: 4
User-Agent: testProgs.exe (LIVE555 Streaming Media v2017.07.18)
Transport: RTP/AVP;unicast;client_port=60738-60739
Session: 1072111189


Received 204 new bytes of response data.
Received a complete SETUP response:
RTSP/1.0 200 OK
CSeq: 4
Session:       1072111189;timeout=60
Transport: RTP/AVP;unicast;client_port=60738-60739;server_port=8354-8355;ssrc=7902bf6e;mode="play"
Date:  Fri, Sep 08 2017 15:35:32 GMT


[URL:"rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"]: Set up the "audio/PCMA" subsession (client ports 60738-60739)
[URL:"rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"]: Created a data sink for the "audio/PCMA" subsession
Sending request: PLAY rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/ RTSP/1.0
CSeq: 5
User-Agent: testProgs.exe (LIVE555 Streaming Media v2017.07.18)
Session: 1072111189
Range: npt=0.000-


Received 313 new bytes of response data.
Received a complete PLAY response:
RTSP/1.0 200 OK
CSeq: 5
Session:       1072111189
RTP-Info: url=rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/trackID=1;seq=379;rtptime=3863118866,url=rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/
Date:  Fri, Sep 08 2017 15:35:32 GMT


[URL:"rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"]: Started playing session...
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; audio/PCMA:   Received 320 bytes.     Presentation time: 1504856199.712277!
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; video/H264:   Received 20 bytes.      Presentation time: 1504856199.713578!
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; video/H264:   Received 4 bytes.       Presentation time: 1504856199.713578!
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; video/H264:   Received 5 bytes.       Presentation time: 1504856199.713578!
MultiFramedRTPSource::doGetNextFrame1(): The total received frame size exceeds the client's buffer size (100000).  41578 bytes of trailing data will be dropped!
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; video/H264:   Received 100000 bytes (with 41578 bytes truncated).     Presentation time: 1504856199.713578!
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; audio/PCMA:   Received 320 bytes.     Presentation time: 1504856199.752277!
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; audio/PCMA:   Received 320 bytes.     Presentation time: 1504856199.792277!
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; audio/PCMA:   Received 320 bytes.     Presentation time: 1504856199.832277!
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; video/H264:   Received 12511 bytes.   Presentation time: 1504856199.793578!
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; video/H264:   Received 9392 bytes.    Presentation time: 1504856199.833578!
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; audio/PCMA:   Received 320 bytes.     Presentation time: 1504856199.872277!
Stream "rtsp://admin:Wz123456@192.168.1.68:554/h264/ch1/main/av_stream/"; audio/PCMA:   Received 320 bytes.     Presentation time: 1504856199.912277!

拉到音频流和视频流

源码分析

int main(int argc, char** argv) {
  // Begin by setting up our usage environment:
  TaskScheduler* scheduler = BasicTaskScheduler::createNew();
  UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);

  // We need at least one "rtsp://" URL argument:
  if (argc < 2) {
    usage(*env, argv[0]);
    return 1;
  }

  // There are argc-1 URLs: argv[1] through argv[argc-1].  Open and start streaming each one:
  for (int i = 1; i <= argc-1; ++i) {
    openURL(*env, argv[0], argv[i]);
  }

  // All subsequent activity takes place within the event loop:
  env->taskScheduler().doEventLoop(&eventLoopWatchVariable);
    // This function call does not return, unless, at some point in time, "eventLoopWatchVariable" gets set to something non-zero.

  return 0;

  // If you choose to continue the application past this point (i.e., if you comment out the "return 0;" statement above),
  // and if you don't intend to do anything more with the "TaskScheduler" and "UsageEnvironment" objects,
  // then you can also reclaim the (small) memory used by these objects by uncommenting the following code:
  /*
    env->reclaim(); env = NULL;
    delete scheduler; scheduler = NULL;
  */
}

主要代码在两句话

  for (int i = 1; i <= argc-1; ++i) {
    openURL(*env, argv[0], argv[i]);
  }
  env->taskScheduler().doEventLoop(&eventLoopWatchVariable);

 主要在这一个函数里面实现

void openURL(UsageEnvironment& env, char const* progName, char const* rtspURL) {
  // Begin by creating a "RTSPClient" object.  Note that there is a separate "RTSPClient" object for each stream that we wish
  // to receive (even if more than stream uses the same "rtsp://" URL).
  RTSPClient* rtspClient = ourRTSPClient::createNew(env, rtspURL, RTSP_CLIENT_VERBOSITY_LEVEL, progName);
  if (rtspClient == NULL) {
    env << "Failed to create a RTSP client for URL "" << rtspURL << "": " << env.getResultMsg() << "
";
    return;
  }

  ++rtspClientCount;

  // Next, send a RTSP "DESCRIBE" command, to get a SDP description for the stream.
  // Note that this command - like all RTSP commands - is sent asynchronously; we do not block, waiting for a response.
  // Instead, the following function call returns immediately, and we handle the RTSP response later, from within the event loop:
  rtspClient->sendDescribeCommand(continueAfterDESCRIBE); 
}

 2.live555的编译及测试(linux)

下载解压

tar -xvf live555-latest.tar.gz

./genMakefiles linux-64bit 

选的 对应的是config.linux-64bit文件的配置

user@g1060server:~/mjl/algo/live$ make
cd liveMedia ; make
make[1]: 正在进入目录 `/home/user/mjl/algo/live/liveMedia'
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 Media.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 MediaSource.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 FramedSource.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 FramedFileSource.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 FramedFilter.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 ByteStreamFileSource.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 ByteStreamMultiFileSource.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 ByteStreamMemoryBufferSource.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 BasicUDPSource.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 DeviceSource.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 AudioInputDevice.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 WAVAudioFileSource.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 MPEG1or2Demux.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 MPEG1or2DemuxedElementaryStream.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 MPEGVideoStreamFramer.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 MPEG1or2VideoStreamFramer.cpp
MPEG1or2VideoStreamFramer.cpp: In member function ‘unsigned int MPEG1or2VideoStreamParser::parseSlice()’:
MPEG1or2VideoStreamFramer.cpp:463:18: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]
        << (void*)next4Bytes << "
";
                  ^
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 MPEG1or2VideoStreamDiscreteFramer.cpp
c++ -c -Iinclude -I../UsageEnvironment/include -I../groupsock/include -m64  -fPIC -I. -O2 -DSOCKLEN_T=socklen_t -D_LARGEFILE_SOURCE=1 -D_FILE_OFFSET_BITS=64 -Wall -DBSD=1 MPEG4VideoStreamFramer.cpp
MPEG4VideoStreamFramer.cpp: In member function ‘unsigned int MPEG4VideoStreamParser::parseVideoObjectPlane()’:
MPEG4VideoStreamFramer.cpp:655:19: warning: cast to pointer from integer of different size [-Wint-to-pointer-cast]

 查看所有的库文件,默认生成静态库,没有动态库

user@g1060server:~/mjl/algo/live$ find ./ -name *.a
./UsageEnvironment/libUsageEnvironment.a
./BasicUsageEnvironment/libBasicUsageEnvironment.a
./liveMedia/libliveMedia.a
./groupsock/libgroupsock.a
user@g1060server:~/mjl/algo/live$ find ./ -name *.so
user@g1060server:~/mjl/algo/live$

 测试拉流的数据

user@g1060server:~/mjl/algo/live/testProgs$ ./testRTSPClient
Usage: ./testRTSPClient <rtsp-url-1> ... <rtsp-url-N>
        (where each <rtsp-url-i> is a "rtsp://" URL)
user@g1060server:~/mjl/algo/live/testProgs$ ./testRTSPClient rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream
Created new TCP socket 3 for connection
Connecting to 192.168.3.229, port 554 on socket 3...
...remote connection opened
Sending request: DESCRIBE rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream RTSP/1.0
CSeq: 2
User-Agent: ./testRTSPClient (LIVE555 Streaming Media v2017.07.18)
Accept: application/sdp


Received 789 new bytes of response data.
Received a complete DESCRIBE response:
RTSP/1.0 200 OK
CSeq: 2
Content-Type: application/sdp
Content-Base: rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/
Content-Length: 628

v=0
o=- 1509489480558527 1509489480558527 IN IP4 192.168.3.229
s=Media Presentation
e=NONE
b=AS:5050
t=0 0
a=control:rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:5000
a=recvonly
a=x-dimensions:1920,1080
a=control:rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/trackID=1
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z00AKpWoHgCJ+WbgICAgQA==,aO48gA==
a=Media_header:MEDIAINFO=494D4B48010200000400000100000000000000000000000000000000000000000000000000000000;
a=appversion:1.0

[URL:"rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"]: Got a SDP description:
v=0
o=- 1509489480558527 1509489480558527 IN IP4 192.168.3.229
s=Media Presentation
e=NONE
b=AS:5050
t=0 0
a=control:rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/
m=video 0 RTP/AVP 96
c=IN IP4 0.0.0.0
b=AS:5000
a=recvonly
a=x-dimensions:1920,1080
a=control:rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/trackID=1
a=rtpmap:96 H264/90000
a=fmtp:96 profile-level-id=420029; packetization-mode=1; sprop-parameter-sets=Z00AKpWoHgCJ+WbgICAgQA==,aO48gA==
a=Media_header:MEDIAINFO=494D4B48010200000400000100000000000000000000000000000000000000000000000000000000;
a=appversion:1.0

[URL:"rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"]: Initiated the "video/H264" subsession (client ports 47872-47873)
Sending request: SETUP rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/trackID=1 RTSP/1.0
CSeq: 3
User-Agent: ./testRTSPClient (LIVE555 Streaming Media v2017.07.18)
Transport: RTP/AVP;unicast;client_port=47872-47873


Received 204 new bytes of response data.
Received a complete SETUP response:
RTSP/1.0 200 OK
CSeq: 3
Session:       2057492403;timeout=60
Transport: RTP/AVP;unicast;client_port=47872-47873;server_port=8314-8315;ssrc=5db62837;mode="play"
Date:  Tue, Oct 31 2017 22:38:00 GMT


[URL:"rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"]: Set up the "video/H264" subsession (client ports 47872-47873)
[URL:"rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"]: Created a data sink for the "video/H264" subsession
Sending request: PLAY rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/ RTSP/1.0
CSeq: 4
User-Agent: ./testRTSPClient (LIVE555 Streaming Media v2017.07.18)
Session: 2057492403
Range: npt=0.000-


Received 212 new bytes of response data.
Received a complete PLAY response:
RTSP/1.0 200 OK
CSeq: 4
Session:       2057492403
RTP-Info: url=rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/trackID=1;seq=48023;rtptime=2121100274
Date:  Tue, Oct 31 2017 22:38:00 GMT


[URL:"rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"]: Started playing session...
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 16 bytes.      Presentation time: 1509459854.583901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 4 bytes.       Presentation time: 1509459854.583901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 5 bytes.       Presentation time: 1509459854.583901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 39452 bytes.   Presentation time: 1509459854.583901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 18056 bytes.   Presentation time: 1509459854.649901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 16 bytes.      Presentation time: 1509459854.683901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 4 bytes.       Presentation time: 1509459854.683901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 5 bytes.       Presentation time: 1509459854.683901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 36213 bytes.   Presentation time: 1509459854.683901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 18184 bytes.   Presentation time: 1509459854.749901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17697 bytes.   Presentation time: 1509459854.783901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17606 bytes.   Presentation time: 1509459854.816901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17251 bytes.   Presentation time: 1509459854.849901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17527 bytes.   Presentation time: 1509459854.883901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 18058 bytes.   Presentation time: 1509459854.949901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17679 bytes.   Presentation time: 1509459854.983901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17784 bytes.   Presentation time: 1509459855.016901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17766 bytes.   Presentation time: 1509459855.049901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17719 bytes.   Presentation time: 1509459855.083901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 18027 bytes.   Presentation time: 1509459855.149901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17820 bytes.   Presentation time: 1509459855.183901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17731 bytes.   Presentation time: 1509459855.216901!

linux 编译so库

user@g1060server:~/mjl/algo/live$

./genMakefiles config.linux-with-shared-libraries
cat: config.config.linux-with-shared-libraries: 没有那个文件或目录
cat: config.config.linux-with-shared-libraries: 没有那个文件或目录
cat: config.config.linux-with-shared-libraries: 没有那个文件或目录
cat: config.config.linux-with-shared-libraries: 没有那个文件或目录
cat: config.config.linux-with-shared-libraries: 没有那个文件或目录
cat: config.config.linux-with-shared-libraries: 没有那个文件或目录
cat: config.config.linux-with-shared-libraries: 没有那个文件或目录
cat: config.config.linux-with-shared-libraries: 没有那个文件或目录

mv config.linux-with-shared-libraries  linux-with-shared-libraries

user@g1060server:~/mjl/algo/live$ ./genMakefiles linux-with-shared-libraries
user@g1060server:~/mjl/algo/live$ make
cd liveMedia ; make
make[1]: 正在进入目录 `/home/user/mjl/algo/live/liveMedia'

live555的提取RTP包的问题

根据业务的要求,希望一路摄像头能够给很多路提供实时视频流查看,以及提供给其他解码器解码,或者必要的时候自己解码

我们知道一个摄像头一般能支持4-5直接访问已经不错了,要是大于这个数字,一般请求就没有应答

所以我们只能在软件上实现处理,第一个访问,软件直接去拉一路,后面访问的都是软件拉取那一路的转发,或者解码后的转发

看了一下live555的功能  testRTSPClient里面出来的好像是RTP包

于是测试了一把,发现得到的包,直接扔进解码器里面不能出任何结果

所以就怀疑是RTP包的问题,于是抓包和下面日志得到的长度对比,完全不对

Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 18027 bytes.   Presentation time: 1509459855.149901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17820 bytes.   Presentation time: 1509459855.183901!
Stream "rtsp://admin:jyzbyj306@192.168.3.229:554/h264/ch1/main/av_stream/"; video/H264: Received 17731 bytes.   Presentation time: 1509459855.216901!

也就是testRTSPClient里面的这段代码(蓝色部分是我添加的回调),数据完全不对

void DummySink::afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
struct timeval presentationTime, unsigned /*durationInMicroseconds*/) {

    msStreamCallback((char*)fReceiveBuffer, frameSize);
    //QTSS_ReflectRTPData(fRelaySession, (char*)fReceiveBuffer, frameSize, fSubsession.trackIndex());

    // We've just received a frame of data.  (Optionally) print out information about it:
#ifdef DEBUG_PRINT_EACH_RECEIVED_FRAME
    if (fStreamId != NULL) 
        envir() << "Stream "" << fStreamId << ""; ";
    envir() << fSubsession.mediumName() << "/" << fSubsession.codecName() << ":	Received " << frameSize << " bytes";
    if (numTruncatedBytes > 0)
        envir() << " (with " << numTruncatedBytes << " bytes truncated)";
    char uSecsStr[6 + 1]; // used to output the 'microseconds' part of the presentation time
    sprintf(uSecsStr, "%06u", (unsigned)presentationTime.tv_usec);
   // envir() << ".	Presentation time: " << (int)presentationTime.tv_sec << "." << uSecsStr;
    fSubsession.rtpSource();
    if (fSubsession.rtpSource() != NULL && !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) {
        fSubsession.rtpSource()->envir() << "!"; // mark the debugging output to indicate that this presentation time is not RTCP-synchronized
    }
#ifdef DEBUG_PRINT_NPT
    envir() << "	NPT: " << fSubsession.getNormalPlayTime(presentationTime);
#endif
    envir() << "
";
#endif

    // Then continue, to request the next frame of data:
    continuePlaying();
}

Boolean DummySink::continuePlaying() {
    if (fSource == NULL) return False; // sanity check (should not happen)

    // Request the next frame of data from our input source.  "afterGettingFrame()" will get called later, when it arrives:
    fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE,
        afterGettingFrame, this,
        onSourceClosure, this);
    return True;
}

所以这个程序对原始的rtp做了处理,具体是什么处理不太清楚,不过从网上的资料来看,原始的RTP好像不能从哪里直接拿

 为了找到他的数据处理过程,就看他的回调如何处理
fSource->getNextFrame
  
void FramedSource::getNextFrame(unsigned char* to, unsigned maxSize,
                afterGettingFunc* afterGettingFunc,
                void* afterGettingClientData,
                onCloseFunc* onCloseFunc,
                void* onCloseClientData) {
  // Make sure we're not already being read:
  if (fIsCurrentlyAwaitingData) {
    envir() << "FramedSource[" << this << "]::getNextFrame(): attempting to read more than once at the same time!
";
    envir().internalError();
  }

  fTo = to;
  fMaxSize = maxSize;
  fNumTruncatedBytes = 0; // by default; could be changed by doGetNextFrame()
  fDurationInMicroseconds = 0; // by default; could be changed by doGetNextFrame()
  fAfterGettingFunc = afterGettingFunc;
  fAfterGettingClientData = afterGettingClientData;
  fOnCloseFunc = onCloseFunc;
  fOnCloseClientData = onCloseClientData;
  fIsCurrentlyAwaitingData = True;

  doGetNextFrame();
}

 fAfterGettingFunc = afterGettingFunc;
afterGettingFunc保存进了成员变量,并且调用了doGetNextFrame();
而doGetNextFrame()是一个虚函数,也就是说谁继承,谁实现
我们关心的RTPSource正好是FramedSource的子类,可是依然没实现doGetNextFrame()
class RTPSource: public FramedSource {

而MultiFramedRTPSource实现了doGetNextFrame()方法

class MultiFramedRTPSource: public RTPSource {
protected:
  MultiFramedRTPSource(UsageEnvironment& env, Groupsock* RTPgs,
               unsigned char rtpPayloadFormat,
               unsigned rtpTimestampFrequency,
               BufferedPacketFactory* packetFactory = NULL);
      // virtual base class
  virtual ~MultiFramedRTPSource();

  virtual Boolean processSpecialHeader(BufferedPacket* packet,
                       unsigned& resultSpecialHeaderSize);
      // Subclasses redefine this to handle any special, payload format
      // specific header that follows the RTP header.

  virtual Boolean packetIsUsableInJitterCalculation(unsigned char* packet,
                            unsigned packetSize);
      // The default implementation returns True, but this can be redefined

protected:
  Boolean fCurrentPacketBeginsFrame;
  Boolean fCurrentPacketCompletesFrame;

protected:
  // redefined virtual functions:
  virtual void doStopGettingFrames();

private:
  // redefined virtual functions:
  virtual void doGetNextFrame();
  virtual void setPacketReorderingThresholdTime(unsigned uSeconds);
void MultiFramedRTPSource::doGetNextFrame() {
  if (!fAreDoingNetworkReads) {
    // Turn on background read handling of incoming packets:
    fAreDoingNetworkReads = True;
    TaskScheduler::BackgroundHandlerProc* handler
      = (TaskScheduler::BackgroundHandlerProc*)&networkReadHandler;
    fRTPInterface.startNetworkReading(handler);
  }

  fSavedTo = fTo;
  fSavedMaxSize = fMaxSize;
  fFrameSize = 0; // for now
  fNeedDelivery = True;
  doGetNextFrame1();
}
void MultiFramedRTPSource::doGetNextFrame1() {
  while (fNeedDelivery) {
    // If we already have packet data available, then deliver it now.
    Boolean packetLossPrecededThis;
    BufferedPacket* nextPacket
      = fReorderingBuffer->getNextCompletedPacket(packetLossPrecededThis);
    if (nextPacket == NULL) break;

    fNeedDelivery = False;

    if (nextPacket->useCount() == 0) {
      // Before using the packet, check whether it has a special header
      // that needs to be processed:
      unsigned specialHeaderSize;
      if (!processSpecialHeader(nextPacket, specialHeaderSize)) {
    // Something's wrong with the header; reject the packet:
    fReorderingBuffer->releaseUsedPacket(nextPacket);
    fNeedDelivery = True;
    continue;
      }
      nextPacket->skip(specialHeaderSize);
    }

    // Check whether we're part of a multi-packet frame, and whether
    // there was packet loss that would render this packet unusable:
    if (fCurrentPacketBeginsFrame) {
      if (packetLossPrecededThis || fPacketLossInFragmentedFrame) {
    // We didn't get all of the previous frame.
    // Forget any data that we used from it:
    fTo = fSavedTo; fMaxSize = fSavedMaxSize;
    fFrameSize = 0;
      }
      fPacketLossInFragmentedFrame = False;
    } else if (packetLossPrecededThis) {
      // We're in a multi-packet frame, with preceding packet loss
      fPacketLossInFragmentedFrame = True;
    }
    if (fPacketLossInFragmentedFrame) {
      // This packet is unusable; reject it:
      fReorderingBuffer->releaseUsedPacket(nextPacket);
      fNeedDelivery = True;
      continue;
    }

    // The packet is usable. Deliver all or part of it to our caller:
    unsigned frameSize;
    nextPacket->use(fTo, fMaxSize, frameSize, fNumTruncatedBytes,
            fCurPacketRTPSeqNum, fCurPacketRTPTimestamp,
            fPresentationTime, fCurPacketHasBeenSynchronizedUsingRTCP,
            fCurPacketMarkerBit);
    fFrameSize += frameSize;

    if (!nextPacket->hasUsableData()) {
      // We're completely done with this packet now
      fReorderingBuffer->releaseUsedPacket(nextPacket);
    }

    if (fCurrentPacketCompletesFrame && fFrameSize > 0) {
      // We have all the data that the client wants.
      if (fNumTruncatedBytes > 0) {
    envir() << "MultiFramedRTPSource::doGetNextFrame1(): The total received frame size exceeds the client's buffer size ("
        << fSavedMaxSize << ").  "
        << fNumTruncatedBytes << " bytes of trailing data will be dropped!
";
      }
      // Call our own 'after getting' function, so that the downstream object can consume the data:
      if (fReorderingBuffer->isEmpty()) {
    // Common case optimization: There are no more queued incoming packets, so this code will not get
    // executed again without having first returned to the event loop.  Call our 'after getting' function
    // directly, because there's no risk of a long chain of recursion (and thus stack overflow):
    afterGetting(this);
      } else {
    // Special case: Call our 'after getting' function via the event loop.
    nextTask() = envir().taskScheduler().scheduleDelayedTask(0,
                                 (TaskFunc*)FramedSource::afterGetting, this);
      }
    } else {
      // This packet contained fragmented data, and does not complete
      // the data that the client wants.  Keep getting data:
      fTo += frameSize; fMaxSize -= frameSize;
      fNeedDelivery = True;
    }
  }
}

 //TODO::中间分析环节

live555的底层读取流也是用简单的socket实现

Boolean RTPInterface::handleRead(unsigned char* buffer, unsigned bufferMaxSize,
                 unsigned& bytesRead, struct sockaddr_in& fromAddress,
                 int& tcpSocketNum, unsigned char& tcpStreamChannelId,
                 Boolean& packetReadWasIncomplete) {
  packetReadWasIncomplete = False; // by default
  Boolean readSuccess;
  if (fNextTCPReadStreamSocketNum < 0) {
    // Normal case: read from the (datagram) 'groupsock':
    tcpSocketNum = -1;
    readSuccess = fGS->handleRead(buffer, bufferMaxSize, bytesRead, fromAddress);
  } else {
    // Read from the TCP connection:
    tcpSocketNum = fNextTCPReadStreamSocketNum;
    tcpStreamChannelId = fNextTCPReadStreamChannelId;

    bytesRead = 0;
    unsigned totBytesToRead = fNextTCPReadSize;
    if (totBytesToRead > bufferMaxSize) totBytesToRead = bufferMaxSize;
    unsigned curBytesToRead = totBytesToRead;
    int curBytesRead;
    while ((curBytesRead = readSocket(envir(), fNextTCPReadStreamSocketNum,
                      &buffer[bytesRead], curBytesToRead,
                      fromAddress)) > 0) {
      bytesRead += curBytesRead;
      if (bytesRead >= totBytesToRead) break;
      curBytesToRead -= curBytesRead;
    }
    fNextTCPReadSize -= bytesRead;
    if (fNextTCPReadSize == 0) {
      // We've read all of the data that we asked for
      readSuccess = True;
    } else if (curBytesRead < 0) {
      // There was an error reading the socket
      bytesRead = 0;
      readSuccess = False;
    } else {
      // We need to read more bytes, and there was not an error reading the socket
      packetReadWasIncomplete = True;
      return True;
    }
    fNextTCPReadStreamSocketNum = -1; // default, for next time
  }

  if (readSuccess && fAuxReadHandlerFunc != NULL) {
    // Also pass the newly-read packet data to our auxilliary handler:
    (*fAuxReadHandlerFunc)(fAuxReadHandlerClientData, buffer, bytesRead);
  }
  return readSuccess;
}

其中的

   while ((curBytesRead = readSocket(envir(), fNextTCPReadStreamSocketNum,
                      &buffer[bytesRead], curBytesToRead,
                      fromAddress)) > 0)
就是读取流的代码,可以看到它的底层实现
int readSocket(UsageEnvironment& env,
           int socket, unsigned char* buffer, unsigned bufferSize,
           struct sockaddr_in& fromAddress) {
  SOCKLEN_T addressSize = sizeof fromAddress;
  int bytesRead = recvfrom(socket, (char*)buffer, bufferSize, 0,
               (struct sockaddr*)&fromAddress,
               &addressSize);
  if (bytesRead < 0) {
    //##### HACK to work around bugs in Linux and Windows:
    int err = env.getErrno();
    if (err == 111 /*ECONNREFUSED (Linux)*/
#if defined(__WIN32__) || defined(_WIN32)
    // What a piece of crap Windows is.  Sometimes
    // recvfrom() returns -1, but with an 'errno' of 0.
    // This appears not to be a real error; just treat
    // it as if it were a read of zero bytes, and hope
    // we don't have to do anything else to 'reset'
    // this alleged error:
    || err == 0 || err == EWOULDBLOCK
#else
    || err == EAGAIN
#endif
    || err == 113 /*EHOSTUNREACH (Linux)*/) { // Why does Linux return this for datagram sock?
      fromAddress.sin_addr.s_addr = 0;
      return 0;
    }
    //##### END HACK
    socketErr(env, "recvfrom() error: ");
  } else if (bytesRead == 0) {
    // "recvfrom()" on a stream socket can return 0 if the remote end has closed the connection.  Treat this as an error:
    return -1;
  }

  return bytesRead;
}

  int bytesRead = recvfrom(socket, (char*)buffer, bufferSize, 0, (struct sockaddr*)&fromAddress, &addressSize);
可谓一目了然








 
原文地址:https://www.cnblogs.com/baldermurphy/p/7444272.html