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.

400 lines
15KB

  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 <signal.h>
  11. #include <iostream>
  12. #include "Util/File.h"
  13. #include "Util/logger.h"
  14. #include "Util/SSLBox.h"
  15. #include "Util/onceToken.h"
  16. #include "Util/CMD.h"
  17. #include "Network/TcpServer.h"
  18. #include "Network/UdpServer.h"
  19. #include "Poller/EventPoller.h"
  20. #include "Common/config.h"
  21. #include "Rtsp/RtspSession.h"
  22. #include "Rtmp/RtmpSession.h"
  23. #include "Shell/ShellSession.h"
  24. #include "Http/WebSocketSession.h"
  25. #include "Rtp/RtpServer.h"
  26. #include "WebApi.h"
  27. #include "WebHook.h"
  28. #if defined(ENABLE_WEBRTC)
  29. #include "../webrtc/WebRtcTransport.h"
  30. #include "../webrtc/WebRtcSession.h"
  31. #endif
  32. #if defined(ENABLE_SRT)
  33. #include "../srt/SrtSession.hpp"
  34. #include "../srt/SrtTransport.hpp"
  35. #endif
  36. #if defined(ENABLE_VERSION)
  37. #include "version.h"
  38. #endif
  39. #if !defined(_WIN32)
  40. #include "System.h"
  41. #endif//!defined(_WIN32)
  42. using namespace std;
  43. using namespace toolkit;
  44. using namespace mediakit;
  45. namespace mediakit {
  46. ////////////HTTP配置///////////
  47. namespace Http {
  48. #define HTTP_FIELD "http."
  49. const string kPort = HTTP_FIELD"port";
  50. const string kSSLPort = HTTP_FIELD"sslport";
  51. onceToken token1([](){
  52. mINI::Instance()[kPort] = 80;
  53. mINI::Instance()[kSSLPort] = 443;
  54. },nullptr);
  55. }//namespace Http
  56. ////////////SHELL配置///////////
  57. namespace Shell {
  58. #define SHELL_FIELD "shell."
  59. const string kPort = SHELL_FIELD"port";
  60. onceToken token1([](){
  61. mINI::Instance()[kPort] = 9000;
  62. },nullptr);
  63. } //namespace Shell
  64. ////////////RTSP服务器配置///////////
  65. namespace Rtsp {
  66. #define RTSP_FIELD "rtsp."
  67. const string kPort = RTSP_FIELD"port";
  68. const string kSSLPort = RTSP_FIELD"sslport";
  69. onceToken token1([](){
  70. mINI::Instance()[kPort] = 554;
  71. mINI::Instance()[kSSLPort] = 332;
  72. },nullptr);
  73. } //namespace Rtsp
  74. ////////////RTMP服务器配置///////////
  75. namespace Rtmp {
  76. #define RTMP_FIELD "rtmp."
  77. const string kPort = RTMP_FIELD"port";
  78. const string kSSLPort = RTMP_FIELD"sslport";
  79. onceToken token1([](){
  80. mINI::Instance()[kPort] = 1935;
  81. mINI::Instance()[kSSLPort] = 19350;
  82. },nullptr);
  83. } //namespace RTMP
  84. ////////////Rtp代理相关配置///////////
  85. namespace RtpProxy {
  86. #define RTP_PROXY_FIELD "rtp_proxy."
  87. const string kPort = RTP_PROXY_FIELD"port";
  88. onceToken token1([](){
  89. mINI::Instance()[kPort] = 10000;
  90. },nullptr);
  91. } //namespace RtpProxy
  92. } // namespace mediakit
  93. class CMD_main : public CMD {
  94. public:
  95. CMD_main() {
  96. _parser.reset(new OptionParser(nullptr));
  97. #if !defined(_WIN32)
  98. (*_parser) << Option('d',/*该选项简称,如果是\x00则说明无简称*/
  99. "daemon",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  100. Option::ArgNone,/*该选项后面必须跟值*/
  101. nullptr,/*该选项默认值*/
  102. false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  103. "是否以Daemon方式启动",/*该选项说明文字*/
  104. nullptr);
  105. #endif//!defined(_WIN32)
  106. (*_parser) << Option('l',/*该选项简称,如果是\x00则说明无简称*/
  107. "level",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  108. Option::ArgRequired,/*该选项后面必须跟值*/
  109. to_string(LTrace).data(),/*该选项默认值*/
  110. false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  111. "日志等级,LTrace~LError(0~4)",/*该选项说明文字*/
  112. nullptr);
  113. (*_parser) << Option('m',/*该选项简称,如果是\x00则说明无简称*/
  114. "max_day",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  115. Option::ArgRequired,/*该选项后面必须跟值*/
  116. "7",/*该选项默认值*/
  117. false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  118. "日志最多保存天数",/*该选项说明文字*/
  119. nullptr);
  120. (*_parser) << Option('c',/*该选项简称,如果是\x00则说明无简称*/
  121. "config",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  122. Option::ArgRequired,/*该选项后面必须跟值*/
  123. (exeDir() + "config.ini").data(),/*该选项默认值*/
  124. false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  125. "配置文件路径",/*该选项说明文字*/
  126. nullptr);
  127. (*_parser) << Option('s',/*该选项简称,如果是\x00则说明无简称*/
  128. "ssl",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  129. Option::ArgRequired,/*该选项后面必须跟值*/
  130. (exeDir() + "default.pem").data(),/*该选项默认值*/
  131. false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  132. "ssl证书文件或文件夹,支持p12/pem类型",/*该选项说明文字*/
  133. nullptr);
  134. (*_parser) << Option('t',/*该选项简称,如果是\x00则说明无简称*/
  135. "threads",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
  136. Option::ArgRequired,/*该选项后面必须跟值*/
  137. to_string(thread::hardware_concurrency()).data(),/*该选项默认值*/
  138. false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
  139. "启动事件触发线程数",/*该选项说明文字*/
  140. nullptr);
  141. #if defined(ENABLE_VERSION)
  142. (*_parser) << Option('v', "version", Option::ArgNone, nullptr, false, "显示版本号",
  143. [](const std::shared_ptr<ostream> &stream, const string &arg) -> bool {
  144. //版本信息
  145. *stream << "编译日期: " << BUILD_TIME << std::endl;
  146. *stream << "代码日期: " << COMMIT_TIME << std::endl;
  147. *stream << "当前git分支: " << BRANCH_NAME << std::endl;
  148. *stream << "当前git hash值: " << COMMIT_HASH << std::endl;
  149. throw ExitException();
  150. });
  151. #endif
  152. }
  153. ~CMD_main() override{}
  154. const char *description() const override{
  155. return "主程序命令参数";
  156. }
  157. };
  158. //全局变量,在WebApi中用于保存配置文件用
  159. string g_ini_file;
  160. int start_main(int argc,char *argv[]) {
  161. {
  162. CMD_main cmd_main;
  163. try {
  164. cmd_main.operator()(argc, argv);
  165. } catch (ExitException &) {
  166. return 0;
  167. } catch (std::exception &ex) {
  168. cout << ex.what() << endl;
  169. return -1;
  170. }
  171. bool bDaemon = cmd_main.hasKey("daemon");
  172. LogLevel logLevel = (LogLevel) cmd_main["level"].as<int>();
  173. logLevel = MIN(MAX(logLevel, LTrace), LError);
  174. g_ini_file = cmd_main["config"];
  175. string ssl_file = cmd_main["ssl"];
  176. int threads = cmd_main["threads"];
  177. //设置日志
  178. Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", logLevel));
  179. #ifndef ANDROID
  180. auto fileChannel = std::make_shared<FileChannel>("FileChannel", exeDir() + "log/", logLevel);
  181. //日志最多保存天数
  182. fileChannel->setMaxDay(cmd_main["max_day"]);
  183. Logger::Instance().add(fileChannel);
  184. #endif//
  185. #if !defined(_WIN32)
  186. pid_t pid = getpid();
  187. bool kill_parent_if_failed = true;
  188. if (bDaemon) {
  189. //启动守护进程
  190. System::startDaemon(kill_parent_if_failed);
  191. }
  192. //开启崩溃捕获等
  193. System::systemSetup();
  194. #endif//!defined(_WIN32)
  195. //启动异步日志线程
  196. Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
  197. InfoL << kServerName;
  198. //加载配置文件,如果配置文件不存在就创建一个
  199. loadIniConfig(g_ini_file.data());
  200. if (!File::is_dir(ssl_file.data())) {
  201. //不是文件夹,加载证书,证书包含公钥和私钥
  202. SSL_Initor::Instance().loadCertificate(ssl_file.data());
  203. } else {
  204. //加载文件夹下的所有证书
  205. File::scanDir(ssl_file, [](const string &path, bool isDir) {
  206. if (!isDir) {
  207. //最后的一个证书会当做默认证书(客户端ssl握手时未指定主机)
  208. SSL_Initor::Instance().loadCertificate(path.data());
  209. }
  210. return true;
  211. });
  212. }
  213. uint16_t shellPort = mINI::Instance()[Shell::kPort];
  214. uint16_t rtspPort = mINI::Instance()[Rtsp::kPort];
  215. uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort];
  216. uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort];
  217. uint16_t rtmpsPort = mINI::Instance()[Rtmp::kSSLPort];
  218. uint16_t httpPort = mINI::Instance()[Http::kPort];
  219. uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];
  220. uint16_t rtpPort = mINI::Instance()[RtpProxy::kPort];
  221. //设置poller线程数,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效
  222. EventPollerPool::setPoolSize(threads);
  223. //简单的telnet服务器,可用于服务器调试,但是不能使用23端口,否则telnet上了莫名其妙的现象
  224. //测试方法:telnet 127.0.0.1 9000
  225. auto shellSrv = std::make_shared<TcpServer>();
  226. //rtsp[s]服务器, 可用于诸如亚马逊echo show这样的设备访问
  227. auto rtspSrv = std::make_shared<TcpServer>();;
  228. auto rtspSSLSrv = std::make_shared<TcpServer>();;
  229. //rtmp[s]服务器
  230. auto rtmpSrv = std::make_shared<TcpServer>();;
  231. auto rtmpsSrv = std::make_shared<TcpServer>();;
  232. //http[s]服务器
  233. auto httpSrv = std::make_shared<TcpServer>();;
  234. auto httpsSrv = std::make_shared<TcpServer>();;
  235. #if defined(ENABLE_RTPPROXY)
  236. //GB28181 rtp推流端口,支持UDP/TCP
  237. auto rtpServer = std::make_shared<RtpServer>();
  238. #endif//defined(ENABLE_RTPPROXY)
  239. #if defined(ENABLE_WEBRTC)
  240. auto rtcSrv_tcp = std::make_shared<TcpServer>();
  241. //webrtc udp服务器
  242. auto rtcSrv_udp = std::make_shared<UdpServer>();
  243. rtcSrv_udp->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {
  244. if (!buf) {
  245. return Socket::createSocket(poller, false);
  246. }
  247. auto new_poller = WebRtcSession::queryPoller(buf);
  248. if (!new_poller) {
  249. //该数据对应的webrtc对象未找到,丢弃之
  250. return Socket::Ptr();
  251. }
  252. return Socket::createSocket(new_poller, false);
  253. });
  254. uint16_t rtcPort = mINI::Instance()[Rtc::kPort];
  255. uint16_t rtcTcpPort = mINI::Instance()[Rtc::kTcpPort];
  256. #endif//defined(ENABLE_WEBRTC)
  257. #if defined(ENABLE_SRT)
  258. auto srtSrv = std::make_shared<UdpServer>();
  259. srtSrv->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {
  260. if (!buf) {
  261. return Socket::createSocket(poller, false);
  262. }
  263. auto new_poller = SRT::SrtSession::queryPoller(buf);
  264. if (!new_poller) {
  265. //握手第一阶段
  266. return Socket::createSocket(poller, false);
  267. }
  268. return Socket::createSocket(new_poller, false);
  269. });
  270. uint16_t srtPort = mINI::Instance()[SRT::kPort];
  271. #endif //defined(ENABLE_SRT)
  272. try {
  273. //rtsp服务器,端口默认554
  274. if (rtspPort) { rtspSrv->start<RtspSession>(rtspPort); }
  275. //rtsps服务器,端口默认322
  276. if (rtspsPort) { rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort); }
  277. //rtmp服务器,端口默认1935
  278. if (rtmpPort) { rtmpSrv->start<RtmpSession>(rtmpPort); }
  279. //rtmps服务器,端口默认19350
  280. if (rtmpsPort) { rtmpsSrv->start<RtmpSessionWithSSL>(rtmpsPort); }
  281. //http服务器,端口默认80
  282. if (httpPort) { httpSrv->start<HttpSession>(httpPort); }
  283. //https服务器,端口默认443
  284. if (httpsPort) { httpsSrv->start<HttpsSession>(httpsPort); }
  285. //telnet远程调试服务器
  286. if (shellPort) { shellSrv->start<ShellSession>(shellPort); }
  287. #if defined(ENABLE_RTPPROXY)
  288. //创建rtp服务器
  289. if (rtpPort) { rtpServer->start(rtpPort); }
  290. #endif//defined(ENABLE_RTPPROXY)
  291. #if defined(ENABLE_WEBRTC)
  292. //webrtc udp服务器
  293. if (rtcPort) { rtcSrv_udp->start<WebRtcSession>(rtcPort);}
  294. if (rtcTcpPort) { rtcSrv_tcp->start<WebRtcSession>(rtcTcpPort);}
  295. #endif//defined(ENABLE_WEBRTC)
  296. #if defined(ENABLE_SRT)
  297. // srt udp服务器
  298. if(srtPort) { srtSrv->start<SRT::SrtSession>(srtPort); }
  299. #endif//defined(ENABLE_SRT)
  300. } catch (std::exception &ex) {
  301. WarnL << "端口占用或无权限:" << ex.what() << endl;
  302. ErrorL << "程序启动失败,请修改配置文件中端口号后重试!" << endl;
  303. sleep(1);
  304. #if !defined(_WIN32)
  305. if (pid != getpid() && kill_parent_if_failed) {
  306. //杀掉守护进程
  307. kill(pid, SIGINT);
  308. }
  309. #endif
  310. return -1;
  311. }
  312. installWebApi();
  313. InfoL << "已启动http api 接口";
  314. installWebHook();
  315. InfoL << "已启动http hook 接口";
  316. //设置退出信号处理函数
  317. static semaphore sem;
  318. signal(SIGINT, [](int) {
  319. InfoL << "SIGINT:exit";
  320. signal(SIGINT, SIG_IGN);// 设置退出信号
  321. sem.post();
  322. });// 设置退出信号
  323. #if !defined(_WIN32)
  324. signal(SIGHUP, [](int) { mediakit::loadIniConfig(g_ini_file.data()); });
  325. #endif
  326. sem.wait();
  327. }
  328. unInstallWebApi();
  329. unInstallWebHook();
  330. //休眠1秒再退出,防止资源释放顺序错误
  331. InfoL << "程序退出中,请等待...";
  332. sleep(1);
  333. InfoL << "程序退出完毕!";
  334. return 0;
  335. }
  336. #ifndef DISABLE_MAIN
  337. int main(int argc,char *argv[]) {
  338. return start_main(argc,argv);
  339. }
  340. #endif //DISABLE_MAIN