zoukankan      html  css  js  c++  java
  • 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);
    可谓一目了然








     
  • 相关阅读:
    CPU深度学习模型推理性能抖动问题
    深度学习推理性能优化
    Winograd Convolution 推导
    Res-Family: From ResNet to SE-ResNeXt
    CPU二则
    CPU TFLOPS 计算
    深度学习专题
    计算系统中互联设备Survey
    深度学习框架演进史
    天池医疗AI大赛支持有感
  • 原文地址:https://www.cnblogs.com/baldermurphy/p/7444272.html
Copyright © 2011-2022 走看看