首页 > 其他分享 >live555 rtsp服务器实战之doGetNextFrame

live555 rtsp服务器实战之doGetNextFrame

时间:2024-07-17 17:51:10浏览次数:19  
标签:live555 handleCmd 函数 rtsp cmdName else char doGetNextFrame

live555关于RTSP协议交互流程

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

live555 rtsp服务器实战之createNewStreamSource

live555 rtsp服务器实战之doGetNextFrame

概要

  live555用于实际项目开发时,createNewStreamSource和doGetNextFrame是必须要实现的两个虚函数,一般会创建两个类来实现这两个函数:假如这两个类为H264LiveVideoServerMediaSubssion和H264FramedLiveSource;H264LiveVideoServerMediaSubssion为实时视频会话类,用于实现createNewStreamSource虚函数;

  H264FramedLiveSource为实时视频帧资源类,用户实现doGetNextFrame函数;

  那么这两个函数是什么时候被调用以及他们的作用是什么呢?本节将详细介绍;

声明:该文章基于H264视频源为基础分析,其他源类似;

  由于这两个类主要是自定义虚函数,所以存在一定的继承关系,首先需要明确这两个类的继承关系(本章只介绍H264FramedLiveSource):

  H264FramedLiveSource:FramedSource:MediaSource:Medium

  doGetNextFrame调用流程

  doGetNextFrame函数声明于FrameSource类:

virtual void doGetNextFrame() = 0;

  该声明为纯虚函数,所以必须有子类对该函数进行实现,H264FramedLiveSource类就是用于实现该函数;doGetNextFrame函数只有一个功能:获取视频帧;

void H264FramedLiveSource::doGetNextFrame()
{
    uint8_t *frame = new uint8_t[1024*1024];
    int len = 0;
    //获取一帧视频帧
    get_frame(&frame, len);
    //将帧长度赋值给父类的成员变量fFrameSize
    fFrameSize = len;
    //将帧数据赋值给父类的成员变量fTo
    memcpy(fTo, frame, len);
    
    delete [] frame;
    // nextTask() = envir().taskScheduler().scheduleDelayedTask(0,(TaskFunc*)FramedSource::afterGetting, this);//表示延迟0秒后再执行 afterGetting 函数
    afterGetting(this);
 
    return;
}

  fFrameSize和fTo的两个父类变量在流传输时会用到;那么问题来了:doGetNextFrame函数在整个流程中在哪里被调用?什么时候调用?

  首先doGetNextFrame函数是在PLAY信令交互的时候被调用;调用流程为:

handleCmd_PLAY->startStream->startPlaying(OnDemandServerMediaSubsession)->startPlaying(MediaSink)->continuePlaying(虚函数,子类H264or5VideoRTPSink实现)->continuePlaying(虚函数,子类MultiFramedRTPSink实现)->buildAndSendPacket->packFrame(MultiFramedRTPSink)->getNextFrame->doGetNextFrame

  下面详解介绍下这个流程,看过我的另一篇文章:"live555 rtsp服务器实战之createNewStreamSource" 的都知道handleRequestBytes函数调用了handleCmd_SETUP;同样handleCmd_PLAY函数也是在这里被调用的;handleRequestBytes函数就是用来处理各种客户端信令交互信息的;handleRequestBytes函数如下:

