天天看点

流媒体学习笔记2(live555中的Session)

毕业课题打算从最原始的地方做起。好吧,那就从live555采集转发本地摄像头视频开始吧。首先从源码开始吧,今天看了点liveMedia中的session,这里做个总结。

整个源码中的继承顺序为

H264VideoFileServerMediaSubsession::FileServerMediaSubsession::                                                                       OnDemandServerMediaSubsession::ServerMediaSubsession ::Medium

ServerMediaSession ::Medium

Medium:抽象了基本的接口,包括环境,task和媒体名和媒体查找函数以及一些辅助函数,几乎所有得处理单元都继承自Medium类。

ServerMediaSession:添加了子会话链表,SDP(Session Description Protocol 会话协议)描述以及一些媒体相关处理函数。其中静态成员函数lookupByName可以从session链表中找到对应的媒体

ServerMediaSubsession:定义了指向ServerMediaSession的父指针,指向下个一个对象的指针。该媒体的SDP信息,该媒体的读取定位函数等。

ServerMediaSubsessionIterator:一个迭代器类,用来遍历subsession链表。

程序为容器中的每一个流建立一个subseesion,然后通过 ServerMediaSession::addSubsession 函数,将subsession 加入到ServerMediaSession。具体代码如下

ServerMediaSession::addSubsession(ServerMediaSubsession* subsession) {
  if (subsession->fParentSession != NULL) return False; // it's already used

  if (fSubsessionsTail == NULL) {
    fSubsessionsHead = subsession;
  } else {
    fSubsessionsTail->fNext = subsession;
  }
  fSubsessionsTail = subsession;

  subsession->fParentSession = this;
  subsession->fTrackNumber = ++fSubsessionCounter;
  return True;
}
           

ServerMediaSession与流名字所指定与文件是没有关系的,也就是说它不会操作文件,而文件的操作是放在 ServerMediaSubsession中的。具体应该是在ServerMediaSubsession的sdpLines()函数中打开。sdpLines()是在派生类OnDemandServerMediaSubsession中实现,代码如下

char const*
OnDemandServerMediaSubsession::sdpLines() {
  if (fSDPLines == NULL) {
    // We need to construct a set of SDP lines that describe this
    // subsession (as a unicast stream).  To do so, we first create
    // dummy (unused) source and "RTPSink" objects,
    // whose parameters we use for the SDP lines:

	//构建一套SDP行来描述这个Subsession。创建未使用的source和RTPSink对象
    unsigned estBitrate;
    FramedSource* inputSource = createNewStreamSource(0, estBitrate);
    if (inputSource == NULL) return NULL; // file not found

    struct in_addr dummyAddr;
    dummyAddr.s_addr = 0;
    Groupsock dummyGroupsock(envir(), dummyAddr, 0, 0);
    unsigned char rtpPayloadType = 96 + trackNumber()-1; // if dynamic
    RTPSink* dummyRTPSink
      = createNewRTPSink(&dummyGroupsock, rtpPayloadType, inputSource);
	//Sink中的到SDP
    setSDPLinesFromRTPSink(dummyRTPSink, inputSource, estBitrate);
    Medium::close(dummyRTPSink);
    closeStreamSource(inputSource);
  }

  return fSDPLines;
}
           

OnDemandServerMediaSubsession类继承 ServerMediaSubsession类,OnDemand实际上用来描述需求,Subsession表示只是一种类型媒体的会话。所以可以通过继承OnDemandServerMediaSubsession来实现不同类型的媒体转发,比如OnDemandServerMediaSubsession的两个子类

1.ProxyServerMediaSubsession:用于实现一个单播RTSP的服务器代理另一个RTSP流

2.FileServerMediaSubsession:虚基类,不同类型媒体通过继承它来实现对于需求,比如音频,视频,比如h263,h264编解码。FileServerMediaSubsession类只是添加了两个成员变量 fFileName,fFileSize用来描述转发的媒体。构造函数如下:

FileServerMediaSubsession
::FileServerMediaSubsession(UsageEnvironment& env, char const* fileName,
			    Boolean reuseFirstSource)
  : OnDemandServerMediaSubsession(env, reuseFirstSource),
    fFileSize(0) {
  fFileName = strDup(fileName);
}
           

而H264VideoFileServerMediaSubsession类继承了FileServerMediaSubsession,它将构造函数放入protected中,并用静态的成员方法createNew来实例对象,这是设计模式的工厂模式。createNew(env, fileName, reuseSource)是每一种类型的流媒体自己的subsession实现。如果 reuseSource == True,则多个客户端共享 StreamState 对象,也即意味着多个客户端播放同一流文件时,服务器端使用同一FileSource 对象对该流文件进行数据读取操作,使用同一RTPSink 对象对流数据进行封装发送操作,这时,多个客户端播放的内容就是同步的,这适合于实时数据播放;如果reuseSource==False,则对每个客户端,服务器端单独维护一个StreamState 对象进行播放操作,此时,多个客户端播放的内容就是独立的,这适合于数据回放。

当然,单看session看不出什么门道来,那看下面

SDP消息组装过程:

      ServerMediaSession负责产生会话公共描述信息,子会话描述由H264VideoFileServerMediaSubsession产生。 H264VideoFileServerMediaSubsession在其父类成员函数OnDemandServerMediaSubsession::sdpLines()中生成会话描述信息。在sdpLines()实现里面,创建一个FramedSource(具体实现类为H264VideoStreamFramer)和RTPSink(具体实现类为H264VideoRTPSink),最后调用OnDemandServerMediaSubsession::setSDPLinesFromRTPSink(...)成员函数生成子会话描述。

好吧,这样思路基本清楚了点,以后要写自己需要的编解码类,那就继承OnDemandServerMediaSubsession吧。对于souce-sink 机制下次讲了....

继续阅读