<NME> main.cpp
<BEF> ﻿/*
 * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.
 *
 * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).
 *
 * Use of this source code is governed by MIT license that can be found in the
 * LICENSE file in the root of the source tree. All contributing project authors
 * may be found in the AUTHORS file in the root of the source tree.
 */

#include <signal.h>
#include <iostream>
#include "Util/File.h"
#include "Util/logger.h"
#include "Util/SSLBox.h"
#include "Util/onceToken.h"
#include "Util/CMD.h"
#include "Network/TcpServer.h"
#include "Network/UdpServer.h"
#include "Poller/EventPoller.h"
#include "Common/config.h"
#include "Rtsp/RtspSession.h"
#include "Rtmp/RtmpSession.h"
#include "Shell/ShellSession.h"
#include "Http/WebSocketSession.h"
#include "Rtp/RtpServer.h"
#include "WebApi.h"
#include "WebHook.h"

#if defined(ENABLE_WEBRTC)
#include "../webrtc/WebRtcTransport.h"
#include "../webrtc/WebRtcSession.h"
#endif

#if defined(ENABLE_SRT)
#include "../srt/SrtSession.hpp"
#include "../srt/SrtTransport.hpp"
#endif

#if defined(ENABLE_VERSION)
#include "version.h"
#endif
#include "RtmpMuxer/FlvMuxer.h"
#include "Player/PlayerProxy.h"
#include "Http/WebSocketSession.h"
#include "System.h"
#include "WebApi.h"

using namespace std;
using namespace toolkit;
using namespace mediakit;
#define HTTP_FIELD "http."
const string kPort = HTTP_FIELD"port";
const string kSSLPort = HTTP_FIELD"sslport";
onceToken token1([](){
    mINI::Instance()[kPort] = 80;
    mINI::Instance()[kSSLPort] = 443;
},nullptr);
}//namespace Http

////////////SHELL配置///////////
namespace Shell {
#define SHELL_FIELD "shell."
const string kPort = SHELL_FIELD"port";
onceToken token1([](){
    mINI::Instance()[kPort] = 9000;
},nullptr);
} //namespace Shell

////////////RTSP服务器配置///////////
namespace Rtsp {
#define RTSP_FIELD "rtsp."
const string kPort = RTSP_FIELD"port";
const string kSSLPort = RTSP_FIELD"sslport";
onceToken token1([](){
    mINI::Instance()[kPort] = 554;
    mINI::Instance()[kSSLPort] = 332;
},nullptr);

} //namespace Rtsp

////////////RTMP服务器配置///////////
namespace Rtmp {
#define RTMP_FIELD "rtmp."
const string kPort = RTMP_FIELD"port";
const string kSSLPort = RTMP_FIELD"sslport";
onceToken token1([](){
    mINI::Instance()[kPort] = 1935;
    mINI::Instance()[kSSLPort] = 19350;
},nullptr);
} //namespace RTMP