void RTSPServer::RTSPClientConnection::handleRequestBytes(int newBytesRead)
{
    .
    .
    .
    if (urlIsRTSPS != fOurRTSPServer.fOurConnectionsUseTLS)
      {
#ifdef DEBUG
        fprintf(stderr, "Calling handleCmd_redirect()\n");
#endif
        handleCmd_redirect(urlSuffix);
      }
      else if (strcmp(cmdName, "OPTIONS") == 0)
      {
        // If the "OPTIONS" command included a "Session:" id for a session that doesn't exist,
        // then treat this as an error:
        if (requestIncludedSessionId && clientSession == NULL)
        {
#ifdef DEBUG
          fprintf(stderr, "Calling handleCmd_sessionNotFound() (case 1)\n");
#endif
          handleCmd_sessionNotFound();
        }
        else
        {
          // Normal case:
          handleCmd_OPTIONS();
        }
      }
      else if (urlPreSuffix[0] == '\0' && urlSuffix[0] == '*' && urlSuffix[1] == '\0')
      {
        // The special "*" URL means: an operation on the entire server.  This works only for GET_PARAMETER and SET_PARAMETER:
        if (strcmp(cmdName, "GET_PARAMETER") == 0)
        {
          handleCmd_GET_PARAMETER((char const *)fRequestBuffer);
        }
        else if (strcmp(cmdName, "SET_PARAMETER") == 0)
        {
          handleCmd_SET_PARAMETER((char const *)fRequestBuffer);
        }
        else
        {
          handleCmd_notSupported();
        }
      }
      else if (strcmp(cmdName, "DESCRIBE") == 0)
      {
        handleCmd_DESCRIBE(urlPreSuffix, urlSuffix, (char const *)fRequestBuffer);
      }
      else if (strcmp(cmdName, "SETUP") == 0)
      {
        Boolean areAuthenticated = True;
 
        if (!requestIncludedSessionId)
        {
          // No session id was present in the request.
          // So create a new "RTSPClientSession" object for this request.
 
          // But first, make sure that we're authenticated to perform this command:
          char urlTotalSuffix[2 * RTSP_PARAM_STRING_MAX];
          // enough space for urlPreSuffix/urlSuffix'\0'
          urlTotalSuffix[0] = '\0';
          if (urlPreSuffix[0] != '\0')
          {
            strcat(urlTotalSuffix, urlPreSuffix);
            strcat(urlTotalSuffix, "/");
          }
          strcat(urlTotalSuffix, urlSuffix);
          if (authenticationOK("SETUP", urlTotalSuffix, (char const *)fRequestBuffer))
          {
            clientSession = (RTSPServer::RTSPClientSession *)fOurRTSPServer.createNewClientSessionWithId();
          }
          else
          {
            areAuthenticated = False;
          }
        }
        if (clientSession != NULL)
        {
          clientSession->handleCmd_SETUP(this, urlPreSuffix, urlSuffix, (char const *)fRequestBuffer);
          playAfterSetup = clientSession->fStreamAfterSETUP;
        }
        else if (areAuthenticated)
        {
#ifdef DEBUG
          fprintf(stderr, "Calling handleCmd_sessionNotFound() (case 2)\n");
#endif
          handleCmd_sessionNotFound();
        }
      }
      else if (strcmp(cmdName, "TEARDOWN") == 0 || strcmp(cmdName, "PLAY") == 0 || strcmp(cmdName, "PAUSE") == 0 || strcmp(cmdName, "GET_PARAMETER") == 0 || strcmp(cmdName, "SET_PARAMETER") == 0)
      {
        if (clientSession != NULL)
        {
          clientSession->handleCmd_withinSession(this, cmdName, urlPreSuffix, urlSuffix, (char const *)fRequestBuffer);
        }
        else
        {
#ifdef DEBUG
          fprintf(stderr, "Calling handleCmd_sessionNotFound() (case 3)\n");
#endif
          handleCmd_sessionNotFound();
        }
      }
      else if (strcmp(cmdName, "REGISTER") == 0 || strcmp(cmdName, "DEREGISTER") == 0)
      {
        // Because - unlike other commands - an implementation of this command needs
        // the entire URL, we re-parse the command to get it:
        char *url = strDupSize((char *)fRequestBuffer);
        if (sscanf((char *)fRequestBuffer, "%*s %s", url) == 1)
        {
          // Check for special command-specific parameters in a "Transport:" header:
          Boolean reuseConnection, deliverViaTCP;
          char *proxyURLSuffix;
          parseTransportHeaderForREGISTER((const char *)fRequestBuffer, reuseConnection, deliverViaTCP, proxyURLSuffix);
 
          handleCmd_REGISTER(cmdName, url, urlSuffix, (char const *)fRequestBuffer, reuseConnection, deliverViaTCP, proxyURLSuffix);
          delete[] proxyURLSuffix;
        }
        else
        {
          handleCmd_bad();
        }
        delete[] url;
      }
      else
      {
        // The command is one that we don't handle:
        handleCmd_notSupported();
      }
      .
      .
      .
}

  handleCmd_withinSession函数内部就调用了handleCmd_PLAY函数;仅整理流程,调用途中的函数这里不做解释;直接跳到packFrame(MultiFramedRTPSink)函数:

