如何通过live555打开本地.sdp文件

时间:2015-09-09 09:02:46

标签: c++ vlc rtsp rtp live555

我需要通过获取每个RTP时间戳来打印每个帧时间(UTC),但vlc API不支持此功能。所以,我刚刚听到名为live555 lib的VLC lib解析RTSP并在testRTSPClient(live555官方网站演示)中找到afterGettingFrame后的函数打印每帧的UTC时间。

我只是使用testRTSPClient在本地PC中打开.sdp文件。但它不起作用。它只能打开" rtsp://123.434.12.4/3523swdawd.sdp"这种形式等等(Failed to Get SDP Description : 404 Stream not found testRTSPClient in LIVE555) 我需要安装rtsp服务器吗?因为我发现它需要向服务器发送一些特殊命令(SETUP,PLAY,OPTIONS)。

如果testRTSPClient只能处理rtsp://123.434.12.4/3523swdawd.sdp这种形式的url,以及VLC Media Player如何在不设置RTSP服务器的情况下处理本地.sdp文件?

TIPS: 此本地.sdp文件适用于我的本地IP摄像头。我可以使用VLC播放器从IP Camera播放视频帧,但我只想使用testRTSPClient处理本地.sdp文件并打印视频帧的UTC时间,是否有人可以通过解决方案解决此问题?

1 个答案:

答案 0 :(得分:2)

要使用live555接收SDP文件描述的RTP流,您需要:

  1. 从SDP创建MediaSession(这将创建关联的MediaSubsession
  2. 启动MediaSubsession以打开将接收RTP / RTCP的UDP端口
  3. 创建重载MediaSink以接收RTP帧
  4. 启动此接收器
  5. 灵感来自testRTSPClient.cpp的简单实现可能类似于:

    #include "liveMedia.hh"
    #include "BasicUsageEnvironment.hh"
    
    UsageEnvironment& operator<<(UsageEnvironment& env, const MediaSubsession& subsession) 
    {
        return env << subsession.mediumName() << "/" << subsession.codecName();
    }
    
    #define DUMMY_SINK_RECEIVE_BUFFER_SIZE 100000
    
    class DummySink: public MediaSink 
    {
        public:
            static DummySink* createNew(UsageEnvironment& env,
                          MediaSubsession& subsession, // identifies the kind of data that's being received
                          char const* streamId = NULL) // identifies the stream itself (optional)
            {
                  return new DummySink(env, subsession, streamId);
            }
    
        private:
            DummySink(UsageEnvironment& env, MediaSubsession& subsession, char const* streamId)  
                : MediaSink(env), fSubsession(subsession) 
            {
                fStreamId = strDup(streamId);
                fReceiveBuffer = new u_int8_t[DUMMY_SINK_RECEIVE_BUFFER_SIZE];
            }
    
            virtual ~DummySink()
            {
                delete[] fReceiveBuffer;
                delete[] fStreamId;
            }
    
            static void afterGettingFrame(void* clientData, unsigned frameSize,
                        unsigned numTruncatedBytes,
                        struct timeval presentationTime,
                        unsigned durationInMicroseconds)
            {
                DummySink* sink = (DummySink*)clientData;
                sink->afterGettingFrame(frameSize, numTruncatedBytes, presentationTime, durationInMicroseconds);        
            }
            void afterGettingFrame(unsigned frameSize, unsigned numTruncatedBytes,
                     struct timeval presentationTime, unsigned durationInMicroseconds)
            {
                if (fStreamId != NULL) envir() << "Stream \"" << fStreamId << "\"; ";
                envir() << fSubsession.mediumName() << "/" << fSubsession.codecName() << ":\tReceived " << 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() << ".\tPresentation time: " << (int)presentationTime.tv_sec << "." << uSecsStr;
                if (fSubsession.rtpSource() != NULL && !fSubsession.rtpSource()->hasBeenSynchronizedUsingRTCP()) 
                {
                    envir() << "!"; // mark the debugging output to indicate that this presentation time is not RTCP-synchronized
                }
                envir() << "\tNPT: " << fSubsession.getNormalPlayTime(presentationTime);
                envir() << "\n";
    
                // Then continue, to request the next frame of data:
                continuePlaying();          
            }
    
        private:
            virtual Boolean continuePlaying()
            {
                if (fSource == NULL) return False; // sanity check (should not happen)          
                fSource->getNextFrame(fReceiveBuffer, DUMMY_SINK_RECEIVE_BUFFER_SIZE, afterGettingFrame, this, onSourceClosure, this);
                return True;
            }
    
        private:
            u_int8_t* fReceiveBuffer;
            MediaSubsession& fSubsession;
            char* fStreamId;
    };
    
    int main(int argc, char** argv) 
    {
        TaskScheduler* scheduler = BasicTaskScheduler::createNew();
        UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
    
        if (argc < 2) 
        {
            *env << "Usage: " << argv[0] << " file.sdp\n";
            return 1;
        }
        const char* filename = argv[1];
        FILE* file = fopen(filename,"r");
        if (file == NULL)
        {
            *env << "Cannot open SDP file:" << filename << "\n";        
            return 1;
        }
        fseek(file, 0, SEEK_END);
        long size = ftell(file);
        fseek(file, 0, SEEK_SET);
        char sdp[size];
        fread(sdp,size,1,file);
        fclose(file);
    
        MediaSession* session = MediaSession::createNew(*env, sdp);               
        if (session == NULL)
        {
            *env << "Failed to create a MediaSession object from the SDP description: " << env->getResultMsg() << "\n";     
            return 1;
        }
    
        MediaSubsessionIterator iter(*session);
        MediaSubsession* subsession = NULL;
        while ((subsession = iter.next()) != NULL) 
        {
            if (!subsession->initiate (0))
            {
                *env << "Failed to initiate the \"" << *subsession << "\" subsession: " << env->getResultMsg() << "\n";
            }
            else
            {
                subsession->sink = DummySink::createNew(*env, *subsession, filename);
                if (subsession->sink == NULL)
                {
                    *env << "Failed to create a data sink for the \"" << *subsession << "\" subsession: " << env->getResultMsg() << "\n";           
                }
                else
                {
                    subsession->sink->startPlaying(*subsession->rtpSource(), NULL, NULL);
                }
            }
        }
    
        char eventLoopWatchVariable = 0;
        env->taskScheduler().doEventLoop(&eventLoopWatchVariable);
    
        return 0;
    }
    

    运行程序给出作为参数的包含SDP的文件的路径将读取RTP流打印帧大小和每个帧的时间戳。
    类似的东西:

    Stream "ffmpeg.sdp"; video/H265:    Received 5131 bytes.    Presentation time: 1442350569.228804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 7917 bytes.    Presentation time: 1442350569.268804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 2383 bytes.    Presentation time: 1442350569.308804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 7780 bytes.    Presentation time: 1442350569.348804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 1773 bytes.    Presentation time: 1442350569.388804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 9580 bytes.    Presentation time: 1442350569.428804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 7934 bytes.    Presentation time: 1442350569.468804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 2180 bytes.    Presentation time: 1442350569.508804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 10804 bytes.   Presentation time: 1442350569.548804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 7801 bytes.    Presentation time: 1442350569.588804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 7816 bytes.    Presentation time: 1442350569.628804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 4028 bytes.    Presentation time: 1442350569.668804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 7959 bytes.    Presentation time: 1442350569.708804!   NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 8062 bytes.    Presentation time: 1442350569.794000    NPT: 0.000000
    Stream "ffmpeg.sdp"; video/H265:    Received 8014 bytes.    Presentation time: 1442350569.834000    NPT: 0.000000