live555搭建实时播放rtsp服务器

live555关于RTSP协议交互流程

live555的核心数据结构值之闭环双向链表

live555 rtsp服务器实战之createNewStreamSource

live555搭建实时播放rtsp服务器

live555 rtsp服务器实战之doGetNextFrame

       live555可以说是rtsp的专项库,既可以搭建rtsp服务器,也可以搭建rtsp客户端;由于客户端可以由vlc,potplayer等工具代替;这章主要讲解rtsp服务器的搭建过程;

        live555开源库可以说是很照顾用户了,搭建rtsp服务器的流程很精简!不用考虑太多按照流程搭建即可;具体的流程参考live555源码例程:ive555-master/testProgs/testOnDemandRTSPServer.cpp;

        当然,和其他开源库源码例程一样,媒体流都是从文件中获取,与实际的项目需求还是有一定差别的;那么该怎么实现网络实时传输的rtsp服务器呢?

        首先看一下例程的源码(以h264媒体流为例):

int main(int argc, char** argv) {
    // Begin by setting up our usage environment(首先设置我们的使用环境):
    //初始化调度任务的类。它负责管理任务的执行顺序和时间,确保多线程环境中的任务正确执行。
    TaskScheduler* scheduler = BasicTaskScheduler::createNew();
    //初始化基本操作环境的类。它封装了各种基本功能,如内存管理、错误处理、日志记录等。
    //接受一个 TaskScheduler 对象作为参数,并使用这个调度器来初始化环境。这确保了环境对象能够与调度器协同工作,正确地调度和管理任务。
    env = BasicUsageEnvironment::createNew(*scheduler);

    UserAuthenticationDatabase* authDB = NULL;

    // Serve regular RTSP (over a TCP connection):
    //创建rtsp服务器的类,里面创建了与客户端交互需要的ipv4和ipv6网络套接字,和各信令(OPTION DESCRIBE SETUP PLAY等)的处理函数
    RTSPServer* rtspServer = RTSPServer::createNew(*env, 8554, authDB);
    if (rtspServer == NULL) {
        *env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
        exit(1);
    }

    char const* descriptionString = "Session streamed by \"testOnDemandRTSPServer\"";
  
    char const* streamName = "h264ESVideoTest";
    char const* inputFileName = "test.264";
    //将streamName descriptionString等信息保存起来,供交互时使用
    ServerMediaSession* sms
      = ServerMediaSession::createNew(*env, streamName, streamName,
              descriptionString);

    int datalen = 0;
    unsigned char *databuf = nullptr;
    databuf = (unsigned char *)malloc(1024);
    //将该H264VideoFileServerMediaSubsession媒体子会话加入到会话链表中;咱们就是实现H264VideoFileServerMediaSubsession部分
    sms->addSubsession(H264VideoFileServerMediaSubsession
           ::createNew(*env, inputFileName, reuseFirstSource));
    //将该会话添加到哈希表中,key就是streamName,val就是sms
    rtspServer->addServerMediaSession(sms);
    //打印rtsp的访问url; test例程自定义的打印接口,不在live555库里,在实际项目中可以不实现;
    announceStream(rtspServer, sms, streamName, inputFileName);
    //进入主循环;循环监视双向闭环链表,延时队列的状态,运行响应的处理函数
    env->taskScheduler().doEventLoop(); // does not return

    return 0; // only to prevent compiler warning
}

        为了方便阅读,代码做了精简!去掉了部分注释和不需要的代码;这样整个服务器的搭建流程就已经很清晰明了了;是不是很简单?

注:

媒体子会话:一个流URL可能链接很多客户端,每个客户端就是一个子会话。

        来!来!来!接下来就是重头戏啦!我们怎么获取我们的网络媒体流呢? 首先我们需要实现两个虚函数:createNewStreamSource和doGetNextFrame;这两个函数的作用及在源码的调度流程我的前两篇文章已经介绍过,感兴趣的话点链接看详情;

        我们需要创建两个类来实现这两个虚函数:

        H264LiveVideoServerMediaSubssion类用于实现createNewStreamSource;因为createNewStreamSource函数被声明在OnDemandServerMediaSubsession类中,所以要继承OnDemandServerMediaSubsession类:

class H264LiveVideoServerMediaSubssion : public OnDemandServerMediaSubsession {

public:
    static H264LiveVideoServerMediaSubssion* createNew(UsageEnvironment& env);

protected: // we're a virtual base class
    H264LiveVideoServerMediaSubssion(UsageEnvironment& env);
    ~H264LiveVideoServerMediaSubssion();

protected: // redefined virtual functions
    FramedSource* createNewStreamSource(unsigned clientSessionId,unsigned& estBitrate);
    RTPSink* createNewRTPSink(Groupsock* rtpGroupsock,
            unsigned char rtpPayloadTypeIfDynamic,
            FramedSource* inputSource);

};

        由于createNewRTPSink在OnDemandServerMediaSubsession类中也是纯虚函数,所以也需要在子类中实现;来看下函数实现:

H264LiveVideoServerMediaSubssion* H264LiveVideoServerMediaSubssion::createNew(UsageEnvironment& env, Boolean reuseFirstSource)
{
    return new H264LiveVideoServerMediaSubssion(env, reuseFirstSource);
}

H264LiveVideoServerMediaSubssion::H264LiveVideoServerMediaSubssion(UsageEnvironment& env, Boolean reuseFirstSource)
: OnDemandServerMediaSubsession(env, reuseFirstSource)                                                                      
{
}


H264LiveVideoServerMediaSubssion::~H264LiveVideoServerMediaSubssion()
{
}

