ZLMediaKit源码阅读:Http接口分析

1060 篇文章 295 订阅

/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

功能

在这里插入图片描述

原理

  1. EventPollerPool::Instance()会初始化CPU核心数个eventPoller,eventPoller可以当成线程
  2. 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;
    }
}

  1. 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

在这里插入图片描述
在这里插入图片描述

/index/api/getRtpInfo

功能

在这里插入图片描述

评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值