“ LIVE555媒体服务器”是完整的RTSP服务器应用程序。 它可以流式传输几种媒体文件。详情可以回去看第一篇的1.5节。
mediaServer是一个完整RTSP服务器程序,所以我们还是从main函数开始看起,然后通过创建的类的对象,画一个类图,把前面学习过的类全部串联起来,这样才清楚明白很多。
我把打印的信息去掉
int main(int argc, char** argv) {
// Begin by setting up our usage environment:
TaskScheduler* scheduler = BasicTaskScheduler::createNew();
UsageEnvironment* env = BasicUsageEnvironment::createNew(*scheduler);
UserAuthenticationDatabase* authDB = NULL;
#ifdef ACCESS_CONTROL
// 要对RTSP服务器实施客户端访问控制,请执行以下操作:
authDB = new UserAuthenticationDatabase;
authDB->addUserRecord("username1", "password1");
// 用真实的字符串替换它们。对要允许访问服务器的每个<用户名>,<密码>重复上述操作。
#endif
// Create the RTSP server. Try first with the default port number (554),
// and then with the alternative port number (8554):
RTSPServer* rtspServer;
portNumBits rtspServerPortNum = 554;
rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
if (rtspServer == NULL) {
rtspServerPortNum = 8554;
rtspServer = DynamicRTSPServer::createNew(*env, rtspServerPortNum, authDB);
}
if (rtspServer == NULL) {
*env << "Failed to create RTSP server: " << env->getResultMsg() << "\n";
exit(1);
}
*env << "LIVE555 Media Server\n";
*env << "\tversion " << MEDIA_SERVER_VERSION_STRING
<< " (LIVE555 Streaming Media library version "
<< LIVEMEDIA_LIBRARY_VERSION_STRING << ").\n";
char* urlPrefix = rtspServer->rtspURLPrefix();
// 另外,尝试为HTTP上的RTSP隧道创建HTTP服务器。
// Try first with the default HTTP port (80), and then with the alternative HTTP
// port numbers (8000 and 8080).
if (rtspServer->setUpTunnelingOverHTTP(80) || rtspServer->setUpTunnelingOverHTTP(8000) || rtspServer->setUpTunnelingOverHTTP(8080)) {
*env << "(We use port " << rtspServer->httpServerPortNum() << " for optional RTSP-over-HTTP tunneling, or for HTTP live streaming (for indexed Transport Stream files only).)\n";
} else {
*env << "(RTSP-over-HTTP tunneling is not available.)\n";
}
env->taskScheduler().doEventLoop(); // does not return
return 0; // only to prevent compiler warning
}
main函数比较简单,做的事情不多:
基础部分,前面几节已经分析过了,现在提供一个类图,根据这个类图就会很好的记住,前面分析的类,类太多了,还是需要用图的方式来记忆,这个靠谱。
这个图内容比较多,我们前面就是讲了这么多个类,这里也刚好回忆回忆。
这次学聪明了,先搞类图,通过类图分析。
我们现在一个一个看。
DynamicRTSPServer*
DynamicRTSPServer::createNew(UsageEnvironment& env, Port ourPort,
UserAuthenticationDatabase* authDatabase,
unsigned reclamationTestSeconds) {
int ourSocket = setUpOurSocket(env, ourPort); //创建一个socket,这个等下再看
if (ourSocket == -1) return NULL; // 判断这个socket是否有效
//调用构造函数
return new DynamicRTSPServer(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds);
}
reclamationTestSeconds:有一个默认参数的=65
DynamicRTSPServer::DynamicRTSPServer(UsageEnvironment& env, int ourSocket,
Port ourPort,
UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds)
: RTSPServerSupportingHTTPStreaming(env, ourSocket, ourPort, authDatabase, reclamationTestSeconds) {
}
这个构造函数就是往父类的构造函数执行,一路执行。
RTSPServerSupportingHTTPStreaming
::RTSPServerSupportingHTTPStreaming(UsageEnvironment& env, int ourSocket, Port rtspPort,
UserAuthenticationDatabase* authDatabase, unsigned reclamationTestSeconds)
: RTSPServer(env, ourSocket, rtspPort, authDatabase, reclamationTestSeconds) {
}
由调用父类的构造函数。
RTSPServer::RTSPServer(UsageEnvironment& env,
int ourSocket, Port ourPort,
UserAuthenticationDatabase* authDatabase,
unsigned reclamationSeconds)
: GenericMediaServer(env, ourSocket, ourPort, reclamationSeconds),
fHTTPServerSocket(-1), fHTTPServerPort(0),
fClientConnectionsForHTTPTunneling(NULL), // will get created if needed
fTCPStreamingDatabase(HashTable::create(ONE_WORD_HASH_KEYS)),
fPendingRegisterOrDeregisterRequests(HashTable::create(ONE_WORD_HASH_KEYS)),
fRegisterOrDeregisterRequestCounter(0), fAuthDB(authDatabase), fAllowStreamingRTPOverTCP(True) {
}
这个构造函数也调用了父类的构造函数,通过也初始化了一些变量。
fHTTPServerSocket:
fHTTPServerPort:
fClientConnectionsForHTTPTunneling:
fTCPStreamingDatabase:
fPendingRegisterOrDeregisterRequests:
fRegisterOrDeregisterRequestCounter:
fAuthDB:访问权限变量
fAllowStreamingRTPOverTCP:
GenericMediaServer
::GenericMediaServer(UsageEnvironment& env, int ourSocket, Port ourPort,
unsigned reclamationSeconds)
: Medium(env),
fServerSocket(ourSocket), fServerPort(ourPort), fReclamationSeconds(reclamationSeconds),
fServerMediaSessions(HashTable::create(STRING_HASH_KEYS)),
fClientConnections(HashTable::create(ONE_WORD_HASH_KEYS)),
fClientSessions(HashTable::create(STRING_HASH_KEYS)),
fPreviousClientSessionId(0)
{
ignoreSigPipeOnSocket(fServerSocket); // so that clients on the same host that are killed don't also kill us
// Arrange to handle connections from others:
env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, incomingConnectionHandler, this);
}
int fServerSocket:
Port fServerPort:
unsigned fReclamationSeconds:
HashTable* fServerMediaSessions:
HashTable* fClientConnections:
HashTable* fClientSessions:
u_int32_t fPreviousClientSessionId:
env.taskScheduler().turnOnBackgroundReadHandling(fServerSocket, incomingConnectionHandler, this);
要监听的 Server socket 及该 socket 上的 I/O 事件处理程序,注册给任务调度器。事件循环中检测到 socket 上出现 I/O 事件时,该处理程序会被调用到。注册的事件处理程序为 GenericMediaServer::incomingConnectionHandler() 。
(会把用到底层这些数据结构的都画在一张图中,这样比较简单明了)
这是一个单独的函数,网络有关的,可以看一下其他博客
void ignoreSigPipeOnSocket(int socketNum) {
#ifdef USE_SIGNALS
#ifdef SO_NOSIGPIPE
int set_option = 1;
setsockopt(socketNum, SOL_SOCKET, SO_NOSIGPIPE, &set_option, sizeof set_option);
#else
signal(SIGPIPE, SIG_IGN);
#endif
#endif
}
已发现一个问题,对一个对端已经关闭的socket调用两次write, 第二次将会生成SIGPIPE信号, 该信号默认结束进程.
https://blog.csdn.net/weixin_33841722/article/details/92183262
https://blog.csdn.net/xinguan1267/article/details/17357093
Medium::Medium(UsageEnvironment& env)
: fEnviron(env), fNextTask(NULL) {
// First generate a name for the new medium:
MediaLookupTable::ourMedia(env)->generateNewName(fMediumName, mediumNameMaxLen);
env.setResultMsg(fMediumName);
// Then add it to our table:
MediaLookupTable::ourMedia(env)->addNew(this, fMediumName);
}
Medium类中也保存了一个env的变量,不知道是做啥用的,先不管,往下走。
UsageEnvironment& fEnviron:
char fMediumName[mediumNameMaxLen]: //存储名字和编号的
TaskToken fNextTask:
接下来就分析一下Medium的构造函数。
先看第一个函数,
MediaLookupTable* MediaLookupTable::ourMedia(UsageEnvironment& env) {
_Tables* ourTables = _Tables::getOurTables(env);
if (ourTables->mediaTable == NULL) {
// 创建一个新表来记录要在此环境中创建的媒体:
ourTables->mediaTable = new MediaLookupTable(env); //这是构造函数
}
return ourTables->mediaTable;
}
这里就有一个中间类,_Table类,我现在也不知道这个类是干嘛的,不过先分析,分析到后面,自然就知道了。
class _Tables {
public:
static _Tables* getOurTables(UsageEnvironment& env, Boolean createIfNotPresent = True);
// returns a pointer to a "_Tables" structure (creating it if necessary)
void reclaimIfPossible();
// used to delete ourselves when we're no longer used
MediaLookupTable* mediaTable;
void* socketTable;
protected:
_Tables(UsageEnvironment& env);
virtual ~_Tables();
private:
UsageEnvironment& fEnv;
};
这个_Table类也是挺简单的,就是有一个fEnv的变量,这个_Table也存储了一个env变量。
下面我们来看看实际操作的函数:
_Tables* _Tables::getOurTables(UsageEnvironment& env, Boolean createIfNotPresent) {
if (env.liveMediaPriv == NULL && createIfNotPresent) {
env.liveMediaPriv = new _Tables(env);
}
return (_Tables*)(env.liveMediaPriv);
}
void _Tables::reclaimIfPossible() {
if (mediaTable == NULL && socketTable == NULL) {
fEnv.liveMediaPriv = NULL;
delete this;
}
}
_Tables::_Tables(UsageEnvironment& env)
: mediaTable(NULL), socketTable(NULL), fEnv(env) {
}
_Tables::~_Tables() {
}
这些函数也挺简单的,就要注意两个变量:
env.liveMediaPriv:这个是env中的一个指针,当初不知道是咋回事,现在知道在这里赋值了,所以要记住。
UsageEnvironment& fEnv:还有一个就是他自己的env,这个env也是上层一层一层传下来的,变量是同一个。
MediaLookupTable mediaTable*:
void socketTable*:
不知道不觉中来了到构造函数。这个MediaLookupTable是负责管理Medium的。
MediaLookupTable::MediaLookupTable(UsageEnvironment& env)
: fEnv(env), fTable(HashTable::create(STRING_HASH_KEYS)), fNameGenerator(0) {
}
UsageEnvironment& fEnv:还是env那个变量
HashTable fTable*: 创建了一个哈希表
unsigned fNameGenerator: liveMedium名字的编号
可以看看MediaLookupTable这个类定义:
// 通过字符串名称查找Medium的数据结构.
// (它仅用于实现“ Medium”,但是如果开发人员希望使用它来迭代我们创建的整个“ Medium”对象集,则在此处将其可见.)
class MediaLookupTable {
public:
static MediaLookupTable* ourMedia(UsageEnvironment& env);
HashTable const& getTable() { return *fTable; }
protected:
MediaLookupTable(UsageEnvironment& env);
virtual ~MediaLookupTable();
private:
friend class Medium;
Medium* lookup(char const* name) const;
// Returns NULL if none already exists
void addNew(Medium* medium, char* mediumName);
void remove(char const* name);
void generateNewName(char* mediumName, unsigned maxLen);
private:
UsageEnvironment& fEnv;
HashTable* fTable;
unsigned fNameGenerator; // liveMedium名字的编号
};
void MediaLookupTable::generateNewName(char* mediumName,
unsigned /*maxLen*/) {
// 我们确实应该在这里使用snprintf(),但并非所有系统都拥有它
sprintf(mediumName, "liveMedia%d", fNameGenerator++);
}
这个函数比较简单,就是用sprintf设置一个名字。
然后我们来到最后一个函数
void MediaLookupTable::addNew(Medium* medium, char* mediumName) {
fTable->Add(mediumName, (void*)medium);
}
这个函数就是把medium对象的指针,和medium名字存储到哈希表中。
在需要用到的时候使用。
虽然把这个创建的过程追踪下来了,但是这一层一层的继承,关系太多了,变量也太多了,所以还不是太明白,不过先这样分析,以后多分析分析,可能就会顿悟了,加油。