FramedSource* H264LiveVideoServerMediaSubssion::createNewStreamSource(unsigned clientSessionId, unsigned& estBitrate)
{
    /* Remain to do : assign estBitrate */
    estBitrate = 1000; // kbps, estimate

    //创建视频源
    H264FramedLiveSource* liveSource = H264FramedLiveSource::createNew(envir());
    if (liveSource == NULL)
    {
        return NULL;
    }

    // Create a framer for the Video Elementary Stream:
    return H264VideoStreamFramer::createNew(envir(), liveSource);
}
RTPSink* H264LiveVideoServerMediaSubssion::createNewRTPSink(Groupsock* rtpGroupsock,unsigned char rtpPayloadTypeIfDynamic,FramedSource* /*inputSource*/) {
  return H264VideoRTPSink::createNew(envir(), rtpGroupsock, rtpPayloadTypeIfDynamic);
}

        createNewStreamSource函数是需要我们实现的函数,函数创建了H264FramedLiveSource类对象,这个类对象就是我们实现doGetNextFrame函数的类;createNewRTPSink函数不需要自己实现;之间创建H264VideoRTPSink类即可;

        接下来看一下H264FramedLiveSource类;该类的doGetNextFrame指明了怎么获取媒体流;读取媒体流的方法;因为doGetNextFrame是在FramedSource中声明的纯虚函数;因此需要继承FramedSource类;

class H264FramedLiveSource : public FramedSource
{
public:
    static H264FramedLiveSource* createNew(UsageEnvironment& env);

protected:
    H264FramedLiveSource(UsageEnvironment& env);
    ~H264FramedLiveSource();

private:
    virtual void doGetNextFrame();
};

        FramedSource类中只有一个doGetNextFrame纯虚函数,用于获取媒体流,其他不用实现;doGetNextFrame函数的实现如下:

H264FramedLiveSource::H264FramedLiveSource(UsageEnvironment& env)
: FramedSource(env)
{
}

H264FramedLiveSource* H264FramedLiveSource::createNew(UsageEnvironment& env)
{
    H264FramedLiveSource* newSource = new H264FramedLiveSource(env);
    return newSource;
}

H264FramedLiveSource::~H264FramedLiveSource()
{
}

void H264FramedLiveSource::doGetNextFrame()
{
    uint8_t *frameData = nullptr;
    int frameLen = 0;
    //获取视频帧,该函数自己实现,可以是接收网络帧,也可以是读取本地数据
    Get_Video_Frame(&frameData, frameLen);
    //赋值父类成员变量
    fFrameSize = frameLen;
    memcpy(fTo, frameData, fFrameSize);
    
    // nextTask() = envir().taskScheduler().scheduleDelayedTask(0,(TaskFunc*)FramedSource::afterGetting, this);//表示延迟0秒后再执行 afterGetting 函数
    //这个是必须的,原因参考我关于doGetNextFrame的文章
    afterGetting(this);

    return;
}

        我是以帧的方式获取的视频流,在实际的开发中,不一定非要一次取一帧;也不一定按帧取;可以任意长度的数据;live555内部会进行分帧;

        定义好这两个函数之后就可以修改main函数中的媒体流相关代码:        

//例程代码
    sms->addSubsession(H264VideoFileServerMediaSubsession
           ::createNew(*env, inputFileName, reuseFirstSource));
//修改代码
    sms->addSubsession(H264LiveVideoServerMediaSubssion
           ::createNew(*env, reuseFirstSource));

        至此,rtsp服务器搭建完成;其实过程很简单;只不过第一次使用live555开源库有点不知从何下手;当然live555源码的实现技巧,运行流程也是很值得借鉴的;源码分析我已经出了一些文章;关注我了解更多;

live555实现实时流rtsp服务器源码:

相关推荐

  1. live555实时播放rtsp服务器

    2024-07-20 09:16:04       19 阅读
  2. live555流式rtsp服务器

    2024-07-20 09:16:04       53 阅读
  3. live555 rtsp服务器实战之createNewStreamSource

    2024-07-20 09:16:04       24 阅读
  4. live555 rtsp服务器实战之doGetNextFrame

    2024-07-20 09:16:04       18 阅读
  5. live555关于RTSP协议交互流程

    2024-07-20 09:16:04       14 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-20 09:16:04       52 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-20 09:16:04       54 阅读
  3. 在Django里面运行非项目文件

    2024-07-20 09:16:04       45 阅读
  4. Python语言-面向对象

    2024-07-20 09:16:04       55 阅读

热门阅读

  1. 服务器相关总结

    2024-07-20 09:16:04       16 阅读
  2. ES6 字符串的新增方法(二十)

    2024-07-20 09:16:04       14 阅读
  3. C语言初学者入门指南

    2024-07-20 09:16:04       13 阅读
  4. 如何看待中国信息协会2024年网络安全大赛

    2024-07-20 09:16:04       14 阅读
  5. Symfony框架概述

    2024-07-20 09:16:04       16 阅读
  6. go reflect的工程使用

    2024-07-20 09:16:04       17 阅读
  7. RKE部署k8s

    2024-07-20 09:16:04       19 阅读
  8. 关于取模的相关注意

    2024-07-20 09:16:04       17 阅读
  9. nodejs使用request后端访问第三方接口

    2024-07-20 09:16:04       17 阅读
  10. docker compose 部署交互模式的容器-以Ubuntu为例

    2024-07-20 09:16:04       17 阅读
  11. Spring源码系列一:入门——Hello World

    2024-07-20 09:16:04       15 阅读
  12. docker build时的网络问题

    2024-07-20 09:16:04       13 阅读