////////////Rtp代理相关配置///////////
namespace RtpProxy {
#define RTP_PROXY_FIELD "rtp_proxy."
const string kPort = RTP_PROXY_FIELD"port";
onceToken token1([](){
    mINI::Instance()[kPort] = 10000;
},nullptr);
} //namespace RtpProxy

}  // namespace mediakit


    CMD_main() {
        _parser.reset(new OptionParser(nullptr));

        (*_parser) << Option('d',/*该选项简称，如果是\x00则说明无简称*/
                             "daemon",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgNone,/*该选项后面必须跟值*/
                             "daemon",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "是否以Daemon方式启动",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('l',/*该选项简称，如果是\x00则说明无简称*/
                             "level",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
        (*_parser) << Option('l',/*该选项简称，如果是\x00则说明无简称*/
                             "level",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             to_string(LTrace).data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "日志等级,LTrace~LError(0~4)",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('m',/*该选项简称，如果是\x00则说明无简称*/
                             "max_day",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             "7",/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "日志最多保存天数",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('c',/*该选项简称，如果是\x00则说明无简称*/
                             "config",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             (exeDir() + "config.ini").data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "配置文件路径",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('s',/*该选项简称，如果是\x00则说明无简称*/
                             "ssl",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             (exeDir() + "default.pem").data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "ssl证书文件或文件夹,支持p12/pem类型",/*该选项说明文字*/
                             nullptr);

        (*_parser) << Option('t',/*该选项简称，如果是\x00则说明无简称*/
                             "threads",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                             Option::ArgRequired,/*该选项后面必须跟值*/
                             to_string(thread::hardware_concurrency()).data(),/*该选项默认值*/
                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                             "启动事件触发线程数",/*该选项说明文字*/
                             nullptr);

extern void installWebHook();
extern void unInstallWebHook();

static void inline listen_shell_input(){
    cout << "> 欢迎进入命令模式，你可以输入\"help\"命令获取帮助" << endl;
    cout << "> " << std::flush;

    signal(SIGTTOU,SIG_IGN);
    signal(SIGTTIN,SIG_IGN);
    SockUtil::setNoBlocked(STDIN_FILENO);
    auto oninput = [](int event) {
        if (event & Event_Read) {
        return "主程序命令参数";
    }
};

//全局变量，在WebApi中用于保存配置文件用
string g_ini_file;

int start_main(int argc,char *argv[]) {
    {
        CMD_main cmd_main;
        try {
            cmd_main.operator()(argc, argv);
        } catch (ExitException &) {
            return 0;
        } catch (std::exception &ex) {
            cout << ex.what() << endl;
            return -1;
        }

        bool bDaemon = cmd_main.hasKey("daemon");
        LogLevel logLevel = (LogLevel) cmd_main["level"].as<int>();
        logLevel = MIN(MAX(logLevel, LTrace), LError);
        g_ini_file = cmd_main["config"];
    };
    EventPollerPool::Instance().getFirstPoller()->addEvent(STDIN_FILENO, Event_Read | Event_Error | Event_LT,oninput);
}
int main(int argc,char *argv[]) {
    {
        CMD_main cmd_main;
        fileChannel->setMaxDay(cmd_main["max_day"]);
        Logger::Instance().add(fileChannel);
#endif//

#if !defined(_WIN32)
        pid_t pid = getpid();
        bool kill_parent_if_failed = true;
        if (bDaemon) {
            //启动守护进程
            System::startDaemon(kill_parent_if_failed);
        }
        //开启崩溃捕获等
        System::systemSetup();
#endif//!defined(_WIN32)

        //启动异步日志线程
        Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
        //加载配置文件，如果配置文件不存在就创建一个
        loadIniConfig(g_ini_file.data());
        Logger::Instance().add(std::make_shared<FileChannel>("FileChannel", exePath() + ".log", logLevel));
#endif

        if (bDaemon) {
            //启动守护进程
            System::startDaemon();
        }

        //开启崩溃捕获等
        System::systemSetup();

        //启动异步日志线程
        Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());

        uint16_t shellPort = mINI::Instance()[Shell::kPort];
        uint16_t rtspPort = mINI::Instance()[Rtsp::kPort];
        uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort];
        uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort];
        uint16_t rtmpsPort = mINI::Instance()[Rtmp::kSSLPort];
        uint16_t httpPort = mINI::Instance()[Http::kPort];
        uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];
        uint16_t rtpPort = mINI::Instance()[RtpProxy::kPort];

        //设置poller线程数,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效
        EventPollerPool::setPoolSize(threads);

        //简单的telnet服务器，可用于服务器调试，但是不能使用23端口，否则telnet上了莫名其妙的现象
        //测试方法:telnet 127.0.0.1 9000
        auto shellSrv = std::make_shared<TcpServer>();

        //rtsp[s]服务器, 可用于诸如亚马逊echo show这样的设备访问
        auto rtspSrv = std::make_shared<TcpServer>();;
        auto rtspSSLSrv = std::make_shared<TcpServer>();;

        //rtmp[s]服务器
        auto rtmpSrv = std::make_shared<TcpServer>();;
        auto rtmpsSrv = std::make_shared<TcpServer>();;

        //http[s]服务器
        auto httpSrv = std::make_shared<TcpServer>();;
        auto httpsSrv = std::make_shared<TcpServer>();;

#if defined(ENABLE_RTPPROXY)
        //GB28181 rtp推流端口，支持UDP/TCP
        auto rtpServer = std::make_shared<RtpServer>();
#endif//defined(ENABLE_RTPPROXY)

#if defined(ENABLE_WEBRTC)
        //webrtc udp服务器
        auto rtcSrv = std::make_shared<UdpServer>();
        rtcSrv->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {
            if (!buf) {
                return Socket::createSocket(poller, false);
            }
            auto new_poller = WebRtcSession::queryPoller(buf);
            if (!new_poller) {
                //该数据对应的webrtc对象未找到，丢弃之
        installWebHook();
        InfoL << "已启动http hook 接口";

        if (!bDaemon) {
            //交互式shell输入
            listen_shell_input();
        }

        //设置退出信号处理函数
        static semaphore sem;
                return Socket::createSocket(poller, false);
            }
            signal(SIGINT, SIG_IGN);// 设置退出信号
            sem.post();
        });// 设置退出信号
        signal(SIGHUP, [](int) { mediakit::loadIniConfig(ini_file.data()); });
        sem.wait();
    }
    unInstallWebApi();

        try {
            //rtsp服务器，端口默认554
            if (rtspPort) { rtspSrv->start<RtspSession>(rtspPort); }
            //rtsps服务器，端口默认322
            if (rtspsPort) { rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort); }

            //rtmp服务器，端口默认1935
            if (rtmpPort) { rtmpSrv->start<RtmpSession>(rtmpPort); }
            //rtmps服务器，端口默认19350
            if (rtmpsPort) { rtmpsSrv->start<RtmpSessionWithSSL>(rtmpsPort); }

            //http服务器，端口默认80
            if (httpPort) { httpSrv->start<HttpSession>(httpPort); }
            //https服务器，端口默认443
            if (httpsPort) { httpsSrv->start<HttpsSession>(httpsPort); }

            //telnet远程调试服务器
            if (shellPort) { shellSrv->start<ShellSession>(shellPort); }

#if defined(ENABLE_RTPPROXY)
            //创建rtp服务器
            if (rtpPort) { rtpServer->start(rtpPort); }
#endif//defined(ENABLE_RTPPROXY)

#if defined(ENABLE_WEBRTC)
            //webrtc udp服务器
            if (rtcPort) { rtcSrv->start<WebRtcSession>(rtcPort); }
#endif//defined(ENABLE_WEBRTC)

#if defined(ENABLE_SRT)
        // srt udp服务器
        if(srtPort) { srtSrv->start<SRT::SrtSession>(srtPort); }
#endif//defined(ENABLE_SRT)

        } catch (std::exception &ex) {
            WarnL << "端口占用或无权限:" << ex.what() << endl;
            ErrorL << "程序启动失败，请修改配置文件中端口号后重试!" << endl;
            sleep(1);
#if !defined(_WIN32)
            if (pid != getpid() && kill_parent_if_failed) {
                //杀掉守护进程
                kill(pid, SIGINT);
            }
#endif
            return -1;
        }

        installWebApi();
        InfoL << "已启动http api 接口";
        installWebHook();
        InfoL << "已启动http hook 接口";

        //设置退出信号处理函数
        static semaphore sem;
        signal(SIGINT, [](int) {
            InfoL << "SIGINT:exit";
            signal(SIGINT, SIG_IGN);// 设置退出信号
            sem.post();
        });// 设置退出信号

#if !defined(_WIN32)
        signal(SIGHUP, [](int) { mediakit::loadIniConfig(g_ini_file.data()); });
#endif
        sem.wait();
    }
    unInstallWebApi();
    unInstallWebHook();
    //休眠1秒再退出，防止资源释放顺序错误
    InfoL << "程序退出中,请等待...";
    sleep(1);
    InfoL << "程序退出完毕!";
    return 0;
}

