毕业课题打算从最原始的地方做起。好吧,那就从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 机制下次讲了....