【live555】推流者使用的ServerMediaSession类,以及如何产生SDP
来源:互联网 发布:cache数据库 东华 编辑:程序博客网 时间:2024/06/07 02:56
对于我个人而言,可能比较关注的是SDP的生成,所以会对SDP的一些方法和属性,加颜色标示。
###############################
(1)推流者用的一个数据结构 server media session,这个session可以含有多个子(sub)session,每个子session可以是音频或者视频。
而接受者,用的是media session。注意,sever media session 和 media session 不同。
(2)server media session 是所有sub session的父类。
所代表的所有的sub session, 所共用的属性有:
【1】 Boolean fIsSSM;
【2】 char* fStreamName; //流的名程
【3】 char* fInfoSDPString; // 是个字符串,表示infoSDP?不懂,这里头是啥呢?
【4】char* fDescriptionSDPString; //表示是个字符串,用处是描述SDP的
【5】 char* fMiscSDPLines; //难道是表示其余各种各样的SDP的行?
【5】struct timeval fCreationTime; 创建session的时间么?
【6】 unsigned fReferenceCount; //表示这个session被引用了多少次么?
【7】 Boolean fDeleteWhenUnreferenced;
有一些共有的行为:
【1】 判断是否是 server media session 函数isServerMediaSession
【2 】得到流的名字
【3】 SDP相关
为整个session 产生SDP 函数 generateSDPDescription
char* fInfoSDPString; // 是个字符串,表示infoSDP?不懂,这里头是啥呢?
char* fDescriptionSDPString; //表示是个字符串,用处是描述SDP的
char* fMiscSDPLines; //难道是表示其余各种各样的SDP的行?
struct timeval fCreationTime; 创建session的时间么?
【4】 sub session 相关
添加 sub session
删掉所有的 sub session
计算 sub session的 总数目
【5】 引用计数相关
增加引用计数
删除引用计数
统计引用计数数目
【6】 等等
testScaleFactor
【7】 遍历各个server media session ????
使用内置的类 ServerMediaSubsessionIterator
################################################################################
// "liveMedia"
// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
// A data structure that represents a session that consists of
// potentially可能的 multiple (audio and/or video) sub-sessions
// (This data structure is used for media *streamers* - i.e., servers.
// For media receivers, use "MediaSession" instead.)
// C++ header
#ifndef _SERVER_MEDIA_SESSION_HH
#define _SERVER_MEDIA_SESSION_HH
#ifndef _MEDIA_HH
#include "Media.hh"
#endif
#ifndef _FRAMED_SOURCE_HH
#include "FramedSource.hh"
#endif
#ifndef _GROUPEID_HH
#include "GroupEId.hh"
#endif
#ifndef _RTP_INTERFACE_HH
#include "RTPInterface.hh" // for ServerRequestAlternativeByteHandler
#endif
class ServerMediaSubsession; // forward
class ServerMediaSession: public Medium {
public:
//这个函数会调用构造函数。
static ServerMediaSession* createNew(UsageEnvironment& env,
char const* streamName = NULL,
char const* info = NULL,
char const* description = NULL,
Boolean isSSM = False,
char const* miscSDPLines= NULL);
static Boolean lookupByName(UsageEnvironment& env,
char const* mediumName,
ServerMediaSession*& resultSession);
//基于整个session产生SDP描述信息
char* generateSDPDescription(); // based on the entire session
// Note: The caller is responsible for freeing the returned string
char const* streamName() const { return fStreamName; } //stream的名字,这个是指啥?
Boolean addSubsession(ServerMediaSubsession* subsession); //加入一个sub session
unsigned numSubsessions() const { return fSubsessionCounter; } //sesssion数目
void testScaleFactor(float& scale); // sets "scale" to the actual supported scale //要缩放么???
float duration() const; //session的持续时间
// a result == 0 means an unbounded session (the default) 0表示无限制
// a result < 0 means: subsession durations differ; the result is -(the largest). 负数,这个是啥意思呢???
// a result > 0 means: this is the duration of a bounded session 有限制
//引用计数是对stream的?对session的???
unsigned referenceCount() const { return fReferenceCount; }
void incrementReferenceCount() { ++fReferenceCount; }
void decrementReferenceCount() { if (fReferenceCount > 0) --fReferenceCount; }
Boolean& deleteWhenUnreferenced() { return fDeleteWhenUnreferenced; }
//作为所有session的父类,就具有了删掉所有sub session的能力。
void deleteAllSubsessions();
// Removes and deletes all subsessions added by "addSubsession()", returning us to an 'empty' state 删掉所有add的sub session,将回退到empty 的状态。
// Note: If you have already added this "ServerMediaSession" to a "RTSPServer" then, before calling this function,
// you must first close any client connections that use it,
// by calling "RTSPServer::closeAllClientSessionsForServerMediaSession()".
protected:
//这是构造函数
ServerMediaSession(UsageEnvironment& env, char const* streamName,
char const* info, char const* description,
Boolean isSSM, char const* miscSDPLines);
// called only by "createNew()"
virtual ~ServerMediaSession();
private: // redefined virtual functions
virtual Boolean isServerMediaSession() const;
private:
Boolean fIsSSM;
// Linkage fields: 这个链表域,用来遍历sub session的。
friend class ServerMediaSubsessionIterator;
ServerMediaSubsession* fSubsessionsHead;
ServerMediaSubsession* fSubsessionsTail;
unsigned fSubsessionCounter;
char* fStreamName; //流的名程
char* fInfoSDPString; // 是个字符串,表示infoSDP?不懂,这里头是啥呢?
char* fDescriptionSDPString; //表示是个字符串,用处是描述SDP的
char* fMiscSDPLines; //难道是表示其余各种各样的SDP的行?
struct timeval fCreationTime; 创建session的时间么?
unsigned fReferenceCount; //表示这个session被引用了多少次么?
Boolean fDeleteWhenUnreferenced;
};
##########################ServerMediaSubsessionIterator 类#########################
class ServerMediaSubsessionIterator {
public:
ServerMediaSubsessionIterator(ServerMediaSession& session);
virtual ~ServerMediaSubsessionIterator();
ServerMediaSubsession* next(); // NULL if none
void reset();
private:
ServerMediaSession& fOurSession;
ServerMediaSubsession* fNextPtr;
};
#############ServerMediaSession实现####################
**********/
// "liveMedia"
// Copyright (c) 1996-2014 Live Networks, Inc. All rights reserved.
// A data structure that represents a session that consists of
// potentially multiple (audio and/or video) sub-sessions
// (This data structure is used for media *streamers* - i.e., servers.
// For media receivers, use "MediaSession" instead.)
// Implementation
#include "ServerMediaSession.hh"
#include <GroupsockHelper.hh>
#include <math.h>
////////// ServerMediaSession //////////
ServerMediaSession* ServerMediaSession
::createNew(UsageEnvironment& env,
char const* streamName, char const* info,
char const* description, Boolean isSSM, char const* miscSDPLines) {
return new ServerMediaSession(env, streamName, info, description,
isSSM, miscSDPLines);
}
Boolean ServerMediaSession
::lookupByName(UsageEnvironment& env, char const* mediumName,
ServerMediaSession*& resultSession) {
resultSession = NULL; // unless we succeed 啥意思????
Medium* medium;
if (!Medium::lookupByName(env, mediumName, medium)) return False;
if (!medium->isServerMediaSession()) {
env.setResultMsg(mediumName, " is not a 'ServerMediaSession' object");
return False;
}
resultSession = (ServerMediaSession*)medium;
return True;
}
static charconst* const libNameStr = "LIVE555 Streaming Media v"; //staic的 ,居然还有俩const啊,都啥意思啊。
char const* const libVersionStr = LIVEMEDIA_LIBRARY_VERSION_STRING;
//构造函数
ServerMediaSession::ServerMediaSession(UsageEnvironment& env,
char const* streamName,
char const* info,
char const* description,
Boolean isSSM, char const* miscSDPLines)
//在构造函数中,初始化各个域
: Medium(env), fIsSSM(isSSM), fSubsessionsHead(NULL),
fSubsessionsTail(NULL), fSubsessionCounter(0),
fReferenceCount(0), fDeleteWhenUnreferenced(False) {
fStreamName = strDup(streamName== NULL ? "" : streamName); //流的名字,可能是空的。
char* libNamePlusVersionStr = NULL; // by default
if (info == NULL || description == NULL) {
libNamePlusVersionStr = new char[strlen(libNameStr) + strlen(libVersionStr) + 1];
sprintf(libNamePlusVersionStr, "%s%s", libNameStr, libVersionStr);
}
//咋这俩一开始的初始化指,都跟live555的版本号有关系啊。。。。
fInfoSDPString= strDup(info == NULL ? libNamePlusVersionStr : info);
fDescriptionSDPString = strDup(description == NULL ? libNamePlusVersionStr : description);
delete[] libNamePlusVersionStr;
fMiscSDPLines = strDup(miscSDPLines == NULL ? "" : miscSDPLines);
gettimeofday(&fCreationTime, NULL); //创建时间有了。
}
ServerMediaSession::~ServerMediaSession() { //析构函数。。。删掉这些字符串。
deleteAllSubsessions();
delete[] fStreamName;
delete[] fInfoSDPString;
delete[] fDescriptionSDPString;
delete[] fMiscSDPLines;
}
//好像维护了一个具有头尾指针的链表,也就是一个队列吧,头部删除,尾部加入。
Boolean
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; //新加入的sub session的父亲是加入他的这个session
subsession->fTrackNumber = ++fSubsessionCounter; //track的序号是这么计算的,加入一个sub sessin,那么track就增加1
return True;
}
//为啥要scale 啊????
void ServerMediaSession::testScaleFactor(float& scale) {
// First, try setting all subsessions to the desired scale.
// If the subsessions' actual scales differ from each other, choose the
// value that's closest to 1, and then try re-setting all subsessions to that
// value. If the subsessions' actual scales still differ, re-set them all to 1.
float minSSScale = 1.0;
float maxSSScale = 1.0;
float bestSSScale = 1.0;
float bestDistanceTo1 = 0.0;
ServerMediaSubsession* subsession;
for (subsession = fSubsessionsHead; subsession != NULL;
subsession = subsession->fNext) {
float ssscale = scale;
subsession->testScaleFactor(ssscale);
if (subsession == fSubsessionsHead) { // this is the first subsession
minSSScale = maxSSScale = bestSSScale = ssscale;
bestDistanceTo1 = (float)fabs(ssscale - 1.0f);
} else {
if (ssscale < minSSScale) {
minSSScale = ssscale;
} else if (ssscale > maxSSScale) {
maxSSScale = ssscale;
}
float distanceTo1 = (float)fabs(ssscale - 1.0f);
if (distanceTo1 < bestDistanceTo1) {
bestSSScale = ssscale;
bestDistanceTo1 = distanceTo1;
}
}
}
if (minSSScale == maxSSScale) {
// All subsessions are at the same scale: minSSScale == bestSSScale == maxSSScale
scale = minSSScale;
return;
}
// The scales for each subsession differ. Try to set each one to the value
// that's closest to 1:
for (subsession = fSubsessionsHead; subsession != NULL;
subsession = subsession->fNext) {
float ssscale = bestSSScale;
subsession->testScaleFactor(ssscale);
if (ssscale != bestSSScale) break; // no luck
}
if (subsession == NULL) {
// All subsessions are at the same scale: bestSSScale
scale = bestSSScale;
return;
}
// Still no luck. Set each subsession's scale to 1:
for (subsession = fSubsessionsHead; subsession != NULL;
subsession = subsession->fNext) {
float ssscale = 1;
subsession->testScaleFactor(ssscale);
}
scale = 1;
}
float ServerMediaSession::duration() const {
float minSubsessionDuration = 0.0;
float maxSubsessionDuration = 0.0;
//这是在遍历sub session链表啊。
for (ServerMediaSubsession* subsession = fSubsessionsHead; subsession != NULL;
subsession = subsession->fNext) {
// Hack: If any subsession supports seeking by 'absolute' time, then return a negative value, to indicate that only subsessions
// will have a "a=range:" attribute: 如果有任意的sub session支持根据绝对时间来seek,那么返回一个负数,表示仅仅sub session 有 a=range: 属性
char* absStartTime = NULL; char* absEndTime = NULL;
subsession->getAbsoluteTimeRange(absStartTime, absEndTime);
if (absStartTime != NULL) return -1.0f;
//选择所有子session中最大的那个,作为这个session 的duration
float ssduration = subsession->duration();
if (subsession == fSubsessionsHead) { // this is the first subsession
minSubsessionDuration = maxSubsessionDuration = ssduration;
} else if (ssduration < minSubsessionDuration) {
minSubsessionDuration = ssduration;
} else if (ssduration > maxSubsessionDuration) {
maxSubsessionDuration = ssduration;
}
}
if (maxSubsessionDuration != minSubsessionDuration) {
return -maxSubsessionDuration; // because subsession durations differ
} else {
return maxSubsessionDuration; // all subsession durations are the same
}
}
//咋个删掉所有的 sub session呢??
void ServerMediaSession::deleteAllSubsessions() {
Medium::close(fSubsessionsHead);
fSubsessionsHead = fSubsessionsTail = NULL;
fSubsessionCounter = 0;
}
Boolean ServerMediaSession::isServerMediaSession() const {
return True;
}
char* ServerMediaSession::generateSDPDescription() {
AddressString ipAddressStr(ourIPAddress(envir())); //ip 地址。。
unsigned ipAddressStrSize = strlen(ipAddressStr.val()); //ip地址的长度
//填充sourceFilterLine 这个行。这个对ssm有效。
// For a SSM sessions, we need a "a=source-filter: incl ..."line also:
char* sourceFilterLine;
if (fIsSSM) {
char const* const sourceFilterFmt =
"a=source-filter: incl IN IP4 * %s\r\n"
"a=rtcp-unicast: reflection\r\n";
unsigned const sourceFilterFmtSize = strlen(sourceFilterFmt) + ipAddressStrSize + 1;
sourceFilterLine = new char[sourceFilterFmtSize];
sprintf(sourceFilterLine, sourceFilterFmt, ipAddressStr.val());
} else {
sourceFilterLine = strDup("");
}
//时间范围??
char* rangeLine = NULL; // for now
char* sdp = NULL; // for now
do {
// Count the lengths of each subsession's media-level SDP lines.
// (We do this first, because the call to "subsession->sdpLines()"
// causes correct subsession 'duration()'s to be calculated later.)
unsigned sdpLength = 0;
ServerMediaSubsession* subsession;
for (subsession = fSubsessionsHead; subsession != NULL;
subsession = subsession->fNext) {
char const* sdpLines = subsession->sdpLines();
if (sdpLines == NULL) continue; // the media's not available
sdpLength += strlen(sdpLines);
}
if (sdpLength == 0) break; // the session has no usable subsessions
// Unless subsessions have differing durations, we also have a "a=range:" line:
float dur = duration();
if (dur == 0.0) {
rangeLine = strDup("a=range:npt=0-\r\n");
} else if (dur > 0.0) {
char buf[100];
sprintf(buf, "a=range:npt=0-%.3f\r\n", dur);
rangeLine = strDup(buf);
} else { // subsessions have differing durations, so "a=range:" lines go there
rangeLine = strDup("");
}
char const* const sdpPrefixFmt =
"v=0\r\n"
"o=- %ld%06ld %d IN IP4 %s\r\n"
"s=%s\r\n"
"i=%s\r\n"
"t=0 0\r\n"
"a=tool:%s%s\r\n"
"a=type:broadcast\r\n"
"a=control:*\r\n"
"%s"
"%s"
"a=x-qt-text-nam:%s\r\n"
"a=x-qt-text-inf:%s\r\n"
"%s";
sdpLength += strlen(sdpPrefixFmt)
+ 20 + 6 + 20 + ipAddressStrSize
+ strlen(fDescriptionSDPString)
+ strlen(fInfoSDPString)
+ strlen(libNameStr) + strlen(libVersionStr)
+ strlen(sourceFilterLine)
+ strlen(rangeLine)
+ strlen(fDescriptionSDPString)
+ strlen(fInfoSDPString)
+ strlen(fMiscSDPLines);
sdp = new char[sdpLength];
if (sdp == NULL) break;
// Generate the SDP prefix (session-level lines):
sprintf(sdp, sdpPrefixFmt,
fCreationTime.tv_sec, fCreationTime.tv_usec, // o= <session id>
1, // o= <version>// (needs to change if params are modified)
ipAddressStr.val(), // o= <address>
fDescriptionSDPString, // s= <description>
fInfoSDPString, // i= <info>
libNameStr, libVersionStr, // a=tool:
sourceFilterLine, // a=source-filter: incl (if a SSM session)
rangeLine, // a=range: line
fDescriptionSDPString, // a=x-qt-text-nam: line
fInfoSDPString, // a=x-qt-text-inf: line
fMiscSDPLines); // miscellaneous session SDP lines (if any)
// Then, add the (media-level) lines foreachsubsession:
char* mediaSDP = sdp;
for (subsession = fSubsessionsHead; subsession != NULL;
subsession = subsession->fNext) {
mediaSDP += strlen(mediaSDP);
char const* sdpLines = subsession->sdpLines();
if (sdpLines != NULL) sprintf(mediaSDP, "%s", sdpLines);
}
} while (0);
delete[] rangeLine; delete[] sourceFilterLine;
return sdp;
}
////////// ServerMediaSessionIterator //////////
ServerMediaSubsessionIterator
::ServerMediaSubsessionIterator(ServerMediaSession& session)
: fOurSession(session) {
reset();
}
ServerMediaSubsessionIterator::~ServerMediaSubsessionIterator() {
}
ServerMediaSubsession* ServerMediaSubsessionIterator::next() {
ServerMediaSubsession* result = fNextPtr;
if (fNextPtr != NULL) fNextPtr = fNextPtr->fNext;
return result;
}
void ServerMediaSubsessionIterator::reset() {
fNextPtr = fOurSession.fSubsessionsHead;
}
- 【live555】推流者使用的ServerMediaSession类,以及如何产生SDP
- live555 源码分析:ServerMediaSession
- Live555学习之SDP信息的生成
- Live555笔记:创建SDP
- Live555源码分析@njzhujinhua[3]:ServerMediaSubsession与ServerMediaSession
- Live555通过SDP文本信息实现对RTP的接收
- 死锁的产生以及如何防止死锁的产生
- 死锁的产生以及如何避免死锁
- 电磁波是如何产生的以及分类
- OOM的产生以及如何避免
- QNX SDP 在window 下如何使用
- live555的使用
- live555 的学习使用
- live555传输Speex音频详解二:Speex 使用SDP及其它事项
- 如何使用eclipse产生的makefile
- C++中指针悬挂问题的产生以及如何避免
- lua如何产生随机数,以及需要注意的问题
- 多线程死锁的产生以及如何避免死锁
- 在网页上显示所在地天气预报的html代码
- Hadoop安装配置
- 未来超速列车概念 上下车无需停站
- FLSL2.0学习笔记(一)
- 定制jQuery File Upload为微博式单文件上传
- 【live555】推流者使用的ServerMediaSession类,以及如何产生SDP
- 黑马程序员-----StringBuffer 字符缓存区
- .NET学习路线
- javascript中的正则表达式学习
- unsigned int 与 int
- 神奇的懒惰正则表达式使用?
- 记事软件Lisa之PC端Java版1.0发布
- 八大排序算法及完整c代码—快速排序
- 后台乱码解决