#ifndef DISABLE_MAIN
int main(int argc,char *argv[]) {
    return start_main(argc,argv);
}
#endif //DISABLE_MAIN



<MSG> 移植Windows

<DFF> @@ -43,9 +43,12 @@
 #include "RtmpMuxer/FlvMuxer.h"
 #include "Player/PlayerProxy.h"
 #include "Http/WebSocketSession.h"
-#include "System.h"
 #include "WebApi.h"
 
+#if !defined(_WIN32)
+#include "System.h"
+#endif//!defined(_WIN32)
+
 using namespace std;
 using namespace toolkit;
 using namespace mediakit;
@@ -105,6 +108,7 @@ public:
     CMD_main() {
         _parser.reset(new OptionParser(nullptr));
 
+#if !defined(_WIN32)
         (*_parser) << Option('d',/*该选项简称，如果是\x00则说明无简称*/
                              "daemon",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
                              Option::ArgNone,/*该选项后面必须跟值*/
@@ -112,6 +116,7 @@ public:
                              false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
                              "是否以Daemon方式启动",/*该选项说明文字*/
                              nullptr);
+#endif//!defined(_WIN32)
 
         (*_parser) << Option('l',/*该选项简称，如果是\x00则说明无简称*/
                              "level",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/
@@ -158,12 +163,14 @@ extern void unInstallWebApi();
 extern void installWebHook();
 extern void unInstallWebHook();
 
+#if !defined(_WIN32)
 static void inline listen_shell_input(){
     cout << "> 欢迎进入命令模式，你可以输入\"help\"命令获取帮助" << endl;
     cout << "> " << std::flush;
 
     signal(SIGTTOU,SIG_IGN);
     signal(SIGTTIN,SIG_IGN);
+
     SockUtil::setNoBlocked(STDIN_FILENO);
     auto oninput = [](int event) {
         if (event & Event_Read) {
@@ -193,6 +200,8 @@ static void inline listen_shell_input(){
     };
     EventPollerPool::Instance().getFirstPoller()->addEvent(STDIN_FILENO, Event_Read | Event_Error | Event_LT,oninput);
 }
+#endif//!defined(_WIN32)
+
 int main(int argc,char *argv[]) {
     {
         CMD_main cmd_main;
@@ -218,13 +227,14 @@ int main(int argc,char *argv[]) {
         Logger::Instance().add(std::make_shared<FileChannel>("FileChannel", exePath() + ".log", logLevel));
 #endif
 
+#if !defined(_WIN32)
         if (bDaemon) {
             //启动守护进程
             System::startDaemon();
         }
-
         //开启崩溃捕获等
         System::systemSetup();
+#endif//!defined(_WIN32)
 
         //启动异步日志线程
         Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
@@ -275,10 +285,12 @@ int main(int argc,char *argv[]) {
         installWebHook();
         InfoL << "已启动http hook 接口";
 
+#if !defined(_WIN32)
         if (!bDaemon) {
             //交互式shell输入
             listen_shell_input();
         }
+#endif
 
         //设置退出信号处理函数
         static semaphore sem;
@@ -287,7 +299,10 @@ int main(int argc,char *argv[]) {
             signal(SIGINT, SIG_IGN);// 设置退出信号
             sem.post();
         });// 设置退出信号
+
+#if !defined(_WIN32)
         signal(SIGHUP, [](int) { mediakit::loadIniConfig(ini_file.data()); });
+#endif
         sem.wait();
     }
     unInstallWebApi();

{'commit': 'diff --git a/CMakeLists.txt b/CMakeLists.txt\nindex f956b4a5..eec4257c 100644\n--- a/CMakeLists.txt\n+++ b/CMakeLists.txt\n@@ -125,11 +125,7 @@ endif ()\n add_subdirectory(tests)\n \n #主服务器\n-if (CMAKE_SYSTEM_NAME MATCHES "Linux")\n-    add_subdirectory(server)\n-elseif (CMAKE_SYSTEM_NAME MATCHES "Darwin")\n-    add_subdirectory(server)\n-endif ()\n+add_subdirectory(server)\n \n \n \ndiff --git a/server/CMakeLists.txt b/server/CMakeLists.txt\nindex 136422c8..c591a376 100644\n--- a/server/CMakeLists.txt\n+++ b/server/CMakeLists.txt\n@@ -2,8 +2,21 @@\n file(GLOB jsoncpp_src_list ../3rdpart/jsoncpp/*.cpp ../3rdpart/jsoncpp/*.h )\n add_library(jsoncpp STATIC ${jsoncpp_src_list})\n \n-file(GLOB MediaServer_src_list ./*.cpp ./*.h)\n+\n+if (CMAKE_SYSTEM_NAME MATCHES "Windows")\n+\tset(MediaServer_src_list ./WebApi.cpp ./WebHook.cpp main.cpp)\n+else()\n+\tfile(GLOB MediaServer_src_list ./*.cpp ./*.h)\n+endif()\n+\n+message(STATUS ${MediaServer_src_list})\n+\n add_executable(MediaServer ${MediaServer_src_list})\n+\n+if(WIN32)\n+\tset_target_properties(MediaServer PROPERTIES COMPILE_FLAGS  ${VS_FALGS} )\n+endif()\n+\n target_link_libraries(MediaServer jsoncpp ${LINK_LIB_LIST})\n \n \ndiff --git a/server/WebApi.cpp b/server/WebApi.cpp\nindex 8521a475..52d34877 100644\n--- a/server/WebApi.cpp\n+++ b/server/WebApi.cpp\n@@ -1,4 +1,4 @@\n-/*\n+\ufeff/*\n  * MIT License\n  *\n  * Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>\n@@ -42,10 +42,13 @@\n #include "Http/HttpSession.h"\n #include "Network/TcpServer.h"\n #include "Player/PlayerProxy.h"\n-#include "FFmpegSource.h"\n #include "Util/MD5.h"\n #include "WebApi.h"\n \n+#if !defined(_WIN32)\n+#include "FFmpegSource.h"\n+#endif//!defined(_WIN32)\n+\n using namespace Json;\n using namespace toolkit;\n using namespace mediakit;\n@@ -245,8 +248,10 @@ static inline string getProxyKey(const string &vhost,const string &app,const str\n     return vhost + "/" + app + "/" + stream;\n }\n \n+#if !defined(_WIN32)\n static unordered_map<string ,FFmpegSource::Ptr> s_ffmpegMap;\n static recursive_mutex s_ffmpegMapMtx;\n+#endif//#if !defined(_WIN32)\n \n /**\n  * 安装api接口\n@@ -322,6 +327,7 @@ void installWebApi() {\n         }\n     });\n \n+#if !defined(_WIN32)\n     //重启服务器,只有Daemon方式才能重启，否则是直接关闭！\n     //测试url http://127.0.0.1/index/api/restartServer\n     API_REGIST(api,restartServer,{\n@@ -340,6 +346,7 @@ void installWebApi() {\n         });\n         val["msg"] = "服务器将在一秒后自动重启";\n     });\n+#endif//#if !defined(_WIN32)\n \n \n     //获取流列表，可选筛选参数\n@@ -507,6 +514,7 @@ void installWebApi() {\n         val["data"]["flag"] = s_proxyMap.erase(allArgs["key"]) == 1;\n     });\n \n+#if !defined(_WIN32)\n     static auto addFFmepgSource = [](const string &src_url,\n                                      const string &dst_url,\n                                      int timeout_ms,\n@@ -563,6 +571,7 @@ void installWebApi() {\n         lock_guard<decltype(s_ffmpegMapMtx)> lck(s_ffmpegMapMtx);\n         val["data"]["flag"] = s_ffmpegMap.erase(allArgs["key"]) == 1;\n     });\n+#endif\n \n     ////////////以下是注册的Hook API////////////\n     API_REGIST(hook,on_publish,{\n@@ -600,12 +609,12 @@ void installWebApi() {\n         throw SuccessException();\n     });\n \n+\n+#if !defined(_WIN32)\n     API_REGIST_INVOKER(hook,on_stream_not_found,{\n         //媒体未找到事件,我们都及时拉流hks作为替代品，目的是为了测试按需拉流\n         CHECK_SECRET();\n         CHECK_ARGS("vhost","app","stream");\n-\n-#if 1\n         //通过FFmpeg按需拉流\n         GET_CONFIG(int,rtmp_port,Rtmp::kPort);\n         string dst_url = StrPrinter\n@@ -627,9 +636,14 @@ void installWebApi() {\n                             }\n                             invoker("200 OK", headerOut, val.toStyledString());\n                         });\n-\n+    });\n \n #else\n+\n+    API_REGIST_INVOKER(hook,on_stream_not_found,{\n+        //媒体未找到事件,我们都及时拉流hks作为替代品，目的是为了测试按需拉流\n+        CHECK_SECRET();\n+        CHECK_ARGS("vhost","app","stream");\n         //通过内置支持的rtsp/rtmp按需拉流\n         addStreamProxy(allArgs["vhost"],\n                        allArgs["app"],\n@@ -648,9 +662,8 @@ void installWebApi() {\n                            }\n                            invoker("200 OK", headerOut, val.toStyledString());\n                        });\n-#endif\n     });\n-\n+#endif // !defined(_WIN32)\n \n     API_REGIST(hook,on_record_mp4,{\n         //录制mp4分片完毕事件\n@@ -701,8 +714,10 @@ void unInstallWebApi(){\n         s_proxyMap.clear();\n     }\n \n+#if !defined(_WIN32)\n     {\n         lock_guard<recursive_mutex> lck(s_ffmpegMapMtx);\n         s_ffmpegMap.clear();\n     }\n+#endif\n }\n\\ No newline at end of file\ndiff --git a/server/WebHook.cpp b/server/WebHook.cpp\nindex 02136f10..038bc321 100644\n--- a/server/WebHook.cpp\n+++ b/server/WebHook.cpp\n@@ -27,7 +27,6 @@\n #include <sstream>\n #include <unordered_map>\n #include <mutex>\n-#include "System.h"\n #include "jsoncpp/json.h"\n #include "Util/logger.h"\n #include "Util/util.h"\ndiff --git a/server/main.cpp b/server/main.cpp\nindex 11b7905a..31ed2837 100644\n--- a/server/main.cpp\n+++ b/server/main.cpp\n@@ -43,9 +43,12 @@\n #include "RtmpMuxer/FlvMuxer.h"\n #include "Player/PlayerProxy.h"\n #include "Http/WebSocketSession.h"\n-#include "System.h"\n #include "WebApi.h"\n \n+#if !defined(_WIN32)\n+#include "System.h"\n+#endif//!defined(_WIN32)\n+\n using namespace std;\n using namespace toolkit;\n using namespace mediakit;\n@@ -105,6 +108,7 @@ public:\n     CMD_main() {\n         _parser.reset(new OptionParser(nullptr));\n \n+#if !defined(_WIN32)\n         (*_parser) << Option(\'d\',/*该选项简称，如果是\\x00则说明无简称*/\n                              "daemon",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n                              Option::ArgNone,/*该选项后面必须跟值*/\n@@ -112,6 +116,7 @@ public:\n                              false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/\n                              "是否以Daemon方式启动",/*该选项说明文字*/\n                              nullptr);\n+#endif//!defined(_WIN32)\n \n         (*_parser) << Option(\'l\',/*该选项简称，如果是\\x00则说明无简称*/\n                              "level",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n@@ -158,12 +163,14 @@ extern void unInstallWebApi();\n extern void installWebHook();\n extern void unInstallWebHook();\n \n+#if !defined(_WIN32)\n static void inline listen_shell_input(){\n     cout << "> 欢迎进入命令模式，你可以输入\\"help\\"命令获取帮助" << endl;\n     cout << "> " << std::flush;\n \n     signal(SIGTTOU,SIG_IGN);\n     signal(SIGTTIN,SIG_IGN);\n+\n     SockUtil::setNoBlocked(STDIN_FILENO);\n     auto oninput = [](int event) {\n         if (event & Event_Read) {\n@@ -193,6 +200,8 @@ static void inline listen_shell_input(){\n     };\n     EventPollerPool::Instance().getFirstPoller()->addEvent(STDIN_FILENO, Event_Read | Event_Error | Event_LT,oninput);\n }\n+#endif//!defined(_WIN32)\n+\n int main(int argc,char *argv[]) {\n     {\n         CMD_main cmd_main;\n@@ -218,13 +227,14 @@ int main(int argc,char *argv[]) {\n         Logger::Instance().add(std::make_shared<FileChannel>("FileChannel", exePath() + ".log", logLevel));\n #endif\n \n+#if !defined(_WIN32)\n         if (bDaemon) {\n             //启动守护进程\n             System::startDaemon();\n         }\n-\n         //开启崩溃捕获等\n         System::systemSetup();\n+#endif//!defined(_WIN32)\n \n         //启动异步日志线程\n         Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());\n@@ -275,10 +285,12 @@ int main(int argc,char *argv[]) {\n         installWebHook();\n         InfoL << "已启动http hook 接口";\n \n+#if !defined(_WIN32)\n         if (!bDaemon) {\n             //交互式shell输入\n             listen_shell_input();\n         }\n+#endif\n \n         //设置退出信号处理函数\n         static semaphore sem;\n@@ -287,7 +299,10 @@ int main(int argc,char *argv[]) {\n             signal(SIGINT, SIG_IGN);// 设置退出信号\n             sem.post();\n         });// 设置退出信号\n+\n+#if !defined(_WIN32)\n         signal(SIGHUP, [](int) { mediakit::loadIniConfig(ini_file.data()); });\n+#endif\n         sem.wait();\n     }\n     unInstallWebApi();\n', 'commit_subject': '移植Windows', 'repo_name': 'xiongziliang/ZLMediaKit', 'committer': {'name': 'xiongziliang', 'email': '2c1ef1aaf06ec408d7b37a0565446d2bba75fe9a@qq.com', 'time_sec': '1560589630', 'tz_offset': '480', 'date': {'seconds': '1560589630'}}, 'commit_tree': '1735bb3c07a5d9f7ec97665f679da2d8910aa1a4', 'commit_message': '移植Windows\n', 'language_name': 'HTML', 'license': 'mit', 'hunks': '@@ -43,9 +43,12 @@\n #include "RtmpMuxer/FlvMuxer.h"\n #include "Player/PlayerProxy.h"\n #include "Http/WebSocketSession.h"\n-#include "System.h"\n #include "WebApi.h"\n \n+#if !defined(_WIN32)\n+#include "System.h"\n+#endif//!defined(_WIN32)\n+\n using namespace std;\n using namespace toolkit;\n using namespace mediakit;\n@@ -105,6 +108,7 @@ public:\n     CMD_main() {\n         _parser.reset(new OptionParser(nullptr));\n \n+#if !defined(_WIN32)\n         (*_parser) << Option(\'d\',/*该选项简称，如果是\\x00则说明无简称*/\n                              "daemon",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n                              Option::ArgNone,/*该选项后面必须跟值*/\n@@ -112,6 +116,7 @@ public:\n                              false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/\n                              "是否以Daemon方式启动",/*该选项说明文字*/\n                              nullptr);\n+#endif//!defined(_WIN32)\n \n         (*_parser) << Option(\'l\',/*该选项简称，如果是\\x00则说明无简称*/\n                              "level",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n@@ -158,12 +163,14 @@ extern void unInstallWebApi();\n extern void installWebHook();\n extern void unInstallWebHook();\n \n+#if !defined(_WIN32)\n static void inline listen_shell_input(){\n     cout << "> 欢迎进入命令模式，你可以输入\\"help\\"命令获取帮助" << endl;\n     cout << "> " << std::flush;\n \n     signal(SIGTTOU,SIG_IGN);\n     signal(SIGTTIN,SIG_IGN);\n+\n     SockUtil::setNoBlocked(STDIN_FILENO);\n     auto oninput = [](int event) {\n         if (event & Event_Read) {\n@@ -193,6 +200,8 @@ static void inline listen_shell_input(){\n     };\n     EventPollerPool::Instance().getFirstPoller()->addEvent(STDIN_FILENO, Event_Read | Event_Error | Event_LT,oninput);\n }\n+#endif//!defined(_WIN32)\n+\n int main(int argc,char *argv[]) {\n     {\n         CMD_main cmd_main;\n@@ -218,13 +227,14 @@ int main(int argc,char *argv[]) {\n         Logger::Instance().add(std::make_shared<FileChannel>("FileChannel", exePath() + ".log", logLevel));\n #endif\n \n+#if !defined(_WIN32)\n         if (bDaemon) {\n             //启动守护进程\n             System::startDaemon();\n         }\n-\n         //开启崩溃捕获等\n         System::systemSetup();\n+#endif//!defined(_WIN32)\n \n         //启动异步日志线程\n         Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());\n@@ -275,10 +285,12 @@ int main(int argc,char *argv[]) {\n         installWebHook();\n         InfoL << "已启动http hook 接口";\n \n+#if !defined(_WIN32)\n         if (!bDaemon) {\n             //交互式shell输入\n             listen_shell_input();\n         }\n+#endif\n \n         //设置退出信号处理函数\n         static semaphore sem;\n@@ -287,7 +299,10 @@ int main(int argc,char *argv[]) {\n             signal(SIGINT, SIG_IGN);// 设置退出信号\n             sem.post();\n         });// 设置退出信号\n+\n+#if !defined(_WIN32)\n         signal(SIGHUP, [](int) { mediakit::loadIniConfig(ini_file.data()); });\n+#endif\n         sem.wait();\n     }\n     unInstallWebApi();\n', 'addition_count': 17, 'deletion_count': 2, 'src_file': 'a/server/main.cpp', 'tgt_file': 'b/server/main.cpp', 'file_extension': '.cpp', 'before_file': '\ufeff/*\n * Copyright (c) 2016 The ZLMediaKit project authors. All Rights Reserved.\n *\n * This file is part of ZLMediaKit(https://github.com/xia-chu/ZLMediaKit).\n *\n * Use of this source code is governed by MIT license that can be found in the\n * LICENSE file in the root of the source tree. All contributing project authors\n * may be found in the AUTHORS file in the root of the source tree.\n */\n\n#include <signal.h>\n#include <iostream>\n#include "Util/File.h"\n#include "Util/logger.h"\n#include "Util/SSLBox.h"\n#include "Util/onceToken.h"\n#include "Util/CMD.h"\n#include "Network/TcpServer.h"\n#include "Network/UdpServer.h"\n#include "Poller/EventPoller.h"\n#include "Common/config.h"\n#include "Rtsp/RtspSession.h"\n#include "Rtmp/RtmpSession.h"\n#include "Shell/ShellSession.h"\n#include "Http/WebSocketSession.h"\n#include "Rtp/RtpServer.h"\n#include "WebApi.h"\n#include "WebHook.h"\n\n#if defined(ENABLE_WEBRTC)\n#include "../webrtc/WebRtcTransport.h"\n#include "../webrtc/WebRtcSession.h"\n#endif\n\n#if defined(ENABLE_SRT)\n#include "../srt/SrtSession.hpp"\n#include "../srt/SrtTransport.hpp"\n#endif\n\n#if defined(ENABLE_VERSION)\n#include "version.h"\n#endif\n#include "RtmpMuxer/FlvMuxer.h"\n#include "Player/PlayerProxy.h"\n#include "Http/WebSocketSession.h"\n#include "System.h"\n#include "WebApi.h"\n\nusing namespace std;\nusing namespace toolkit;\nusing namespace mediakit;\n#define HTTP_FIELD "http."\nconst string kPort = HTTP_FIELD"port";\nconst string kSSLPort = HTTP_FIELD"sslport";\nonceToken token1([](){\n    mINI::Instance()[kPort] = 80;\n    mINI::Instance()[kSSLPort] = 443;\n},nullptr);\n}//namespace Http\n\n////////////SHELL配置///////////\nnamespace Shell {\n#define SHELL_FIELD "shell."\nconst string kPort = SHELL_FIELD"port";\nonceToken token1([](){\n    mINI::Instance()[kPort] = 9000;\n},nullptr);\n} //namespace Shell\n\n////////////RTSP服务器配置///////////\nnamespace Rtsp {\n#define RTSP_FIELD "rtsp."\nconst string kPort = RTSP_FIELD"port";\nconst string kSSLPort = RTSP_FIELD"sslport";\nonceToken token1([](){\n    mINI::Instance()[kPort] = 554;\n    mINI::Instance()[kSSLPort] = 332;\n},nullptr);\n\n} //namespace Rtsp\n\n////////////RTMP服务器配置///////////\nnamespace Rtmp {\n#define RTMP_FIELD "rtmp."\nconst string kPort = RTMP_FIELD"port";\nconst string kSSLPort = RTMP_FIELD"sslport";\nonceToken token1([](){\n    mINI::Instance()[kPort] = 1935;\n    mINI::Instance()[kSSLPort] = 19350;\n},nullptr);\n} //namespace RTMP\n\n////////////Rtp代理相关配置///////////\nnamespace RtpProxy {\n#define RTP_PROXY_FIELD "rtp_proxy."\nconst string kPort = RTP_PROXY_FIELD"port";\nonceToken token1([](){\n    mINI::Instance()[kPort] = 10000;\n},nullptr);\n} //namespace RtpProxy\n\n}  // namespace mediakit\n\n\n    CMD_main() {\n        _parser.reset(new OptionParser(nullptr));\n\n        (*_parser) << Option(\'d\',/*该选项简称，如果是\\x00则说明无简称*/\n                             "daemon",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n                             Option::ArgNone,/*该选项后面必须跟值*/\n                             "daemon",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/\n                             "是否以Daemon方式启动",/*该选项说明文字*/\n                             nullptr);\n\n        (*_parser) << Option(\'l\',/*该选项简称，如果是\\x00则说明无简称*/\n                             "level",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n        (*_parser) << Option(\'l\',/*该选项简称，如果是\\x00则说明无简称*/\n                             "level",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n                             Option::ArgRequired,/*该选项后面必须跟值*/\n                             to_string(LTrace).data(),/*该选项默认值*/\n                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/\n                             "日志等级,LTrace~LError(0~4)",/*该选项说明文字*/\n                             nullptr);\n\n        (*_parser) << Option(\'m\',/*该选项简称，如果是\\x00则说明无简称*/\n                             "max_day",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n                             Option::ArgRequired,/*该选项后面必须跟值*/\n                             "7",/*该选项默认值*/\n                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/\n                             "日志最多保存天数",/*该选项说明文字*/\n                             nullptr);\n\n        (*_parser) << Option(\'c\',/*该选项简称，如果是\\x00则说明无简称*/\n                             "config",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n                             Option::ArgRequired,/*该选项后面必须跟值*/\n                             (exeDir() + "config.ini").data(),/*该选项默认值*/\n                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/\n                             "配置文件路径",/*该选项说明文字*/\n                             nullptr);\n\n        (*_parser) << Option(\'s\',/*该选项简称，如果是\\x00则说明无简称*/\n                             "ssl",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n                             Option::ArgRequired,/*该选项后面必须跟值*/\n                             (exeDir() + "default.pem").data(),/*该选项默认值*/\n                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/\n                             "ssl证书文件或文件夹,支持p12/pem类型",/*该选项说明文字*/\n                             nullptr);\n\n        (*_parser) << Option(\'t\',/*该选项简称，如果是\\x00则说明无简称*/\n                             "threads",/*该选项全称,每个选项必须有全称；不得为null或空字符串*/\n                             Option::ArgRequired,/*该选项后面必须跟值*/\n                             to_string(thread::hardware_concurrency()).data(),/*该选项默认值*/\n                             false,/*该选项是否必须赋值，如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/\n                             "启动事件触发线程数",/*该选项说明文字*/\n                             nullptr);\n\nextern void installWebHook();\nextern void unInstallWebHook();\n\nstatic void inline listen_shell_input(){\n    cout << "> 欢迎进入命令模式，你可以输入\\"help\\"命令获取帮助" << endl;\n    cout << "> " << std::flush;\n\n    signal(SIGTTOU,SIG_IGN);\n    signal(SIGTTIN,SIG_IGN);\n    SockUtil::setNoBlocked(STDIN_FILENO);\n    auto oninput = [](int event) {\n        if (event & Event_Read) {\n        return "主程序命令参数";\n    }\n};\n\n//全局变量，在WebApi中用于保存配置文件用\nstring g_ini_file;\n\nint start_main(int argc,char *argv[]) {\n    {\n        CMD_main cmd_main;\n        try {\n            cmd_main.operator()(argc, argv);\n        } catch (ExitException &) {\n            return 0;\n        } catch (std::exception &ex) {\n            cout << ex.what() << endl;\n            return -1;\n        }\n\n        bool bDaemon = cmd_main.hasKey("daemon");\n        LogLevel logLevel = (LogLevel) cmd_main["level"].as<int>();\n        logLevel = MIN(MAX(logLevel, LTrace), LError);\n        g_ini_file = cmd_main["config"];\n    };\n    EventPollerPool::Instance().getFirstPoller()->addEvent(STDIN_FILENO, Event_Read | Event_Error | Event_LT,oninput);\n}\nint main(int argc,char *argv[]) {\n    {\n        CMD_main cmd_main;\n        fileChannel->setMaxDay(cmd_main["max_day"]);\n        Logger::Instance().add(fileChannel);\n#endif//\n\n#if !defined(_WIN32)\n        pid_t pid = getpid();\n        bool kill_parent_if_failed = true;\n        if (bDaemon) {\n            //启动守护进程\n            System::startDaemon(kill_parent_if_failed);\n        }\n        //开启崩溃捕获等\n        System::systemSetup();\n#endif//!defined(_WIN32)\n\n        //启动异步日志线程\n        Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());\n        //加载配置文件，如果配置文件不存在就创建一个\n        loadIniConfig(g_ini_file.data());\n        Logger::Instance().add(std::make_shared<FileChannel>("FileChannel", exePath() + ".log", logLevel));\n#endif\n\n        if (bDaemon) {\n            //启动守护进程\n            System::startDaemon();\n        }\n\n        //开启崩溃捕获等\n        System::systemSetup();\n\n        //启动异步日志线程\n        Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());\n\n        uint16_t shellPort = mINI::Instance()[Shell::kPort];\n        uint16_t rtspPort = mINI::Instance()[Rtsp::kPort];\n        uint16_t rtspsPort = mINI::Instance()[Rtsp::kSSLPort];\n        uint16_t rtmpPort = mINI::Instance()[Rtmp::kPort];\n        uint16_t rtmpsPort = mINI::Instance()[Rtmp::kSSLPort];\n        uint16_t httpPort = mINI::Instance()[Http::kPort];\n        uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];\n        uint16_t rtpPort = mINI::Instance()[RtpProxy::kPort];\n\n        //设置poller线程数,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效\n        EventPollerPool::setPoolSize(threads);\n\n        //简单的telnet服务器，可用于服务器调试，但是不能使用23端口，否则telnet上了莫名其妙的现象\n        //测试方法:telnet 127.0.0.1 9000\n        auto shellSrv = std::make_shared<TcpServer>();\n\n        //rtsp[s]服务器, 可用于诸如亚马逊echo show这样的设备访问\n        auto rtspSrv = std::make_shared<TcpServer>();;\n        auto rtspSSLSrv = std::make_shared<TcpServer>();;\n\n        //rtmp[s]服务器\n        auto rtmpSrv = std::make_shared<TcpServer>();;\n        auto rtmpsSrv = std::make_shared<TcpServer>();;\n\n        //http[s]服务器\n        auto httpSrv = std::make_shared<TcpServer>();;\n        auto httpsSrv = std::make_shared<TcpServer>();;\n\n#if defined(ENABLE_RTPPROXY)\n        //GB28181 rtp推流端口，支持UDP/TCP\n        auto rtpServer = std::make_shared<RtpServer>();\n#endif//defined(ENABLE_RTPPROXY)\n\n#if defined(ENABLE_WEBRTC)\n        //webrtc udp服务器\n        auto rtcSrv = std::make_shared<UdpServer>();\n        rtcSrv->setOnCreateSocket([](const EventPoller::Ptr &poller, const Buffer::Ptr &buf, struct sockaddr *, int) {\n            if (!buf) {\n                return Socket::createSocket(poller, false);\n            }\n            auto new_poller = WebRtcSession::queryPoller(buf);\n            if (!new_poller) {\n                //该数据对应的webrtc对象未找到，丢弃之\n        installWebHook();\n        InfoL << "已启动http hook 接口";\n\n        if (!bDaemon) {\n            //交互式shell输入\n            listen_shell_input();\n        }\n\n        //设置退出信号处理函数\n        static semaphore sem;\n                return Socket::createSocket(poller, false);\n            }\n            signal(SIGINT, SIG_IGN);// 设置退出信号\n            sem.post();\n        });// 设置退出信号\n        signal(SIGHUP, [](int) { mediakit::loadIniConfig(ini_file.data()); });\n        sem.wait();\n    }\n    unInstallWebApi();\n\n        try {\n            //rtsp服务器，端口默认554\n            if (rtspPort) { rtspSrv->start<RtspSession>(rtspPort); }\n            //rtsps服务器，端口默认322\n            if (rtspsPort) { rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort); }\n\n            //rtmp服务器，端口默认1935\n            if (rtmpPort) { rtmpSrv->start<RtmpSession>(rtmpPort); }\n            //rtmps服务器，端口默认19350\n            if (rtmpsPort) { rtmpsSrv->start<RtmpSessionWithSSL>(rtmpsPort); }\n\n            //http服务器，端口默认80\n            if (httpPort) { httpSrv->start<HttpSession>(httpPort); }\n            //https服务器，端口默认443\n            if (httpsPort) { httpsSrv->start<HttpsSession>(httpsPort); }\n\n            //telnet远程调试服务器\n            if (shellPort) { shellSrv->start<ShellSession>(shellPort); }\n\n#if defined(ENABLE_RTPPROXY)\n            //创建rtp服务器\n            if (rtpPort) { rtpServer->start(rtpPort); }\n#endif//defined(ENABLE_RTPPROXY)\n\n#if defined(ENABLE_WEBRTC)\n            //webrtc udp服务器\n            if (rtcPort) { rtcSrv->start<WebRtcSession>(rtcPort); }\n#endif//defined(ENABLE_WEBRTC)\n\n#if defined(ENABLE_SRT)\n        // srt udp服务器\n        if(srtPort) { srtSrv->start<SRT::SrtSession>(srtPort); }\n#endif//defined(ENABLE_SRT)\n\n        } catch (std::exception &ex) {\n            WarnL << "端口占用或无权限:" << ex.what() << endl;\n            ErrorL << "程序启动失败，请修改配置文件中端口号后重试!" << endl;\n            sleep(1);\n#if !defined(_WIN32)\n            if (pid != getpid() && kill_parent_if_failed) {\n                //杀掉守护进程\n                kill(pid, SIGINT);\n            }\n#endif\n            return -1;\n        }\n\n        installWebApi();\n        InfoL << "已启动http api 接口";\n        installWebHook();\n        InfoL << "已启动http hook 接口";\n\n        //设置退出信号处理函数\n        static semaphore sem;\n        signal(SIGINT, [](int) {\n            InfoL << "SIGINT:exit";\n            signal(SIGINT, SIG_IGN);// 设置退出信号\n            sem.post();\n        });// 设置退出信号\n\n#if !defined(_WIN32)\n        signal(SIGHUP, [](int) { mediakit::loadIniConfig(g_ini_file.data()); });\n#endif\n        sem.wait();\n    }\n    unInstallWebApi();\n    unInstallWebHook();\n    //休眠1秒再退出，防止资源释放顺序错误\n    InfoL << "程序退出中,请等待...";\n    sleep(1);\n    InfoL << "程序退出完毕!";\n    return 0;\n}\n\n#ifndef DISABLE_MAIN\nint main(int argc,char *argv[]) {\n    return start_main(argc,argv);\n}\n#endif //DISABLE_MAIN\n\n\n', 'message': '移植Windows\n', 'author_name': 'xiongziliang', 'lang': 'cpp'}