You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1798 lines
69KB

  1. /*
  2. * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
  3. *
  4. * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
  5. *
  6. * Use of this source code is governed by MIT license that can be found in the
  7. * LICENSE file in the root of the source tree. All contributing project authors
  8. * may be found in the AUTHORS file in the root of the source tree.
  9. */
  10. #include <sys/stat.h>
  11. #include <math.h>
  12. #include <signal.h>
  13. #include <functional>
  14. #include <unordered_map>
  15. #include "Util/util.h"
  16. #include "Util/logger.h"
  17. #include "Util/onceToken.h"
  18. #include "Util/NoticeCenter.h"
  19. #include "Util/File.h"
  20. #ifdef ENABLE_MYSQL
  21. #include "Util/SqlPool.h"
  22. #endif //ENABLE_MYSQL
  23. #include "Common/config.h"
  24. #include "Common/MediaSource.h"
  25. #include "Http/HttpRequester.h"
  26. #include "Http/HttpSession.h"
  27. #include "Network/TcpServer.h"
  28. #include "Network/UdpServer.h"
  29. #include "Player/PlayerProxy.h"
  30. #include "Pusher/PusherProxy.h"
  31. #include "Util/MD5.h"
  32. #include "WebApi.h"
  33. #include "WebHook.h"
  34. #include "Thread/WorkThreadPool.h"
  35. #include "Rtp/RtpSelector.h"
  36. #include "FFmpegSource.h"
  37. #if defined(ENABLE_RTPPROXY)
  38. #include "Rtp/RtpServer.h"
  39. #endif
  40. #ifdef ENABLE_WEBRTC
  41. #include "../webrtc/WebRtcPlayer.h"
  42. #include "../webrtc/WebRtcPusher.h"
  43. #include "../webrtc/WebRtcEchoTest.h"
  44. #endif
  45. #ifdef _WIN32
  46. #include <io.h>
  47. #include <iostream>
  48. #include <tchar.h>
  49. #endif // _WIN32
  50. #if defined(ENABLE_VERSION)
  51. #include "version.h"
  52. #endif
  53. using namespace std;
  54. using namespace Json;
  55. using namespace toolkit;
  56. using namespace mediakit;
  57. namespace API {
  58. #define API_FIELD "api."
  59. const string kApiDebug = API_FIELD"apiDebug";
  60. const string kSecret = API_FIELD"secret";
  61. const string kSnapRoot = API_FIELD"snapRoot";
  62. const string kDefaultSnap = API_FIELD"defaultSnap";
  63. static onceToken token([]() {
  64. mINI::Instance()[kApiDebug] = "1";
  65. mINI::Instance()[kSecret] = "035c73f7-bb6b-4889-a715-d9eb2d1925cc";
  66. mINI::Instance()[kSnapRoot] = "./www/snap/";
  67. mINI::Instance()[kDefaultSnap] = "./www/logo.png";
  68. });
  69. }//namespace API
  70. using HttpApi = function<void(const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, SockInfo &sender)>;
  71. //http api列表
  72. static map<string, HttpApi> s_map_api;
  73. static void responseApi(const Json::Value &res, const HttpSession::HttpResponseInvoker &invoker){
  74. GET_CONFIG(string, charSet, Http::kCharSet);
  75. HttpSession::KeyValue headerOut;
  76. headerOut["Content-Type"] = string("application/json; charset=") + charSet;
  77. invoker(200, headerOut, res.toStyledString());
  78. };
  79. static void responseApi(int code, const string &msg, const HttpSession::HttpResponseInvoker &invoker){
  80. Json::Value res;
  81. res["code"] = code;
  82. res["msg"] = msg;
  83. responseApi(res, invoker);
  84. }
  85. static ApiArgsType getAllArgs(const Parser &parser);
  86. static HttpApi toApi(const function<void(API_ARGS_MAP_ASYNC)> &cb) {
  87. return [cb](const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, SockInfo &sender) {
  88. GET_CONFIG(string, charSet, Http::kCharSet);
  89. HttpSession::KeyValue headerOut;
  90. headerOut["Content-Type"] = string("application/json; charset=") + charSet;
  91. Json::Value val;
  92. val["code"] = API::Success;
  93. //参数解析成map
  94. auto args = getAllArgs(parser);
  95. cb(sender, headerOut, HttpAllArgs<decltype(args)>(parser, args), val, invoker);
  96. };
  97. }
  98. static HttpApi toApi(const function<void(API_ARGS_MAP)> &cb) {
  99. return toApi([cb](API_ARGS_MAP_ASYNC) {
  100. cb(API_ARGS_VALUE);
  101. invoker(200, headerOut, val.toStyledString());
  102. });
  103. }
  104. static HttpApi toApi(const function<void(API_ARGS_JSON_ASYNC)> &cb) {
  105. return [cb](const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, SockInfo &sender) {
  106. GET_CONFIG(string, charSet, Http::kCharSet);
  107. HttpSession::KeyValue headerOut;
  108. headerOut["Content-Type"] = string("application/json; charset=") + charSet;
  109. Json::Value val;
  110. val["code"] = API::Success;
  111. if (parser["Content-Type"].find("application/json") == string::npos) {
  112. throw InvalidArgsException("该接口只支持json格式的请求");
  113. }
  114. //参数解析成json对象然后处理
  115. Json::Value args;
  116. Json::Reader reader;
  117. reader.parse(parser.Content(), args);
  118. cb(sender, headerOut, HttpAllArgs<decltype(args)>(parser, args), val, invoker);
  119. };
  120. }
  121. static HttpApi toApi(const function<void(API_ARGS_JSON)> &cb) {
  122. return toApi([cb](API_ARGS_JSON_ASYNC) {
  123. cb(API_ARGS_VALUE);
  124. invoker(200, headerOut, val.toStyledString());
  125. });
  126. }
  127. static HttpApi toApi(const function<void(API_ARGS_STRING_ASYNC)> &cb) {
  128. return [cb](const Parser &parser, const HttpSession::HttpResponseInvoker &invoker, SockInfo &sender) {
  129. GET_CONFIG(string, charSet, Http::kCharSet);
  130. HttpSession::KeyValue headerOut;
  131. headerOut["Content-Type"] = string("application/json; charset=") + charSet;
  132. Json::Value val;
  133. val["code"] = API::Success;
  134. cb(sender, headerOut, HttpAllArgs<string>(parser, (string &)parser.Content()), val, invoker);
  135. };
  136. }
  137. static HttpApi toApi(const function<void(API_ARGS_STRING)> &cb) {
  138. return toApi([cb](API_ARGS_STRING_ASYNC) {
  139. cb(API_ARGS_VALUE);
  140. invoker(200, headerOut, val.toStyledString());
  141. });
  142. }
  143. void api_regist(const string &api_path, const function<void(API_ARGS_MAP)> &func) {
  144. s_map_api.emplace(api_path, toApi(func));
  145. }
  146. void api_regist(const string &api_path, const function<void(API_ARGS_MAP_ASYNC)> &func) {
  147. s_map_api.emplace(api_path, toApi(func));
  148. }
  149. void api_regist(const string &api_path, const function<void(API_ARGS_JSON)> &func) {
  150. s_map_api.emplace(api_path, toApi(func));
  151. }
  152. void api_regist(const string &api_path, const function<void(API_ARGS_JSON_ASYNC)> &func) {
  153. s_map_api.emplace(api_path, toApi(func));
  154. }
  155. void api_regist(const string &api_path, const function<void(API_ARGS_STRING)> &func){
  156. s_map_api.emplace(api_path, toApi(func));
  157. }
  158. void api_regist(const string &api_path, const function<void(API_ARGS_STRING_ASYNC)> &func){
  159. s_map_api.emplace(api_path, toApi(func));
  160. }
  161. //获取HTTP请求中url参数、content参数
  162. static ApiArgsType getAllArgs(const Parser &parser) {
  163. ApiArgsType allArgs;
  164. if (parser["Content-Type"].find("application/x-www-form-urlencoded") == 0) {
  165. auto contentArgs = parser.parseArgs(parser.Content());
  166. for (auto &pr : contentArgs) {
  167. allArgs[pr.first] = HttpSession::urlDecode(pr.second);
  168. }
  169. } else if (parser["Content-Type"].find("application/json") == 0) {
  170. try {
  171. stringstream ss(parser.Content());
  172. Value jsonArgs;
  173. ss >> jsonArgs;
  174. auto keys = jsonArgs.getMemberNames();
  175. for (auto key = keys.begin(); key != keys.end(); ++key) {
  176. allArgs[*key] = jsonArgs[*key].asString();
  177. }
  178. } catch (std::exception &ex) {
  179. WarnL << ex.what();
  180. }
  181. } else if (!parser["Content-Type"].empty()) {
  182. WarnL << "invalid Content-Type:" << parser["Content-Type"];
  183. }
  184. for (auto &pr : parser.getUrlArgs()) {
  185. allArgs[pr.first] = pr.second;
  186. }
  187. return allArgs;
  188. }
  189. extern uint64_t getTotalMemUsage();
  190. extern uint64_t getTotalMemBlock();
  191. extern uint64_t getThisThreadMemUsage();
  192. extern uint64_t getThisThreadMemBlock();
  193. extern std::vector<size_t> getBlockTypeSize();
  194. extern uint64_t getTotalMemBlockByType(int type);
  195. extern uint64_t getThisThreadMemBlockByType(int type) ;
  196. static void *web_api_tag = nullptr;
  197. static inline void addHttpListener(){
  198. GET_CONFIG(bool, api_debug, API::kApiDebug);
  199. //注册监听kBroadcastHttpRequest事件
  200. NoticeCenter::Instance().addListener(&web_api_tag, Broadcast::kBroadcastHttpRequest, [](BroadcastHttpRequestArgs) {
  201. auto it = s_map_api.find(parser.Url());
  202. if (it == s_map_api.end()) {
  203. return;
  204. }
  205. //该api已被消费
  206. consumed = true;
  207. if(api_debug){
  208. auto newInvoker = [invoker, parser](int code, const HttpSession::KeyValue &headerOut, const HttpBody::Ptr &body) {
  209. //body默认为空
  210. ssize_t size = 0;
  211. if (body && body->remainSize()) {
  212. //有body,获取body大小
  213. size = body->remainSize();
  214. }
  215. LogContextCapture log(getLogger(), LDebug, __FILE__, "http api debug", __LINE__);
  216. log << "\r\n# request:\r\n" << parser.Method() << " " << parser.FullUrl() << "\r\n";
  217. log << "# header:\r\n";
  218. for (auto &pr : parser.getHeader()) {
  219. log << pr.first << " : " << pr.second << "\r\n";
  220. }
  221. auto &content = parser.Content();
  222. log << "# content:\r\n" << (content.size() > 4 * 1024 ? content.substr(0, 4 * 1024) : content) << "\r\n";
  223. if (size > 0 && size < 4 * 1024) {
  224. auto response = body->readData(size);
  225. log << "# response:\r\n" << response->data() << "\r\n";
  226. invoker(code, headerOut, response);
  227. } else {
  228. log << "# response size:" << size << "\r\n";
  229. invoker(code, headerOut, body);
  230. }
  231. };
  232. ((HttpSession::HttpResponseInvoker &) invoker) = newInvoker;
  233. }
  234. try {
  235. it->second(parser, invoker, sender);
  236. } catch (ApiRetException &ex) {
  237. responseApi(ex.code(), ex.what(), invoker);
  238. }
  239. #ifdef ENABLE_MYSQL
  240. catch(SqlException &ex){
  241. responseApi(API::SqlFailed, StrPrinter << "操作数据库失败:" << ex.what() << ":" << ex.getSql(), invoker);
  242. }
  243. #endif// ENABLE_MYSQL
  244. catch (std::exception &ex) {
  245. responseApi(API::Exception, ex.what(), invoker);
  246. }
  247. });
  248. }
  249. //拉流代理器列表
  250. static unordered_map<string, PlayerProxy::Ptr> s_proxyMap;
  251. static recursive_mutex s_proxyMapMtx;
  252. //推流代理器列表
  253. static unordered_map<string, PusherProxy::Ptr> s_proxyPusherMap;
  254. static recursive_mutex s_proxyPusherMapMtx;
  255. //FFmpeg拉流代理器列表
  256. static unordered_map<string, FFmpegSource::Ptr> s_ffmpegMap;
  257. static recursive_mutex s_ffmpegMapMtx;
  258. #if defined(ENABLE_RTPPROXY)
  259. //rtp服务器列表
  260. static unordered_map<string, RtpServer::Ptr> s_rtpServerMap;
  261. static recursive_mutex s_rtpServerMapMtx;
  262. #endif
  263. static inline string getProxyKey(const string &vhost, const string &app, const string &stream) {
  264. return vhost + "/" + app + "/" + stream;
  265. }
  266. static inline string getPusherKey(const string &schema, const string &vhost, const string &app, const string &stream,
  267. const string &dst_url) {
  268. return schema + "/" + vhost + "/" + app + "/" + stream + "/" + MD5(dst_url).hexdigest();
  269. }
  270. static void fillSockInfo(Value& val, SockInfo* info) {
  271. val["peer_ip"] = info->get_peer_ip();
  272. val["peer_port"] = info->get_peer_port();
  273. val["local_port"] = info->get_local_port();
  274. val["local_ip"] = info->get_local_ip();
  275. val["identifier"] = info->getIdentifier();
  276. }
  277. Value makeMediaSourceJson(MediaSource &media){
  278. Value item;
  279. item["schema"] = media.getSchema();
  280. item[VHOST_KEY] = media.getVhost();
  281. item["app"] = media.getApp();
  282. item["stream"] = media.getId();
  283. item["createStamp"] = (Json::UInt64) media.getCreateStamp();
  284. item["aliveSecond"] = (Json::UInt64) media.getAliveSecond();
  285. item["bytesSpeed"] = media.getBytesSpeed();
  286. item["readerCount"] = media.readerCount();
  287. item["totalReaderCount"] = media.totalReaderCount();
  288. item["originType"] = (int) media.getOriginType();
  289. item["originTypeStr"] = getOriginTypeString(media.getOriginType());
  290. item["originUrl"] = media.getOriginUrl();
  291. item["isRecordingMP4"] = media.isRecording(Recorder::type_mp4);
  292. item["isRecordingHLS"] = media.isRecording(Recorder::type_hls);
  293. auto originSock = media.getOriginSock();
  294. if (originSock) {
  295. fillSockInfo(item["originSock"], originSock.get());
  296. } else {
  297. item["originSock"] = Json::nullValue;
  298. }
  299. //getLossRate有线程安全问题;使用getMediaInfo接口才能获取丢包率;getMediaList接口将忽略丢包率
  300. auto current_thread = false;
  301. try { current_thread = media.getOwnerPoller()->isCurrentThread();} catch (...) {}
  302. float last_loss = -1;
  303. for(auto &track : media.getTracks(false)){
  304. Value obj;
  305. auto codec_type = track->getTrackType();
  306. obj["codec_id"] = track->getCodecId();
  307. obj["codec_id_name"] = track->getCodecName();
  308. obj["ready"] = track->ready();
  309. obj["codec_type"] = codec_type;
  310. if (current_thread) {
  311. //rtp推流只有一个统计器,但是可能有多个track,如果短时间多次获取间隔丢包率,第二次会获取为-1
  312. auto loss = media.getLossRate(codec_type);
  313. if (loss == -1) {
  314. loss = last_loss;
  315. } else {
  316. last_loss = loss;
  317. }
  318. obj["loss"] = loss;
  319. }
  320. switch(codec_type){
  321. case TrackAudio : {
  322. auto audio_track = dynamic_pointer_cast<AudioTrack>(track);
  323. obj["sample_rate"] = audio_track->getAudioSampleRate();
  324. obj["channels"] = audio_track->getAudioChannel();
  325. obj["sample_bit"] = audio_track->getAudioSampleBit();
  326. break;
  327. }
  328. case TrackVideo : {
  329. auto video_track = dynamic_pointer_cast<VideoTrack>(track);
  330. obj["width"] = video_track->getVideoWidth();
  331. obj["height"] = video_track->getVideoHeight();
  332. obj["fps"] = round(video_track->getVideoFps());
  333. break;
  334. }
  335. default:
  336. break;
  337. }
  338. item["tracks"].append(obj);
  339. }
  340. return item;
  341. }
  342. #if defined(ENABLE_RTPPROXY)
  343. uint16_t openRtpServer(uint16_t local_port, const string &stream_id, int tcp_mode, const string &local_ip, bool re_use_port, uint32_t ssrc) {
  344. lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
  345. if (s_rtpServerMap.find(stream_id) != s_rtpServerMap.end()) {
  346. //为了防止RtpProcess所有权限混乱的问题,不允许重复添加相同的stream_id
  347. return 0;
  348. }
  349. RtpServer::Ptr server = std::make_shared<RtpServer>();
  350. server->start(local_port, stream_id, (RtpServer::TcpMode)tcp_mode, local_ip.c_str(), re_use_port, ssrc);
  351. server->setOnDetach([stream_id]() {
  352. //设置rtp超时移除事件
  353. lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
  354. s_rtpServerMap.erase(stream_id);
  355. });
  356. //保存对象
  357. s_rtpServerMap.emplace(stream_id, server);
  358. //回复json
  359. return server->getPort();
  360. }
  361. void connectRtpServer(const string &stream_id, const string &dst_url, uint16_t dst_port, const function<void(const SockException &ex)> &cb) {
  362. lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
  363. auto it = s_rtpServerMap.find(stream_id);
  364. if (it == s_rtpServerMap.end()) {
  365. cb(SockException(Err_other, "未找到rtp服务"));
  366. return;
  367. }
  368. it->second->connectToServer(dst_url, dst_port, cb);
  369. }
  370. bool closeRtpServer(const string &stream_id) {
  371. lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
  372. auto it = s_rtpServerMap.find(stream_id);
  373. if (it == s_rtpServerMap.end()) {
  374. return false;
  375. }
  376. auto server = it->second;
  377. s_rtpServerMap.erase(it);
  378. return true;
  379. }
  380. #endif
  381. void getStatisticJson(const function<void(Value &val)> &cb) {
  382. auto obj = std::make_shared<Value>(objectValue);
  383. auto &val = *obj;
  384. val["MediaSource"] = (Json::UInt64)(ObjectStatistic<MediaSource>::count());
  385. val["MultiMediaSourceMuxer"] = (Json::UInt64)(ObjectStatistic<MultiMediaSourceMuxer>::count());
  386. val["TcpServer"] = (Json::UInt64)(ObjectStatistic<TcpServer>::count());
  387. val["TcpSession"] = (Json::UInt64)(ObjectStatistic<TcpSession>::count());
  388. val["UdpServer"] = (Json::UInt64)(ObjectStatistic<UdpServer>::count());
  389. val["UdpSession"] = (Json::UInt64)(ObjectStatistic<UdpSession>::count());
  390. val["TcpClient"] = (Json::UInt64)(ObjectStatistic<TcpClient>::count());
  391. val["Socket"] = (Json::UInt64)(ObjectStatistic<Socket>::count());
  392. val["FrameImp"] = (Json::UInt64)(ObjectStatistic<FrameImp>::count());
  393. val["Frame"] = (Json::UInt64)(ObjectStatistic<Frame>::count());
  394. val["Buffer"] = (Json::UInt64)(ObjectStatistic<Buffer>::count());
  395. val["BufferRaw"] = (Json::UInt64)(ObjectStatistic<BufferRaw>::count());
  396. val["BufferLikeString"] = (Json::UInt64)(ObjectStatistic<BufferLikeString>::count());
  397. val["BufferList"] = (Json::UInt64)(ObjectStatistic<BufferList>::count());
  398. val["RtpPacket"] = (Json::UInt64)(ObjectStatistic<RtpPacket>::count());
  399. val["RtmpPacket"] = (Json::UInt64)(ObjectStatistic<RtmpPacket>::count());
  400. #ifdef ENABLE_MEM_DEBUG
  401. auto bytes = getTotalMemUsage();
  402. val["totalMemUsage"] = (Json::UInt64) bytes;
  403. val["totalMemUsageMB"] = (int) (bytes / 1024 / 1024);
  404. val["totalMemBlock"] = (Json::UInt64) getTotalMemBlock();
  405. static auto block_type_size = getBlockTypeSize();
  406. {
  407. int i = 0;
  408. string str;
  409. size_t last = 0;
  410. for (auto sz : block_type_size) {
  411. str.append(to_string(last) + "~" + to_string(sz) + ":" + to_string(getTotalMemBlockByType(i++)) + ";");
  412. last = sz;
  413. }
  414. str.pop_back();
  415. val["totalMemBlockTypeCount"] = str;
  416. }
  417. auto thread_size = EventPollerPool::Instance().getExecutorSize() + WorkThreadPool::Instance().getExecutorSize();
  418. std::shared_ptr<vector<Value> > thread_mem_info = std::make_shared<vector<Value> >(thread_size);
  419. shared_ptr<void> finished(nullptr, [thread_mem_info, cb, obj](void *) {
  420. for (auto &val : *thread_mem_info) {
  421. (*obj)["threadMem"].append(val);
  422. }
  423. //触发回调
  424. cb(*obj);
  425. });
  426. auto pos = 0;
  427. auto lam0 = [&](TaskExecutor &executor) {
  428. auto &val = (*thread_mem_info)[pos++];
  429. executor.async([finished, &val]() {
  430. auto bytes = getThisThreadMemUsage();
  431. val["threadName"] = getThreadName();
  432. val["threadMemUsage"] = (Json::UInt64) bytes;
  433. val["threadMemUsageMB"] = (Json::UInt64) (bytes / 1024 / 1024);
  434. val["threadMemBlock"] = (Json::UInt64) getThisThreadMemBlock();
  435. {
  436. int i = 0;
  437. string str;
  438. size_t last = 0;
  439. for (auto sz : block_type_size) {
  440. str.append(to_string(last) + "~" + to_string(sz) + ":" + to_string(getThisThreadMemBlockByType(i++)) + ";");
  441. last = sz;
  442. }
  443. str.pop_back();
  444. val["threadMemBlockTypeCount"] = str;
  445. }
  446. });
  447. };
  448. auto lam1 = [lam0](const TaskExecutor::Ptr &executor) {
  449. lam0(*executor);
  450. };
  451. EventPollerPool::Instance().for_each(lam1);
  452. WorkThreadPool::Instance().for_each(lam1);
  453. #else
  454. cb(*obj);
  455. #endif
  456. }
  457. void addStreamProxy(const string &vhost, const string &app, const string &stream, const string &url, int retry_count,
  458. const ProtocolOption &option, int rtp_type, float timeout_sec,
  459. const function<void(const SockException &ex, const string &key)> &cb) {
  460. auto key = getProxyKey(vhost, app, stream);
  461. lock_guard<recursive_mutex> lck(s_proxyMapMtx);
  462. if (s_proxyMap.find(key) != s_proxyMap.end()) {
  463. //已经在拉流了
  464. cb(SockException(Err_success), key);
  465. return;
  466. }
  467. //添加拉流代理
  468. auto player = std::make_shared<PlayerProxy>(vhost, app, stream, option, retry_count ? retry_count : -1);
  469. s_proxyMap[key] = player;
  470. //指定RTP over TCP(播放rtsp时有效)
  471. (*player)[Client::kRtpType] = rtp_type;
  472. if (timeout_sec > 0.1) {
  473. //播放握手超时时间
  474. (*player)[Client::kTimeoutMS] = timeout_sec * 1000;
  475. }
  476. //开始播放,如果播放失败或者播放中止,将会自动重试若干次,默认一直重试
  477. player->setPlayCallbackOnce([cb, key](const SockException &ex) {
  478. if (ex) {
  479. lock_guard<recursive_mutex> lck(s_proxyMapMtx);
  480. s_proxyMap.erase(key);
  481. }
  482. cb(ex, key);
  483. });
  484. //被主动关闭拉流
  485. player->setOnClose([key](const SockException &ex) {
  486. lock_guard<recursive_mutex> lck(s_proxyMapMtx);
  487. s_proxyMap.erase(key);
  488. });
  489. player->play(url);
  490. };
  491. template <typename Type>
  492. static void getArgsValue(const HttpAllArgs<ApiArgsType> &allArgs, const string &key, Type &value) {
  493. auto val = allArgs[key];
  494. if (!val.empty()) {
  495. value = (Type)val;
  496. }
  497. }
  498. /**
  499. * 安装api接口
  500. * 所有api都支持GET和POST两种方式
  501. * POST方式参数支持application/json和application/x-www-form-urlencoded方式
  502. */
  503. void installWebApi() {
  504. addHttpListener();
  505. GET_CONFIG(string,api_secret,API::kSecret);
  506. //获取线程负载
  507. //测试url http://127.0.0.1/index/api/getThreadsLoad
  508. api_regist("/index/api/getThreadsLoad",[](API_ARGS_MAP_ASYNC){
  509. EventPollerPool::Instance().getExecutorDelay([invoker, headerOut](const vector<int> &vecDelay) {
  510. Value val;
  511. auto vec = EventPollerPool::Instance().getExecutorLoad();
  512. int i = API::Success;
  513. for (auto load : vec) {
  514. Value obj(objectValue);
  515. obj["load"] = load;
  516. obj["delay"] = vecDelay[i++];
  517. val["data"].append(obj);
  518. }
  519. val["code"] = API::Success;
  520. invoker(200, headerOut, val.toStyledString());
  521. });
  522. });
  523. //获取后台工作线程负载
  524. //测试url http://127.0.0.1/index/api/getWorkThreadsLoad
  525. api_regist("/index/api/getWorkThreadsLoad", [](API_ARGS_MAP_ASYNC){
  526. WorkThreadPool::Instance().getExecutorDelay([invoker, headerOut](const vector<int> &vecDelay) {
  527. Value val;
  528. auto vec = WorkThreadPool::Instance().getExecutorLoad();
  529. int i = 0;
  530. for (auto load : vec) {
  531. Value obj(objectValue);
  532. obj["load"] = load;
  533. obj["delay"] = vecDelay[i++];
  534. val["data"].append(obj);
  535. }
  536. val["code"] = API::Success;
  537. invoker(200, headerOut, val.toStyledString());
  538. });
  539. });
  540. //获取服务器配置
  541. //测试url http://127.0.0.1/index/api/getServerConfig
  542. api_regist("/index/api/getServerConfig",[](API_ARGS_MAP){
  543. CHECK_SECRET();
  544. Value obj;
  545. for (auto &pr : mINI::Instance()) {
  546. obj[pr.first] = (string &) pr.second;
  547. }
  548. val["data"].append(obj);
  549. });
  550. //设置服务器配置
  551. //测试url(比如关闭http api调试) http://127.0.0.1/index/api/setServerConfig?api.apiDebug=0
  552. //你也可以通过http post方式传参,可以通过application/x-www-form-urlencoded或application/json方式传参
  553. api_regist("/index/api/setServerConfig",[](API_ARGS_MAP){
  554. CHECK_SECRET();
  555. auto &ini = mINI::Instance();
  556. int changed = API::Success;
  557. for (auto &pr : allArgs.getArgs()) {
  558. if (ini.find(pr.first) == ini.end()) {
  559. #if 1
  560. //没有这个key
  561. continue;
  562. #else
  563. // 新增配置选项,为了动态添加多个ffmpeg cmd 模板
  564. ini[pr.first] = pr.second;
  565. // 防止changed变化
  566. continue;
  567. #endif
  568. }
  569. if (ini[pr.first] == pr.second) {
  570. continue;
  571. }
  572. ini[pr.first] = pr.second;
  573. //替换成功
  574. ++changed;
  575. }
  576. if (changed > 0) {
  577. NoticeCenter::Instance().emitEvent(Broadcast::kBroadcastReloadConfig);
  578. ini.dumpFile(g_ini_file);
  579. }
  580. val["changed"] = changed;
  581. });
  582. static auto s_get_api_list = [](API_ARGS_MAP){
  583. CHECK_SECRET();
  584. for(auto &pr : s_map_api){
  585. val["data"].append(pr.first);
  586. }
  587. };
  588. //获取服务器api列表
  589. //测试url http://127.0.0.1/index/api/getApiList
  590. api_regist("/index/api/getApiList",[](API_ARGS_MAP){
  591. s_get_api_list(API_ARGS_VALUE);
  592. });
  593. //获取服务器api列表
  594. //测试url http://127.0.0.1/index/
  595. api_regist("/index/",[](API_ARGS_MAP){
  596. s_get_api_list(API_ARGS_VALUE);
  597. });
  598. #if !defined(_WIN32)
  599. //重启服务器,只有Daemon方式才能重启,否则是直接关闭!
  600. //测试url http://127.0.0.1/index/api/restartServer
  601. api_regist("/index/api/restartServer",[](API_ARGS_MAP){
  602. CHECK_SECRET();
  603. EventPollerPool::Instance().getPoller()->doDelayTask(1000,[](){
  604. //尝试正常退出
  605. ::kill(getpid(), SIGINT);
  606. //3秒后强制退出
  607. EventPollerPool::Instance().getPoller()->doDelayTask(3000,[](){
  608. exit(0);
  609. return 0;
  610. });
  611. return 0;
  612. });
  613. val["msg"] = "服务器将在一秒后自动重启";
  614. });
  615. #else
  616. //增加Windows下的重启代码
  617. api_regist("/index/api/restartServer", [](API_ARGS_MAP) {
  618. CHECK_SECRET();
  619. //创建重启批处理脚本文件
  620. FILE *pf;
  621. errno_t err = ::_wfopen_s(&pf, L"RestartServer.cmd", L"w"); //“w”如果该文件存在,其内容将被覆盖
  622. if (err == 0) {
  623. char szExeName[1024];
  624. char drive[_MAX_DRIVE] = { 0 };
  625. char dir[_MAX_DIR] = { 0 };
  626. char fname[_MAX_FNAME] = { 0 };
  627. char ext[_MAX_EXT] = { 0 };
  628. char exeName[_MAX_FNAME] = { 0 };
  629. GetModuleFileNameA(NULL, szExeName, 1024); //获取进程的全路径
  630. _splitpath(szExeName, drive, dir, fname, ext);
  631. strcpy(exeName, fname);
  632. strcat(exeName, ext);
  633. fprintf(pf, "@echo off\ntaskkill /f /im %s\nstart \"\" \"%s\"\ndel %%0", exeName, szExeName);
  634. fclose(pf);
  635. // 1秒后执行创建的批处理脚本
  636. EventPollerPool::Instance().getPoller()->doDelayTask(1000, []() {
  637. STARTUPINFO si;
  638. PROCESS_INFORMATION pi;
  639. ZeroMemory(&si, sizeof si);
  640. ZeroMemory(&pi, sizeof pi);
  641. si.cb = sizeof si;
  642. si.dwFlags = STARTF_USESHOWWINDOW;
  643. si.wShowWindow = SW_HIDE;
  644. TCHAR winSysDir[1024];
  645. ZeroMemory(winSysDir, sizeof winSysDir);
  646. GetSystemDirectory(winSysDir, 1024);
  647. TCHAR appName[1024];
  648. ZeroMemory(appName, sizeof appName);
  649. _stprintf(appName, "%s\\cmd.exe", winSysDir);
  650. BOOL bRet = CreateProcess(appName, " /c RestartServer.cmd", NULL, NULL, FALSE, 0, NULL, NULL, &si, &pi);
  651. if (bRet == FALSE) {
  652. int err = GetLastError();
  653. cout << endl << "无法执行重启操作,错误代码:" << err << endl;
  654. }
  655. WaitForSingleObject(pi.hProcess, INFINITE);
  656. CloseHandle(pi.hProcess);
  657. CloseHandle(pi.hThread);
  658. return 0;
  659. });
  660. val["msg"] = "服务器将在一秒后自动重启";
  661. } else {
  662. val["msg"] = "创建重启脚本文件失败";
  663. val["code"] = API::OtherFailed;
  664. }
  665. });
  666. #endif//#if !defined(_WIN32)
  667. //获取流列表,可选筛选参数
  668. //测试url0(获取所有流) http://127.0.0.1/index/api/getMediaList
  669. //测试url1(获取虚拟主机为"__defaultVost__"的流) http://127.0.0.1/index/api/getMediaList?vhost=__defaultVost__
  670. //测试url2(获取rtsp类型的流) http://127.0.0.1/index/api/getMediaList?schema=rtsp
  671. api_regist("/index/api/getMediaList",[](API_ARGS_MAP){
  672. CHECK_SECRET();
  673. //获取所有MediaSource列表
  674. MediaSource::for_each_media([&](const MediaSource::Ptr &media) {
  675. val["data"].append(makeMediaSourceJson(*media));
  676. }, allArgs["schema"], allArgs["vhost"], allArgs["app"], allArgs["stream"]);
  677. });
  678. //测试url http://127.0.0.1/index/api/isMediaOnline?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
  679. api_regist("/index/api/isMediaOnline",[](API_ARGS_MAP){
  680. CHECK_SECRET();
  681. CHECK_ARGS("schema","vhost","app","stream");
  682. val["online"] = (bool) (MediaSource::find(allArgs["schema"],allArgs["vhost"],allArgs["app"],allArgs["stream"]));
  683. });
  684. //获取媒体流播放器列表
  685. //测试url http://127.0.0.1/index/api/getMediaPlayerList?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
  686. api_regist("/index/api/getMediaPlayerList",[](API_ARGS_MAP_ASYNC){
  687. CHECK_SECRET();
  688. CHECK_ARGS("schema", "vhost", "app", "stream");
  689. auto src = MediaSource::find(allArgs["schema"], allArgs["vhost"], allArgs["app"], allArgs["stream"]);
  690. if (!src) {
  691. throw ApiRetException("can not find the stream", API::NotFound);
  692. }
  693. src->getPlayerList(
  694. [=](const std::list<std::shared_ptr<void>> &info_list) mutable {
  695. val["code"] = API::Success;
  696. auto &data = val["data"];
  697. data = Value(arrayValue);
  698. for (auto &info : info_list) {
  699. auto obj = static_pointer_cast<Value>(info);
  700. data.append(std::move(*obj));
  701. }
  702. invoker(200, headerOut, val.toStyledString());
  703. },
  704. [](std::shared_ptr<void> &&info) -> std::shared_ptr<void> {
  705. auto obj = std::make_shared<Value>();
  706. auto session = static_pointer_cast<Session>(info);
  707. fillSockInfo(*obj, session.get());
  708. (*obj)["typeid"] = toolkit::demangle(typeid(*session).name());
  709. return obj;
  710. });
  711. });
  712. //测试url http://127.0.0.1/index/api/getMediaInfo?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs
  713. api_regist("/index/api/getMediaInfo",[](API_ARGS_MAP_ASYNC){
  714. CHECK_SECRET();
  715. CHECK_ARGS("schema","vhost","app","stream");
  716. auto src = MediaSource::find(allArgs["schema"],allArgs["vhost"],allArgs["app"],allArgs["stream"]);
  717. if(!src){
  718. throw ApiRetException("can not find the stream", API::NotFound);
  719. }
  720. src->getOwnerPoller()->async([=]() mutable {
  721. auto val = makeMediaSourceJson(*src);
  722. val["code"] = API::Success;
  723. invoker(200, headerOut, val.toStyledString());
  724. });
  725. });
  726. //主动关断流,包括关断拉流、推流
  727. //测试url http://127.0.0.1/index/api/close_stream?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
  728. api_regist("/index/api/close_stream",[](API_ARGS_MAP_ASYNC){
  729. CHECK_SECRET();
  730. CHECK_ARGS("schema","vhost","app","stream");
  731. //踢掉推流器
  732. auto src = MediaSource::find(allArgs["schema"],
  733. allArgs["vhost"],
  734. allArgs["app"],
  735. allArgs["stream"]);
  736. if (!src) {
  737. throw ApiRetException("can not find the stream", API::NotFound);
  738. }
  739. bool force = allArgs["force"].as<bool>();
  740. src->getOwnerPoller()->async([=]() mutable {
  741. bool flag = src->close(force);
  742. val["result"] = flag ? 0 : -1;
  743. val["msg"] = flag ? "success" : "close failed";
  744. val["code"] = flag ? API::Success : API::OtherFailed;
  745. invoker(200, headerOut, val.toStyledString());
  746. });
  747. });
  748. //批量主动关断流,包括关断拉流、推流
  749. //测试url http://127.0.0.1/index/api/close_streams?schema=rtsp&vhost=__defaultVhost__&app=live&stream=obs&force=1
  750. api_regist("/index/api/close_streams",[](API_ARGS_MAP){
  751. CHECK_SECRET();
  752. //筛选命中个数
  753. int count_hit = 0;
  754. int count_closed = 0;
  755. list<MediaSource::Ptr> media_list;
  756. MediaSource::for_each_media([&](const MediaSource::Ptr &media) {
  757. ++count_hit;
  758. media_list.emplace_back(media);
  759. }, allArgs["schema"], allArgs["vhost"], allArgs["app"], allArgs["stream"]);
  760. bool force = allArgs["force"].as<bool>();
  761. for (auto &media : media_list) {
  762. if (media->close(force)) {
  763. ++count_closed;
  764. }
  765. }
  766. val["count_hit"] = count_hit;
  767. val["count_closed"] = count_closed;
  768. });
  769. //获取所有Session列表信息
  770. //可以根据本地端口和远端ip来筛选
  771. //测试url(筛选某端口下的tcp会话) http://127.0.0.1/index/api/getAllSession?local_port=1935
  772. api_regist("/index/api/getAllSession",[](API_ARGS_MAP){
  773. CHECK_SECRET();
  774. Value jsession;
  775. uint16_t local_port = allArgs["local_port"].as<uint16_t>();
  776. string peer_ip = allArgs["peer_ip"];
  777. SessionMap::Instance().for_each_session([&](const string &id,const Session::Ptr &session){
  778. if(local_port != 0 && local_port != session->get_local_port()){
  779. return;
  780. }
  781. if(!peer_ip.empty() && peer_ip != session->get_peer_ip()){
  782. return;
  783. }
  784. fillSockInfo(jsession, session.get());
  785. jsession["id"] = id;
  786. jsession["typeid"] = toolkit::demangle(typeid(*session).name());
  787. val["data"].append(jsession);
  788. });
  789. });
  790. //断开tcp连接,比如说可以断开rtsp、rtmp播放器等
  791. //测试url http://127.0.0.1/index/api/kick_session?id=123456
  792. api_regist("/index/api/kick_session",[](API_ARGS_MAP){
  793. CHECK_SECRET();
  794. CHECK_ARGS("id");
  795. //踢掉tcp会话
  796. auto session = SessionMap::Instance().get(allArgs["id"]);
  797. if(!session){
  798. throw ApiRetException("can not find the target",API::OtherFailed);
  799. }
  800. session->safeShutdown();
  801. });
  802. //批量断开tcp连接,比如说可以断开rtsp、rtmp播放器等
  803. //测试url http://127.0.0.1/index/api/kick_sessions?local_port=1935
  804. api_regist("/index/api/kick_sessions",[](API_ARGS_MAP){
  805. CHECK_SECRET();
  806. uint16_t local_port = allArgs["local_port"].as<uint16_t>();
  807. string peer_ip = allArgs["peer_ip"];
  808. size_t count_hit = 0;
  809. list<Session::Ptr> session_list;
  810. SessionMap::Instance().for_each_session([&](const string &id,const Session::Ptr &session){
  811. if(local_port != 0 && local_port != session->get_local_port()){
  812. return;
  813. }
  814. if(!peer_ip.empty() && peer_ip != session->get_peer_ip()){
  815. return;
  816. }
  817. session_list.emplace_back(session);
  818. ++count_hit;
  819. });
  820. for(auto &session : session_list){
  821. session->safeShutdown();
  822. }
  823. val["count_hit"] = (Json::UInt64)count_hit;
  824. });
  825. static auto addStreamPusherProxy = [](const string &schema,
  826. const string &vhost,
  827. const string &app,
  828. const string &stream,
  829. const string &url,
  830. int retry_count,
  831. int rtp_type,
  832. float timeout_sec,
  833. const function<void(const SockException &ex, const string &key)> &cb) {
  834. auto key = getPusherKey(schema, vhost, app, stream, url);
  835. auto src = MediaSource::find(schema, vhost, app, stream);
  836. if (!src) {
  837. cb(SockException(Err_other, "can not find the source stream"), key);
  838. return;
  839. }
  840. lock_guard<recursive_mutex> lck(s_proxyPusherMapMtx);
  841. if (s_proxyPusherMap.find(key) != s_proxyPusherMap.end()) {
  842. //已经在推流了
  843. cb(SockException(Err_success), key);
  844. return;
  845. }
  846. //添加推流代理
  847. PusherProxy::Ptr pusher(new PusherProxy(src, retry_count ? retry_count : -1));
  848. s_proxyPusherMap[key] = pusher;
  849. //指定RTP over TCP(播放rtsp时有效)
  850. (*pusher)[Client::kRtpType] = rtp_type;
  851. if (timeout_sec > 0.1) {
  852. //推流握手超时时间
  853. (*pusher)[Client::kTimeoutMS] = timeout_sec * 1000;
  854. }
  855. //开始推流,如果推流失败或者推流中止,将会自动重试若干次,默认一直重试
  856. pusher->setPushCallbackOnce([cb, key, url](const SockException &ex) {
  857. if (ex) {
  858. WarnL << "Push " << url << " failed, key: " << key << ", err: " << ex.what();
  859. lock_guard<recursive_mutex> lck(s_proxyPusherMapMtx);
  860. s_proxyPusherMap.erase(key);
  861. }
  862. cb(ex, key);
  863. });
  864. //被主动关闭推流
  865. pusher->setOnClose([key, url](const SockException &ex) {
  866. WarnL << "Push " << url << " failed, key: " << key << ", err: " << ex.what();
  867. lock_guard<recursive_mutex> lck(s_proxyPusherMapMtx);
  868. s_proxyPusherMap.erase(key);
  869. });
  870. pusher->publish(url);
  871. };
  872. //动态添加rtsp/rtmp推流代理
  873. //测试url http://127.0.0.1/index/api/addStreamPusherProxy?schema=rtmp&vhost=__defaultVhost__&app=proxy&stream=0&dst_url=rtmp://127.0.0.1/live/obs
  874. api_regist("/index/api/addStreamPusherProxy", [](API_ARGS_MAP_ASYNC) {
  875. CHECK_SECRET();
  876. CHECK_ARGS("schema", "vhost", "app", "stream", "dst_url");
  877. auto dst_url = allArgs["dst_url"];
  878. addStreamPusherProxy(allArgs["schema"],
  879. allArgs["vhost"],
  880. allArgs["app"],
  881. allArgs["stream"],
  882. allArgs["dst_url"],
  883. allArgs["retry_count"],
  884. allArgs["rtp_type"],
  885. allArgs["timeout_sec"],
  886. [invoker, val, headerOut, dst_url](const SockException &ex, const string &key) mutable {
  887. if (ex) {
  888. val["code"] = API::OtherFailed;
  889. val["msg"] = ex.what();
  890. } else {
  891. val["data"]["key"] = key;
  892. InfoL << "Publish success, please play with player:" << dst_url;
  893. }
  894. invoker(200, headerOut, val.toStyledString());
  895. });
  896. });
  897. //关闭推流代理
  898. //测试url http://127.0.0.1/index/api/delStreamPusherProxy?key=__defaultVhost__/proxy/0
  899. api_regist("/index/api/delStreamPusherProxy", [](API_ARGS_MAP) {
  900. CHECK_SECRET();
  901. CHECK_ARGS("key");
  902. lock_guard<recursive_mutex> lck(s_proxyPusherMapMtx);
  903. val["data"]["flag"] = s_proxyPusherMap.erase(allArgs["key"]) == 1;
  904. });
  905. //动态添加rtsp/rtmp拉流代理
  906. //测试url http://127.0.0.1/index/api/addStreamProxy?vhost=__defaultVhost__&app=proxy&enable_rtsp=1&enable_rtmp=1&stream=0&url=rtmp://127.0.0.1/live/obs
  907. api_regist("/index/api/addStreamProxy",[](API_ARGS_MAP_ASYNC){
  908. CHECK_SECRET();
  909. CHECK_ARGS("vhost","app","stream","url");
  910. ProtocolOption option(allArgs);
  911. addStreamProxy(allArgs["vhost"],
  912. allArgs["app"],
  913. allArgs["stream"],
  914. allArgs["url"],
  915. allArgs["retry_count"],
  916. option,
  917. allArgs["rtp_type"],
  918. allArgs["timeout_sec"],
  919. [invoker,val,headerOut](const SockException &ex,const string &key) mutable{
  920. if (ex) {
  921. val["code"] = API::OtherFailed;
  922. val["msg"] = ex.what();
  923. } else {
  924. val["data"]["key"] = key;
  925. }
  926. invoker(200, headerOut, val.toStyledString());
  927. });
  928. });
  929. //关闭拉流代理
  930. //测试url http://127.0.0.1/index/api/delStreamProxy?key=__defaultVhost__/proxy/0
  931. api_regist("/index/api/delStreamProxy",[](API_ARGS_MAP){
  932. CHECK_SECRET();
  933. CHECK_ARGS("key");
  934. lock_guard<recursive_mutex> lck(s_proxyMapMtx);
  935. val["data"]["flag"] = s_proxyMap.erase(allArgs["key"]) == 1;
  936. });
  937. static auto addFFmpegSource = [](const string &ffmpeg_cmd_key,
  938. const string &src_url,
  939. const string &dst_url,
  940. int timeout_ms,
  941. bool enable_hls,
  942. bool enable_mp4,
  943. const function<void(const SockException &ex, const string &key)> &cb) {
  944. auto key = MD5(dst_url).hexdigest();
  945. lock_guard<decltype(s_ffmpegMapMtx)> lck(s_ffmpegMapMtx);
  946. if (s_ffmpegMap.find(key) != s_ffmpegMap.end()) {
  947. //已经在拉流了
  948. cb(SockException(Err_success), key);
  949. return;
  950. }
  951. FFmpegSource::Ptr ffmpeg = std::make_shared<FFmpegSource>();
  952. s_ffmpegMap[key] = ffmpeg;
  953. ffmpeg->setOnClose([key]() {
  954. lock_guard<decltype(s_ffmpegMapMtx)> lck(s_ffmpegMapMtx);
  955. s_ffmpegMap.erase(key);
  956. });
  957. ffmpeg->setupRecordFlag(enable_hls, enable_mp4);
  958. ffmpeg->play(ffmpeg_cmd_key, src_url, dst_url, timeout_ms, [cb, key](const SockException &ex) {
  959. if (ex) {
  960. lock_guard<decltype(s_ffmpegMapMtx)> lck(s_ffmpegMapMtx);
  961. s_ffmpegMap.erase(key);
  962. }
  963. cb(ex, key);
  964. });
  965. };
  966. //动态添加rtsp/rtmp拉流代理
  967. //测试url http://127.0.0.1/index/api/addFFmpegSource?src_url=http://live.hkstv.hk.lxdns.com/live/hks2/playlist.m3u8&dst_url=rtmp://127.0.0.1/live/hks2&timeout_ms=10000
  968. api_regist("/index/api/addFFmpegSource",[](API_ARGS_MAP_ASYNC){
  969. CHECK_SECRET();
  970. CHECK_ARGS("src_url","dst_url","timeout_ms");
  971. auto src_url = allArgs["src_url"];
  972. auto dst_url = allArgs["dst_url"];
  973. int timeout_ms = allArgs["timeout_ms"];
  974. auto enable_hls = allArgs["enable_hls"].as<int>();
  975. auto enable_mp4 = allArgs["enable_mp4"].as<int>();
  976. addFFmpegSource(allArgs["ffmpeg_cmd_key"], src_url, dst_url, timeout_ms, enable_hls, enable_mp4,
  977. [invoker, val, headerOut](const SockException &ex, const string &key) mutable{
  978. if (ex) {
  979. val["code"] = API::OtherFailed;
  980. val["msg"] = ex.what();
  981. } else {
  982. val["data"]["key"] = key;
  983. }
  984. invoker(200, headerOut, val.toStyledString());
  985. });
  986. });
  987. //关闭拉流代理
  988. //测试url http://127.0.0.1/index/api/delFFmepgSource?key=key
  989. api_regist("/index/api/delFFmpegSource",[](API_ARGS_MAP){
  990. CHECK_SECRET();
  991. CHECK_ARGS("key");
  992. lock_guard<decltype(s_ffmpegMapMtx)> lck(s_ffmpegMapMtx);
  993. val["data"]["flag"] = s_ffmpegMap.erase(allArgs["key"]) == 1;
  994. });
  995. //新增http api下载可执行程序文件接口
  996. //测试url http://127.0.0.1/index/api/downloadBin
  997. api_regist("/index/api/downloadBin",[](API_ARGS_MAP_ASYNC){
  998. CHECK_SECRET();
  999. invoker.responseFile(allArgs.getParser().getHeader(),StrCaseMap(),exePath());
  1000. });
  1001. #if defined(ENABLE_RTPPROXY)
  1002. api_regist("/index/api/getRtpInfo",[](API_ARGS_MAP){
  1003. CHECK_SECRET();
  1004. CHECK_ARGS("stream_id");
  1005. auto process = RtpSelector::Instance().getProcess(allArgs["stream_id"], false);
  1006. if (!process) {
  1007. val["exist"] = false;
  1008. return;
  1009. }
  1010. val["exist"] = true;
  1011. fillSockInfo(val, process.get());
  1012. });
  1013. api_regist("/index/api/openRtpServer",[](API_ARGS_MAP){
  1014. CHECK_SECRET();
  1015. CHECK_ARGS("port", "stream_id");
  1016. auto stream_id = allArgs["stream_id"];
  1017. auto tcp_mode = allArgs["tcp_mode"].as<int>();
  1018. if (allArgs["enable_tcp"].as<int>() && !tcp_mode) {
  1019. //兼容老版本请求,新版本去除enable_tcp参数并新增tcp_mode参数
  1020. tcp_mode = 1;
  1021. }
  1022. auto port = openRtpServer(allArgs["port"], stream_id, tcp_mode, "::", allArgs["re_use_port"].as<bool>(),
  1023. allArgs["ssrc"].as<uint32_t>());
  1024. if (port == 0) {
  1025. throw InvalidArgsException("该stream_id已存在");
  1026. }
  1027. //回复json
  1028. val["port"] = port;
  1029. });
  1030. api_regist("/index/api/connectRtpServer", [](API_ARGS_MAP_ASYNC) {
  1031. CHECK_SECRET();
  1032. CHECK_ARGS("stream_id", "dst_url", "dst_port");
  1033. connectRtpServer(
  1034. allArgs["stream_id"], allArgs["dst_url"], allArgs["dst_port"],
  1035. [val, headerOut, invoker](const SockException &ex) mutable {
  1036. if (ex) {
  1037. val["code"] = API::OtherFailed;
  1038. val["msg"] = ex.what();
  1039. }
  1040. invoker(200, headerOut, val.toStyledString());
  1041. });
  1042. });
  1043. api_regist("/index/api/closeRtpServer",[](API_ARGS_MAP){
  1044. CHECK_SECRET();
  1045. CHECK_ARGS("stream_id");
  1046. if(!closeRtpServer(allArgs["stream_id"])){
  1047. val["hit"] = 0;
  1048. return;
  1049. }
  1050. val["hit"] = 1;
  1051. });
  1052. api_regist("/index/api/listRtpServer",[](API_ARGS_MAP){
  1053. CHECK_SECRET();
  1054. lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
  1055. for (auto &pr : s_rtpServerMap) {
  1056. Value obj;
  1057. obj["stream_id"] = pr.first;
  1058. obj["port"] = pr.second->getPort();
  1059. val["data"].append(obj);
  1060. }
  1061. });
  1062. api_regist("/index/api/startSendRtp",[](API_ARGS_MAP_ASYNC){
  1063. CHECK_SECRET();
  1064. CHECK_ARGS("vhost", "app", "stream", "ssrc", "dst_url", "dst_port", "is_udp");
  1065. auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"], allArgs["from_mp4"].as<int>());
  1066. if (!src) {
  1067. throw ApiRetException("can not find the source stream", API::NotFound);
  1068. }
  1069. MediaSourceEvent::SendRtpArgs args;
  1070. args.passive = false;
  1071. args.dst_url = allArgs["dst_url"];
  1072. args.dst_port = allArgs["dst_port"];
  1073. args.ssrc = allArgs["ssrc"];
  1074. args.is_udp = allArgs["is_udp"];
  1075. args.src_port = allArgs["src_port"];
  1076. args.pt = allArgs["pt"].empty() ? 96 : allArgs["pt"].as<int>();
  1077. args.use_ps = allArgs["use_ps"].empty() ? true : allArgs["use_ps"].as<bool>();
  1078. args.only_audio = allArgs["only_audio"].as<bool>();
  1079. args.udp_rtcp_timeout = allArgs["udp_rtcp_timeout"];
  1080. TraceL << "startSendRtp, pt " << int(args.pt) << " ps " << args.use_ps << " audio " << args.only_audio;
  1081. src->getOwnerPoller()->async([=]() mutable {
  1082. src->startSendRtp(args, [val, headerOut, invoker](uint16_t local_port, const SockException &ex) mutable {
  1083. if (ex) {
  1084. val["code"] = API::OtherFailed;
  1085. val["msg"] = ex.what();
  1086. }
  1087. val["local_port"] = local_port;
  1088. invoker(200, headerOut, val.toStyledString());
  1089. });
  1090. });
  1091. });
  1092. api_regist("/index/api/startSendRtpPassive",[](API_ARGS_MAP_ASYNC){
  1093. CHECK_SECRET();
  1094. CHECK_ARGS("vhost", "app", "stream", "ssrc");
  1095. auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"], allArgs["from_mp4"].as<int>());
  1096. if (!src) {
  1097. throw ApiRetException("can not find the source stream", API::NotFound);
  1098. }
  1099. MediaSourceEvent::SendRtpArgs args;
  1100. args.passive = true;
  1101. args.ssrc = allArgs["ssrc"];
  1102. args.is_udp = false;
  1103. args.src_port = allArgs["src_port"];
  1104. args.pt = allArgs["pt"].empty() ? 96 : allArgs["pt"].as<int>();
  1105. args.use_ps = allArgs["use_ps"].empty() ? true : allArgs["use_ps"].as<bool>();
  1106. args.only_audio = allArgs["only_audio"].as<bool>();
  1107. TraceL << "startSendRtpPassive, pt " << int(args.pt) << " ps " << args.use_ps << " audio " << args.only_audio;
  1108. src->getOwnerPoller()->async([=]() mutable {
  1109. src->startSendRtp(args, [val, headerOut, invoker](uint16_t local_port, const SockException &ex) mutable {
  1110. if (ex) {
  1111. val["code"] = API::OtherFailed;
  1112. val["msg"] = ex.what();
  1113. }
  1114. val["local_port"] = local_port;
  1115. invoker(200, headerOut, val.toStyledString());
  1116. });
  1117. });
  1118. });
  1119. api_regist("/index/api/stopSendRtp",[](API_ARGS_MAP_ASYNC){
  1120. CHECK_SECRET();
  1121. CHECK_ARGS("vhost", "app", "stream");
  1122. auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"]);
  1123. if (!src) {
  1124. throw ApiRetException("can not find the stream", API::NotFound);
  1125. }
  1126. src->getOwnerPoller()->async([=]() mutable {
  1127. // ssrc如果为空,关闭全部
  1128. if (!src->stopSendRtp(allArgs["ssrc"])) {
  1129. val["code"] = API::OtherFailed;
  1130. val["msg"] = "stopSendRtp failed";
  1131. invoker(200, headerOut, val.toStyledString());
  1132. return;
  1133. }
  1134. invoker(200, headerOut, val.toStyledString());
  1135. });
  1136. });
  1137. api_regist("/index/api/pauseRtpCheck", [](API_ARGS_MAP) {
  1138. CHECK_SECRET();
  1139. CHECK_ARGS("stream_id");
  1140. //只是暂停流的检查,流媒体服务器做为流负载服务,收流就转发,RTSP/RTMP有自己暂停协议
  1141. auto rtp_process = RtpSelector::Instance().getProcess(allArgs["stream_id"], false);
  1142. if (rtp_process) {
  1143. rtp_process->setStopCheckRtp(true);
  1144. } else {
  1145. val["code"] = API::NotFound;
  1146. }
  1147. });
  1148. api_regist("/index/api/resumeRtpCheck", [](API_ARGS_MAP) {
  1149. CHECK_SECRET();
  1150. CHECK_ARGS("stream_id");
  1151. auto rtp_process = RtpSelector::Instance().getProcess(allArgs["stream_id"], false);
  1152. if (rtp_process) {
  1153. rtp_process->setStopCheckRtp(false);
  1154. } else {
  1155. val["code"] = API::NotFound;
  1156. }
  1157. });
  1158. #endif//ENABLE_RTPPROXY
  1159. // 开始录制hls或MP4
  1160. api_regist("/index/api/startRecord",[](API_ARGS_MAP_ASYNC){
  1161. CHECK_SECRET();
  1162. CHECK_ARGS("type","vhost","app","stream");
  1163. auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"] );
  1164. if (!src) {
  1165. throw ApiRetException("can not find the stream", API::NotFound);
  1166. }
  1167. src->getOwnerPoller()->async([=]() mutable {
  1168. auto result = src->setupRecord((Recorder::type)allArgs["type"].as<int>(), true, allArgs["customized_path"], allArgs["max_second"].as<size_t>());
  1169. val["result"] = result;
  1170. val["code"] = result ? API::Success : API::OtherFailed;
  1171. val["msg"] = result ? "success" : "start record failed";
  1172. invoker(200, headerOut, val.toStyledString());
  1173. });
  1174. });
  1175. //设置录像流播放速度
  1176. api_regist("/index/api/setRecordSpeed", [](API_ARGS_MAP_ASYNC) {
  1177. CHECK_SECRET();
  1178. CHECK_ARGS("schema", "vhost", "app", "stream", "speed");
  1179. auto src = MediaSource::find(allArgs["schema"],
  1180. allArgs["vhost"],
  1181. allArgs["app"],
  1182. allArgs["stream"]);
  1183. if (!src) {
  1184. throw ApiRetException("can not find the stream", API::NotFound);
  1185. }
  1186. auto speed = allArgs["speed"].as<float>();
  1187. src->getOwnerPoller()->async([=]() mutable {
  1188. bool flag = src->speed(speed);
  1189. val["result"] = flag ? 0 : -1;
  1190. val["msg"] = flag ? "success" : "set failed";
  1191. val["code"] = flag ? API::Success : API::OtherFailed;
  1192. invoker(200, headerOut, val.toStyledString());
  1193. });
  1194. });
  1195. api_regist("/index/api/seekRecordStamp", [](API_ARGS_MAP_ASYNC) {
  1196. CHECK_SECRET();
  1197. CHECK_ARGS("schema", "vhost", "app", "stream", "stamp");
  1198. auto src = MediaSource::find(allArgs["schema"],
  1199. allArgs["vhost"],
  1200. allArgs["app"],
  1201. allArgs["stream"]);
  1202. if (!src) {
  1203. throw ApiRetException("can not find the stream", API::NotFound);
  1204. }
  1205. auto stamp = allArgs["stamp"].as<size_t>();
  1206. src->getOwnerPoller()->async([=]() mutable {
  1207. bool flag = src->seekTo(stamp);
  1208. val["result"] = flag ? 0 : -1;
  1209. val["msg"] = flag ? "success" : "seek failed";
  1210. val["code"] = flag ? API::Success : API::OtherFailed;
  1211. invoker(200, headerOut, val.toStyledString());
  1212. });
  1213. });
  1214. // 停止录制hls或MP4
  1215. api_regist("/index/api/stopRecord",[](API_ARGS_MAP_ASYNC){
  1216. CHECK_SECRET();
  1217. CHECK_ARGS("type","vhost","app","stream");
  1218. auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"] );
  1219. if (!src) {
  1220. throw ApiRetException("can not find the stream", API::NotFound);
  1221. }
  1222. auto type = (Recorder::type)allArgs["type"].as<int>();
  1223. src->getOwnerPoller()->async([=]() mutable {
  1224. auto result = src->setupRecord(type, false, "", 0);
  1225. val["result"] = result;
  1226. val["code"] = result ? API::Success : API::OtherFailed;
  1227. val["msg"] = result ? "success" : "stop record failed";
  1228. invoker(200, headerOut, val.toStyledString());
  1229. });
  1230. });
  1231. // 获取hls或MP4录制状态
  1232. api_regist("/index/api/isRecording",[](API_ARGS_MAP_ASYNC){
  1233. CHECK_SECRET();
  1234. CHECK_ARGS("type","vhost","app","stream");
  1235. auto src = MediaSource::find(allArgs["vhost"], allArgs["app"], allArgs["stream"]);
  1236. if (!src) {
  1237. throw ApiRetException("can not find the stream", API::NotFound);
  1238. }
  1239. auto type = (Recorder::type)allArgs["type"].as<int>();
  1240. src->getOwnerPoller()->async([=]() mutable {
  1241. val["status"] = src->isRecording(type);
  1242. invoker(200, headerOut, val.toStyledString());
  1243. });
  1244. });
  1245. // 删除录像文件夹
  1246. // http://127.0.0.1/index/api/deleteRecordDirectroy?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01-01
  1247. api_regist("/index/api/deleteRecordDirectory", [](API_ARGS_MAP) {
  1248. CHECK_SECRET();
  1249. CHECK_ARGS("vhost", "app", "stream");
  1250. auto record_path = Recorder::getRecordPath(Recorder::type_mp4, allArgs["vhost"], allArgs["app"], allArgs["stream"], allArgs["customized_path"]);
  1251. auto period = allArgs["period"];
  1252. record_path = record_path + period + "/";
  1253. int result = File::delete_file(record_path.data());
  1254. if (result) {
  1255. // 不等于0时代表失败
  1256. record_path = "delete error";
  1257. }
  1258. val["path"] = record_path;
  1259. val["code"] = result;
  1260. });
  1261. //获取录像文件夹列表或mp4文件列表
  1262. //http://127.0.0.1/index/api/getMp4RecordFile?vhost=__defaultVhost__&app=live&stream=ss&period=2020-01
  1263. api_regist("/index/api/getMp4RecordFile", [](API_ARGS_MAP){
  1264. CHECK_SECRET();
  1265. CHECK_ARGS("vhost", "app", "stream");
  1266. auto record_path = Recorder::getRecordPath(Recorder::type_mp4, allArgs["vhost"], allArgs["app"], allArgs["stream"], allArgs["customized_path"]);
  1267. auto period = allArgs["period"];
  1268. //判断是获取mp4文件列表还是获取文件夹列表
  1269. bool search_mp4 = period.size() == sizeof("2020-02-01") - 1;
  1270. if (search_mp4) {
  1271. record_path = record_path + period + "/";
  1272. }
  1273. Json::Value paths(arrayValue);
  1274. //这是筛选日期,获取文件夹列表
  1275. File::scanDir(record_path, [&](const string &path, bool isDir) {
  1276. auto pos = path.rfind('/');
  1277. if (pos != string::npos) {
  1278. string relative_path = path.substr(pos + 1);
  1279. if (search_mp4) {
  1280. if (!isDir) {
  1281. //我们只收集mp4文件,对文件夹不感兴趣
  1282. paths.append(relative_path);
  1283. }
  1284. } else if (isDir && relative_path.find(period) == 0) {
  1285. //匹配到对应日期的文件夹
  1286. paths.append(relative_path);
  1287. }
  1288. }
  1289. return true;
  1290. }, false);
  1291. val["data"]["rootPath"] = record_path;
  1292. val["data"]["paths"] = paths;
  1293. });
  1294. static auto responseSnap = [](const string &snap_path,
  1295. const HttpSession::KeyValue &headerIn,
  1296. const HttpSession::HttpResponseInvoker &invoker,
  1297. const string &err_msg = "") {
  1298. static bool s_snap_success_once = false;
  1299. StrCaseMap headerOut;
  1300. GET_CONFIG(string, defaultSnap, API::kDefaultSnap);
  1301. if (!File::fileSize(snap_path.data())) {
  1302. if (!err_msg.empty() && (!s_snap_success_once || defaultSnap.empty())) {
  1303. //重来没截图成功过或者默认截图图片为空,那么直接返回FFmpeg错误日志
  1304. headerOut["Content-Type"] = HttpFileManager::getContentType(".txt");
  1305. invoker.responseFile(headerIn, headerOut, err_msg, false, false);
  1306. return;
  1307. }
  1308. //截图成功过一次,那么认为配置无错误,截图失败时,返回预设默认图片
  1309. const_cast<string &>(snap_path) = File::absolutePath("", defaultSnap);
  1310. headerOut["Content-Type"] = HttpFileManager::getContentType(snap_path.data());
  1311. } else {
  1312. s_snap_success_once = true;
  1313. //之前生成的截图文件,我们默认为jpeg格式
  1314. headerOut["Content-Type"] = HttpFileManager::getContentType(".jpeg");
  1315. }
  1316. //返回图片给http客户端
  1317. invoker.responseFile(headerIn, headerOut, snap_path);
  1318. };
  1319. //获取截图缓存或者实时截图
  1320. //http://127.0.0.1/index/api/getSnap?url=rtmp://127.0.0.1/record/robot.mp4&timeout_sec=10&expire_sec=3
  1321. api_regist("/index/api/getSnap", [](API_ARGS_MAP_ASYNC){
  1322. CHECK_SECRET();
  1323. CHECK_ARGS("url", "timeout_sec", "expire_sec");
  1324. GET_CONFIG(string, snap_root, API::kSnapRoot);
  1325. bool have_old_snap = false, res_old_snap = false;
  1326. int expire_sec = allArgs["expire_sec"];
  1327. auto scan_path = File::absolutePath(MD5(allArgs["url"]).hexdigest(), snap_root) + "/";
  1328. string new_snap = StrPrinter << scan_path << time(NULL) << ".jpeg";
  1329. File::scanDir(scan_path, [&](const string &path, bool isDir) {
  1330. if (isDir || !end_with(path, ".jpeg")) {
  1331. //忽略文件夹或其他类型的文件
  1332. return true;
  1333. }
  1334. //找到截图
  1335. auto tm = FindField(path.data() + scan_path.size(), nullptr, ".jpeg");
  1336. if (atoll(tm.data()) + expire_sec < time(NULL)) {
  1337. //截图已经过期,改名,以便再次请求时,可以返回老截图
  1338. rename(path.data(), new_snap.data());
  1339. have_old_snap = true;
  1340. return true;
  1341. }
  1342. //截图存在,且未过期,那么返回之
  1343. res_old_snap = true;
  1344. responseSnap(path, allArgs.getParser().getHeader(), invoker);
  1345. //中断遍历
  1346. return false;
  1347. });
  1348. if (res_old_snap) {
  1349. //已经回复了旧的截图
  1350. return;
  1351. }
  1352. //无截图或者截图已经过期
  1353. if (!have_old_snap) {
  1354. //无过期截图,生成一个空文件,目的是顺便创建文件夹路径
  1355. //同时防止在FFmpeg生成截图途中不停的尝试调用该api多次启动FFmpeg进程
  1356. auto file = File::create_file(new_snap.data(), "wb");
  1357. if (file) {
  1358. fclose(file);
  1359. }
  1360. }
  1361. //启动FFmpeg进程,开始截图,生成临时文件,截图成功后替换为正式文件
  1362. auto new_snap_tmp = new_snap + ".tmp";
  1363. FFmpegSnap::makeSnap(allArgs["url"], new_snap_tmp, allArgs["timeout_sec"], [invoker, allArgs, new_snap, new_snap_tmp](bool success, const string &err_msg) {
  1364. if (!success) {
  1365. //生成截图失败,可能残留空文件
  1366. File::delete_file(new_snap_tmp.data());
  1367. } else {
  1368. //临时文件改成正式文件
  1369. File::delete_file(new_snap.data());
  1370. rename(new_snap_tmp.data(), new_snap.data());
  1371. }
  1372. responseSnap(new_snap, allArgs.getParser().getHeader(), invoker, err_msg);
  1373. });
  1374. });
  1375. api_regist("/index/api/getStatistic",[](API_ARGS_MAP_ASYNC){
  1376. CHECK_SECRET();
  1377. getStatisticJson([headerOut, val, invoker](const Value &data) mutable{
  1378. val["data"] = data;
  1379. invoker(200, headerOut, val.toStyledString());
  1380. });
  1381. });
  1382. #ifdef ENABLE_WEBRTC
  1383. class WebRtcArgsImp : public WebRtcArgs {
  1384. public:
  1385. WebRtcArgsImp(const HttpAllArgs<string> &args, std::string session_id)
  1386. : _args(args)
  1387. , _session_id(std::move(session_id)) {}
  1388. ~WebRtcArgsImp() override = default;
  1389. variant operator[](const string &key) const override {
  1390. if (key == "url") {
  1391. return getUrl();
  1392. }
  1393. return _args[key];
  1394. }
  1395. private:
  1396. string getUrl() const{
  1397. auto &allArgs = _args;
  1398. CHECK_ARGS("app", "stream");
  1399. return StrPrinter << RTC_SCHEMA << "://" << _args["Host"] << "/" << _args["app"] << "/"
  1400. << _args["stream"] << "?" << _args.getParser().Params() + "&session=" + _session_id;
  1401. }
  1402. private:
  1403. HttpAllArgs<string> _args;
  1404. std::string _session_id;
  1405. };
  1406. api_regist("/index/api/webrtc",[](API_ARGS_STRING_ASYNC){
  1407. CHECK_ARGS("type");
  1408. auto type = allArgs["type"];
  1409. auto offer = allArgs.getArgs();
  1410. CHECK(!offer.empty(), "http body(webrtc offer sdp) is empty");
  1411. WebRtcPluginManager::Instance().getAnswerSdp(*(static_cast<Session *>(&sender)), type,
  1412. WebRtcArgsImp(allArgs, sender.getIdentifier()),
  1413. [invoker, val, offer, headerOut](const WebRtcInterface &exchanger) mutable {
  1414. //设置返回类型
  1415. headerOut["Content-Type"] = HttpFileManager::getContentType(".json");
  1416. //设置跨域
  1417. headerOut["Access-Control-Allow-Origin"] = "*";
  1418. try {
  1419. val["sdp"] = const_cast<WebRtcInterface &>(exchanger).getAnswerSdp(offer);
  1420. val["id"] = exchanger.getIdentifier();
  1421. val["type"] = "answer";
  1422. invoker(200, headerOut, val.toStyledString());
  1423. } catch (std::exception &ex) {
  1424. val["code"] = API::Exception;
  1425. val["msg"] = ex.what();
  1426. invoker(200, headerOut, val.toStyledString());
  1427. }
  1428. });
  1429. });
  1430. #endif
  1431. #if defined(ENABLE_VERSION)
  1432. api_regist("/index/api/version",[](API_ARGS_MAP_ASYNC){
  1433. CHECK_SECRET();
  1434. Value ver;
  1435. ver["buildTime"] = BUILD_TIME;
  1436. ver["branchName"] = BRANCH_NAME;
  1437. ver["commitHash"] = COMMIT_HASH;
  1438. val["data"] = ver;
  1439. invoker(200, headerOut, val.toStyledString());
  1440. });
  1441. #endif
  1442. ////////////以下是注册的Hook API////////////
  1443. api_regist("/index/hook/on_publish",[](API_ARGS_JSON){
  1444. //开始推流事件
  1445. //转换hls
  1446. val["enable_hls"] = true;
  1447. //不录制mp4
  1448. val["enable_mp4"] = false;
  1449. });
  1450. api_regist("/index/hook/on_play",[](API_ARGS_JSON){
  1451. //开始播放事件
  1452. });
  1453. api_regist("/index/hook/on_flow_report",[](API_ARGS_JSON){
  1454. //流量统计hook api
  1455. });
  1456. api_regist("/index/hook/on_rtsp_realm",[](API_ARGS_JSON){
  1457. //rtsp是否需要鉴权,默认需要鉴权
  1458. val["code"] = API::Success;
  1459. val["realm"] = "zlmediakit_reaml";
  1460. });
  1461. api_regist("/index/hook/on_rtsp_auth",[](API_ARGS_JSON){
  1462. //rtsp鉴权密码,密码等于用户名
  1463. //rtsp可以有双重鉴权!后面还会触发on_play事件
  1464. CHECK_ARGS("user_name");
  1465. val["code"] = API::Success;
  1466. val["encrypted"] = false;
  1467. val["passwd"] = allArgs["user_name"].data();
  1468. });
  1469. api_regist("/index/hook/on_stream_changed",[](API_ARGS_JSON){
  1470. //媒体注册或反注册事件
  1471. });
  1472. #if !defined(_WIN32)
  1473. api_regist("/index/hook/on_stream_not_found_ffmpeg",[](API_ARGS_MAP_ASYNC){
  1474. //媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流
  1475. CHECK_SECRET();
  1476. CHECK_ARGS("vhost","app","stream");
  1477. //通过FFmpeg按需拉流
  1478. GET_CONFIG(int,rtmp_port,Rtmp::kPort);
  1479. GET_CONFIG(int,timeout_sec,Hook::kTimeoutSec);
  1480. string dst_url = StrPrinter
  1481. << "rtmp://127.0.0.1:"
  1482. << rtmp_port << "/"
  1483. << allArgs["app"] << "/"
  1484. << allArgs["stream"] << "?vhost="
  1485. << allArgs["vhost"];
  1486. addFFmpegSource("", "http://hls-ott-zhibo.wasu.tv/live/272/index.m3u8",/** ffmpeg拉流支持任意编码格式任意协议 **/
  1487. dst_url,
  1488. (1000 * timeout_sec) - 500,
  1489. false,
  1490. false,
  1491. [invoker,val,headerOut](const SockException &ex,const string &key) mutable{
  1492. if(ex){
  1493. val["code"] = API::OtherFailed;
  1494. val["msg"] = ex.what();
  1495. }else{
  1496. val["data"]["key"] = key;
  1497. }
  1498. invoker(200, headerOut, val.toStyledString());
  1499. });
  1500. });
  1501. #endif//!defined(_WIN32)
  1502. api_regist("/index/hook/on_stream_not_found",[](API_ARGS_MAP_ASYNC){
  1503. //媒体未找到事件,我们都及时拉流hks作为替代品,目的是为了测试按需拉流
  1504. CHECK_SECRET();
  1505. CHECK_ARGS("vhost","app","stream", "schema");
  1506. ProtocolOption option;
  1507. option.enable_hls = allArgs["schema"] == HLS_SCHEMA;
  1508. option.enable_mp4 = false;
  1509. //通过内置支持的rtsp/rtmp按需拉流
  1510. addStreamProxy(allArgs["vhost"],
  1511. allArgs["app"],
  1512. allArgs["stream"],
  1513. /** 支持rtsp和rtmp方式拉流 ,rtsp支持h265/h264/aac,rtmp仅支持h264/aac **/
  1514. "rtsp://184.72.239.149/vod/mp4:BigBuckBunny_115k.mov",
  1515. -1,/*无限重试*/
  1516. option,
  1517. 0,//rtp over tcp方式拉流
  1518. 10,//10秒超时
  1519. [invoker,val,headerOut](const SockException &ex,const string &key) mutable{
  1520. if(ex){
  1521. val["code"] = API::OtherFailed;
  1522. val["msg"] = ex.what();
  1523. }else{
  1524. val["data"]["key"] = key;
  1525. }
  1526. invoker(200, headerOut, val.toStyledString());
  1527. });
  1528. });
  1529. api_regist("/index/hook/on_record_mp4",[](API_ARGS_JSON){
  1530. //录制mp4分片完毕事件
  1531. });
  1532. api_regist("/index/hook/on_shell_login",[](API_ARGS_JSON){
  1533. //shell登录调试事件
  1534. });
  1535. api_regist("/index/hook/on_stream_none_reader",[](API_ARGS_JSON){
  1536. //无人观看流默认关闭
  1537. val["close"] = true;
  1538. });
  1539. api_regist("/index/hook/on_send_rtp_stopped",[](API_ARGS_JSON){
  1540. //发送rtp(startSendRtp)被动关闭时回调
  1541. });
  1542. static auto checkAccess = [](const string &params){
  1543. //我们假定大家都要权限访问
  1544. return true;
  1545. };
  1546. api_regist("/index/hook/on_http_access",[](API_ARGS_MAP){
  1547. //在这里根据allArgs["params"](url参数)来判断该http客户端是否有权限访问该文件
  1548. if(!checkAccess(allArgs["params"])){
  1549. //无访问权限
  1550. val["err"] = "无访问权限";
  1551. //仅限制访问当前目录
  1552. val["path"] = "";
  1553. //标记该客户端无权限1分钟
  1554. val["second"] = 60;
  1555. return;
  1556. }
  1557. //可以访问
  1558. val["err"] = "";
  1559. //只能访问当前目录
  1560. val["path"] = "";
  1561. //该http客户端用户被授予10分钟的访问权限,该权限仅限访问当前目录
  1562. val["second"] = 10 * 60;
  1563. });
  1564. api_regist("/index/hook/on_server_started",[](API_ARGS_JSON){
  1565. //服务器重启报告
  1566. });
  1567. api_regist("/index/hook/on_server_keepalive",[](API_ARGS_JSON){
  1568. //心跳hook
  1569. });
  1570. api_regist("/index/hook/on_rtp_server_timeout",[](API_ARGS_JSON){
  1571. //rtp server 超时
  1572. TraceL <<allArgs.getArgs().toStyledString();
  1573. });
  1574. }
  1575. void unInstallWebApi(){
  1576. {
  1577. lock_guard<recursive_mutex> lck(s_proxyMapMtx);
  1578. s_proxyMap.clear();
  1579. }
  1580. {
  1581. lock_guard<recursive_mutex> lck(s_ffmpegMapMtx);
  1582. s_ffmpegMap.clear();
  1583. }
  1584. {
  1585. lock_guard<recursive_mutex> lck(s_proxyPusherMapMtx);
  1586. s_proxyPusherMap.clear();
  1587. }
  1588. {
  1589. #if defined(ENABLE_RTPPROXY)
  1590. RtpSelector::Instance().clear();
  1591. lock_guard<recursive_mutex> lck(s_rtpServerMapMtx);
  1592. s_rtpServerMap.clear();
  1593. #endif
  1594. }
  1595. NoticeCenter::Instance().delListener(&web_api_tag);
  1596. }