【问题标题】:How to Open the local .sdp file by live555live555如何打开本地.sdp文件
【发布时间】:2015-09-09 09:02:45
【问题描述】:

我需要通过获取每个 RTP 时间戳来打印每个帧时间(UTC),但 vlc API 不支持此功能。所以,我只是听说VLC库调用live555库来解析RTSP,找到testRTSPClient(live555官网Demo)中的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媒体播放器如何在不设置RTSP服务器的情况下处理本地的.sdp文件?

提示: 这个本地 .sdp 文件用于我的本地 IP 摄像机。我可以使用 VLC Player 播放来自 IP 摄像机的视频帧,但我只想使用 testRTSPClient 处理本地 .sdp 文件并打印视频帧的 UTC 时间,有没有人可以解决这个问题?

【问题讨论】:

  • 如果您想获得 RTP 时间,wireshark 可以帮助您。
  • @mpromonet 层数太低。我不想解析相关的Packet,我想获取RTP时间并同步显示视频。我的项目是实时监控。它需要在视频帧上打印每一帧的UTC时间并显示。

标签: c++ vlc rtsp rtp live555


【解决方案1】:

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

  1. 从 SDP 创建一个 MediaSession(这将创建关联的 MediaSubsession
  2. 启动 MediaSubsession 以打开将接收 RTP/RTCP 的 UDP 端口
  3. 创建一个重载的MediaSink 来接收 RTP 帧
  4. 启动此接收器

受 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

【讨论】:

  • 谢谢你的重播,而且我需要解码帧数据才能显示视频,请问live555怎么办?你的解决方法就是打印每一帧的演示时间。以及如何显示和视频帧同步在屏幕上?我需要找到帧数据并解码显示。
  • @MathxHchen : live555 既没有解码能力也没有渲染能力,这段代码只是做 testRTSPClient 读取 SDP 文件的事情。为了解码,您可以使用ffmpegSDL_ttf 构建 OSD 帧并使用SDL 进行显示。
  • live555 可以获取每个 RTP 数据包?如果可以,我如何获取?我需要在解码之前提取每个 RTP 数据包的时间戳。而且我看了你的代码,在解码前我找不到任何关于每帧缓冲区的信息(需要使用FFMPEG解码帧数据)
  • DummySink::continuePlaying 要求在fReceiveBuffer 中调用fSource-&gt;getNextFrame 接收数据。 RTP 帧内容可在 afterGetting 回调中使用此缓冲区的第一个 frameSize 字节。
  • 谢谢你,我想知道演示时间是 UTC,它看起来像 1970 的秒数。 1. 1 到现在。那么,我不必提取 RTP 的时间戳?因为我在live555源码中发现RTP时间戳被转换成某种形式,代码如下:
猜你喜欢
  • 2012-07-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-03-06
  • 2020-08-24
  • 1970-01-01
相关资源
最近更新 更多