void MultiFramedRTPSink::packFrame()
{
  // Get the next frame.
 
  // First, skip over the space we'll use for any frame-specific header:
  fCurFrameSpecificHeaderPosition = fOutBuf->curPacketSize();
  fCurFrameSpecificHeaderSize = frameSpecificHeaderSize();
  fOutBuf->skipBytes(fCurFrameSpecificHeaderSize);
  fTotalFrameSpecificHeaderSizes += fCurFrameSpecificHeaderSize;
 
  // See if we have an overflow frame that was too big for the last pkt
  if (fOutBuf->haveOverflowData())
  {
    // Use this frame before reading a new one from the source
    unsigned frameSize = fOutBuf->overflowDataSize();
    struct timeval presentationTime = fOutBuf->overflowPresentationTime();
    unsigned durationInMicroseconds = fOutBuf->overflowDurationInMicroseconds();
    fOutBuf->useOverflowData();
 
    afterGettingFrame1(frameSize, 0, presentationTime, durationInMicroseconds);
  }
  else
  {
    // Normal case: we need to read a new frame from the source
    if (fSource == NULL)
      return;
 
    fSource->getNextFrame(fOutBuf->curPtr(), fOutBuf->totalBytesAvailable(),
                          afterGettingFrame, this, ourHandleClosure, this);
  }
}

  这里调用了getNextFrame函数,来看下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!\n";
    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();
}

  发现啦!发现doGetNextFrame函数啦!所以doGetNextFrame函数就是在getNextFrame内被调用的;但是问题来了:搜索可以发现在live555中doGetNextFrame函数很多;怎么就确定这里的doGetNextFrame和我们自定义的doGetNextFrame有关呢?

问的好!那这的关键点就在于fSource->getNextFrame的fSource变量到底是什么类的对象;才能确定doGetNextFrame到底调用的是哪个类的函数;fSource属于MediaSink类的成员变量:

FramedSource* fSource;

  但是FrameSource子类很多;所以需要确定fSource到底指向的是哪个子类;

  不好意思,内容太多,懒得再拷贝了;具体内容参考上面的链接!

  后续还会继续更新关于doGetNextFrame函数获取的帧数据是怎么处理和发送的,H264VideoStreamFramer中的doGetNextFrame函数和MPEGVideoStreamFramer中的doGetNextFrame函数都是什么作用!期待的话关注我,了解最新动态!

  该文章持续更新!如果有错误或者模糊的地方欢迎留言探讨!

标签:live555,handleCmd,函数,rtsp,cmdName,else,char,doGetNextFrame
From: https://www.cnblogs.com/yhfs/p/18307995

