live555搭建实时播放rtsp服务器
创始人
2024-09-26 01:22:52
0

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服务器源码:

相关内容

热门资讯

车载安卓下载苹果系统,揭秘如何... 你有没有想过,你的车载系统竟然也能装上苹果系统?没错,就是那个我们平时手机上用的iOS系统!听起来是...
安卓电脑系统崩溃,原因分析及解... 最近我的安卓电脑系统突然崩溃了,这可真是让我头疼不已。你知道,我那台安卓电脑可是我日常工作和娱乐的好...
乐动力支持安卓系统吗,乐动力安... 最近有没有发现,你的手机里又多了一个健身APP?是不是在犹豫要不要下载乐动力呢?别急,别急,让我来给...
原生谷歌安卓系统下载,探索纯净... 亲爱的手机控们,你是否曾梦想拥有一部运行原生谷歌安卓系统的手机?那种纯净、高效、充满科技感的体验,是...
小米现在安卓系统优化,打造极致... 你有没有发现,最近小米的手机在安卓系统优化上可是下足了功夫呢?这不,我就来给你好好扒一扒,看看小米是...
云南游攻略系统和安卓,探寻彩云... 你打算去云南旅行了吗?那可得好好规划不然可就浪费了大好时光呢!今天,我就要给你安利一个超棒的云南游攻...
安卓系统怎么取消横屏,安卓系统... 你是不是也和我一样,有时候在使用安卓手机时,不小心把屏幕横过来,然后发现好多应用都是横着看的,简直让...
安卓系统慢动作摄影,捕捉精彩瞬... 你有没有发现,手机拍照已经不能满足我们追求个性的需求了?现在,安卓系统里的慢动作摄影功能,简直就像给...
安卓xp系统安装版,体验复古智... 你有没有想过,如果你的安卓手机也能装上XP系统,那会是怎样的体验呢?想象你手中的设备瞬间穿越回那个经...
怎么让安卓手机换回系统,轻松换... 亲爱的手机控们,你是不是也和我一样,对安卓手机的系统更新充满了期待?但是,有时候更新后的系统可能并不...
安卓系统界面停止运用,迈向未来 你知道吗?最近安卓系统界面上有个大变动,那就是它将停止运用啦!是不是有点惊讶?别急,让我带你一探究竟...
安卓系统中的drm服务,功能、... 你有没有发现,每次打开手机,安卓系统里总有一些神秘的玩意儿在默默运行?没错,说的就是那个让人又爱又恨...
华为OS是安卓系统吗,华为OS... 你有没有想过,华为的操作系统是不是安卓系统呢?这个问题,估计不少手机控都好奇过吧!今天,就让我带你一...
windows11安装安卓子系... 亲爱的电脑迷们,你是否对Windows 11的新功能感到好奇?今天,我要带你一起探索一个超级酷的功能...
安卓系统如何清密码,轻松解锁您... 手机解锁密码忘记了?别急,今天就来教你怎么轻松搞定安卓系统的密码清除问题。想象你正坐在沙发上,手里拿...
安卓11.0系统怎么关闭hd,... 你有没有发现,自从升级到了安卓11.0系统,手机界面看起来是不是更炫酷了?不过,有时候这高清的视觉体...
二手安卓原装系统,揭秘其性能与... 你有没有想过,手机更新换代的速度简直就像光速一样快?每次新款手机一出,旧款手机就变成了“古董”。但是...
安卓13系统还会卡,安卓13系... 你有没有发现,尽管安卓系统一直在更新迭代,但安卓13系统还是有点小卡呢?别急,今天咱们就来聊聊这个话...
2015年安卓系统版本,从Lo... 你有没有发现,手机里的安卓系统版本更新得可真是飞快啊!记得2015年那会儿,安卓系统版本可是发生了不...
安卓微信老是退出系统,探究原因... 你是不是也遇到了这样的烦恼?每次打开微信,还没聊两句,它就突然退出系统了!这可真是让人头疼啊。今天,...