/index/api/getApiList
功能
原理
//检查http参数中是否附带secret密钥的宏,127.0.0.1的ip不检查密钥
#define CHECK_SECRET() \
if(sender.get_peer_ip() != "127.0.0.1"){ \
CHECK_ARGS("secret"); \
if(api_secret != allArgs["secret"]){ \
throw AuthException("secret错误"); \
} \
}
static auto s_get_api_list = [](API_ARGS_MAP){
CHECK_SECRET();
for(auto &pr : s_map_api){
val["data"].append(pr.first);
}
};
//获取服务器api列表
//测试url http://127.0.0.1/index/api/getApiList
api_regist("/index/api/getApiList",[](API_ARGS_MAP){
s_get_api_list(API_ARGS_VALUE);
});
实现很简单,就是遍历s_map_api,然后回传
获取网络线程负载:/index/api/getThreadsLoad
功能
原理
EventPollerPool::Instance()
会初始化CPU核心数个eventPoller,eventPoller可以当成线程getExecutorDelay(...)
:相当于每个线程中都执行了一个滴答定时器任务。
- 当任务执行完了,会获取任务执行时间,然后放入delay_vec数组
- 当任务执行完了,还会调用callback((*delay_vec));,将delay_vec数组的地址传递给callback
void TaskExecutorGetterImp::getExecutorDelay(const function<void(const vector<int> &)> &callback) {
std::shared_ptr<vector<int> > delay_vec = std::make_shared<vector<int>>(_threads.size());
shared_ptr<void> finished(nullptr, [callback, delay_vec](void *) {
//此析构回调触发时,说明已执行完毕所有async任务
callback((*delay_vec));
});
int index = 0;
for (auto &th : _threads) {
std::shared_ptr<Ticker> delay_ticker = std::make_shared<Ticker>();
th->async([finished, delay_vec, index, delay_ticker]() {
(*delay_vec)[index] = (int) delay_ticker->elapsedTime();
}, false);
++index;
}
}
- callback干了写啥?
上面的callback就是getExecutorDelay(参数)中的参数,这个参数是一个函数指针
..... getExecutorDelay([invoker, headerOut](const vector<int> &vecDelay) {
...
}
callback根据vec生成一个回应通过invoker返回给对端
返回值
获取服务器配置:/index/api/getServerConfig
功能
原理
api_regist("/index/api/getServerConfig",[](API_ARGS_MAP){
CHECK_SECRET();
Value obj;
for (auto &pr : mINI::Instance()) {
obj[pr.first] = (string &) pr.second;
}
val["data"].append(obj);
});
原理:将配置文件 mINI::Instance()
生成一个json返回。具体返回操作由下面函数执行
static HttpApi toApi(const function<void(
toolkit::SockInfo &sender,
mediakit::HttpSession::KeyValue &headerOut,
const HttpAllArgs<ApiArgsType> &allArgs,
Json::Value &val)> &cb) {
return toApi([cb](
const mediakit::HttpSession::HttpResponseInvoker &invoker
) {
cb(sender, headerOut, allArgs, val);
invoker(200, headerOut, val.toStyledString());
});
}
设置服务器配置:/index/api/setServerConfig
功能
原理
有4步:
(1)修改内存中的配置项,就是修改一个map
auto &ini = mINI::Instance();
int changed = API::Success;
for (auto &pr : allArgs.getArgs()) {
if (ini.find(pr.first) == ini.end()) {
continue;
}
if (ini[pr.first] == pr.second) {
continue;
}
ini[pr.first] = pr.second;
//替换成功
++changed;
}
(2)广播有配置项修改了
NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastReloadConfig);
(3)将内存中的配置项写入到文件
ini.dumpFile(g_ini_file);
(4)回应对端
重启服务器:/index/api/restartServer
功能
原理
分为两步:
(1)它先是添加了一个延时任务,这个任务将会在1s之后自动执行
EventPollerPool::Instance().getPoller()->doDelayTask(1000,[](){ .....)
(2)回应客户端,具体返回操作由toAPI()
执行
val["msg"] = "服务器将在一秒后自动重启";
然后我们来看下延时任务做了什么:
//尝试正常退出
::kill(getpid(), SIGINT);
//3秒后强制退出
EventPollerPool::Instance().getPoller()->doDelayTask(3000,[](){
exit(0);
return 0;
});
return 0;
疑问:服务器是怎么重启的,没有看见重启的代码呀???
获取流列表:/index/api/getMediaList
原理
在解析完HTTP参数之后,它会调度下面的遍历函数
//获取所有MediaSource列表
MediaSource::for_each_media(.....);
我们来看下 MediaSource::for_each_media
做了写什么?
void MediaSource::for_each_media(const function<void(const Ptr &src)> &cb,
const string &schema,
const string &vhost,
const string &app,
const string &stream) {
deque<Ptr> src_list;
{
lock_guard<recursive_mutex> lock(s_media_source_mtx);
for_each_media_l(s_media_source_map, src_list, 筛选条件);
}
for (auto &src : src_list) {
cb(src);
}
}
它先从s_media_source_map根据一些筛选条件就是传入的schema、vhost、app、stream(这四个参数都是HTTP参数)选出一些符合条件的压入src_list
然后遍历src_list,对每一项调用cb(src),这个cb
就是 MediaSource::for_each_media(.....);
中的....
,也就是:
[&](const MediaSource::Ptr &media) {
val["data"].append(makeMediaSourceJson(*media));
}
相当于:
[&](src) {
val["data"].append(makeMediaSourceJson(*src));
}
功能是将src转为json然后返回回去,我们稍后再来分析makeMediaSourceJson,现在我们来看下s_media_source_map:
static MediaSource::SchemaVhostAppStreamMap s_media_source_map;
/**
* 媒体源,任何rtsp/rtmp的直播流都源自该对象
*/
class MediaSource: public TrackSource, public std::enable_shared_from_this<MediaSource> {
public:
static MediaSource * const NullMediaSource;
using Ptr = std::shared_ptr<MediaSource>;
using StreamMap = std::unordered_map<std::string/*strema_id*/, std::weak_ptr<MediaSource> >;
using AppStreamMap = std::unordered_map<std::string/*app*/, StreamMap>;
using VhostAppStreamMap = std::unordered_map<std::string/*vhost*/, AppStreamMap>;
using SchemaVhostAppStreamMap = std::unordered_map<std::string/*schema*/, VhostAppStreamMap>; //......
可以看出它本质是一个map,map套map,最里面是一个MediaSource类。那什么时候会往s_media_source_map写数据,什么时候会往s_media_source_map删除数据呢?
void MediaSource::regist() {
{
//减小互斥锁临界区
lock_guard<recursive_mutex> lock(s_media_source_mtx);
auto &ref = s_media_source_map[_schema][_vhost][_app][_stream_id];
......
}
}
void MediaSource::regist() {
{
//减小互斥锁临界区
lock_guard<recursive_mutex> lock(s_media_source_mtx);
auto &ref = s_media_source_map[_schema][_vhost][_app][_stream_id];
.....
/index/api/openRtpServer
功能
实现
创建RtpServer并插入到s_rtpServerMap
(1)检测参数
CHECK_SECRET();
CHECK_ARGS("port", "enable_tcp", "stream_id");
(2)检测stream_id
对应的服务器是否已经创建,如果创建了,就返回stream_id不存在
auto stream_id = allArgs["stream_id"];
lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
if (s_rtpServerMap.find(stream_id) != s_rtpServerMap.end()) {
//为了防止RtpProcess所有权限混乱的问题,不允许重复添加相同的stream_id
throw InvalidArgsException("该stream_id已存在");
}
(3)创建RtpServer:
RtpServer::Ptr server = std::make_shared<RtpServer>();
server->start(allArgs["port"], stream_id, allArgs["enable_tcp"].as<bool>(), "::",
allArgs["re_use_port"].as<bool>(), allArgs["ssrc"].as<uint32_t>());
server->setOnDetach([stream_id]() {
//设置rtp超时移除事件
lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
s_rtpServerMap.erase(stream_id);
});
//保存对象
s_rtpServerMap.emplace(stream_id, server);
(4)回复
val["port"] = server->getPort();
RtpServer::start 怎么实现
(1)创建rtp和rtcp的socket
(2)绑定端口
(3)设置UDP SOCKET读缓存
(4)创建TCP服务器
(5)创建UDP服务器
(6)设置清理资源时的回调函数
/index/api/closeRtpServer
功能
实现
从s_rtpServerMap中擦除stream_id对应的RtpServer
/index/api/listRtpServer
功能
实现
遍历s_rtpServerMap并返回
/index/api/getMediaList