相关文章

  • Windows平台如何实现多路RTSP|RTMP流合成后录像或转发RTMP服务
    技术背景我们在对接Windows平台RTSP|RTMP直播播放模块的时候,有开发者提出来这样的技术需求,他们做驾考、全景摄像头、多路会议录制等场景的时候,希望把多路视频流数据,合并到一路保存或者对外推送到RTMP服务。技术实现多路RTSP|RTMP流合流,实际上我们2016年就有这块demo,当时合流......
  • ubuntu中gstreamer缺少rtspserversink插件怎么安装这个插件?
    在Ubuntu中,如果GStreamer缺少rtspserversink插件,这通常意味着gst-rtsp-server模块没有正确安装或配置。rtspserversink是gst-rtsp-server库的一部分,它用于构建RTSP服务器,支持媒体流的发送。以下是详细的安装步骤,这些步骤将帮助你安装gst-rtsp-server及其相关插件:首先,你需要安......
  • 全面解析开源RTSP流媒体服务器:功能、性能与应用场景对比
    本文综合分析了多个开源RTSP流媒体服务器,包括EasyDarwin、RtspServer、SRS等,深入探讨它们的功能特性、技术实现、性能对比及应用场景,旨在为开发者提供全面的选型参考。文章目录开源RTSP流媒体服务器概述RTSP协议简介开源RTSP服务器的重要性主要开源项目概览EasyDarwin......
  • 【网络通信】一文读懂网络应用层常见协议的区别(HTTP 、HTTPS、MQTT、FTP、RTSP、RTMP)
        应用层协议是计算机网络中至关重要的组成部分,它们定义了应用程序如何与网络进行交互,实现数据的传输、接收和处理。本文将重点介绍几种常见的应用层协议:HTTP、HTTPS、MQTT、FTP、RTSP和RTMP,分析它们的特点、区别、工作原理以及应用场景。一、HTTP协议      ......
  • 直播协议详解 RTMP、HLS、HTTP-FLV、WebRTC、RTSP
    直播协议详解rtmp、hls、http-flv、WebRTC、rtsp 本期我们详细讨论直播的相关协议,包括:HTTP-FLV、HLS、RTMP、Web-RTC、RTSP等等。我们将会详细介绍这些协议的工作原理、应用场景、及延迟的原因。我们按这样的顺序讨论​:RTMP、HTTP-FLVHLSWeb-RTCRTSP一、RTMP、HTTP-FLV......
  • 前端在浏览器显示摄像头传回的RTSP视频流,前端采用的技术有VUE+video.js+flv.js
    在前端使用Vue.js框架,结合video.js和flv.js播放RTSP视频流,需要经过一系列步骤,因为浏览器并不能直接播放RTSP流,所以通常需要一个服务器来将RTSP流转为HLS或FLV格式,然后前端再通过flv.js播放。以下是一个基于此思路的基本实现指南:确保你已经安装了Vue.js、video.js、flv.js相关的依......
  • Vlc.DotNet.Wpf,播放rtsp视频,
    Vlc.DotNet.Wpf,播放rtsp视频, 1.NuGet上下载Vlc.DotNet.Wpf,在https://github.com/ZeBobo5/Vlc.DotNet上下载的源码都是最新版本的,里面有调用的示例,每个版本调用方法都不一样。下面代码以2.2.1为例。安装完成后,程序中会自动引用相关dll 2.播放视频相关代码......
  • WPF中使用LibVLCSharp.WPF 播放rtsp
    目录LibVLCSharp.WPF简介vlc:VideoView基本使用安装LibVLC播放rtsp引入命名空间xaml代码cs代码截图概述代码示例vlc:VideoView进阶使用空域问题宽高比设置全屏问题拉伸问题响应鼠标点击事件播放其他类型多视频重叠画中画引用 LibVLCShar......
  • Github最受欢迎的TOP 10开源RTSP流媒体项目
    Github最受欢迎的TOP10开源RTSP流媒体项目一块程序圆关注IP属地:河南0.1812020.09.2209:45:20字数457阅读6,684Github选出 TOP10开源免费的RTSP流媒体项目,以下是具体排名及星星数。 1、Easydarwin星星数:4,307Easydarwin是国内团队开发的开源流媒体框架......
  • Live555开源项目(1)----初步介绍
    Live555开源项目(1)----初步介绍小雨爱民谣的程序员 23人赞同了该文章Live555背景介绍:首先live555是一个开源项目,是一个为流媒体提供解决方案的跨平台的C++开源项目,它实现了对标准流媒体传输是一个为流媒体提供解决方案的跨平台的C++开源项目,它实现......