增加windows系统服务
This commit is contained in:
parent
3554fe920f
commit
5bc7116e2a
|
|
@ -4,9 +4,9 @@ add_library(jsoncpp STATIC ${jsoncpp_src_list})
|
||||||
|
|
||||||
|
|
||||||
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
if (CMAKE_SYSTEM_NAME MATCHES "Windows")
|
||||||
set(MediaServer_src_list ./WebApi.cpp ./WebHook.cpp main.cpp)
|
set(MediaServer_src_list ./MiniDumper.cpp ./WebApi.cpp ./WebHook.cpp win32main.cpp)
|
||||||
else()
|
else()
|
||||||
file(GLOB MediaServer_src_list ./*.cpp ./*.h)
|
file(GLOB MediaServer_src_list ./WebApi.cpp ./WebHook.cpp ./System.cpp ./Process.cpp ./FFmpegSource.cpp main.cpp)
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
#message(STATUS ${MediaServer_src_list})
|
#message(STATUS ${MediaServer_src_list})
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,20 @@
|
||||||
|
#ifndef Server_MediaServer_H
|
||||||
|
#define Server_MediaServer_H
|
||||||
|
|
||||||
|
|
||||||
|
//## package Server
|
||||||
|
|
||||||
|
enum
|
||||||
|
{
|
||||||
|
|
||||||
|
MediaServerStartingUpState = 0,
|
||||||
|
MediaServerRunningState = 1,
|
||||||
|
MediaServerRefusingConnectionsState = 2,
|
||||||
|
MediaServerFatalErrorState = 3,// a fatal error has occurred, not shutting down yet
|
||||||
|
MediaServerShuttingDownState = 4,
|
||||||
|
MediaServerIdleState = 5 // Like refusing connections state, but will also kill any currently connected clients
|
||||||
|
};
|
||||||
|
typedef int MediaServer_ServerState;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,325 @@
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
#include <tchar.h>
|
||||||
|
#include <strsafe.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <assert.h>
|
||||||
|
#include <time.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <strsafe.h>
|
||||||
|
#include <dbghelp.h>
|
||||||
|
#include "miniDumper.h"
|
||||||
|
|
||||||
|
#ifdef UNICODE
|
||||||
|
#define _tcssprintf wsprintf
|
||||||
|
#define tcsplitpath _wsplitpath
|
||||||
|
#else
|
||||||
|
#define _tcssprintf sprintf
|
||||||
|
#define tcsplitpath _splitpath
|
||||||
|
#endif
|
||||||
|
|
||||||
|
const int USER_DATA_BUFFER_SIZE = 4096;
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// GLOBALS
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
CMiniDumper* CMiniDumper::s_pMiniDumper = NULL;
|
||||||
|
LPCRITICAL_SECTION CMiniDumper::s_pCriticalSection = NULL;
|
||||||
|
|
||||||
|
// Based on dbghelp.h
|
||||||
|
typedef BOOL (WINAPI *MINIDUMPWRITEDUMP)(HANDLE hProcess,
|
||||||
|
DWORD dwPid,
|
||||||
|
HANDLE hFile,
|
||||||
|
MINIDUMP_TYPE DumpType,
|
||||||
|
CONST PMINIDUMP_EXCEPTION_INFORMATION ExceptionParam,
|
||||||
|
CONST PMINIDUMP_USER_STREAM_INFORMATION UserStreamParam,
|
||||||
|
CONST PMINIDUMP_CALLBACK_INFORMATION CallbackParam);
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Name: CMiniDumper()
|
||||||
|
// Desc: Constructor
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
CMiniDumper::CMiniDumper( bool bPromptUserForMiniDump )
|
||||||
|
{
|
||||||
|
// Our CMiniDumper should act alone as a singleton.
|
||||||
|
assert( !s_pMiniDumper );
|
||||||
|
|
||||||
|
s_pMiniDumper = this;
|
||||||
|
m_bPromptUserForMiniDump = bPromptUserForMiniDump;
|
||||||
|
|
||||||
|
// The SetUnhandledExceptionFilter function enables an application to
|
||||||
|
// supersede the top-level exception handler of each thread and process.
|
||||||
|
// After calling this function, if an exception occurs in a process
|
||||||
|
// that is not being debugged, and the exception makes it to the
|
||||||
|
// unhandled exception filter, that filter will call the exception
|
||||||
|
// filter function specified by the lpTopLevelExceptionFilter parameter.
|
||||||
|
::SetUnhandledExceptionFilter( unhandledExceptionHandler );
|
||||||
|
|
||||||
|
// Since dbghelp.dll is not inherently thread-safe, making calls into it
|
||||||
|
// from more than one thread simultaneously may yield undefined behavior.
|
||||||
|
// This means that if your application has multiple threads, or is
|
||||||
|
// called by multiple threads in a non-synchronized manner, you need to
|
||||||
|
// make sure that all calls into dbghelp.dll are isolated via a global
|
||||||
|
// critical section.
|
||||||
|
s_pCriticalSection = new CRITICAL_SECTION;
|
||||||
|
|
||||||
|
if( s_pCriticalSection )
|
||||||
|
InitializeCriticalSection( s_pCriticalSection );
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Name: ~CMiniDumper()
|
||||||
|
// Desc: Destructor
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
CMiniDumper::~CMiniDumper( void )
|
||||||
|
{
|
||||||
|
if( s_pCriticalSection )
|
||||||
|
{
|
||||||
|
DeleteCriticalSection( s_pCriticalSection );
|
||||||
|
delete s_pCriticalSection;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Name: unhandledExceptionHandler()
|
||||||
|
// Desc: Call-back filter function for unhandled exceptions
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
LONG CMiniDumper::unhandledExceptionHandler( _EXCEPTION_POINTERS *pExceptionInfo )
|
||||||
|
{
|
||||||
|
if( !s_pMiniDumper )
|
||||||
|
return EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
|
||||||
|
return s_pMiniDumper->writeMiniDump( pExceptionInfo );
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Name: setMiniDumpFileName()
|
||||||
|
// Desc:
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
void CMiniDumper::setMiniDumpFileName( void )
|
||||||
|
{
|
||||||
|
time_t currentTime;
|
||||||
|
time(¤tTime);
|
||||||
|
|
||||||
|
sprintf( m_szMiniDumpPath,
|
||||||
|
"%s%s.%ld.dmp",
|
||||||
|
m_szAppPath,
|
||||||
|
m_szAppBaseName,
|
||||||
|
currentTime);
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Name: getImpersonationToken()
|
||||||
|
// Desc: The method acts as a potential workaround for the fact that the
|
||||||
|
// current thread may not have a token assigned to it, and if not, the
|
||||||
|
// process token is received.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
bool CMiniDumper::getImpersonationToken( HANDLE* phToken )
|
||||||
|
{
|
||||||
|
*phToken = NULL;
|
||||||
|
|
||||||
|
if( !OpenThreadToken( GetCurrentThread(),
|
||||||
|
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
|
||||||
|
TRUE,
|
||||||
|
phToken) )
|
||||||
|
{
|
||||||
|
if( GetLastError() == ERROR_NO_TOKEN )
|
||||||
|
{
|
||||||
|
// No impersonation token for the current thread is available.
|
||||||
|
// Let's go for the process token instead.
|
||||||
|
if( !OpenProcessToken( GetCurrentProcess(),
|
||||||
|
TOKEN_QUERY | TOKEN_ADJUST_PRIVILEGES,
|
||||||
|
phToken) )
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Name: enablePrivilege()
|
||||||
|
// Desc: Since a MiniDump contains a lot of meta-data about the OS and
|
||||||
|
// application state at the time of the dump, it is a rather privileged
|
||||||
|
// operation. This means we need to set the SeDebugPrivilege to be able
|
||||||
|
// to call MiniDumpWriteDump.
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
BOOL CMiniDumper::enablePrivilege( LPCTSTR pszPriv, HANDLE hToken, TOKEN_PRIVILEGES* ptpOld )
|
||||||
|
{
|
||||||
|
BOOL bOk = FALSE;
|
||||||
|
|
||||||
|
TOKEN_PRIVILEGES tp;
|
||||||
|
tp.PrivilegeCount = 1;
|
||||||
|
tp.Privileges[0].Attributes = SE_PRIVILEGE_ENABLED;
|
||||||
|
bOk = LookupPrivilegeValue( 0, pszPriv, &tp.Privileges[0].Luid );
|
||||||
|
|
||||||
|
if( bOk )
|
||||||
|
{
|
||||||
|
DWORD cbOld = sizeof(*ptpOld);
|
||||||
|
bOk = AdjustTokenPrivileges( hToken, FALSE, &tp, cbOld, ptpOld, &cbOld );
|
||||||
|
}
|
||||||
|
|
||||||
|
return (bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()));
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Name: restorePrivilege()
|
||||||
|
// Desc:
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
BOOL CMiniDumper::restorePrivilege( HANDLE hToken, TOKEN_PRIVILEGES* ptpOld )
|
||||||
|
{
|
||||||
|
BOOL bOk = AdjustTokenPrivileges(hToken, FALSE, ptpOld, 0, NULL, NULL);
|
||||||
|
return ( bOk && (ERROR_NOT_ALL_ASSIGNED != GetLastError()) );
|
||||||
|
}
|
||||||
|
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
// Name: writeMiniDump()
|
||||||
|
// Desc:
|
||||||
|
//-----------------------------------------------------------------------------
|
||||||
|
LONG CMiniDumper::writeMiniDump( _EXCEPTION_POINTERS *pExceptionInfo )
|
||||||
|
{
|
||||||
|
LONG retval = EXCEPTION_CONTINUE_SEARCH;
|
||||||
|
m_pExceptionInfo = pExceptionInfo;
|
||||||
|
|
||||||
|
HANDLE hImpersonationToken = NULL;
|
||||||
|
if( !getImpersonationToken( &hImpersonationToken ) )
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
// You have to find the right dbghelp.dll.
|
||||||
|
// Look next to the EXE first since the one in System32 might be old (Win2k)
|
||||||
|
|
||||||
|
HMODULE hDll = NULL;
|
||||||
|
char szDbgHelpPath[MAX_PATH];
|
||||||
|
|
||||||
|
if( GetModuleFileNameA( NULL, m_szAppPath, _MAX_PATH ) )
|
||||||
|
{
|
||||||
|
char* pSlash =strrchr((char*)m_szAppPath, '\\' );
|
||||||
|
|
||||||
|
if( pSlash )
|
||||||
|
{
|
||||||
|
_tcscpy( (char*)m_szAppBaseName, (const char*)pSlash + 1);
|
||||||
|
*(pSlash+1) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
wcscpy( (wchar_t*)szDbgHelpPath, (const wchar_t*)m_szAppPath );
|
||||||
|
wcscat( (wchar_t*)szDbgHelpPath, (const wchar_t*)"dbghelp.dll");
|
||||||
|
|
||||||
|
hDll = ::LoadLibraryA( szDbgHelpPath );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( hDll == NULL )
|
||||||
|
{
|
||||||
|
// If we haven't found it yet - try one more time.
|
||||||
|
hDll = ::LoadLibraryA( "dbghelp.dll");
|
||||||
|
}
|
||||||
|
|
||||||
|
const char* szResult = NULL;
|
||||||
|
|
||||||
|
if( hDll )
|
||||||
|
{
|
||||||
|
// Get the address of the MiniDumpWriteDump function, which writes
|
||||||
|
// user-mode mini-dump information to a specified file.
|
||||||
|
MINIDUMPWRITEDUMP MiniDumpWriteDump =
|
||||||
|
(MINIDUMPWRITEDUMP)::GetProcAddress( hDll, "MiniDumpWriteDump" );
|
||||||
|
|
||||||
|
if( MiniDumpWriteDump != NULL )
|
||||||
|
{
|
||||||
|
char szScratch[USER_DATA_BUFFER_SIZE];
|
||||||
|
|
||||||
|
setMiniDumpFileName();
|
||||||
|
|
||||||
|
// Ask the user if he or she wants to save a mini-dump file...
|
||||||
|
sprintf( szScratch,
|
||||||
|
"There was an unexpected error:\n\nWould you "
|
||||||
|
"like to create a mini-dump file?\n\n%s ",
|
||||||
|
m_szMiniDumpPath);
|
||||||
|
|
||||||
|
// Create the mini-dump file...
|
||||||
|
HANDLE hFile = ::CreateFileA( m_szMiniDumpPath,
|
||||||
|
GENERIC_WRITE,
|
||||||
|
FILE_SHARE_WRITE,
|
||||||
|
NULL,
|
||||||
|
CREATE_ALWAYS,
|
||||||
|
FILE_ATTRIBUTE_NORMAL,
|
||||||
|
NULL );
|
||||||
|
|
||||||
|
if( hFile != INVALID_HANDLE_VALUE )
|
||||||
|
{
|
||||||
|
_MINIDUMP_EXCEPTION_INFORMATION ExInfo;
|
||||||
|
ExInfo.ThreadId = ::GetCurrentThreadId();
|
||||||
|
ExInfo.ExceptionPointers = pExceptionInfo;
|
||||||
|
ExInfo.ClientPointers = NULL;
|
||||||
|
|
||||||
|
// We need the SeDebugPrivilege to be able to run MiniDumpWriteDump
|
||||||
|
TOKEN_PRIVILEGES tp;
|
||||||
|
BOOL bPrivilegeEnabled = enablePrivilege( SE_DEBUG_NAME, hImpersonationToken, &tp );
|
||||||
|
|
||||||
|
BOOL bOk;
|
||||||
|
|
||||||
|
// dbghelp.dll is not thread-safe, so we need to restrict access...
|
||||||
|
EnterCriticalSection( s_pCriticalSection );
|
||||||
|
{
|
||||||
|
// Write out the mini-dump data to the file...
|
||||||
|
bOk = MiniDumpWriteDump( GetCurrentProcess(),
|
||||||
|
GetCurrentProcessId(),
|
||||||
|
hFile,
|
||||||
|
MiniDumpNormal,
|
||||||
|
&ExInfo,
|
||||||
|
NULL,
|
||||||
|
NULL );
|
||||||
|
}
|
||||||
|
LeaveCriticalSection( s_pCriticalSection );
|
||||||
|
|
||||||
|
// Restore the privileges when done
|
||||||
|
if( bPrivilegeEnabled )
|
||||||
|
restorePrivilege( hImpersonationToken, &tp );
|
||||||
|
|
||||||
|
if( bOk )
|
||||||
|
{
|
||||||
|
szResult = NULL;
|
||||||
|
retval = EXCEPTION_EXECUTE_HANDLER;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf( szScratch,
|
||||||
|
"Failed to save the mini-dump file to '%s' (error %d)",
|
||||||
|
m_szMiniDumpPath,
|
||||||
|
GetLastError() );
|
||||||
|
|
||||||
|
szResult = szScratch;
|
||||||
|
}
|
||||||
|
|
||||||
|
::CloseHandle( hFile );
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
sprintf( szScratch,
|
||||||
|
"Failed to create the mini-dump file '%s' (error %d)",
|
||||||
|
m_szMiniDumpPath,
|
||||||
|
GetLastError() );
|
||||||
|
|
||||||
|
szResult = szScratch;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
szResult = "Call to GetProcAddress failed to find MiniDumpWriteDump. "
|
||||||
|
"The dbghelp.dll is possibly outdated." ;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
szResult = "Call to LoadLibrary failed to find dbghelp.dll.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if( szResult && m_bPromptUserForMiniDump )
|
||||||
|
::MessageBoxA( NULL, szResult, NULL, MB_OK );
|
||||||
|
|
||||||
|
TerminateProcess( GetCurrentProcess(), 0 );
|
||||||
|
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef MINIDUMPER_H
|
||||||
|
#define MINIDUMPER_H
|
||||||
|
|
||||||
|
#ifdef _WIN32
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
class CMiniDumper
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
|
||||||
|
CMiniDumper(bool bPromptUserForMiniDump);
|
||||||
|
~CMiniDumper(void);
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
static LONG WINAPI unhandledExceptionHandler(struct _EXCEPTION_POINTERS *pExceptionInfo);
|
||||||
|
void setMiniDumpFileName(void);
|
||||||
|
bool getImpersonationToken(HANDLE* phToken);
|
||||||
|
BOOL enablePrivilege(LPCTSTR pszPriv, HANDLE hToken, TOKEN_PRIVILEGES* ptpOld);
|
||||||
|
BOOL restorePrivilege(HANDLE hToken, TOKEN_PRIVILEGES* ptpOld);
|
||||||
|
LONG writeMiniDump(_EXCEPTION_POINTERS *pExceptionInfo );
|
||||||
|
|
||||||
|
_EXCEPTION_POINTERS *m_pExceptionInfo;
|
||||||
|
char m_szMiniDumpPath[MAX_PATH];
|
||||||
|
char m_szAppPath[MAX_PATH];
|
||||||
|
char m_szAppBaseName[MAX_PATH];
|
||||||
|
bool m_bPromptUserForMiniDump;
|
||||||
|
|
||||||
|
static CMiniDumper* s_pMiniDumper;
|
||||||
|
static LPCRITICAL_SECTION s_pCriticalSection;
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
#endif // MINIDUMPER_H
|
||||||
|
|
@ -0,0 +1,667 @@
|
||||||
|
/*
|
||||||
|
* MIT License
|
||||||
|
*
|
||||||
|
* Copyright (c) 2016-2019 xiongziliang <771730766@qq.com>
|
||||||
|
*
|
||||||
|
* This file is part of ZLMediaKit(https://github.com/xiongziliang/ZLMediaKit).
|
||||||
|
*
|
||||||
|
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||||
|
* of this software and associated documentation files (the "Software"), to deal
|
||||||
|
* in the Software without restriction, including without limitation the rights
|
||||||
|
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||||
|
* copies of the Software, and to permit persons to whom the Software is
|
||||||
|
* furnished to do so, subject to the following conditions:
|
||||||
|
*
|
||||||
|
* The above copyright notice and this permission notice shall be included in all
|
||||||
|
* copies or substantial portions of the Software.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||||
|
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||||
|
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||||
|
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||||
|
* SOFTWARE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <map>
|
||||||
|
#include <signal.h>
|
||||||
|
#include <stdio.h>
|
||||||
|
#include <iostream>
|
||||||
|
#include "Util/MD5.h"
|
||||||
|
#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 "Poller/EventPoller.h"
|
||||||
|
#include "Common/config.h"
|
||||||
|
#include "Rtsp/UDPServer.h"
|
||||||
|
#include "Rtsp/RtspSession.h"
|
||||||
|
#include "Rtmp/RtmpSession.h"
|
||||||
|
#include "Shell/ShellSession.h"
|
||||||
|
#include "Rtmp/FlvMuxer.h"
|
||||||
|
#include "Player/PlayerProxy.h"
|
||||||
|
#include "Http/WebSocketSession.h"
|
||||||
|
#include "WebApi.h"
|
||||||
|
#include "WebHook.h"
|
||||||
|
|
||||||
|
#include "MiniDumper.h"
|
||||||
|
#include "MediaServer.h"
|
||||||
|
|
||||||
|
using namespace std;
|
||||||
|
using namespace toolkit;
|
||||||
|
using namespace mediakit;
|
||||||
|
|
||||||
|
namespace mediakit {
|
||||||
|
typedef enum { rConsole = 0, rServer, rInstall, rUninstall} RunMode;
|
||||||
|
////////////HTTP配置///////////
|
||||||
|
namespace Http {
|
||||||
|
#define HTTP_FIELD "http."
|
||||||
|
#define HTTP_PORT 10080
|
||||||
|
const string kPort = HTTP_FIELD"port";
|
||||||
|
#define HTTPS_PORT 10443
|
||||||
|
const string kSSLPort = HTTP_FIELD"sslport";
|
||||||
|
onceToken token1([](){
|
||||||
|
mINI::Instance()[kPort] = HTTP_PORT;
|
||||||
|
mINI::Instance()[kSSLPort] = HTTPS_PORT;
|
||||||
|
},nullptr);
|
||||||
|
}//namespace Http
|
||||||
|
|
||||||
|
////////////SHELL配置///////////
|
||||||
|
namespace Shell {
|
||||||
|
#define SHELL_FIELD "shell."
|
||||||
|
#define SHELL_PORT 9000
|
||||||
|
const string kPort = SHELL_FIELD"port";
|
||||||
|
onceToken token1([](){
|
||||||
|
mINI::Instance()[kPort] = SHELL_PORT;
|
||||||
|
},nullptr);
|
||||||
|
} //namespace Shell
|
||||||
|
|
||||||
|
////////////RTSP服务器配置///////////
|
||||||
|
namespace Rtsp {
|
||||||
|
#define RTSP_FIELD "rtsp."
|
||||||
|
#define RTSP_PORT 10554
|
||||||
|
#define RTSPS_PORT 322
|
||||||
|
const string kPort = RTSP_FIELD"port";
|
||||||
|
const string kSSLPort = RTSP_FIELD"sslport";
|
||||||
|
onceToken token1([](){
|
||||||
|
mINI::Instance()[kPort] = RTSP_PORT;
|
||||||
|
mINI::Instance()[kSSLPort] = RTSPS_PORT;
|
||||||
|
},nullptr);
|
||||||
|
|
||||||
|
} //namespace Rtsp
|
||||||
|
|
||||||
|
////////////RTMP服务器配置///////////
|
||||||
|
namespace Rtmp {
|
||||||
|
#define RTMP_FIELD "rtmp."
|
||||||
|
#define RTMP_PORT 10935
|
||||||
|
const string kPort = RTMP_FIELD"port";
|
||||||
|
onceToken token1([](){
|
||||||
|
mINI::Instance()[kPort] = RTMP_PORT;
|
||||||
|
},nullptr);
|
||||||
|
} //namespace RTMP
|
||||||
|
} // namespace mediakit
|
||||||
|
|
||||||
|
class CMD_main;
|
||||||
|
|
||||||
|
static MediaServer_ServerState sServerState = MediaServerStartingUpState;
|
||||||
|
const char* theXMLFilePath = 0;
|
||||||
|
char* theServerName = "";
|
||||||
|
static SERVICE_STATUS_HANDLE sServiceStatusHandle = 0;
|
||||||
|
|
||||||
|
static void ReportStatus(DWORD inCurrentState, DWORD inExitCode);
|
||||||
|
static void InstallService(char* inServiceName);
|
||||||
|
static void RemoveService(char *inServiceName);
|
||||||
|
static void RunAsService(char* inServiceName);
|
||||||
|
void WINAPI ServiceControl(DWORD);
|
||||||
|
void WINAPI ServiceMain(DWORD argc, LPTSTR *argv);
|
||||||
|
|
||||||
|
MediaServer_ServerState StartServer(CMD_main& cmd_main);
|
||||||
|
void RunServer();
|
||||||
|
|
||||||
|
static void signalHandler(int signo) {
|
||||||
|
std::cerr << "Shutting down" << std::endl;
|
||||||
|
sServerState = MediaServerShuttingDownState;
|
||||||
|
}
|
||||||
|
|
||||||
|
class CMD_main : public CMD {
|
||||||
|
public:
|
||||||
|
CMD_main() {
|
||||||
|
_parser.reset(new OptionParser(nullptr));
|
||||||
|
|
||||||
|
(*_parser) << Option('r',/*该选项简称,如果是\x00则说明无简称*/
|
||||||
|
"runmode",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
|
||||||
|
Option::ArgRequired,/*该选项后面必须跟值*/
|
||||||
|
to_string(rConsole).data(),/*该选项默认值*/
|
||||||
|
true,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
|
||||||
|
"控制台运行、服务方式运行、安装到服务中,从服务中卸载(0~3)",/*该选项说明文字*/
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
(*_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,/*该选项后面必须跟值*/
|
||||||
|
"30",/*该选项默认值*/
|
||||||
|
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() + "ssl.p12").data(),/*该选项默认值*/
|
||||||
|
false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
|
||||||
|
"ssl证书文件或文件夹,支持p12/pem类型",/*该选项说明文字*/
|
||||||
|
nullptr);
|
||||||
|
|
||||||
|
(*_parser) << Option('t',/*该选项简称,如果是\x00则说明无简称*/
|
||||||
|
"threads",/*该选项全称,每个选项必须有全称;不得为null或空字符串*/
|
||||||
|
Option::ArgRequired,/*该选项后面必须跟值*/
|
||||||
|
to_string(2*thread::hardware_concurrency()).data(),/*该选项默认值*/
|
||||||
|
false,/*该选项是否必须赋值,如果没有默认值且为ArgRequired时用户必须提供该参数否则将抛异常*/
|
||||||
|
"启动事件触发线程数",/*该选项说明文字*/
|
||||||
|
nullptr);
|
||||||
|
}
|
||||||
|
|
||||||
|
virtual ~CMD_main() {}
|
||||||
|
virtual const char *description() const {
|
||||||
|
return "主程序命令参数";
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//全局变量,在WebApi中用于保存配置文件用
|
||||||
|
string g_ini_file;
|
||||||
|
CMD_main cmd_main;
|
||||||
|
|
||||||
|
int main(int argc,char *argv[]) {
|
||||||
|
bool notAService = false;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cmd_main.operator()(argc, argv);
|
||||||
|
} catch (std::exception &ex) {
|
||||||
|
cout << ex.what() << endl;
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
g_ini_file = cmd_main["config"];
|
||||||
|
RunMode rMode = (RunMode)cmd_main["runmode"].as<int>();
|
||||||
|
|
||||||
|
switch (rMode) {
|
||||||
|
case rConsole:
|
||||||
|
notAService = true;
|
||||||
|
break;
|
||||||
|
case rServer:
|
||||||
|
break;
|
||||||
|
case rInstall:
|
||||||
|
::InstallService("MediaServer");
|
||||||
|
printf("Starting the MediaServer service...\n");
|
||||||
|
::RunAsService("MediaServer");
|
||||||
|
::exit(0);
|
||||||
|
break;
|
||||||
|
case rUninstall:
|
||||||
|
printf("Removing the MediaServer service...\n");
|
||||||
|
::RemoveService("MediaServer");
|
||||||
|
::exit(0);
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
::exit(-1);
|
||||||
|
break;
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
if (notAService) {
|
||||||
|
if (signal(SIGINT, signalHandler) == SIG_ERR) {
|
||||||
|
std::cerr << "Couldn't install signal handler for SIGINT" << std::endl;
|
||||||
|
::exit(-1);
|
||||||
|
}
|
||||||
|
if (signal(SIGTERM, signalHandler) == SIG_ERR) {
|
||||||
|
std::cerr << "Couldn't install signal handler for SIGTERM" << std::endl;
|
||||||
|
::exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
// If we're running off the command-line, don't do the service initiation crap.^M
|
||||||
|
sServerState = ::StartServer(cmd_main); // No stats update interval for now
|
||||||
|
if (sServerState != MediaServerFatalErrorState) {
|
||||||
|
::RunServer();
|
||||||
|
::exit(0);
|
||||||
|
}
|
||||||
|
::exit(-1);
|
||||||
|
}
|
||||||
|
SERVICE_TABLE_ENTRY dispatchTable[] =
|
||||||
|
{
|
||||||
|
{ "", ServiceMain },
|
||||||
|
{ NULL, NULL }
|
||||||
|
};
|
||||||
|
|
||||||
|
// In case someone runs the server improperly, print out a friendly message.
|
||||||
|
printf("MediaServer must either be started from the DOS Console\n");
|
||||||
|
printf("using the -r command-line option, or using the Service Control Manager\n\n");
|
||||||
|
printf("Waiting for the Service Control Manager to start MediaServer...\n");
|
||||||
|
BOOL theErr = ::StartServiceCtrlDispatcher(dispatchTable);
|
||||||
|
if (!theErr)
|
||||||
|
{
|
||||||
|
printf("Fatal Error: Couldn't start Service\n");
|
||||||
|
::exit(-1);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void __stdcall ServiceMain(DWORD /*argc*/, LPTSTR *argv)
|
||||||
|
{
|
||||||
|
theServerName = argv[0];
|
||||||
|
|
||||||
|
sServiceStatusHandle = ::RegisterServiceCtrlHandler(theServerName, &ServiceControl);
|
||||||
|
if (sServiceStatusHandle == 0)
|
||||||
|
{
|
||||||
|
printf("Failure registering service handler");
|
||||||
|
::exit(-1);
|
||||||
|
}
|
||||||
|
|
||||||
|
//
|
||||||
|
// Report our status
|
||||||
|
::ReportStatus(SERVICE_START_PENDING, NO_ERROR);
|
||||||
|
|
||||||
|
// Start & Run the server - no stats update interval for now
|
||||||
|
sServerState = ::StartServer(cmd_main);
|
||||||
|
if (sServerState != MediaServerFatalErrorState)
|
||||||
|
{
|
||||||
|
::ReportStatus(SERVICE_RUNNING, NO_ERROR);
|
||||||
|
::RunServer(); // This function won't return until the server has died
|
||||||
|
|
||||||
|
//
|
||||||
|
// Ok, server is done...
|
||||||
|
::ReportStatus(SERVICE_STOPPED, NO_ERROR);
|
||||||
|
::exit(0);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
::ReportStatus(SERVICE_STOPPED, ERROR_BAD_COMMAND); // I dunno... report some error
|
||||||
|
::exit(-1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void WINAPI ServiceControl(DWORD inControlCode)
|
||||||
|
{
|
||||||
|
DWORD theStatusReport = SERVICE_START_PENDING;
|
||||||
|
|
||||||
|
switch (inControlCode)
|
||||||
|
{
|
||||||
|
// Stop the service.
|
||||||
|
//
|
||||||
|
case SERVICE_CONTROL_STOP:
|
||||||
|
case SERVICE_CONTROL_SHUTDOWN:
|
||||||
|
{
|
||||||
|
if (sServerState == MediaServerStartingUpState)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Signal the server to shut down.
|
||||||
|
sServerState = MediaServerShuttingDownState;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SERVICE_CONTROL_PAUSE:
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if (sServerState != RunningState)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Signal the server to refuse new connections.
|
||||||
|
theState = qtssRefusingConnectionsState;
|
||||||
|
if (theServer != NULL)
|
||||||
|
theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SERVICE_CONTROL_CONTINUE:
|
||||||
|
{
|
||||||
|
#if 0
|
||||||
|
if (theState != qtssRefusingConnectionsState)
|
||||||
|
break;
|
||||||
|
|
||||||
|
// Signal the server to refuse new connections.
|
||||||
|
theState = qtssRefusingConnectionsState;
|
||||||
|
if (theServer != NULL)
|
||||||
|
theServer->SetValue(qtssSvrState, 0, &theState, sizeof(theState));
|
||||||
|
#endif
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case SERVICE_CONTROL_INTERROGATE:
|
||||||
|
break; // Just update our status
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// Convert a QTSS state to a Win32 Service state^M
|
||||||
|
switch (sServerState)
|
||||||
|
{
|
||||||
|
case MediaServerStartingUpState: theStatusReport = SERVICE_START_PENDING; break;
|
||||||
|
case MediaServerRunningState: theStatusReport = SERVICE_RUNNING; break;
|
||||||
|
case MediaServerRefusingConnectionsState: theStatusReport = SERVICE_PAUSED; break;
|
||||||
|
case MediaServerFatalErrorState: theStatusReport = SERVICE_STOP_PENDING; break;
|
||||||
|
case MediaServerShuttingDownState: theStatusReport = SERVICE_STOP_PENDING; break;
|
||||||
|
default: theStatusReport = SERVICE_RUNNING; break;
|
||||||
|
}
|
||||||
|
|
||||||
|
printf("Reporting status from ServiceControl function\n");
|
||||||
|
::ReportStatus(theStatusReport, NO_ERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ReportStatus(DWORD inCurrentState, DWORD inExitCode)
|
||||||
|
{
|
||||||
|
static bool sFirstTime = true;
|
||||||
|
static unsigned long sCheckpoint = 0;
|
||||||
|
static SERVICE_STATUS sStatus;
|
||||||
|
|
||||||
|
if (sFirstTime)
|
||||||
|
{
|
||||||
|
sFirstTime = false;
|
||||||
|
|
||||||
|
// Setup the status structure
|
||||||
|
sStatus.dwServiceType = SERVICE_WIN32_OWN_PROCESS;
|
||||||
|
sStatus.dwCurrentState = SERVICE_START_PENDING;
|
||||||
|
sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP | SERVICE_ACCEPT_PAUSE_CONTINUE | SERVICE_ACCEPT_SHUTDOWN;
|
||||||
|
//sStatus.dwControlsAccepted = SERVICE_ACCEPT_STOP;
|
||||||
|
sStatus.dwWin32ExitCode = 0;
|
||||||
|
sStatus.dwServiceSpecificExitCode = 0;
|
||||||
|
sStatus.dwCheckPoint = 0;
|
||||||
|
sStatus.dwWaitHint = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sStatus.dwCurrentState == SERVICE_START_PENDING)
|
||||||
|
sStatus.dwCheckPoint = ++sCheckpoint;
|
||||||
|
else
|
||||||
|
sStatus.dwCheckPoint = 0;
|
||||||
|
|
||||||
|
sStatus.dwCurrentState = inCurrentState;
|
||||||
|
sStatus.dwServiceSpecificExitCode = inExitCode;
|
||||||
|
BOOL theErr = SetServiceStatus(sServiceStatusHandle, &sStatus);
|
||||||
|
if (theErr == 0)
|
||||||
|
{
|
||||||
|
DWORD theerrvalue = ::GetLastError();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunAsService(char* inServiceName)
|
||||||
|
{
|
||||||
|
SC_HANDLE theService;
|
||||||
|
SC_HANDLE theSCManager;
|
||||||
|
|
||||||
|
theSCManager = ::OpenSCManager(
|
||||||
|
NULL, // machine (NULL == local)
|
||||||
|
NULL, // database (NULL == default)
|
||||||
|
SC_MANAGER_ALL_ACCESS // access required
|
||||||
|
);
|
||||||
|
if (!theSCManager)
|
||||||
|
return;
|
||||||
|
|
||||||
|
theService = ::OpenService(
|
||||||
|
theSCManager, // SCManager database
|
||||||
|
inServiceName, // name of service
|
||||||
|
SERVICE_ALL_ACCESS);
|
||||||
|
|
||||||
|
SERVICE_STATUS lpServiceStatus;
|
||||||
|
|
||||||
|
if (theService)
|
||||||
|
{
|
||||||
|
const int kNotRunning = 1062;
|
||||||
|
bool stopped = ::ControlService(theService, SERVICE_CONTROL_STOP, &lpServiceStatus);
|
||||||
|
if (!stopped && ((int) ::GetLastError() != kNotRunning))
|
||||||
|
printf("Stopping Service Error: %d\n", ::GetLastError());
|
||||||
|
|
||||||
|
bool started = ::StartService(theService, 0, NULL);
|
||||||
|
if (!started)
|
||||||
|
printf("Starting Service Error: %d\n", ::GetLastError());
|
||||||
|
|
||||||
|
::CloseServiceHandle(theService);
|
||||||
|
}
|
||||||
|
|
||||||
|
::CloseServiceHandle(theSCManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
void InstallService(char* inServiceName)
|
||||||
|
{
|
||||||
|
SC_HANDLE theService;
|
||||||
|
SC_HANDLE theSCManager;
|
||||||
|
|
||||||
|
TCHAR thePath[512];
|
||||||
|
TCHAR theAppBaseName[512];
|
||||||
|
TCHAR theQuotedPath[522];
|
||||||
|
|
||||||
|
BOOL theErr = ::GetModuleFileName(NULL, thePath, 512);
|
||||||
|
if (!theErr)
|
||||||
|
return;
|
||||||
|
|
||||||
|
char* pSlash = strrchr((char*)thePath, '\\');
|
||||||
|
if (pSlash) {
|
||||||
|
strcpy((char*)theAppBaseName, (const char*)pSlash + 1);
|
||||||
|
*(pSlash + 1) = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
sprintf(theQuotedPath, "\"%s%s\" -r 1 -c \"%s\"", thePath, theAppBaseName, g_ini_file.c_str());
|
||||||
|
|
||||||
|
theSCManager = ::OpenSCManager(
|
||||||
|
NULL, // machine (NULL == local)
|
||||||
|
NULL, // database (NULL == default)
|
||||||
|
SC_MANAGER_ALL_ACCESS // access required
|
||||||
|
);
|
||||||
|
if (!theSCManager)
|
||||||
|
{
|
||||||
|
printf("Failed to install Service\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
theService = CreateService(
|
||||||
|
theSCManager, // SCManager database
|
||||||
|
inServiceName, // name of service
|
||||||
|
inServiceName, // name to display
|
||||||
|
SERVICE_ALL_ACCESS, // desired access
|
||||||
|
SERVICE_WIN32_OWN_PROCESS, // service type
|
||||||
|
SERVICE_AUTO_START, // start type
|
||||||
|
SERVICE_ERROR_NORMAL, // error control type
|
||||||
|
theQuotedPath, // service's binary
|
||||||
|
NULL, // no load ordering group
|
||||||
|
NULL, // no tag identifier
|
||||||
|
NULL, // dependencies
|
||||||
|
NULL, // LocalSystem account
|
||||||
|
NULL); // no password
|
||||||
|
|
||||||
|
if (theService)
|
||||||
|
{
|
||||||
|
SERVICE_DESCRIPTION desc;
|
||||||
|
desc.lpDescription = "流媒体";
|
||||||
|
if (ChangeServiceConfig2(theService, SERVICE_CONFIG_DESCRIPTION, &desc))
|
||||||
|
{
|
||||||
|
printf("Added MediaServer Service Description\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
SERVICE_DELAYED_AUTO_START_INFO info = { true };
|
||||||
|
|
||||||
|
if (ChangeServiceConfig2(theService, SERVICE_CONFIG_DELAYED_AUTO_START_INFO, &info))
|
||||||
|
{
|
||||||
|
printf("Added MediaServer Service Delayed Auto Start\n");
|
||||||
|
}
|
||||||
|
|
||||||
|
SERVICE_FAILURE_ACTIONS failact = { 0 };
|
||||||
|
SC_ACTION act[3];
|
||||||
|
act[0].Delay = act[1].Delay = act[2].Delay = 1 * 1000;
|
||||||
|
act[0].Type = act[1].Type = act[2].Type = SC_ACTION_RESTART;
|
||||||
|
failact.cActions = 3;
|
||||||
|
failact.lpsaActions = act;
|
||||||
|
failact.dwResetPeriod = 0;
|
||||||
|
if (ChangeServiceConfig2(theService, SERVICE_CONFIG_FAILURE_ACTIONS, &failact))
|
||||||
|
{
|
||||||
|
printf("Seted MediaServer Service failure actions\n");
|
||||||
|
}
|
||||||
|
::CloseServiceHandle(theService);
|
||||||
|
printf("Installed MediaServer Service\n");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("Failed to install MediaServer Service\n");
|
||||||
|
|
||||||
|
::CloseServiceHandle(theSCManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RemoveService(char *inServiceName)
|
||||||
|
{
|
||||||
|
SC_HANDLE theSCManager;
|
||||||
|
SC_HANDLE theService;
|
||||||
|
SERVICE_STATUS sStatus;
|
||||||
|
|
||||||
|
theSCManager = ::OpenSCManager(
|
||||||
|
NULL, // machine (NULL == local)
|
||||||
|
NULL, // database (NULL == default)
|
||||||
|
SC_MANAGER_ALL_ACCESS // access required
|
||||||
|
);
|
||||||
|
if (!theSCManager)
|
||||||
|
{
|
||||||
|
printf("Failed to remove MediaServer Service\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
theService = ::OpenService(theSCManager, inServiceName, SERVICE_ALL_ACCESS);
|
||||||
|
if (theService) {
|
||||||
|
if (::ControlService(theService, SERVICE_CONTROL_STOP, &sStatus)) {
|
||||||
|
printf("Stopping Service: %s", inServiceName);
|
||||||
|
Sleep(1000);
|
||||||
|
|
||||||
|
while (::QueryServiceStatus(theService, &sStatus)) {
|
||||||
|
if (SERVICE_STOP_PENDING == sStatus.dwCurrentState) {
|
||||||
|
printf(".");
|
||||||
|
Sleep(1000);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (SERVICE_STOPPED == sStatus.dwCurrentState)
|
||||||
|
printf("%s stopped.\n", inServiceName);
|
||||||
|
else
|
||||||
|
printf("%s failed to stopp.\n", inServiceName);
|
||||||
|
}
|
||||||
|
if (::DeleteService(theService))
|
||||||
|
printf("Removed MediaServer Service!\n");
|
||||||
|
else
|
||||||
|
printf("Remove MediaServer Service failed!\n");
|
||||||
|
::CloseServiceHandle(theService);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
printf("Failed to remove iDAS Service\n");
|
||||||
|
::CloseServiceHandle(theSCManager);
|
||||||
|
}
|
||||||
|
|
||||||
|
MediaServer_ServerState StartServer(CMD_main& cmd_main) {
|
||||||
|
MediaServer_ServerState theServerState = MediaServerStartingUpState;
|
||||||
|
LogLevel logLevel = (LogLevel)cmd_main["level"].as<int>();
|
||||||
|
logLevel = MIN(MAX(logLevel, LTrace), LError);
|
||||||
|
string ssl_file = cmd_main["ssl"];
|
||||||
|
int threads = cmd_main["threads"];
|
||||||
|
|
||||||
|
//设置日志
|
||||||
|
Logger::Instance().add(std::make_shared<ConsoleChannel>("ConsoleChannel", logLevel));
|
||||||
|
#ifndef ANDROID
|
||||||
|
auto fileChannel = std::make_shared<FileChannel>("FileChannel", exeDir() + "log/", logLevel);
|
||||||
|
//日志最多保存天数
|
||||||
|
fileChannel->setMaxDay(cmd_main["max_day"]);
|
||||||
|
Logger::Instance().add(fileChannel);
|
||||||
|
#endif//
|
||||||
|
|
||||||
|
//启动异步日志线程
|
||||||
|
Logger::Instance().setWriter(std::make_shared<AsyncLogWriter>());
|
||||||
|
//加载配置文件,如果配置文件不存在就创建一个
|
||||||
|
loadIniConfig(g_ini_file.data());
|
||||||
|
|
||||||
|
if (!File::is_dir(ssl_file.data())) {
|
||||||
|
//不是文件夹,加载证书,证书包含公钥和私钥
|
||||||
|
SSL_Initor::Instance().loadCertificate(ssl_file.data());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
//加载文件夹下的所有证书
|
||||||
|
File::scanDir(ssl_file, [](const string &path, bool isDir) {
|
||||||
|
if (!isDir) {
|
||||||
|
//最后的一个证书会当做默认证书(客户端ssl握手时未指定主机)
|
||||||
|
SSL_Initor::Instance().loadCertificate(path.data());
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
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 httpPort = mINI::Instance()[Http::kPort];
|
||||||
|
uint16_t httpsPort = mINI::Instance()[Http::kSSLPort];
|
||||||
|
|
||||||
|
//设置poller线程数,该函数必须在使用ZLToolKit网络相关对象之前调用才能生效
|
||||||
|
EventPollerPool::setPoolSize(threads);
|
||||||
|
|
||||||
|
//简单的telnet服务器,可用于服务器调试,但是不能使用23端口,否则telnet上了莫名其妙的现象
|
||||||
|
//测试方法:telnet 127.0.0.1 9000
|
||||||
|
TcpServer::Ptr shellSrv(new TcpServer());
|
||||||
|
TcpServer::Ptr rtspSrv(new TcpServer());
|
||||||
|
TcpServer::Ptr rtmpSrv(new TcpServer());
|
||||||
|
TcpServer::Ptr httpSrv(new TcpServer());
|
||||||
|
|
||||||
|
shellSrv->start<ShellSession>(shellPort);
|
||||||
|
rtspSrv->start<RtspSession>(rtspPort);//默认10554
|
||||||
|
rtmpSrv->start<RtmpSession>(rtmpPort);//默认10935
|
||||||
|
//http服务器
|
||||||
|
httpSrv->start<HttpSession>(httpPort);//默认80
|
||||||
|
|
||||||
|
//如果支持ssl,还可以开启https服务器
|
||||||
|
TcpServer::Ptr httpsSrv(new TcpServer());
|
||||||
|
//https服务器,支持websocket
|
||||||
|
httpsSrv->start<HttpsSession>(httpsPort);//默认443
|
||||||
|
|
||||||
|
//支持ssl加密的rtsp服务器,可用于诸如亚马逊echo show这样的设备访问
|
||||||
|
TcpServer::Ptr rtspSSLSrv(new TcpServer());
|
||||||
|
rtspSSLSrv->start<RtspSessionWithSSL>(rtspsPort);//默认322
|
||||||
|
|
||||||
|
installWebApi();
|
||||||
|
InfoL << "已启动http api 接口";
|
||||||
|
installWebHook();
|
||||||
|
InfoL << "已启动http hook 接口";
|
||||||
|
theServerState = MediaServerRunningState;
|
||||||
|
|
||||||
|
return theServerState;
|
||||||
|
}
|
||||||
|
|
||||||
|
void RunServer() {
|
||||||
|
|
||||||
|
#ifdef WIN32
|
||||||
|
CMiniDumper _miniDumper(true);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
while ((sServerState != MediaServerShuttingDownState) &&
|
||||||
|
(sServerState != MediaServerFatalErrorState)) {
|
||||||
|
#ifdef WIN32
|
||||||
|
Sleep(1000);
|
||||||
|
#else
|
||||||
|
usleep(1000 * 1000);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
unInstallWebApi();
|
||||||
|
unInstallWebHook();
|
||||||
|
//休眠1秒再退出,防止资源释放顺序错误
|
||||||
|
InfoL << "程序退出中,请等待...";
|
||||||
|
sleep(1);
|
||||||
|
InfoL << "程序退出完毕!";
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue