live555 rtsp服务器实战之createNewStreamSource

live555关于RTSP协议交互流程

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

live555 rtsp服务器实战之createNewStreamSource

概述

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

H264LiveVideoServerMediaSubssion为实时视频会话类,用于实现createNewStreamSource虚函数;

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

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

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

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

H264LiveVideoServerMediaSubssion:OnDemandServerMediaSubsession:ServerMediaSubsession:Medium


createNewStreamSource

本章介绍createNewStreamSource函数;

虚函数createNewStreamSource声明于OnDemandServerMediaSubsession类:

cpp 复制代码
virtual FramedSource* createNewStreamSource(unsigned clientSessionId,
                unsigned& estBitrate) = 0;

可以看出声明时为纯虚函数(live555提供的对外接口,用户自定义实现),必须有子类对该函数进行实现;该函数用于获取媒体流源的资源对象,简单点说就是指明服务器调用哪个doGetNextFrame函数获取视频流;

因此H264LiveVideoServerMediaSubssion类需要继承OnDemandServerMediaSubsession类;

createNewStreamSource函数实现解析:

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

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

    // Create a framer for the Video Elementary Stream:
    return H264VideoStreamFramer::createNew(envir(), liveSource);
}

上述代码就是最基础的虚函数createNewStreamSource的实现,其中H264FramedLiveSource就是视频源资源的类;也就是获取视频帧的函数doGetNextFrame所在的类;该函数主要做了两件事:

  1. 创建h264帧资源类对象liveSource;目的是获取调用doGetNextFrame的方法;

  2. 返回H264VideoStreamFramer类对象,并将h264帧资源类对象传递进去,最终liveSource赋值给StreamParser类中成员变量fInputSource和FramedFilter类的成员变量fInputSource;

该函数在SETUP信令交互时期被调用,首先看一下信令处理函数handleRequestBytes:

cpp 复制代码
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();
      }
      .
      .
      .
}

handleRequestBytes函数是在doEventLoop主循环中监测的RTSP客户端有数据发送时调用的函数(关于rtsp的tcp udp协议交互,参考上面的文章),作用就是处理各种信令(OPTION DESCRIBE SETUP等)及数据;其中就调用了handleCmd_SETUP处理SETUP信令的函数;handleCmd_SETUP函数又调用了getStreamParameters函数:

cpp 复制代码
void OnDemandServerMediaSubsession ::getStreamParameters(unsigned clientSessionId,
                                                         struct sockaddr_storage const &clientAddress,
                                                         Port const &clientRTPPort,
                                                         Port const &clientRTCPPort,
                                                         int tcpSocketNum,
                                                         unsigned char rtpChannelId,
                                                         unsigned char rtcpChannelId,
                                                         TLSState *tlsState,
                                                         struct sockaddr_storage &destinationAddress,
                                                         u_int8_t & /*destinationTTL*/,
                                                         Boolean &isMulticast,
                                                         Port &serverRTPPort,
                                                         Port &serverRTCPPort,
                                                         void *&streamToken)
{
    if (addressIsNull(destinationAddress))
    {
        // normal case - use the client address as the destination address:
        destinationAddress = clientAddress;
    }
  isMulticast = False;

  if (fLastStreamToken != NULL && fReuseFirstSource)
  {
    // Special case: Rather than creating a new 'StreamState',
    // we reuse the one that we've already created:
    serverRTPPort = ((StreamState *)fLastStreamToken)->serverRTPPort();
    serverRTCPPort = ((StreamState *)fLastStreamToken)->serverRTCPPort();
    ++((StreamState *)fLastStreamToken)->referenceCount();
    streamToken = fLastStreamToken;
  }
  else
  {
    // Normal case: Create a new media source:
    unsigned streamBitrate;
    FramedSource *mediaSource = createNewStreamSource(clientSessionId, streamBitrate);
    .
    .
    .
  }
  .
  .
  .
}

该函数就在OnDemandServerMediaSubsession类,也就是createNewStreamSource实现类H264LiveVideoServerMediaSubssion的父类,这时调用的createNewStreamSource就是我们自己实现的逻辑啦!返回值mediaSource后续被用来调用doGetNextFrame函数获取视频帧使用!

可以看出这两件事的目的都是为了告诉rtsp服务器怎么获取视频帧数据;关于fInputSource变量什么时候被使用的,请参考我的下篇文章:未知地址!哈哈哈!

下节的doGetNextFrame分析才是重点,敬请期待!

相关推荐
AlfredZhao1 天前
vi 删除指定范围的行,不用再反复按 dd
linux·vi
RTC实战笔记1 天前
Android 实时音视频接入教程:媒体补充增强信息(SEI)
音视频·媒体·rtc
用户9718356334661 天前
银河麒麟 KY10 申威(SW64) 安装 nginx-1.16.1-2.p01.ky10.sw_64.rpm 详细步骤
linux
猪脚踏浪1 天前
linux 拷贝文件或目录到指定的位置
linux
摇滚侠2 天前
Linux CentOS7 rpm 安装 MySQL 5.7
linux·运维·mysql
潜创微科技2 天前
HDMI1.3 无线传输芯片方案 空旷 150 米量产级音视频方案
音视频
bush42 天前
嵌入式linux学习记录十四、术语
linux·嵌入式
VidDown2 天前
VidDown 工具站:免费、本地优先的开发者工具箱
javascript·编辑器·音视频·视频编解码·视频
换个昵称都难2 天前
音频格式之WAV
音视频
载数而行5202 天前
Linux 11 动态监控指令top
linux