Merge branch 'feature/mstsc' of github.com:tp4444eleport into feature/mstsc

pull/236/head
Apex Liu 2019-11-19 10:16:14 +08:00
commit 814cab3c31
123 changed files with 9981 additions and 5295 deletions

6
.gitignore vendored
View File

@ -28,8 +28,10 @@ __pycache__
**/.idea/misc.xml
**/.idea/dictionaries
**/.idea/watcherTasks.xml
**/.idea/dataSources.*
**/.idea/inspectionProfiles
**/.idea/codeStyles
**/.idea/dataSources
**/.idea/inspectionProfiles
**/.idea/vcs.xml
**/.idea/modules.xml
@ -100,3 +102,7 @@ profile
*.moved-aside
/server/share/tmp
/server/tp_core/testssh/Debug
/server/tp_core/testssh/Release
/external/zlib
/client/tools/qt-redist

View File

@ -3,10 +3,10 @@
################################################################
# Basic settings.
################################################################
VER_PYTHON="3.7.0"
VER_PYTHON="3.7.4"
VER_PYTHON_SHORT="3.7"
VER_OPENSSL="1.0.2p"
VER_SQLITE="3250000"
VER_OPENSSL="1.0.2s"
VER_SQLITE="3290000"
VER_ZLIB="1.2.11"
VER_PYTHON_LIB="${VER_PYTHON_SHORT}m"
@ -77,7 +77,7 @@ function step_download_files()
dlfile "python source tarball" "https://www.python.org/ftp/python/${VER_PYTHON}/" "Python-${VER_PYTHON}.tgz" ${PATH_DOWNLOAD}
dlfile "openssl source tarball" "https://www.openssl.org/source/" "openssl-${VER_OPENSSL}.tar.gz" ${PATH_DOWNLOAD}
dlfile "sqlite source tarball" "http://sqlite.org/2018/" "sqlite-autoconf-${VER_SQLITE}.tar.gz" ${PATH_DOWNLOAD}
dlfile "sqlite source tarball" "http://sqlite.org/2019/" "sqlite-autoconf-${VER_SQLITE}.tar.gz" ${PATH_DOWNLOAD}
dlfile "zlib source tarball" "https://www.zlib.net/" "zlib-${VER_ZLIB}.tar.gz" ${PATH_DOWNLOAD}
}

View File

@ -13,21 +13,24 @@ class BuilderBase:
def __init__(self):
self.out_dir = ''
def build_exe(self):
pass
def build_assist(self):
cc.e("this is a pure-virtual function.")
def build_player(self):
cc.e("this is a pure-virtual function.")
def build_rdp(self):
pass
cc.e("this is a pure-virtual function.")
def build_installer(self):
pass
cc.e("this is a pure-virtual function.")
class BuilderWin(BuilderBase):
def __init__(self):
super().__init__()
def build_exe(self):
def build_assist(self):
cc.i('build tp_assist...')
sln_file = os.path.join(env.root_path, 'client', 'tp_assist_win', 'tp_assist.vs2017.sln')
out_file = os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path, 'tp_assist.exe')
@ -36,6 +39,15 @@ class BuilderWin(BuilderBase):
utils.msvc_build(sln_file, 'tp_assist', ctx.target_path, ctx.bits_path, False)
utils.ensure_file_exists(out_file)
def build_player(self):
cc.i('build tp-player...')
prj_path = os.path.join(env.root_path, 'client', 'tp-player')
out_file = os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path, 'tp-player.exe')
if os.path.exists(out_file):
utils.remove(out_file)
utils.qt_build_win(prj_path, 'tp-player', ctx.bits_path, ctx.target_path)
utils.ensure_file_exists(out_file)
# def build_rdp(self):
# cc.n('build tp_rdp...')
# sln_file = os.path.join(ROOT_PATH, 'client', 'tp_rdp', 'tp_rdp.2015.sln')
@ -74,12 +86,13 @@ class BuilderWin(BuilderBase):
utils.makedirs(tmp_cfg_path)
utils.copy_file(os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path), tmp_app_path, 'tp_assist.exe')
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, ('tp-assist.windows.json', 'tp-assist.json'))
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, ('tp-assist.windows.json', 'tp-assist.json'))
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'cacert.cer')
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'localhost.key')
utils.copy_file(os.path.join(env.root_path, 'client', 'cfg'), tmp_cfg_path, 'localhost.pem')
# assist configuration web page
utils.copy_ex(os.path.join(env.root_path, 'client', 'tp_assist_win'), tmp_app_path, 'site')
utils.makedirs(os.path.join(tmp_app_path, 'tools', 'putty'))
@ -91,13 +104,35 @@ class BuilderWin(BuilderBase):
utils.makedirs(os.path.join(tmp_app_path, 'tools', 'tprdp'))
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-client.exe')
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-replay.exe')
# utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'tprdp-replay.exe')
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'libeay32.dll')
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'ssleay32.dll')
utils.copy_file(os.path.join(env.root_path, 'client', 'tools', 'tprdp'), os.path.join(tmp_app_path, 'tools', 'tprdp'), 'msvcr120.dll')
utils.copy_file(os.path.join(env.root_path, 'client', 'tools'), os.path.join(tmp_app_path, 'tools'), 'securecrt-telnet.vbs')
# tp-player
utils.copy_file(os.path.join(env.root_path, 'out', 'client', ctx.bits_path, ctx.target_path), tmp_app_path, 'tp-player.exe')
# qt-redist
qt_redist_path = os.path.join(env.root_path, 'client', 'tools', 'qt-redist')
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Core.dll')
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Gui.dll')
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Network.dll')
utils.copy_file(qt_redist_path, tmp_app_path, 'Qt5Widgets.dll')
utils.copy_ex(os.path.join(qt_redist_path, 'platforms'), os.path.join(tmp_app_path, 'platforms'))
utils.copy_ex(os.path.join(qt_redist_path, 'styles'), os.path.join(tmp_app_path, 'styles'))
utils.copy_ex(os.path.join(qt_redist_path, 'translations'), os.path.join(tmp_app_path, 'translations'))
# zlib
suffix = 'd' if ctx.target_path == 'debug' else ''
utils.copy_file(os.path.join(env.root_path, 'external', 'zlib', 'build', ctx.target_path), tmp_app_path, 'zlib{}.dll'.format(suffix))
# openssl
utils.copy_file(os.path.join(env.root_path, 'external', 'openssl', 'bin'), tmp_app_path, 'libcrypto-1_1.dll')
utils.copy_file(os.path.join(env.root_path, 'external', 'openssl', 'bin'), tmp_app_path, 'libssl-1_1.dll')
# final build
utils.nsis_build(os.path.join(env.root_path, 'dist', 'client', 'windows', 'assist', 'installer.nsi'))
@ -105,7 +140,7 @@ class BuilderMacOS(BuilderBase):
def __init__(self):
super().__init__()
def build_exe(self):
def build_assist(self):
cc.i('build tp_assist...')
configuration = ctx.target_path.capitalize()
@ -169,7 +204,7 @@ class BuilderLinux(BuilderBase):
def __init__(self):
super().__init__()
def build_exe(self):
def build_assist(self):
cc.e('not support linux.')
# def build_rdp(self):
@ -215,7 +250,8 @@ def main():
builder = gen_builder(ctx.host_os)
if 'exe' in argv:
builder.build_exe()
builder.build_assist()
builder.build_player()
# elif 'rdp' in argv:
# builder.build_rdp()
elif 'installer' in argv:

View File

@ -27,19 +27,25 @@ class BuilderBase:
def build_jsoncpp(self):
file_name = 'jsoncpp-{}.zip'.format(env.ver_jsoncpp)
if not utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name):
return
# if not utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name):
# return
self._build_jsoncpp(file_name)
def _download_jsoncpp(self, file_name):
return utils.download_file('jsoncpp source tarball', 'https://github.com/open-source-parsers/jsoncpp/archive/{}.zip'.format(env.ver_jsoncpp), PATH_DOWNLOAD, file_name)
def _build_jsoncpp(self, file_name):
cc.e("this is a pure-virtual function.")
def build_mongoose(self):
file_name = 'mongoose-{}.zip'.format(env.ver_mongoose)
if not utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name):
return
# if not utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name):
# return
self._build_mongoose(file_name)
def _download_mongoose(self, file_name):
return utils.download_file('mongoose source tarball', 'https://github.com/cesanta/mongoose/archive/{}.zip'.format(env.ver_mongoose), PATH_DOWNLOAD, file_name)
def _build_mongoose(self, file_name):
cc.e("this is a pure-virtual function.")
@ -47,38 +53,64 @@ class BuilderBase:
file_name = 'openssl-{}.zip'.format(env.ver_ossl)
self._build_openssl(file_name)
def _build_openssl(self, file_name):
def _download_openssl(self, file_name):
_alt_ver = '_'.join(env.ver_ossl.split('.'))
if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name):
cc.e("can not download openssl source tarball.")
return False
else:
return True
return utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name)
def _build_openssl(self, file_name):
cc.e("this is a pure-virtual function.")
# _alt_ver = '_'.join(env.ver_ossl.split('.'))
# if not utils.download_file('openssl source tarball', 'https://github.com/openssl/openssl/archive/OpenSSL_{}.zip'.format(_alt_ver), PATH_DOWNLOAD, file_name):
# cc.e("can not download openssl source tarball.")
# return False
# else:
# return True
def build_libuv(self):
file_name = 'libuv-{}.zip'.format(env.ver_libuv)
if not utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name):
return
# if not utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name):
# return
self._build_libuv(file_name)
def _download_libuv(self, file_name):
return utils.download_file('libuv source tarball', 'https://github.com/libuv/libuv/archive/v{}.zip'.format(env.ver_libuv), PATH_DOWNLOAD, file_name)
def _build_libuv(self, file_name):
cc.e("this is a pure-virtual function.")
def build_mbedtls(self):
file_name = 'mbedtls-mbedtls-{}.zip'.format(env.ver_mbedtls)
if not utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name):
return
# if not utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name):
# return
self._build_mbedtls(file_name)
def _download_mbedtls(self, file_name):
return utils.download_file('mbedtls source tarball', 'https://github.com/ARMmbed/mbedtls/archive/mbedtls-{}.zip'.format(env.ver_mbedtls), PATH_DOWNLOAD, file_name)
def _build_mbedtls(self, file_name):
cc.e("this is a pure-virtual function.")
def build_zlib(self):
file_name = 'zlilb{}.zip'.format(env.ver_zlib_number)
# if not utils.download_file('mbedtls source tarball', 'https://www.zlib.net/zlib{}.zip'.format(env.ver_zlib_number), PATH_DOWNLOAD, file_name):
# return
self._build_zlib(file_name)
def _download_zlib(self, file_name):
return utils.download_file('mbedtls source tarball', 'https://www.zlib.net/zlib{}.zip'.format(env.ver_zlib_number), PATH_DOWNLOAD, file_name)
def _build_zlib(self, file_name):
cc.e("this is a pure-virtual function.")
def build_libssh(self):
file_name = 'libssh-{}.zip'.format(env.ver_libssh)
if not utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name):
return
# if not utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name):
# return
self._build_libssh(file_name)
def _download_libssh(self, file_name):
return utils.download_file('libssh source tarball', 'https://git.libssh.org/projects/libssh.git/snapshot/libssh-{}.zip'.format(env.ver_libssh), PATH_DOWNLOAD, file_name)
def _build_libssh(self, file_name):
cc.e("this is a pure-virtual function.")
@ -103,9 +135,10 @@ class BuilderWin(BuilderBase):
self.MBEDTLS_PATH_SRC = os.path.join(PATH_EXTERNAL, 'mbedtls')
self.LIBUV_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libuv')
self.LIBSSH_PATH_SRC = os.path.join(PATH_EXTERNAL, 'libssh')
self.ZLIB_PATH_SRC = os.path.join(PATH_EXTERNAL, 'zlib')
def _prepare_python(self):
cc.n('prepare python header files ...', end='')
cc.n('prepare python header files ... ', end='')
if os.path.exists(os.path.join(PATH_EXTERNAL, 'python', 'include', 'Python.h')):
cc.w('already exists, skip.')
@ -125,53 +158,85 @@ class BuilderWin(BuilderBase):
utils.copy_ex(_header_path, os.path.join(PATH_EXTERNAL, 'python', 'include'))
def _build_openssl(self, file_name):
cc.n('build openssl static library from source code... ')
if not super()._build_openssl(file_name):
return
_chk_output = [
os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'),
os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'),
os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'),
]
need_build = False
for f in _chk_output:
if not os.path.exists(f):
need_build = True
break
if not need_build:
cc.n('build openssl static library from source code... ', end='')
cc.n('prepare OpenSSL pre-built package ... ', end='')
if os.path.exists(self.OPENSSL_PATH_SRC):
cc.w('already exists, skip.')
return
cc.v('')
cc.n('prepare openssl source code...')
_alt_ver = '_'.join(env.ver_ossl.split('.'))
if not os.path.exists(self.OPENSSL_PATH_SRC):
utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
if not os.path.exists(self.OPENSSL_PATH_SRC):
raise RuntimeError('can not prepare openssl source code.')
else:
cc.w('already exists, skip.')
os.chdir(self.OPENSSL_PATH_SRC)
os.system('""{}" Configure VC-WIN32"'.format(env.perl))
os.system(r'ms\do_nasm')
# for vs2015
# utils.sys_exec(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
# for vs2017 community
utils.sys_exec(r'"{}VC\Auxiliary\Build\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
file_name = 'Win32OpenSSL-{}.msi'.format(_alt_ver)
installer = os.path.join(PATH_DOWNLOAD, file_name)
for f in _chk_output:
if not os.path.exists(f):
raise RuntimeError('build openssl static library from source code failed.')
if not os.path.exists(installer):
if not utils.download_file('openssl installer', 'http://slproweb.com/download/{}'.format(filename), PATH_DOWNLOAD, file_name):
cc.e('can not download pre-built installer of OpenSSL.')
return
utils.ensure_file_exists(installer)
cc.w('On Windows, we use pre-built package of OpenSSL.')
cc.w('The installer have been downloaded at "{}".'.format(installer))
cc.w('please install OpenSSL into "{}".'.format(self.OPENSSL_PATH_SRC))
cc.w('\nOnce the OpenSSL installed, press Enter to continue or Q to quit...', end='')
try:
x = env.input()
except EOFError:
x = 'q'
if x == 'q':
return
# cc.n('build openssl static library from source code... ')
# if not super()._build_openssl(file_name):
# return
# _chk_output = [
# os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'libeay32.lib'),
# os.path.join(self.OPENSSL_PATH_SRC, 'out32', 'ssleay32.lib'),
# os.path.join(self.OPENSSL_PATH_SRC, 'inc32', 'openssl', 'opensslconf.h'),
# ]
# need_build = False
# for f in _chk_output:
# if not os.path.exists(f):
# need_build = True
# break
# if not need_build:
# cc.n('build openssl static library from source code... ', end='')
# cc.w('already exists, skip.')
# return
# cc.v('')
# cc.n('prepare openssl source code...')
# _alt_ver = '_'.join(env.ver_ossl.split('.'))
# if not os.path.exists(self.OPENSSL_PATH_SRC):
# utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
# os.rename(os.path.join(PATH_EXTERNAL, 'openssl-OpenSSL_{}'.format(_alt_ver)), self.OPENSSL_PATH_SRC)
# if not os.path.exists(self.OPENSSL_PATH_SRC):
# raise RuntimeError('can not prepare openssl source code.')
# else:
# cc.w('already exists, skip.')
# os.chdir(self.OPENSSL_PATH_SRC)
# os.system('""{}" Configure VC-WIN32"'.format(env.perl))
# os.system(r'ms\do_nasm')
# # for vs2015
# # utils.sys_exec(r'"{}\VC\bin\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
# # for vs2017 community
# utils.sys_exec(r'"{}VC\Auxiliary\Build\vcvars32.bat" && nmake -f ms\nt.mak'.format(env.visual_studio_path), direct_output=True)
# for f in _chk_output:
# if not os.path.exists(f):
# raise RuntimeError('build openssl static library from source code failed.')
def _build_libssh(self, file_name):
cc.n('build libssh static library from source code... ', end='')
if not self._download_libssh(file_name):
return
cc.n('build libssh library from source code... ', end='')
if not os.path.exists(self.LIBSSH_PATH_SRC):
cc.v('')
@ -210,7 +275,7 @@ class BuilderWin(BuilderBase):
cc.i('build libssh...')
sln_file = os.path.join(self.LIBSSH_PATH_SRC, 'build', 'libssh.sln')
utils.msvc_build(sln_file, 'ssh_shared', ctx.target_path, 'win32', False)
utils.msvc_build(sln_file, 'ssh', ctx.target_path, 'win32', False)
utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.lib'))
utils.ensure_file_exists(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path, 'ssh.dll'))
utils.copy_file(os.path.join(self.LIBSSH_PATH_SRC, 'build', 'src', ctx.target_path), os.path.join(self.LIBSSH_PATH_SRC, 'lib', ctx.target_path), 'ssh.lib')
@ -218,7 +283,53 @@ class BuilderWin(BuilderBase):
utils.ensure_file_exists(out_file_lib)
utils.ensure_file_exists(out_file_dll)
def _build_zlib(self, file_name):
if not self._download_zlib(file_name):
return
cc.n('build zlib library from source code... ', end='')
if not os.path.exists(self.ZLIB_PATH_SRC):
cc.v('')
utils.unzip(os.path.join(PATH_DOWNLOAD, file_name), PATH_EXTERNAL)
os.rename(os.path.join(PATH_EXTERNAL, 'zlib-{}'.format(env.ver_zlib)), self.ZLIB_PATH_SRC)
if ctx.target_path == 'debug':
olib = 'zlibd.lib'
odll = 'zlibd.dll'
else:
olib = 'zlib.lib'
odll = 'zlib.dll'
out_file_lib = os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, olib)
out_file_dll = os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, odll)
if os.path.exists(out_file_lib) and os.path.exists(out_file_dll):
cc.w('already exists, skip.')
return
cc.v('')
cc.w('On Windows, when build zlib, need you use cmake-gui.exe to generate solution file')
cc.w('for Visual Studio 2017. Visit https://docs.tp4a.com for more details.')
cc.w('\nOnce the zlib.sln generated, press Enter to continue or Q to quit...', end='')
try:
x = env.input()
except EOFError:
x = 'q'
if x == 'q':
return
cc.i('build zlib...')
sln_file = os.path.join(self.ZLIB_PATH_SRC, 'build', 'zlib.sln')
utils.msvc_build(sln_file, 'zlib', ctx.target_path, 'win32', False)
# utils.ensure_file_exists(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, 'zlib.lib'))
# utils.ensure_file_exists(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path, 'zlib.dll'))
# utils.copy_file(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path), os.path.join(self.ZLIB_PATH_SRC, 'lib', ctx.target_path), 'zlib.lib')
# utils.copy_file(os.path.join(self.ZLIB_PATH_SRC, 'build', ctx.target_path), os.path.join(self.ZLIB_PATH_SRC, 'lib', ctx.target_path), 'zlib.dll')
utils.ensure_file_exists(out_file_lib)
utils.ensure_file_exists(out_file_dll)
def _build_jsoncpp(self, file_name):
if not self._download_jsoncpp(file_name):
return
cc.n('prepare jsoncpp source code... ', end='')
if not os.path.exists(self.JSONCPP_PATH_SRC):
cc.v('')
@ -228,6 +339,8 @@ class BuilderWin(BuilderBase):
cc.w('already exists, skip.')
def _build_mongoose(self, file_name):
if not self._download_mongoose(file_name):
return
cc.n('prepare mongoose source code... ', end='')
if not os.path.exists(self.MONGOOSE_PATH_SRC):
cc.v('')
@ -237,6 +350,8 @@ class BuilderWin(BuilderBase):
cc.w('already exists, skip.')
def _build_mbedtls(self, file_name):
if not self._download_mbedtls(file_name):
return
cc.n('prepare mbedtls source code... ', end='')
if not os.path.exists(self.MBEDTLS_PATH_SRC):
cc.v('')
@ -254,6 +369,8 @@ class BuilderWin(BuilderBase):
# utils.copy_file(os.path.join(PATH_EXTERNAL, 'fix-external', 'mbedtls', 'library'), os.path.join(self.MBEDTLS_PATH_SRC, 'library'), 'rsa.c')
def _build_libuv(self, file_name):
if not self._download_libuv(file_name):
return
cc.n('prepare libuv source code... ', end='')
if not os.path.exists(self.LIBUV_PATH_SRC):
cc.v('')
@ -674,6 +791,7 @@ def main():
builder.build_openssl()
builder.build_libuv()
builder.build_mbedtls()
builder.build_zlib()
builder.build_libssh()
builder.fix_output()

View File

@ -148,6 +148,11 @@ class Env(object):
if warn_miss_tool:
cc.w(' - can not locate `nsis`, so I can not make installer.')
if 'qt' in _tmp:
self.qt = _tmp['qt']
else:
self.qt = None
elif self.is_linux or self.is_macos:
if 'cmake' in _tmp:
self.cmake = _tmp['cmake']
@ -178,6 +183,10 @@ class Env(object):
self.ver_ossl = _v_openssl[0].strip()
self.ver_ossl_number = _v_openssl[1].strip()
_v_zlib = _tmp['zlib'].split(',')
self.ver_zlib = _v_zlib[0].strip()
self.ver_zlib_number = _v_zlib[1].strip()
self.ver_libuv = _tmp['libuv']
self.ver_mbedtls = _tmp['mbedtls']
# self.ver_sqlite = _tmp['sqlite']

View File

@ -320,6 +320,21 @@ def msvc_build(sln_file, proj_name, target, platform, force_rebuild):
raise RuntimeError('build MSVC project `{}` failed.'.format(proj_name))
def qt_build_win(prj_path, prj_name, bit_path, target_path):
cc.n(env.visual_studio_path)
if env.qt is None:
raise RuntimeError('where is `qt`?')
if env.is_win:
tmp_path = os.path.join(env.root_path, 'out', '_tmp_', prj_name, bit_path)
# C:\Windows\System32\cmd.exe /A /Q /K C:\Qt\Qt5.12.0\5.12.0\msvc2017\bin\qtenv2.bat
cmd = 'C:\\Windows\\System32\\cmd.exe /A /Q /C ""{}\qt-helper.bat" "{}\\bin\\qtenv2.bat" "{}VC\\Auxiliary\\Build\\vcvarsall.bat" {} "{}" "{}" {}"'.format(env.build_path, env.qt, env.visual_studio_path, bit_path, tmp_path, prj_path, target_path)
ret, _ = sys_exec(cmd, direct_output=True)
if ret != 0:
raise RuntimeError('build XCode project `{}` failed.'.format(proj_name))
def xcode_build(proj_file, proj_name, target, force_rebuild):
if force_rebuild:
cmd = 'xcodebuild -project "{}" -target {} -configuration {} clean'.format(proj_file, proj_name, target)

View File

@ -1,3 +1,3 @@
# -*- coding: utf8 -*-
VER_TP_SERVER = "3.3.1"
VER_TP_ASSIST = "3.3.1"
VER_TP_SERVER = "3.5.1"
VER_TP_ASSIST = "3.5.1"

View File

@ -71,12 +71,18 @@
"rdp" : {
"available" : [
{
"app" : "mstsc.exe",
"cmdline" : "\"{tmp_rdp_file}\"",
"display" : "微软RDP客户端系统自带",
"name" : "mstsc"
},
{
"app" : "{assist_tools_path}\\tprdp\\tprdp-client.exe",
"cmdline" : "/v:{host_ip}:{host_port} /u:{user_name} /t:\"TP#{real_ip}\"",
"display" : "FreeRDP内置",
"name" : "freerdp"
}
],
"selected" : "freerdp"
"selected" : "mstsc"
}
}

718
client/tp-player/bar.cpp Normal file
View File

@ -0,0 +1,718 @@
#include "bar.h"
#include <QPainter>
#include <QDebug>
#include "mainwindow.h"
#define FONT_SIZE_DEFAULT 12
#define FONT_SIZE_TIME 14
#define TEXT_COLOR QColor(255,255,255,153)
#define SPEED_BTN_WIDTH 42
#define CHKBOX_RIGHT_PADDING 6
#define PADDING_TIME_PROGRESS_BAR 10
#define SPEED_BTN_PADDING_TOP 8
#define SPEED_BTN_PADDING_RIGHT 8
#define SKIP_PADDING_TOP 10
#define BAR_ALIGN_TOP 10
#define BAR_PADDING_TOP 18
#define BAR_PADDING_LEFT 15
#define BAR_PADDING_RIGHT 15
typedef struct RES_MAP {
RES_ID id;
const char* name;
}RES_MAP;
static RES_MAP img_res[res__max] = {
{res_bg_left, "bg-left"},
{res_bg_mid, "bg-mid"},
{res_bg_right, "bg-right"},
{res_btn_normal_left, "btn-normal-left"},
{res_btn_normal_mid, "btn-normal-mid"},
{res_btn_normal_right, "btn-normal-right"},
{res_btn_sel_left, "btn-sel-left"},
{res_btn_sel_mid, "btn-sel-mid"},
{res_btn_sel_right, "btn-sel-right"},
{res_btn_hover_left, "btn-hover-left"},
{res_btn_hover_mid, "btn-hover-mid"},
{res_btn_hover_right, "btn-hover-right"},
{res_prgbarh_left, "prgbarh-left"},
{res_prgbarh_mid, "prgbarh-mid"},
{res_prgbar_mid, "prgbar-mid"},
{res_prgbar_right, "prgbar-right"},
{res_chkbox_normal, "chkbox-normal"},
{res_chkbox_hover, "chkbox-hover"},
{res_chkbox_sel_normal, "chkbox-sel-normal"},
{res_chkbox_sel_hover, "chkbox-sel-hover"},
};
typedef struct SPEED_MAP {
int id;
const char* title;
}SPEED_MAP;
static SPEED_MAP speed[speed_count] = {
{speed_1x, "1x"},
{speed_2x, "2x"},
{speed_4x, "4x"},
{speed_8x, "8x"},
};
static inline int min(int a, int b){
return a < b ? a : b;
}
static inline int max(int a, int b){
return a > b ? a : b;
}
Bar::Bar() {
m_img_ready = false;
m_width = 0;
m_height = 0;
m_str_total_time = "00:00";
m_str_passed_time = "00:00";
m_str_passed_time_last_draw = "--:--";
m_percent = 0;
m_percent_last_draw = -1;
m_play_hover = false;
m_playing = true; // false=paused
m_speed_selected = speed_1x;
m_speed_hover = speed_count; // speed_count=no-hover
m_skip_selected = false;
m_skip_hover = false;
m_progress_hover = false;
m_progress_pressed = false;
m_resume_ms = 0;
}
Bar::~Bar() {
}
bool Bar::init(MainWindow* owner) {
m_owner = owner;
// 加载所需的图像资源
int i = 0;
for(i = 0; i < res__max; ++i) {
QString name;
name.sprintf(":/tp-player/res/bar/%s.png", img_res[i].name);
if(!m_res[i].load(name))
return false;
}
// 无需合成的图像
if(!m_img_btn_play[play_running][widget_normal].load(":/tp-player/res/bar/play-normal.png")
|| !m_img_btn_play[play_running][widget_hover].load(":/tp-player/res/bar/play-hover.png")
|| !m_img_btn_play[play_paused][widget_normal].load(":/tp-player/res/bar/pause-normal.png")
|| !m_img_btn_play[play_paused][widget_hover].load(":/tp-player/res/bar/pause-hover.png")
|| !m_img_progress_pointer[widget_normal].load(":/tp-player/res/bar/prgpt-normal.png")
|| !m_img_progress_pointer[widget_hover].load(":/tp-player/res/bar/prgpt-hover.png")
) {
return false;
}
m_height = m_res[res_bg_left].height();
return true;
}
void Bar::start(uint32_t total_ms, int width) {
bool is_first_start = (m_width == 0);
m_width = width;
m_total_ms = total_ms;
_ms_to_str(total_ms, m_str_total_time);
// 首次播放时调整位置左右居中距窗口顶部10点处。
if(is_first_start) {
_init_imgages();
QRect rc = m_owner->rect();
m_rc = QRect(0, 0, m_width, m_height);
m_rc.moveTo((rc.width() - m_width)/2, BAR_ALIGN_TOP);
}
}
void Bar::end() {
if(m_played_ms != m_total_ms)
update_passed_time(m_total_ms);
m_playing = false;
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
}
void Bar::_init_imgages() {
m_img_bg = QPixmap(m_width, m_height);
m_img_bg.fill(Qt::transparent);//用透明色填充
QPainter pp(&m_img_bg);
QFont font = pp.font();
// 合成背景图像
{
pp.drawPixmap(0, 0, m_res[res_bg_left].width(), m_res[res_bg_left].height(), m_res[res_bg_left]);
pp.drawPixmap(m_res[res_bg_left].width(), 0, m_width - m_res[res_bg_left].width() - m_res[res_bg_right].width(), m_height, m_res[res_bg_mid]);
pp.drawPixmap(m_width-m_res[res_bg_right].width(), 0, m_res[res_bg_right].width(), m_height, m_res[res_bg_right]);
}
{
m_rc_btn_play = QRect(BAR_PADDING_LEFT, (m_height - m_img_btn_play[play_running][widget_normal].height())/2 , m_img_btn_play[play_running][widget_normal].width(), m_img_btn_play[play_running][widget_normal].height());
}
// 合成速度按钮
{
int w = SPEED_BTN_WIDTH, h = m_res[res_btn_normal_left].height();
QRect rc(0, 0, w, h);
QPixmap btn[btnspd_state_count];
// 未选中状态
btn[btnspd_normal] = QPixmap(w, h);
btn[btnspd_normal].fill(Qt::transparent);//用透明色填充
QPainter pn(&btn[btnspd_normal]);
pn.drawPixmap(0, 0, m_res[res_btn_normal_left].width(), m_res[res_btn_normal_left].height(), m_res[res_btn_normal_left]);
pn.drawPixmap(m_res[res_btn_normal_left].width(), 0, w - m_res[res_btn_normal_left].width() - m_res[res_btn_normal_right].width(), h, m_res[res_btn_normal_mid]);
pn.drawPixmap(w-m_res[res_btn_normal_right].width(), 0, m_res[res_btn_normal_right].width(), h, m_res[res_btn_normal_right]);
// 选中状态
btn[btnspd_sel] = QPixmap(w, h);
btn[btnspd_sel].fill(Qt::transparent);//用透明色填充
QPainter ps(&btn[btnspd_sel]);
ps.drawPixmap(0, 0, m_res[res_btn_sel_left].width(), m_res[res_btn_sel_left].height(), m_res[res_btn_sel_left]);
ps.drawPixmap(m_res[res_btn_sel_left].width(), 0, w - m_res[res_btn_sel_left].width() - m_res[res_btn_sel_right].width(), h, m_res[res_btn_sel_mid]);
ps.drawPixmap(w-m_res[res_btn_sel_right].width(), 0, m_res[res_btn_sel_right].width(), h, m_res[res_btn_sel_right]);
// 鼠标滑过状态
btn[btnspd_hover] = QPixmap(w, h);
btn[btnspd_hover].fill(Qt::transparent);//用透明色填充
QPainter ph(&btn[btnspd_hover]);
ph.drawPixmap(0, 0, m_res[res_btn_hover_left].width(), m_res[res_btn_hover_left].height(), m_res[res_btn_hover_left]);
ph.drawPixmap(m_res[res_btn_hover_left].width(), 0, w - m_res[res_btn_hover_left].width() - m_res[res_btn_hover_right].width(), h, m_res[res_btn_hover_mid]);
ph.drawPixmap(w-m_res[res_btn_hover_right].width(), 0, m_res[res_btn_hover_right].width(), h, m_res[res_btn_hover_right]);
for(int i = 0; i < btnspd_state_count; ++i) {
for(int j = 0; j < speed_count; ++j) {
m_img_btn_speed[j][i] = QPixmap(w, h);
m_img_btn_speed[j][i].fill(Qt::transparent);
QPainter ps(&m_img_btn_speed[j][i]);
ps.setPen(TEXT_COLOR);
QFont font = ps.font();
font.setFamily("consolas");
font.setPixelSize(FONT_SIZE_DEFAULT);
ps.setFont(font);
ps.drawPixmap(0, 0, w, h, btn[i]);
ps.drawText(rc, Qt::AlignCenter, speed[j].title);
}
}
}
// 合成跳过无操作选项
{
// 计算显示跳过无操作选项字符串的宽高
font.setFamily("微软雅黑");
font.setBold(false);
font.setPixelSize(FONT_SIZE_DEFAULT);
pp.setFont(font);
QFontMetrics fm = pp.fontMetrics();
{
int h = fm.height();
if(h < m_res[res_chkbox_normal].height())
h = m_res[res_chkbox_normal].height();
m_rc_skip = QRect(0, 0, fm.width(LOCAL8BIT("无操作则跳过")) + CHKBOX_RIGHT_PADDING + m_res[res_chkbox_normal].width(), h);
}
int w = m_rc_skip.width();
int h = m_rc_skip.height();
int chkbox_top = (m_rc_skip.height() - m_res[res_chkbox_normal].height()) / 2;
int text_left = m_res[res_chkbox_normal].width() + CHKBOX_RIGHT_PADDING;
int text_top = (m_rc_skip.height() - fm.height()) / 2;
for(int i = 0; i < chkbox_state_count; ++i) {
for(int j = 0; j < widget_state_count; ++j) {
m_img_skip[i][j] = QPixmap(w,h);
m_img_skip[i][j].fill(Qt::transparent);
QPainter ps(&m_img_skip[i][j]);
ps.setPen(TEXT_COLOR);
QFont font = ps.font();
font.setFamily("微软雅黑");
font.setPixelSize(FONT_SIZE_DEFAULT);
ps.setFont(font);
QPixmap* img = nullptr;
if(i == chkbox_normal && j == widget_normal)
img = &m_res[res_chkbox_normal];
else if(i == chkbox_normal && j == widget_hover)
img = &m_res[res_chkbox_hover];
else if(i == chkbox_selected && j == widget_normal)
img = &m_res[res_chkbox_sel_normal];
else if(i == chkbox_selected && j == widget_hover)
img = &m_res[res_chkbox_sel_hover];
if(img == nullptr) {
qDebug("ERROR: can not load image for check-box.");
img = &m_res[res_chkbox_normal];
}
ps.drawPixmap(0, chkbox_top, img->width(), img->height(), *img);
ps.drawText(QRect(text_left, text_top, w-text_left, h-text_top), Qt::AlignCenter, LOCAL8BIT("无操作则跳过"));
}
}
}
// 定位进度条
{
// 计算显示时间所需的宽高
font.setFamily("consolas");
font.setBold(true);
font.setPixelSize(FONT_SIZE_TIME);
pp.setFont(font);
{
QFontMetrics fm = pp.fontMetrics();
m_rc_time_passed = QRect(0, 0, fm.width("00:00:00"), fm.height());
m_rc_time_total = m_rc_time_passed;
}
m_img_time_total = QPixmap(m_rc_time_total.width(), m_rc_time_total.height());
m_img_time_total.fill(Qt::transparent);
QPainter pp(&m_img_time_total);
pp.setPen(TEXT_COLOR);
QFont font = pp.font();
font.setFamily("consolas");
font.setBold(true);
font.setPixelSize(FONT_SIZE_TIME);
pp.setFont(font);
pp.drawText(m_rc_time_total, Qt::AlignLeft, m_str_total_time);
// 定位时间字符串的位置
m_rc_time_passed.moveTo(BAR_PADDING_LEFT+m_img_btn_play[play_running][widget_normal].width()+PADDING_TIME_PROGRESS_BAR, BAR_PADDING_TOP);
m_rc_time_total.moveTo(m_width - BAR_PADDING_RIGHT - m_rc_time_total.width(), BAR_PADDING_TOP);
int prog_width = m_rc_time_total.left() - PADDING_TIME_PROGRESS_BAR - PADDING_TIME_PROGRESS_BAR - m_rc_time_passed.right();
int prog_height = max(m_res[res_prgbarh_left].height(), m_img_progress_pointer->height());
m_rc_progress = QRect(0, 0, prog_width, prog_height);
m_rc_progress.moveTo(m_rc_time_passed.right() + PADDING_TIME_PROGRESS_BAR, m_rc_time_passed.top() + (m_rc_time_passed.height() - prog_height)/2);
}
// 定位速度按钮
{
int left = m_rc_time_passed.right() + PADDING_TIME_PROGRESS_BAR;
int top = m_rc_time_passed.bottom() + SPEED_BTN_PADDING_TOP;
for(int i = 0; i < speed_count; i++) {
m_rc_btn_speed[i] = QRect(left, top, m_img_btn_speed[i][widget_normal].width(), m_img_btn_speed[i][widget_normal].height());
left += m_img_btn_speed[i][widget_normal].width() + SPEED_BTN_PADDING_RIGHT;
}
}
// 定位跳过选项
{
int left = m_rc_time_total.left() - m_rc_skip.width() - PADDING_TIME_PROGRESS_BAR;
int top = m_rc_time_passed.bottom() + SKIP_PADDING_TOP;//m_rc_btn_speed[0].top() + (m_rc_btn_speed[0].height() - m_rc_skip.height())/2;
m_rc_skip.moveTo(left, top);
}
m_img_ready = true;
}
void Bar::_ms_to_str(uint32_t ms, QString& str) {
int h = 0, m = 0, s = 0;
s = ms / 1000;
if(ms % 1000 > 500)
s += 1;
h = s / 3600;
s = s % 3600;
m = s / 60;
s = s % 60;
if(h > 0)
str.sprintf("%02d:%02d:%02d", h, m, s);
else
str.sprintf("%02d:%02d", m, s);
}
void Bar::update_passed_time(uint32_t ms) {
QString str_passed;
_ms_to_str(ms, str_passed);
if(m_str_passed_time != str_passed)
{
m_str_passed_time = str_passed;
m_owner->update(m_rc.left()+m_rc_time_passed.left(), m_rc.top()+m_rc_time_passed.top(), m_rc_time_passed.width(), m_rc_time_passed.height());
}
int percent = 0;
if(ms >= m_total_ms) {
percent = 100;
m_played_ms = m_total_ms;
}
else {
m_played_ms = ms;
//percent = (int)(((double)m_played_ms / (double)m_total_ms) * 100);
percent = m_played_ms * 100 / m_total_ms;
}
if(percent != m_percent) {
m_percent = percent;
m_owner->update(m_rc.left()+m_rc_progress.left(), m_rc.top()+m_rc_progress.top(), m_rc_progress.width(), m_rc_progress.height());
}
}
void Bar::onMouseMove(int x, int y) {
// 映射鼠标坐标点到本浮动窗内部的相对位置
QPoint pt(x-m_rc.left(), y-m_rc.top());
if(m_progress_pressed) {
// 重新设置进度条指示器位置
int percent = 0;
if(pt.x() < m_rc_progress.left()) {
percent = 0;
m_resume_ms = 1;
}
else if(pt.x() > m_rc_progress.right()) {
percent = 100;
m_resume_ms = m_total_ms;
}
else {
percent = (pt.x() + m_img_progress_pointer[widget_normal].width()/2 - m_rc_progress.left()) * 100 / m_rc_progress.width();
m_resume_ms = m_total_ms * percent / 100;
}
update_passed_time(m_resume_ms);
return;
}
bool play_hover = m_rc_btn_play.contains(pt);
if(play_hover != m_play_hover) {
m_play_hover = play_hover;
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
}
if(play_hover)
return;
int speed_hover = speed_count;
for(int i = 0; i < speed_count; ++i) {
if(m_rc_btn_speed[i].contains(pt)) {
speed_hover = i;
break;
}
}
if(m_speed_hover != speed_hover) {
if(m_speed_hover != speed_count) {
m_owner->update(m_rc.left()+m_rc_btn_speed[m_speed_hover].left(), m_rc.top()+m_rc_btn_speed[m_speed_hover].top(), m_rc_btn_speed[m_speed_hover].width(), m_rc_btn_speed[m_speed_hover].height());
}
m_speed_hover = speed_hover;
if(m_speed_hover != speed_count) {
m_owner->update(m_rc.left()+m_rc_btn_speed[m_speed_hover].left(), m_rc.top()+m_rc_btn_speed[m_speed_hover].top(), m_rc_btn_speed[m_speed_hover].width(), m_rc_btn_speed[m_speed_hover].height());
}
}
bool skip_hover = m_rc_skip.contains(pt);
if(skip_hover != m_skip_hover) {
m_skip_hover = skip_hover;
m_owner->update(m_rc.left()+m_rc_skip.left(), m_rc.top()+m_rc_skip.top(), m_rc_skip.width(), m_rc_skip.height());
}
if(skip_hover)
return;
}
void Bar::onMousePress(int x, int y, Qt::MouseButton button) {
// 我们只关心左键按下
if(button != Qt::LeftButton)
return;
// 映射鼠标坐标点到本浮动窗内部的相对位置
QPoint pt(x-m_rc.left(), y-m_rc.top());
if(m_rc_btn_play.contains(pt)) {
if(m_playing)
m_owner->pause();
else
m_owner->resume(false, 0);
m_playing = !m_playing;
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
return;
}
int speed_sel = speed_count;
for(int i = 0; i < speed_count; ++i) {
if(m_rc_btn_speed[i].contains(pt)) {
speed_sel = i;
break;
}
}
if(m_speed_selected != speed_sel && speed_sel != speed_count) {
int old_sel = m_speed_selected;
m_speed_selected = speed_sel;
m_owner->set_speed(get_speed());
m_owner->update(m_rc.left()+m_rc_btn_speed[old_sel].left(), m_rc.top()+m_rc_btn_speed[old_sel].top(), m_rc_btn_speed[old_sel].width(), m_rc_btn_speed[old_sel].height());
m_owner->update(m_rc.left()+m_rc_btn_speed[m_speed_hover].left(), m_rc.top()+m_rc_btn_speed[m_speed_hover].top(), m_rc_btn_speed[m_speed_hover].width(), m_rc_btn_speed[m_speed_hover].height());
return;
}
if(m_rc_skip.contains(pt)) {
m_skip_selected = !m_skip_selected;
m_owner->set_skip(m_skip_selected);
m_owner->update(m_rc.left()+m_rc_skip.left(), m_rc.top()+m_rc_skip.top(), m_rc_skip.width(), m_rc_skip.height());
return;
}
//
if(m_rc_progress.contains(pt)) {
m_progress_pressed = true;
// TODO: 暂停播放,按比例计算出点击位置占整个录像时长的百分比,定位到此位置准备播放。
// TODO: 如果点击的位置是进度条指示标志,则仅暂停播放
m_owner->pause();
m_playing = false;
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
int percent = 0;
if(pt.x() < m_rc_progress.left()) {
percent = 0;
m_resume_ms = 0;
}
else if(pt.x() > m_rc_progress.right()) {
percent = 100;
m_resume_ms = m_total_ms;
}
else {
percent = (pt.x() + m_img_progress_pointer[widget_normal].width()/2 - m_rc_progress.left()) * 100 / m_rc_progress.width();
m_resume_ms = m_total_ms * percent / 100;
}
update_passed_time(m_resume_ms);
}
}
void Bar::onMouseRelease(int, int, Qt::MouseButton button) {
// 我们只关心左键释放
if(button != Qt::LeftButton)
return;
if(m_progress_pressed) {
m_progress_pressed = false;
qDebug("resume at %dms.", m_resume_ms);
m_owner->resume(true, m_resume_ms);
m_playing = true;
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
}
}
int Bar::get_speed() {
switch (m_speed_selected) {
case speed_1x:
return 1;
case speed_2x:
return 2;
case speed_4x:
return 4;
case speed_8x:
return 8;
default:
return 1;
}
}
void Bar::draw(QPainter& painter, const QRect& rc_draw){
if(!m_width)
return;
if(!rc_draw.intersects(m_rc))
return;
// 绘制背景
{
QRect rc(m_rc);
//rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
int from_x = max(rc_draw.left(), m_rc.left()) - m_rc.left();
int from_y = max(rc_draw.top(), m_rc.top()) - m_rc.top();
int w = min(m_rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(m_rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = m_rc.left() + from_x;
int to_y = m_rc.top() + from_y;
painter.drawPixmap(to_x, to_y, m_img_bg, from_x, from_y, w, h);
}
// 绘制播放按钮
{
QRect rc(m_rc_btn_play);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
if(rc_draw.intersects(rc)) {
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
if(m_playing){
if(m_play_hover)
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_paused][widget_hover], from_x, from_y, w, h);
else
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_paused][widget_normal], from_x, from_y, w, h);
} else {
if(m_play_hover)
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_running][widget_hover], from_x, from_y, w, h);
else
painter.drawPixmap(to_x, to_y, m_img_btn_play[play_running][widget_normal], from_x, from_y, w, h);
}
}
}
// 绘制已播放时间
{
QRect rc(m_rc_time_passed);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
if(rc_draw.intersects(rc)) {
if(m_str_passed_time != m_str_passed_time_last_draw) {
m_img_time_passed = QPixmap(m_rc_time_passed.width(), m_rc_time_passed.height());
m_img_time_passed.fill(Qt::transparent);
QPainter pp(&m_img_time_passed);
pp.setPen(TEXT_COLOR);
QFont font = pp.font();
font.setFamily("consolas");
font.setBold(true);
font.setPixelSize(FONT_SIZE_TIME);
pp.setFont(font);
pp.drawText(QRect(0,0,m_rc_time_passed.width(), m_rc_time_passed.height()), Qt::AlignRight, m_str_passed_time);
m_str_passed_time_last_draw = m_str_passed_time;
}
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
painter.drawPixmap(to_x, to_y, m_img_time_passed, from_x, from_y, w, h);
}
}
// 绘制总时间
{
QRect rc(m_rc_time_total);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
if(rc_draw.intersects(rc)) {
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
painter.drawPixmap(to_x, to_y, m_img_time_total, from_x, from_y, w, h);
}
}
// 绘制进度条
{
QRect rc(m_rc_progress);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
if(rc_draw.intersects(rc)) {
if(m_percent_last_draw != m_percent) {
m_img_progress = QPixmap(m_rc_progress.width(), m_rc_progress.height());
m_img_progress.fill(Qt::transparent);
QPainter pp(&m_img_progress);
// 进度条
int top = (rc.height() - m_res[res_prgbarh_left].height())/2;
int passed_width = rc.width() * m_percent / 100; // 已经播放的进度条宽度
int remain_width = rc.width() - passed_width; // 剩下未播放的进度条宽度
if(passed_width >= m_res[res_prgbarh_left].width())
pp.drawPixmap(0, top , m_res[res_prgbarh_left].width(), m_res[res_prgbarh_left].height(), m_res[res_prgbarh_left]);
if(passed_width > 0) {
//pp.drawPixmap(m_res[res_pbh_left].width(), top, passed_width - m_res[res_pbh_left].width(), m_res[res_pbh_mid].height(), m_res[res_pbh_mid]);
if(remain_width > m_res[res_prgbar_right].width())
pp.drawPixmap(m_res[res_prgbarh_left].width(), top, passed_width - m_res[res_prgbarh_left].width(), m_res[res_prgbarh_mid].height(), m_res[res_prgbarh_mid]);
else
pp.drawPixmap(m_res[res_prgbarh_left].width(), top, passed_width - m_res[res_prgbarh_left].width() - m_res[res_prgbar_right].width(), m_res[res_prgbarh_mid].height(), m_res[res_prgbarh_mid]);
}
if(remain_width > 0)
pp.drawPixmap(passed_width, top, remain_width - m_res[res_prgbar_right].width(), m_res[res_prgbar_mid].height(), m_res[res_prgbar_mid]);
if(remain_width >= m_res[res_prgbar_right].width())
pp.drawPixmap(rc.width() - m_res[res_prgbar_right].width(), top , m_res[res_prgbar_right].width(), m_res[res_prgbar_right].height(), m_res[res_prgbar_right]);
// 进度位置指示
int left = passed_width - m_img_progress_pointer->width() / 2;
if(left < 0)
left = 0;
if(left + m_img_progress_pointer->width() > rc.width())
left = rc.width() - m_img_progress_pointer->width();
top = (rc.height() - m_img_progress_pointer[widget_normal].height())/2;
pp.drawPixmap(left, top , m_img_progress_pointer[widget_normal].width(), m_img_progress_pointer[widget_normal].height(), m_img_progress_pointer[widget_normal]);
m_percent_last_draw = m_percent;
}
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
painter.drawPixmap(to_x, to_y, m_img_progress, from_x, from_y, w, h);
}
}
// 绘制速度按钮
{
for(int i = 0; i < speed_count; i++) {
QRect rc(m_rc_btn_speed[i]);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
if(rc_draw.intersects(rc)) {
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
if(m_speed_hover == i)
painter.drawPixmap(to_x, to_y, m_img_btn_speed[i][btnspd_hover], from_x, from_y, w, h);
else if(m_speed_selected == i)
painter.drawPixmap(to_x, to_y, m_img_btn_speed[i][btnspd_sel], from_x, from_y, w, h);
else
painter.drawPixmap(to_x, to_y, m_img_btn_speed[i][btnspd_normal], from_x, from_y, w, h);
}
}
}
// 绘制跳过选项
{
QRect rc(m_rc_skip);
rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
// painter.fillRect(rc, QColor(255, 255, 255));
if(rc_draw.intersects(rc)) {
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
//qDebug("skip (%d,%d), (%d,%d)/(%d,%d)", to_x, to_y, from_x, from_y, w, h);
if(m_skip_selected) {
if(m_skip_hover)
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_selected][widget_hover], from_x, from_y, w, h);
else
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_selected][widget_normal], from_x, from_y, w, h);
}
else {
if(m_skip_hover)
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_normal][widget_hover], from_x, from_y, w, h);
else
painter.drawPixmap(to_x, to_y, m_img_skip[chkbox_normal][widget_normal], from_x, from_y, w, h);
}
}
}
}

149
client/tp-player/bar.h Normal file
View File

@ -0,0 +1,149 @@
#ifndef BAR_H
#define BAR_H
#include <QPainter>
#include <QPixmap>
#include <QWidget>
typedef enum {
res_bg_left = 0, // 背景
res_bg_mid,
res_bg_right,
res_btn_normal_left, // 按钮(速度选择),普通状态
res_btn_normal_mid,
res_btn_normal_right,
res_btn_sel_left, // 按钮(速度选择),已选中
res_btn_sel_mid,
res_btn_sel_right,
res_btn_hover_left, // 按钮(速度选择),鼠标滑过
res_btn_hover_mid,
res_btn_hover_right,
res_prgbarh_left, // 进度条(已经过)左侧
res_prgbarh_mid, // 进度条(已经过)中间,拉伸填充
res_prgbar_mid, // 进度条(未到达)中间,拉伸填充
res_prgbar_right, // 进度条(未到达)右侧
res_chkbox_normal, // 复选框
res_chkbox_hover,
res_chkbox_sel_normal,
res_chkbox_sel_hover,
res__max
}RES_ID;
//typedef enum {
// widget_normal = 0,
// widget_hover,
// widget__max
//}WIDGET_STAT;
#define widget_normal 0
#define widget_hover 1
#define widget_state_count 2
//typedef enum {
// play_running = 0,
// play_paused,
// play__max
//}PLAY_STAT;
#define play_running 0
#define play_paused 1
#define play_state_count 2
#define speed_1x 0
#define speed_2x 1
#define speed_4x 2
#define speed_8x 3
#define speed_count 4
#define btnspd_normal 0
#define btnspd_sel 1
#define btnspd_hover 2
#define btnspd_state_count 3
#define chkbox_normal 0
#define chkbox_selected 1
#define chkbox_state_count 2
class MainWindow;
class Bar {
public:
Bar();
~Bar();
bool init(MainWindow* owner);
void start(uint32_t total_ms, int width);
void end();
void draw(QPainter& painter, const QRect& rc);
void update_passed_time(uint32_t ms);
int get_speed();
QRect rc(){return m_rc;}
void onMouseMove(int x, int y);
void onMousePress(int x, int y, Qt::MouseButton button);
void onMouseRelease(int x, int y, Qt::MouseButton button);
private:
void _init_imgages();
void _ms_to_str(uint32_t ms, QString& str);
private:
MainWindow* m_owner;
uint32_t m_total_ms; // 录像的总时长
uint32_t m_played_ms; // 已经播放了的时长
int m_percent; // 已经播放了的百分比0~100
int m_percent_last_draw;
QString m_str_total_time;
QString m_str_passed_time;
QString m_str_passed_time_last_draw;
bool m_img_ready;
// 从资源中加载的原始图像
QPixmap m_res[res__max];
QPixmap m_img_progress_pointer[widget_state_count];
int m_width;
int m_height;
// 此浮动窗相对于父窗口的坐标和大小
QRect m_rc;
// 尺寸和定位(此浮动窗内部的相对坐标)
QRect m_rc_btn_play;
QRect m_rc_btn_speed[speed_count];
QRect m_rc_time_passed;
QRect m_rc_time_total;
QRect m_rc_progress;
QRect m_rc_skip;
// 画布,最终输出的图像
//QPixmap m_canvas;
// 合成的图像
QPixmap m_img_bg;
QPixmap m_img_btn_play[play_state_count][widget_state_count];
QPixmap m_img_btn_speed[speed_count][btnspd_state_count];
QPixmap m_img_progress;
QPixmap m_img_skip[chkbox_state_count][widget_state_count];
QPixmap m_img_time_passed;
QPixmap m_img_time_total;
// 各种状态
bool m_playing; // 0=play, 2=pause
bool m_play_hover;
int m_speed_selected;
int m_speed_hover; // speed__max=no-hover
bool m_skip_selected;
bool m_skip_hover;
bool m_progress_hover;
bool m_progress_pressed;
uint32_t m_resume_ms; // after drag progress-pointer, resume play from here.
};
#endif // BAR_H

View File

@ -0,0 +1,120 @@
#include "downloader.h"
#include "record_format.h"
#include <QEventLoop>
#include <QNetworkReply>
#include <qelapsedtimer.h>
Downloader::Downloader() : QObject () {
m_data = nullptr;
m_reply = nullptr;
m_result = false;
}
Downloader::~Downloader() {
}
bool Downloader::request(const QString& url, const QString& sid, const QString& filename) {
return _request(url, sid, filename, nullptr);
}
bool Downloader::request(const QString& url, const QString& sid, QByteArray* data) {
QString fname;
return _request(url, sid, fname, data);
}
bool Downloader::_request(const QString& url, const QString& sid, const QString& filename, QByteArray* data) {
if(filename.isEmpty() && data == nullptr)
return false;
if(!filename.isEmpty() && data != nullptr)
return false;
m_data = data;
if(!filename.isEmpty()) {
m_file.setFileName(filename);
if(!m_file.open(QIODevice::WriteOnly | QFile::Truncate)){
qDebug("open file for write failed.");
return false;
}
}
QString cookie = QString("_sid=%1\r\n").arg(sid);
QNetworkRequest req;
req.setUrl(QUrl(url));
req.setRawHeader("Cookie", cookie.toLatin1());
QNetworkAccessManager* nam = new QNetworkAccessManager();
QEventLoop eloop;
m_reply = nam->get(req);
connect(m_reply, &QNetworkReply::finished, &eloop, &QEventLoop::quit);
connect(m_reply, &QNetworkReply::finished, this, &Downloader::_on_finished);
connect(m_reply, &QIODevice::readyRead, this, &Downloader::_on_data_ready);
// qDebug("before eventLoop.exec(%p)", &eloop);
eloop.exec();
// qDebug("after eventLoop.exec()");
disconnect(m_reply, &QNetworkReply::finished, &eloop, &QEventLoop::quit);
disconnect(m_reply, &QNetworkReply::finished, this, &Downloader::_on_finished);
disconnect(m_reply, &QIODevice::readyRead, this, &Downloader::_on_data_ready);
delete m_reply;
m_reply = nullptr;
delete nam;
qDebug("Downloader::_request() end.");
return m_result;
}
void Downloader::_on_data_ready() {
// qDebug("Downloader::_on_data_ready(%p).", this);
QNetworkReply *reply = reinterpret_cast<QNetworkReply*>(sender());
if(m_data != nullptr) {
m_data->push_back(reply->readAll());
}
else {
m_file.write(reply->readAll());
}
}
void Downloader::abort() {
if(m_reply) {
qDebug("Downloader::abort().");
m_reply->abort();
}
}
void Downloader::_on_finished() {
// qDebug("Downloader::_on_finished(%p).", this);
QNetworkReply *reply = reinterpret_cast<QNetworkReply*>(sender());
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (reply->error() != QNetworkReply::NoError) {
// reply->abort() got "Operation canceled"
//QString strError = reply->errorString();
qDebug() << "ERROR:" << reply->errorString();
if(m_data == nullptr) {
m_file.flush();
m_file.close();
}
m_result = false;
return;
}
if(m_data != nullptr) {
m_data->push_back(reply->readAll());
}
else {
m_file.write(reply->readAll());
m_file.flush();
m_file.close();
}
reply->deleteLater();
m_result = true;
}

View File

@ -0,0 +1,40 @@
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
#include <QFile>
#include <QNetworkAccessManager>
class Downloader : public QObject {
Q_OBJECT
public:
// 从url下载数据写入到filename文件中或放入data中。
Downloader();
~Downloader();
bool request(const QString& url, const QString& sid, const QString& filename);
bool request(const QString& url, const QString& sid, QByteArray* data);
void abort();
private:
bool _request(const QString& url, const QString& sid, const QString& filename, QByteArray* data);
private slots:
void _on_data_ready(); // 有数据可读了,读取并写入文件
void _on_finished(); // 下载结束了
private:
QFile m_file;
QByteArray* m_data;
bool m_result;
QNetworkReply* m_reply;
};
typedef struct DownloadParam {
QString url;
QString sid;
QString fname;
}DownloadParam;
#endif

98
client/tp-player/main.cpp Normal file
View File

@ -0,0 +1,98 @@
#include "mainwindow.h"
#include <QApplication>
#include <QCommandLineParser>
#include <QDebug>
#include <QMessageBox>
#include <QTextCodec>
// 编译出来的可执行程序复制到单独目录,然后执行 windeployqt 应用程序文件名
// 即可自动将依赖的动态库等复制到此目录中。有些文件是多余的,可以酌情删除。
// 命令行参数格式:
// ## 本地文件或目录
// tp-player.exe path/of/tp-rdp.tpr 一个 .tpr 文件的文件名
// tp-player.exe path/contains/tp-rdp.tpr 包含 .tpr 文件的路径
//
// ## 从TP服务器上下载
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意并不直接访问此URI实际上其并不存在)
// TP服务器地址(可能包含子路径,例如上例中的{sub/path}部分)/session-id(用于判断当前授权用户)/录像会话编号
// 按 “/” 进行分割后去掉最后两个项剩下部分是TP服务器的WEB地址用于合成后续的文件下载URL。
// 根据下载的.tpr文件内容本地合成类似于 "000000256-admin-administrator-123.45.77.88-20191109-020047" 的路径来存放下载的文件
// 特别注意,如果账号是 domain\user 这种形式,需要将 "\" 替换为下划线,否则此符号作为路径分隔符,会导致路径不存在而无法保存下载的文件。
// - 获取文件大小: http://127.0.0.1:7190/audit/get-file?act=size&type=rdp&rid=yyyyy&f=file-name
// - 'act'为`size`表示获取文件大小(返回一个数字字符串,就是指定的文件大小)
// - 'type'可以是`rdp`或`ssh`,目前仅用了`rdp`
// - 'rid'是录像会话编号(在服务端,一个会话的录像文件均放在录像会话编号命名的目录下)
// - 'f' 是文件名,即会话编号目录下的指定文件,例如 'tp-rdp.tpr'
// - 读取文件内容: http://127.0.0.1:7190/audit/get-file?act=read&type=rdp&rid=yyyyy&f=file-name&offset=1234&length=1024
// - 'act'为`read`表示获取文件内容
// - 'offset'表示要读取的偏移,如果未指定,则表示从文件起始处开始读取,即默认为 offset=0
// - 'length'表示要读取的大小,如果未指定,表示读取整个文件,即默认为 length=-1服务端对length=-1做完全读取处理
// - 搭配使用 offst 和 length 可以做到分块下载、断点续传。
void show_usage(QCommandLineParser& parser) {
QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(),
"<html><head/><body><pre>"
+ parser.helpText()
+ "\n\n"
+ "RESOURCE could be:\n"
+ " - teleport record file (.tpr).\n"
+ " - a directory contains .tpr file.\n"
+ " - an URL to download teleport record file."
+ "</pre></body></html>");
}
int main(int argc, char *argv[])
{
//#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
//#endif
QApplication a(argc, argv);
//#ifdef __APPLE__
// QString data_path_base = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
// data_path_base += "/tp-testdata/";
//#else
// QString data_path_base = QCoreApplication::applicationDirPath() + "/record";
//#endif
// qDebug("data-path-base: %s", data_path_base.toStdString().c_str());
// return 0;
QGuiApplication::setApplicationDisplayName(LOCAL8BIT("[Teleport播放器]"));
QCommandLineParser parser;
const QCommandLineOption opt_help = parser.addHelpOption();
parser.addPositionalArgument("RESOURCE", "teleport record resource to be play.");
if(!parser.parse(QCoreApplication::arguments())) {
QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(),
//"<html><head/><body><h2 style=\"color:#ff0000;\">" + parser.errorText() + "</h2><pre>"
"<html><head/><body><h2>" + parser.errorText() + "</h2><pre>"
+ parser.helpText() + "</pre></body></html>");
return 1;
}
if(parser.isSet(opt_help)) {
show_usage(parser);
return 2;
}
const QStringList args = parser.positionalArguments();
if(0 == args.size()) {
show_usage(parser);
return 2;
}
QString resource = args.at(0);
qDebug() << resource;
MainWindow w;
w.set_resource(resource);
w.show();
return a.exec();
}

View File

@ -0,0 +1,433 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QMatrix>
#include <QDebug>
#include <QPainter>
#include <QDesktopWidget>
#include <QPaintEvent>
#include <QMessageBox>
#include <QDialogButtonBox>
static inline int min(int a, int b){
return a < b ? a : b;
}
static inline int max(int a, int b){
return a > b ? a : b;
}
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
m_show_default = true;
m_bar_shown = false;
m_bar_fade_in = false;
m_bar_fading = false;
m_bar_opacity = 1.0;
m_show_message = false;
memset(&m_pt, 0, sizeof(TS_RECORD_RDP_POINTER));
m_thr_play = nullptr;
m_play_state = PLAY_STATE_UNKNOWN;
m_thr_data = nullptr;
m_disable_draw = false;
ui->setupUi(this);
ui->centralWidget->setMouseTracking(true);
setMouseTracking(true);
// frame-less window.
//#ifdef __APPLE__
// setWindowFlags(Qt::FramelessWindowHint | Qt::MSWindowsFixedSizeDialogHint | Qt::Window);
// OSXCode::fixWin(winId());
//#else
// setWindowFlags(Qt::FramelessWindowHint | Qt::MSWindowsFixedSizeDialogHint | windowFlags());
//#endif //__APPLE__
m_pt_normal.load(":/tp-player/res/cursor.png");
m_default_bg.load(":/tp-player/res/bg.png");
m_canvas = QPixmap(m_default_bg.width(), m_default_bg.height());
QPainter pp(&m_canvas);
pp.drawPixmap(0, 0, m_default_bg, 0, 0, m_default_bg.width(), m_default_bg.height());
setWindowFlags(windowFlags()&~Qt::WindowMaximizeButtonHint); // 禁止最大化按钮
setFixedSize(m_default_bg.width(), m_default_bg.height()); // 禁止拖动窗口大小
if(!m_bar.init(this)) {
qDebug("bar init failed.");
return;
}
connect(&m_timer_first_run, SIGNAL(timeout()), this, SLOT(_do_first_run()));
connect(&m_timer_bar_fade, SIGNAL(timeout()), this, SLOT(_do_bar_fade()));
connect(&m_timer_bar_delay_hide, SIGNAL(timeout()), this, SLOT(_do_bar_delay_hide()));
m_timer_first_run.setSingleShot(true);
m_timer_first_run.start(500);
}
MainWindow::~MainWindow()
{
if(m_thr_play) {
m_thr_play->stop();
disconnect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
delete m_thr_play;
m_thr_play = nullptr;
}
if(m_thr_data) {
m_thr_data->stop();
disconnect(m_thr_data, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
delete m_thr_data;
m_thr_data = nullptr;
}
delete ui;
}
void MainWindow::set_resource(const QString &res) {
m_res = res;
}
void MainWindow::_do_first_run() {
m_thr_data = new ThrData(this, m_res);
connect(m_thr_data, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
m_thr_data->start(QThread::TimeCriticalPriority);
m_thr_play = new ThrPlay(this);
connect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
m_thr_play->speed(m_bar.get_speed());
m_thr_play->start();
}
void MainWindow::set_speed(int s) {
if(m_thr_play)
m_thr_play->speed(s);
}
void MainWindow::set_skip(bool s) {
if(m_thr_play)
m_thr_play->skip(s);
}
void MainWindow::paintEvent(QPaintEvent *e)
{
QPainter painter(this);
if(m_show_default) {
painter.drawPixmap(e->rect(), m_default_bg, e->rect());
}
else {
painter.drawPixmap(e->rect(), m_canvas, e->rect());
QRect rcpt(m_pt_normal.rect());
rcpt.moveTo(m_pt.x - m_pt_normal.width()/2, m_pt.y-m_pt_normal.height()/2);
if(e->rect().intersects(rcpt)) {
painter.drawPixmap(m_pt.x-m_pt_normal.width()/2, m_pt.y-m_pt_normal.height()/2, m_pt_normal);
}
// 绘制浮动控制窗
if(m_bar_fading) {
painter.setOpacity(m_bar_opacity);
m_bar.draw(painter, e->rect());
}
else if(m_bar_shown) {
m_bar.draw(painter, e->rect());
}
}
if(m_show_message) {
QRect rc_draw = e->rect();
QRect rc(m_rc_message);
if(e->rect().intersects(rc)) {
int from_x = max(rc_draw.left(), rc.left()) - rc.left();
int from_y = max(rc_draw.top(), rc.top()) - rc.top();
int w = min(rc.right(), rc_draw.right()) - rc.left() - from_x + 1;
int h = min(rc.bottom(), rc_draw.bottom()) - rc.top() - from_y + 1;
int to_x = rc.left() + from_x;
int to_y = rc.top() + from_y;
painter.drawPixmap(to_x, to_y, m_img_message, from_x, from_y, w, h);
}
}
}
void MainWindow::pause() {
if(m_play_state != PLAY_STATE_RUNNING)
return;
m_thr_play->pause();
m_play_state = PLAY_STATE_PAUSE;
}
void MainWindow::resume(bool relocate, uint32_t ms) {
if(m_play_state == PLAY_STATE_PAUSE) {
if(relocate)
m_thr_data->restart(ms);
m_thr_play->resume(relocate, ms);
}
else if(m_play_state == PLAY_STATE_STOP) {
m_thr_data->restart(ms);
m_thr_play->resume(true, ms);
}
m_play_state = PLAY_STATE_RUNNING;
}
void MainWindow::_do_update_data(UpdateData* dat) {
if(!dat)
return;
UpdateDataHelper data_helper(dat);
if(dat->data_type() == TYPE_POINTER) {
TS_RECORD_RDP_POINTER pt;
memcpy(&pt, &m_pt, sizeof(TS_RECORD_RDP_POINTER));
// 更新虚拟鼠标信息,这样下一次绘制界面时就会在新的位置绘制出虚拟鼠标
memcpy(&m_pt, dat->get_pointer(), sizeof(TS_RECORD_RDP_POINTER));
update(m_pt.x - m_pt_normal.width()/2, m_pt.y - m_pt_normal.width()/2, m_pt_normal.width(), m_pt_normal.height());
update(pt.x - m_pt_normal.width()/2, pt.y - m_pt_normal.width()/2, m_pt_normal.width(), m_pt_normal.height());
return;
}
else if(dat->data_type() == TYPE_IMAGE) {
const UpdateImages uimgs = dat->get_images();
if(uimgs.size() == 0)
return;
if(uimgs.size() > 1 && !m_disable_draw) {
// 禁止界面更新
setUpdatesEnabled(false);
}
QPainter pp(&m_canvas);
for(int i = 0; i < uimgs.size(); ++i) {
pp.drawImage(uimgs[i].x, uimgs[i].y, *(uimgs[i].img), 0, 0, uimgs[i].w, uimgs[i].h, Qt::AutoColor);
if(!m_disable_draw)
update(uimgs[i].x, uimgs[i].y, uimgs[i].w, uimgs[i].h);
}
if(uimgs.size() > 1 && !m_disable_draw) {
// 允许界面更新
setUpdatesEnabled(true);
}
return;
}
else if(dat->data_type() == TYPE_PLAYED_MS) {
m_bar.update_passed_time(dat->played_ms());
return;
}
else if(dat->data_type() == TYPE_DISABLE_DRAW) {
// 禁止界面更新
m_disable_draw = true;
setUpdatesEnabled(false);
return;
}
else if(dat->data_type() == TYPE_ENABLE_DRAW) {
// 允许界面更新
m_disable_draw = false;
setUpdatesEnabled(true);
update();
return;
}
else if(dat->data_type() == TYPE_MESSAGE) {
if(dat->message().isEmpty()) {
m_show_message = false;
return;
}
m_show_message = true;
qDebug("1message, w=%d, h=%d", m_canvas.width(), m_canvas.height());
QPainter pp(&m_canvas);
QRect rcWin(0, 0, m_canvas.width(), m_canvas.height());
pp.drawText(rcWin, Qt::AlignLeft|Qt::TextDontPrint, dat->message(), &m_rc_message);
qDebug("2message, w=%d, h=%d", m_rc_message.width(), m_rc_message.height());
m_rc_message.setWidth(m_rc_message.width()+60);
m_rc_message.setHeight(m_rc_message.height()+60);
m_img_message = QPixmap(m_rc_message.width(), m_rc_message.height());
m_img_message.fill(Qt::transparent);
QPainter pm(&m_img_message);
pm.setPen(QColor(255,255,255,153));
pm.fillRect(m_rc_message, QColor(0,0,0,190));
QRect rcRect(m_rc_message);
rcRect.setWidth(rcRect.width()-1);
rcRect.setHeight(rcRect.height()-1);
pm.drawRect(rcRect);
QRect rcText(m_rc_message);
rcText.setLeft(30);
rcText.setTop(30);
pm.drawText(rcText, Qt::AlignLeft, dat->message());
m_rc_message.moveTo(
(m_canvas.width() - m_rc_message.width())/2,
(m_canvas.height() - m_rc_message.height())/3
);
update(m_rc_message.x(), m_rc_message.y(), m_rc_message.width(), m_rc_message.height());
return;
}
else if(dat->data_type() == TYPE_ERROR) {
QMessageBox::critical(this, QGuiApplication::applicationDisplayName(), dat->message());
QApplication::instance()->exit(0);
return;
}
// 这是播放开始时收到的第一个数据包
else if(dat->data_type() == TYPE_HEADER_INFO) {
TS_RECORD_HEADER* hdr = dat->get_header();
if(hdr == nullptr)
return;
memcpy(&m_rec_hdr, hdr, sizeof(TS_RECORD_HEADER));
qDebug() << "resize (" << m_rec_hdr.basic.width << "," << m_rec_hdr.basic.height << ")";
m_canvas = QPixmap(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
QDesktopWidget *desktop = QApplication::desktop(); // =qApp->desktop();也可以
qDebug("desktop w:%d,h:%d, this w:%d,h:%d", desktop->width(), desktop->height(), width(), height());
move(10, (desktop->height() - m_rec_hdr.basic.height)/2);
setFixedSize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
m_canvas.fill(QColor(38, 73, 111));
m_show_default = false;
repaint();
m_bar.start(m_rec_hdr.info.time_ms, 640);
m_bar_shown = true;
m_play_state = PLAY_STATE_RUNNING;
update(m_bar.rc());
m_bar_fade_in = false;
m_bar_fading = true;
m_timer_bar_delay_hide.start(2000);
QString title;
if (m_rec_hdr.basic.conn_port == 3389) {
title = QString(LOCAL8BIT("用户 %1 访问 %2 的 %3 账号").arg(m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip, m_rec_hdr.basic.acc_username));
}
else {
QString _port;
_port.sprintf("%d", m_rec_hdr.basic.conn_port);
title = QString(LOCAL8BIT("用户 %1 访问 %2:%3 的 %4 账号").arg(m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip, _port, m_rec_hdr.basic.acc_username));
}
setWindowTitle(title);
return;
}
else if(dat->data_type() == TYPE_END) {
m_bar.end();
m_play_state = PLAY_STATE_STOP;
return;
}
}
void MainWindow::_do_bar_delay_hide() {
m_bar_fading = true;
m_timer_bar_delay_hide.stop();
m_timer_bar_fade.stop();
m_timer_bar_fade.start(50);
}
void MainWindow::_do_bar_fade() {
if(m_bar_fade_in) {
if(m_bar_opacity < 1.0)
m_bar_opacity += 0.3;
if(m_bar_opacity >= 1.0) {
m_bar_opacity = 1.0;
m_bar_shown = true;
m_bar_fading = false;
m_timer_bar_fade.stop();
}
}
else {
if(m_bar_opacity > 0.0)
m_bar_opacity -= 0.2;
if(m_bar_opacity <= 0.0) {
m_bar_opacity = 0.0;
m_bar_shown = false;
m_bar_fading = false;
m_timer_bar_fade.stop();
}
}
update(m_bar.rc());
}
void MainWindow::mouseMoveEvent(QMouseEvent *e) {
if(!m_show_default) {
QRect rc = m_bar.rc();
if(e->y() > rc.top() - 20 && e->y() < rc.bottom() + 20) {
if((!m_bar_shown && !m_bar_fading) || (m_bar_fading && !m_bar_fade_in)) {
m_bar_fade_in = true;
m_bar_fading = true;
m_timer_bar_delay_hide.stop();
m_timer_bar_fade.stop();
m_timer_bar_fade.start(50);
}
if(rc.contains(e->pos()))
m_bar.onMouseMove(e->x(), e->y());
}
else {
if((m_bar_shown && !m_bar_fading) || (m_bar_fading && m_bar_fade_in)) {
m_bar_fade_in = false;
m_bar_fading = true;
m_timer_bar_fade.stop();
m_timer_bar_delay_hide.stop();
if(m_bar_opacity != 1.0)
m_timer_bar_fade.start(50);
else
m_timer_bar_delay_hide.start(1000);
}
}
}
}
void MainWindow::mousePressEvent(QMouseEvent *e) {
if(!m_show_default) {
QRect rc = m_bar.rc();
if(rc.contains(e->pos())) {
m_bar.onMousePress(e->x(), e->y(), e->button());
}
}
}
void MainWindow::mouseReleaseEvent(QMouseEvent *e) {
m_bar.onMouseRelease(e->x(), e->y(), e->button());
}

View File

@ -0,0 +1,90 @@
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QMessageBox>
#include <QTimer>
#include "bar.h"
#include "thr_play.h"
#include "thr_data.h"
#include "update_data.h"
#include "record_format.h"
#include "util.h"
#include "downloader.h"
#define PLAY_STATE_UNKNOWN 0
#define PLAY_STATE_RUNNING 1
#define PLAY_STATE_PAUSE 2
#define PLAY_STATE_STOP 3
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow();
void set_resource(const QString& res);
void pause();
void resume(bool relocate, uint32_t ms);
void restart();
void set_speed(int s);
void set_skip(bool s);
// TODO: 将thr_data移动到thr_play线程由play线程进行管理
ThrData* get_thr_data() {return m_thr_data;}
private:
void paintEvent(QPaintEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
private slots:
void _do_first_run(); // 默认界面加载完成后,开始播放操作(可能会进行数据下载)
void _do_update_data(UpdateData*);
void _do_bar_fade();
void _do_bar_delay_hide();
private:
Ui::MainWindow *ui;
bool m_show_default;
bool m_bar_shown;
QPixmap m_default_bg;
QString m_res;
ThrPlay* m_thr_play;
ThrData* m_thr_data;
QPixmap m_canvas;
Bar m_bar;
TS_RECORD_HEADER m_rec_hdr;
QPixmap m_pt_normal;
TS_RECORD_RDP_POINTER m_pt;
QTimer m_timer_first_run;
QTimer m_timer_bar_fade;
QTimer m_timer_bar_delay_hide;
bool m_bar_fade_in;
bool m_bar_fading;
qreal m_bar_opacity;
int m_play_state;
bool m_show_message;
QPixmap m_img_message;
QRect m_rc_message;
bool m_disable_draw;
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>MainWindow</class>
<widget class="QMainWindow" name="MainWindow">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>500</width>
<height>360</height>
</rect>
</property>
<property name="font">
<font>
<family>微软雅黑 Light</family>
</font>
</property>
<property name="windowTitle">
<string>Teleport Replayer</string>
</property>
<widget class="QWidget" name="centralWidget"/>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>
<connections/>
</ui>

View File

@ -0,0 +1,109 @@
#ifndef RECORD_FORMAT_H
#define RECORD_FORMAT_H
#include <Qt>
#define TS_TPPR_TYPE_UNKNOWN 0x0000
#define TS_TPPR_TYPE_SSH 0x0001
#define TS_TPPR_TYPE_RDP 0x0101
#define TS_RECORD_TYPE_RDP_POINTER 0x12 // 鼠标坐标位置改变,用于绘制虚拟鼠标
#define TS_RECORD_TYPE_RDP_IMAGE 0x13 // 服务端返回的图像,用于展示
#define TS_RECORD_TYPE_RDP_KEYFRAME 0x14 //
#define TS_RDP_BTN_FREE 0
#define TS_RDP_BTN_PRESSED 1
#define TS_RDP_IMG_RAW 0 // 未压缩原始数据根据bitsPerPixel多个字节对应一个点的颜色
#define TS_RDP_IMG_BMP 1 // 压缩的BMP数据
#define TS_RDP_IMG_ALT 2
#pragma pack(push,1)
// 录像文件头(随着录像数据写入,会改变的部分)
typedef struct TS_RECORD_HEADER_INFO {
uint32_t magic; // "TPPR" 标志 TelePort Protocol Record
uint16_t ver; // 录像文件版本从3.5.0开始为4
uint16_t type; //
// uint32_t packages; // 总包数
uint32_t time_ms; // 总耗时(毫秒)
uint32_t dat_file_count; // 数据文件数量
uint8_t _reserve[64-4-2-2-4-4];
}TS_RECORD_HEADER_INFO;
#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO)
// 录像文件头(固定不变部分)
typedef struct TS_RECORD_HEADER_BASIC {
uint16_t protocol_type; // 协议1=RDP, 2=SSH, 3=Telnet
uint16_t protocol_sub_type; // 子协议100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet
uint64_t timestamp; // 本次录像的起始时间UTC时间戳
uint16_t width; // 初始屏幕尺寸:宽
uint16_t height; // 初始屏幕尺寸:高
char user_username[64]; // teleport账号
char acc_username[64]; // 远程主机用户名
char host_ip[40]; // 远程主机IP
char conn_ip[40]; // 远程主机IP
uint16_t conn_port; // 远程主机端口
char client_ip[40]; // 客户端IP
// // RDP专有
// uint8_t rdp_security; // 0 = RDP, 1 = TLS
// uint8_t _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - 1 - ts_record_header_info_size];
uint8_t _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - ts_record_header_info_size];
}TS_RECORD_HEADER_BASIC;
#define ts_record_header_basic_size sizeof(TS_RECORD_HEADER_BASIC)
typedef struct TS_RECORD_HEADER {
TS_RECORD_HEADER_INFO info;
TS_RECORD_HEADER_BASIC basic;
}TS_RECORD_HEADER;
// header部分header-info + header-basic = 512B
#define ts_record_header_size sizeof(TS_RECORD_HEADER)
// 一个数据包的头
typedef struct TS_RECORD_PKG {
uint8_t type; // 包的数据类型
uint8_t _reserve[3]; // 保留
uint32_t size; // 这个包的总大小(不含包头)
uint32_t time_ms; // 这个包距起始时间的时间差毫秒意味着一个连接不能持续超过49天
// uint32_t index; // 这个包的序号最后一个包的序号与TS_RECORD_HEADER_INFO::packages数量匹配
}TS_RECORD_PKG;
typedef struct TS_RECORD_RDP_POINTER {
uint16_t x;
uint16_t y;
uint8_t button;
uint8_t pressed;
}TS_RECORD_RDP_POINTER;
// RDP图像更新
typedef struct TS_RECORD_RDP_IMAGE_INFO {
uint16_t destLeft;
uint16_t destTop;
uint16_t destRight;
uint16_t destBottom;
uint16_t width;
uint16_t height;
uint16_t bitsPerPixel;
uint8_t format;
uint8_t _reserved;
uint32_t dat_len;
uint32_t zip_len;
}TS_RECORD_RDP_IMAGE_INFO;
// 关键帧索引
typedef struct TS_RECORD_RDP_KEYFRAME_INFO {
uint32_t time_ms; // 此关键帧的时间点
uint32_t file_index; // 此关键帧图像数据位于哪一个数据文件中
uint32_t offset; // 此关键帧图像数据在数据文件中的偏移
}TS_RECORD_RDP_KEYFRAME_INFO;
#pragma pack(pop)
#endif // RECORD_FORMAT_H

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1008 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1016 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1003 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1004 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

BIN
client/tp-player/res/bg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 63 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

974
client/tp-player/rle.c Normal file
View File

@ -0,0 +1,974 @@
/* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client.
Bitmap decompression routines
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
/* three seperate function for speed when decompressing the bitmaps
when modifing one function make the change in the others
jay.sorg@gmail.com */
/* indent is confused by this file */
/* *INDENT-OFF* */
#include <stdlib.h>
#include "rle.h"
/* Specific rename for RDPY integration */
#define unimpl(str, code)
//#define RD_BOOL int
//#define False 0
//#define True 1
/* end specific rename */
#define CVAL(p) (*(p++))
//#ifdef NEED_ALIGN
//#ifdef L_ENDIAN
#define CVAL2(p, v) { v = (*(p++)); v |= (*(p++)) << 8; }
//#else
//#define CVAL2(p, v) { v = (*(p++)) << 8; v |= (*(p++)); }
//#endif /* L_ENDIAN */
//#else
//#define CVAL2(p, v) { v = (*((uint16*)p)); p += 2; }
//#endif /* NEED_ALIGN */
#define UNROLL8(exp) { exp exp exp exp exp exp exp exp }
#define REPEAT(statement) \
{ \
while((count & ~0x7) && ((x+8) < width)) \
UNROLL8( statement; count--; x++; ); \
\
while((count > 0) && (x < width)) \
{ \
statement; \
count--; \
x++; \
} \
}
#define MASK_UPDATE() \
{ \
mixmask <<= 1; \
if (mixmask == 0) \
{ \
mask = fom_mask ? fom_mask : CVAL(input); \
mixmask = 1; \
} \
}
/* 1 byte bitmap decompress */
RD_BOOL
bitmap_decompress1(uint8 * output, int width, int height, const uint8 * input, int size)
{
const uint8 *end = input + size;
uint8 *prevline = NULL, *line = NULL;
int opcode, count, offset, isfillormix, x = width;
int lastopcode = -1, insertmix = False, bicolour = False;
uint8 code;
uint8 colour1 = 0, colour2 = 0;
uint8 mixmask, mask = 0;
uint8 mix = 0xff;
int fom_mask = 0;
while (input < end)
{
fom_mask = 0;
code = CVAL(input);
opcode = code >> 4;
/* Handle different opcode forms */
switch (opcode)
{
case 0xc:
case 0xd:
case 0xe:
opcode -= 6;
count = code & 0xf;
offset = 16;
break;
case 0xf:
opcode = code & 0xf;
if (opcode < 9)
{
count = CVAL(input);
count |= CVAL(input) << 8;
}
else
{
count = (opcode < 0xb) ? 8 : 1;
}
offset = 0;
break;
default:
opcode >>= 1;
count = code & 0x1f;
offset = 32;
break;
}
/* Handle strange cases for counts */
if (offset != 0)
{
isfillormix = ((opcode == 2) || (opcode == 7));
if (count == 0)
{
if (isfillormix)
count = CVAL(input) + 1;
else
count = CVAL(input) + offset;
}
else if (isfillormix)
{
count <<= 3;
}
}
/* Read preliminary data */
switch (opcode)
{
case 0: /* Fill */
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
insertmix = True;
break;
case 8: /* Bicolour */
colour1 = CVAL(input);
case 3: /* Colour */
colour2 = CVAL(input);
break;
case 6: /* SetMix/Mix */
case 7: /* SetMix/FillOrMix */
mix = CVAL(input);
opcode -= 5;
break;
case 9: /* FillOrMix_1 */
mask = 0x03;
opcode = 0x02;
fom_mask = 3;
break;
case 0x0a: /* FillOrMix_2 */
mask = 0x05;
opcode = 0x02;
fom_mask = 5;
break;
}
lastopcode = opcode;
mixmask = 0;
/* Output body */
while (count > 0)
{
if (x >= width)
{
if (height <= 0)
return False;
x = 0;
height--;
prevline = line;
line = output + height * width;
}
switch (opcode)
{
case 0: /* Fill */
if (insertmix)
{
if (prevline == NULL)
line[x] = mix;
else
line[x] = prevline[x] ^ mix;
insertmix = False;
count--;
x++;
}
if (prevline == NULL)
{
REPEAT(line[x] = 0)
}
else
{
REPEAT(line[x] = prevline[x])
}
break;
case 1: /* Mix */
if (prevline == NULL)
{
REPEAT(line[x] = mix)
}
else
{
REPEAT(line[x] = prevline[x] ^ mix)
}
break;
case 2: /* Fill or Mix */
if (prevline == NULL)
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
line[x] = mix;
else
line[x] = 0;
)
}
else
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
line[x] = prevline[x] ^ mix;
else
line[x] = prevline[x];
)
}
break;
case 3: /* Colour */
REPEAT(line[x] = colour2)
break;
case 4: /* Copy */
REPEAT(line[x] = CVAL(input))
break;
case 8: /* Bicolour */
REPEAT
(
if (bicolour)
{
line[x] = colour2;
bicolour = False;
}
else
{
line[x] = colour1;
bicolour = True; count++;
}
)
break;
case 0xd: /* White */
REPEAT(line[x] = 0xff)
break;
case 0xe: /* Black */
REPEAT(line[x] = 0)
break;
default:
unimpl("bitmap opcode 0x%x\n", opcode);
return False;
}
}
}
return True;
}
/* 2 byte bitmap decompress */
RD_BOOL
bitmap_decompress2(uint8 * output, int width, int height, const uint8 * input, int size)
{
const uint8 *end = input + size;
uint16 *prevline = NULL, *line = NULL;
int opcode, count, offset, isfillormix, x = width;
int lastopcode = -1, insertmix = False, bicolour = False;
uint8 code;
uint16 colour1 = 0, colour2 = 0;
uint8 mixmask, mask = 0;
uint16 mix = 0xffff;
int fom_mask = 0;
while (input < end)
{
fom_mask = 0;
code = CVAL(input);
opcode = code >> 4;
/* Handle different opcode forms */
switch (opcode)
{
case 0xc:
case 0xd:
case 0xe:
opcode -= 6;
count = code & 0xf;
offset = 16;
break;
case 0xf:
opcode = code & 0xf;
if (opcode < 9)
{
count = CVAL(input);
count |= CVAL(input) << 8;
}
else
{
count = (opcode < 0xb) ? 8 : 1;
}
offset = 0;
break;
default:
opcode >>= 1;
count = code & 0x1f;
offset = 32;
break;
}
/* Handle strange cases for counts */
if (offset != 0)
{
isfillormix = ((opcode == 2) || (opcode == 7));
if (count == 0)
{
if (isfillormix)
count = CVAL(input) + 1;
else
count = CVAL(input) + offset;
}
else if (isfillormix)
{
count <<= 3;
}
}
/* Read preliminary data */
switch (opcode)
{
case 0: /* Fill */
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
insertmix = True;
break;
case 8: /* Bicolour */
CVAL2(input, colour1);
case 3: /* Colour */
CVAL2(input, colour2);
break;
case 6: /* SetMix/Mix */
case 7: /* SetMix/FillOrMix */
CVAL2(input, mix);
opcode -= 5;
break;
case 9: /* FillOrMix_1 */
mask = 0x03;
opcode = 0x02;
fom_mask = 3;
break;
case 0x0a: /* FillOrMix_2 */
mask = 0x05;
opcode = 0x02;
fom_mask = 5;
break;
}
lastopcode = opcode;
mixmask = 0;
/* Output body */
while (count > 0)
{
if (x >= width)
{
if (height <= 0)
return False;
x = 0;
height--;
prevline = line;
line = ((uint16 *) output) + height * width;
}
switch (opcode)
{
case 0: /* Fill */
if (insertmix)
{
if (prevline == NULL)
line[x] = mix;
else
line[x] = prevline[x] ^ mix;
insertmix = False;
count--;
x++;
}
if (prevline == NULL)
{
REPEAT(line[x] = 0)
}
else
{
REPEAT(line[x] = prevline[x])
}
break;
case 1: /* Mix */
if (prevline == NULL)
{
REPEAT(line[x] = mix)
}
else
{
REPEAT(line[x] = prevline[x] ^ mix)
}
break;
case 2: /* Fill or Mix */
if (prevline == NULL)
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
line[x] = mix;
else
line[x] = 0;
)
}
else
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
line[x] = prevline[x] ^ mix;
else
line[x] = prevline[x];
)
}
break;
case 3: /* Colour */
REPEAT(line[x] = colour2)
break;
case 4: /* Copy */
REPEAT(CVAL2(input, line[x]))
break;
case 8: /* Bicolour */
REPEAT
(
if (bicolour)
{
line[x] = colour2;
bicolour = False;
}
else
{
line[x] = colour1;
bicolour = True;
count++;
}
)
break;
case 0xd: /* White */
REPEAT(line[x] = 0xffff)
break;
case 0xe: /* Black */
REPEAT(line[x] = 0)
break;
default:
unimpl("bitmap opcode 0x%x\n", opcode);
return False;
}
}
}
return True;
}
/* 3 byte bitmap decompress */
RD_BOOL
bitmap_decompress3(uint8 * output, int width, int height, const uint8 * input, int size)
{
uint8 *end = input + size;
uint8 *prevline = NULL, *line = NULL;
int opcode, count, offset, isfillormix, x = width;
int lastopcode = -1, insertmix = False, bicolour = False;
uint8 code;
uint8 colour1[3] = {0, 0, 0}, colour2[3] = {0, 0, 0};
uint8 mixmask, mask = 0;
uint8 mix[3] = {0xff, 0xff, 0xff};
int fom_mask = 0;
while (input < end)
{
fom_mask = 0;
code = CVAL(input);
opcode = code >> 4;
/* Handle different opcode forms */
switch (opcode)
{
case 0xc:
case 0xd:
case 0xe:
opcode -= 6;
count = code & 0xf;
offset = 16;
break;
case 0xf:
opcode = code & 0xf;
if (opcode < 9)
{
count = CVAL(input);
count |= CVAL(input) << 8;
}
else
{
count = (opcode <
0xb) ? 8 : 1;
}
offset = 0;
break;
default:
opcode >>= 1;
count = code & 0x1f;
offset = 32;
break;
}
/* Handle strange cases for counts */
if (offset != 0)
{
isfillormix = ((opcode == 2) || (opcode == 7));
if (count == 0)
{
if (isfillormix)
count = CVAL(input) + 1;
else
count = CVAL(input) + offset;
}
else if (isfillormix)
{
count <<= 3;
}
}
/* Read preliminary data */
switch (opcode)
{
case 0: /* Fill */
if ((lastopcode == opcode) && !((x == width) && (prevline == NULL)))
insertmix = True;
break;
case 8: /* Bicolour */
colour1[0] = CVAL(input);
colour1[1] = CVAL(input);
colour1[2] = CVAL(input);
case 3: /* Colour */
colour2[0] = CVAL(input);
colour2[1] = CVAL(input);
colour2[2] = CVAL(input);
break;
case 6: /* SetMix/Mix */
case 7: /* SetMix/FillOrMix */
mix[0] = CVAL(input);
mix[1] = CVAL(input);
mix[2] = CVAL(input);
opcode -= 5;
break;
case 9: /* FillOrMix_1 */
mask = 0x03;
opcode = 0x02;
fom_mask = 3;
break;
case 0x0a: /* FillOrMix_2 */
mask = 0x05;
opcode = 0x02;
fom_mask = 5;
break;
}
lastopcode = opcode;
mixmask = 0;
/* Output body */
while (count > 0)
{
if (x >= width)
{
if (height <= 0)
return False;
x = 0;
height--;
prevline = line;
line = output + height * (width * 3);
}
switch (opcode)
{
case 0: /* Fill */
if (insertmix)
{
if (prevline == NULL)
{
line[x * 3] = mix[0];
line[x * 3 + 1] = mix[1];
line[x * 3 + 2] = mix[2];
}
else
{
line[x * 3] =
prevline[x * 3] ^ mix[0];
line[x * 3 + 1] =
prevline[x * 3 + 1] ^ mix[1];
line[x * 3 + 2] =
prevline[x * 3 + 2] ^ mix[2];
}
insertmix = False;
count--;
x++;
}
if (prevline == NULL)
{
REPEAT
(
line[x * 3] = 0;
line[x * 3 + 1] = 0;
line[x * 3 + 2] = 0;
)
}
else
{
REPEAT
(
line[x * 3] = prevline[x * 3];
line[x * 3 + 1] = prevline[x * 3 + 1];
line[x * 3 + 2] = prevline[x * 3 + 2];
)
}
break;
case 1: /* Mix */
if (prevline == NULL)
{
REPEAT
(
line[x * 3] = mix[0];
line[x * 3 + 1] = mix[1];
line[x * 3 + 2] = mix[2];
)
}
else
{
REPEAT
(
line[x * 3] =
prevline[x * 3] ^ mix[0];
line[x * 3 + 1] =
prevline[x * 3 + 1] ^ mix[1];
line[x * 3 + 2] =
prevline[x * 3 + 2] ^ mix[2];
)
}
break;
case 2: /* Fill or Mix */
if (prevline == NULL)
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
{
line[x * 3] = mix[0];
line[x * 3 + 1] = mix[1];
line[x * 3 + 2] = mix[2];
}
else
{
line[x * 3] = 0;
line[x * 3 + 1] = 0;
line[x * 3 + 2] = 0;
}
)
}
else
{
REPEAT
(
MASK_UPDATE();
if (mask & mixmask)
{
line[x * 3] =
prevline[x * 3] ^ mix [0];
line[x * 3 + 1] =
prevline[x * 3 + 1] ^ mix [1];
line[x * 3 + 2] =
prevline[x * 3 + 2] ^ mix [2];
}
else
{
line[x * 3] =
prevline[x * 3];
line[x * 3 + 1] =
prevline[x * 3 + 1];
line[x * 3 + 2] =
prevline[x * 3 + 2];
}
)
}
break;
case 3: /* Colour */
REPEAT
(
line[x * 3] = colour2 [0];
line[x * 3 + 1] = colour2 [1];
line[x * 3 + 2] = colour2 [2];
)
break;
case 4: /* Copy */
REPEAT
(
line[x * 3] = CVAL(input);
line[x * 3 + 1] = CVAL(input);
line[x * 3 + 2] = CVAL(input);
)
break;
case 8: /* Bicolour */
REPEAT
(
if (bicolour)
{
line[x * 3] = colour2[0];
line[x * 3 + 1] = colour2[1];
line[x * 3 + 2] = colour2[2];
bicolour = False;
}
else
{
line[x * 3] = colour1[0];
line[x * 3 + 1] = colour1[1];
line[x * 3 + 2] = colour1[2];
bicolour = True;
count++;
}
)
break;
case 0xd: /* White */
REPEAT
(
line[x * 3] = 0xff;
line[x * 3 + 1] = 0xff;
line[x * 3 + 2] = 0xff;
)
break;
case 0xe: /* Black */
REPEAT
(
line[x * 3] = 0;
line[x * 3 + 1] = 0;
line[x * 3 + 2] = 0;
)
break;
default:
unimpl("bitmap opcode 0x%x\n", opcode);
return False;
}
}
}
return True;
}
/* decompress a colour plane */
static int
process_plane(uint8 * in, int width, int height, uint8 * out, int size)
{
int indexw;
int indexh;
int code;
int collen;
int replen;
int color;
int x;
int revcode;
uint8 * last_line;
uint8 * this_line;
uint8 * org_in;
uint8 * org_out;
org_in = in;
org_out = out;
last_line = 0;
indexh = 0;
while (indexh < height)
{
out = (org_out + width * height * 4) - ((indexh + 1) * width * 4);
color = 0;
this_line = out;
indexw = 0;
if (last_line == 0)
{
while (indexw < width)
{
code = CVAL(in);
replen = code & 0xf;
collen = (code >> 4) & 0xf;
revcode = (replen << 4) | collen;
if ((revcode <= 47) && (revcode >= 16))
{
replen = revcode;
collen = 0;
}
while (collen > 0)
{
color = CVAL(in);
*out = color;
out += 4;
indexw++;
collen--;
}
while (replen > 0)
{
*out = color;
out += 4;
indexw++;
replen--;
}
}
}
else
{
while (indexw < width)
{
code = CVAL(in);
replen = code & 0xf;
collen = (code >> 4) & 0xf;
revcode = (replen << 4) | collen;
if ((revcode <= 47) && (revcode >= 16))
{
replen = revcode;
collen = 0;
}
while (collen > 0)
{
x = CVAL(in);
if (x & 1)
{
x = x >> 1;
x = x + 1;
color = -x;
}
else
{
x = x >> 1;
color = x;
}
x = last_line[indexw * 4] + color;
*out = x;
out += 4;
indexw++;
collen--;
}
while (replen > 0)
{
x = last_line[indexw * 4] + color;
*out = x;
out += 4;
indexw++;
replen--;
}
}
}
indexh++;
last_line = this_line;
}
return (int) (in - org_in);
}
/* 4 byte bitmap decompress */
RD_BOOL
bitmap_decompress4(uint8 * output, int width, int height, const uint8 * input, int size)
{
int code;
int bytes_pro;
int total_pro;
code = CVAL(input);
if (code != 0x10)
{
return False;
}
total_pro = 1;
bytes_pro = process_plane(input, width, height, output + 3, size - total_pro);
total_pro += bytes_pro;
input += bytes_pro;
bytes_pro = process_plane(input, width, height, output + 2, size - total_pro);
total_pro += bytes_pro;
input += bytes_pro;
bytes_pro = process_plane(input, width, height, output + 1, size - total_pro);
total_pro += bytes_pro;
input += bytes_pro;
bytes_pro = process_plane(input, width, height, output + 0, size - total_pro);
total_pro += bytes_pro;
return size == total_pro;
}
int
bitmap_decompress_15(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
uint8 * temp = (uint8*)malloc(input_width * input_height * 2);
RD_BOOL rv = bitmap_decompress2(temp, input_width, input_height, input, size);
// convert to rgba
for (int y = 0; y < output_height; y++) {
for (int x = 0; x < output_width; x++) {
uint16 a = ((uint16*)temp)[y * input_width + x];
uint8 r = (a & 0x7c00) >> 10;
uint8 g = (a & 0x03e0) >> 5;
uint8 b = (a & 0x001f);
r = r * 255 / 31;
g = g * 255 / 31;
b = b * 255 / 31;
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
}
}
free(temp);
return rv;
}
int
bitmap_decompress_16(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
uint8 * temp = (uint8*)malloc(input_width * input_height * 2);
RD_BOOL rv = bitmap_decompress2(temp, input_width, input_height, input, size);
// convert to rgba
for (int y = 0; y < output_height; y++) {
for (int x = 0; x < output_width; x++) {
uint16 a = ((uint16*)temp)[y * input_width + x];
uint8 r = (a & 0xf800) >> 11;
uint8 g = (a & 0x07e0) >> 5;
uint8 b = (a & 0x001f);
r = r * 255 / 31;
g = g * 255 / 63;
b = b * 255 / 31;
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
}
}
free(temp);
return rv;
}
int
bitmap_decompress_24(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
uint8 * temp = (uint8*)malloc(input_width * input_height * 3);
RD_BOOL rv = bitmap_decompress3(temp, input_width, input_height, input, size);
// convert to rgba
for (int y = 0; y < output_height; y++) {
for (int x = 0; x < output_width; x++) {
uint8 r = temp[(y * input_width + x) * 3];
uint8 g = temp[(y * input_width + x) * 3 + 1];
uint8 b = temp[(y * input_width + x) * 3 + 2];
((uint32*)output)[y * output_width + x] = 0xff << 24 | b << 16 | g << 8 | r;
}
}
free(temp);
return rv;
}
int
bitmap_decompress_32(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size) {
uint8 * temp = (uint8*)malloc(input_width * input_height * 4);
RD_BOOL rv = bitmap_decompress4(temp, input_width, input_height, input, size);
// convert to rgba
for (int y = 0; y < output_height; y++) {
for (int x = 0; x < output_width; x++) {
uint8 r = temp[(y * input_width + x) * 4];
uint8 g = temp[(y * input_width + x) * 4 + 1];
uint8 b = temp[(y * input_width + x) * 4 + 2];
uint8 a = temp[(y * input_width + x) * 4 + 3];
((uint32*)output)[y * output_width + x] = 0xff << 24 | r << 16 | g << 8 | b;
}
}
free(temp);
return rv;
}

31
client/tp-player/rle.h Normal file
View File

@ -0,0 +1,31 @@
#ifndef RLE_H
#define RLE_H
#define RD_BOOL int
#define False 0
#define True 1
#define uint8 unsigned char
#define uint16 unsigned short
#define uint32 unsigned int
#ifdef __cplusplus
extern "C" {
#endif
RD_BOOL bitmap_decompress1(uint8 * output, int width, int height, const uint8 * input, int size);
RD_BOOL bitmap_decompress2(uint8 * output, int width, int height, const uint8 * input, int size);
RD_BOOL bitmap_decompress3(uint8 * output, int width, int height, const uint8 * input, int size);
RD_BOOL bitmap_decompress4(uint8 * output, int width, int height, const uint8 * input, int size);
int bitmap_decompress_15(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
int bitmap_decompress_16(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
int bitmap_decompress_24(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
int bitmap_decompress_32(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size);
#ifdef __cplusplus
}
#endif
#endif // RLE_H

View File

@ -0,0 +1,709 @@
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QNetworkCookie>
#include <QStandardPaths>
#include <qcoreapplication.h>
#include <inttypes.h>
#include <zlib.h>
#include "thr_play.h"
#include "thr_data.h"
#include "util.h"
#include "downloader.h"
#include "record_format.h"
#include "mainwindow.h"
#include "rle.h"
static QImage* _rdpimg2QImage(int w, int h, int bitsPerPixel, bool isCompressed, const uint8_t* dat, uint32_t len) {
QImage* out;
switch(bitsPerPixel) {
case 15:
if(isCompressed) {
uint8_t* _dat = reinterpret_cast<uint8_t*>(calloc(1, w*h*2));
if(!bitmap_decompress1(_dat, w, h, dat, len)) {
free(_dat);
return nullptr;
}
out = new QImage(_dat, w, h, QImage::Format_RGB555);
free(_dat);
}
else {
out = new QImage(QImage(dat, w, h, QImage::Format_RGB555).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)));
}
return out;
case 16:
if(isCompressed) {
uint8_t* _dat = reinterpret_cast<uint8_t*>(calloc(1, w*h*2));
if(!bitmap_decompress2(_dat, w, h, dat, len)) {
free(_dat);
qDebug() << "22------------------DECOMPRESS2 failed.";
return nullptr;
}
// TODO: 这里需要进一步优化直接操作QImage的buffer。
out = new QImage(w, h, QImage::Format_RGB16);
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
uint16 a = ((uint16*)_dat)[y * w + x];
uint8 r = ((a & 0xf800) >> 11) * 255 / 31;
uint8 g = ((a & 0x07e0) >> 5) * 255 / 63;
uint8 b = (a & 0x001f) * 255 / 31;
out->setPixelColor(x, y, QColor(r,g,b));
}
}
free(_dat);
return out;
}
else {
out = new QImage(QImage(dat, w, h, QImage::Format_RGB16).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)));
}
return out;
case 24:
case 32:
default:
qDebug() << "--------NOT support UNKNOWN bitsPerPix" << bitsPerPixel;
return nullptr;
}
}
static QImage* _raw2QImage(int w, int h, const uint8_t* dat, uint32_t len) {
QImage* out;
// TODO: 这里需要进一步优化直接操作QImage的buffer。
out = new QImage(w, h, QImage::Format_RGB16);
for(int y = 0; y < h; y++) {
for(int x = 0; x < w; x++) {
uint16 a = ((uint16*)dat)[y * w + x];
uint8 r = ((a & 0xf800) >> 11) * 255 / 31;
uint8 g = ((a & 0x07e0) >> 5) * 255 / 63;
uint8 b = (a & 0x001f) * 255 / 31;
out->setPixelColor(x, y, QColor(r,g,b));
}
}
return out;
}
//=================================================================
// ThrData
//=================================================================
ThrData::ThrData(MainWindow* mainwin, const QString& res) {
m_mainwin = mainwin;
m_res = res;
m_need_download = false;
m_need_stop = false;
m_need_restart = false;
m_wait_restart = false;
m_need_show_kf = false;
m_file_idx = 0;
m_offset = 0;
#ifdef __APPLE__
m_data_path_base = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
m_data_path_base += "/tp-testdata/";
#else
m_data_path_base = QCoreApplication::applicationDirPath() + "/record";
#endif
qDebug("data-path-base: %s", m_data_path_base.toStdString().c_str());
// qDebug() << "AppConfigLocation:" << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
// qDebug() << "AppDataLocation:" << QStandardPaths::writableLocation(QStandardPaths::AppDataLocation);
// qDebug() << "AppLocalDataLocation:" << QStandardPaths::writableLocation(QStandardPaths::AppLocalDataLocation);
// qDebug() << "ConfigLocation:" << QStandardPaths::writableLocation(QStandardPaths::ConfigLocation);
// qDebug() << "CacheLocation:" << QStandardPaths::writableLocation(QStandardPaths::CacheLocation);
// qDebug() << "GenericCacheLocation:" << QStandardPaths::writableLocation(QStandardPaths::GenericCacheLocation);
/*
AppConfigLocation: "C:/Users/apex/AppData/Local/tp-player"
AppDataLocation: "C:/Users/apex/AppData/Roaming/tp-player"
AppLocalDataLocation: "C:/Users/apex/AppData/Local/tp-player"
ConfigLocation: "C:/Users/apex/AppData/Local/tp-player"
CacheLocation: "C:/Users/apex/AppData/Local/tp-player/cache"
GenericCacheLocation: "C:/Users/apex/AppData/Local/cache"
*/
}
ThrData::~ThrData() {
_clear_data();
}
void ThrData::stop() {
if(!isRunning())
return;
m_need_stop = true;
wait();
qDebug("data thread stop() end.");
}
void ThrData::_notify_message(const QString& msg) {
UpdateData* _msg = new UpdateData(TYPE_MESSAGE);
_msg->message(msg);
emit signal_update_data(_msg);
}
void ThrData::_notify_error(const QString& msg) {
UpdateData* _msg = new UpdateData(TYPE_ERROR);
_msg->message(msg);
emit signal_update_data(_msg);
}
void ThrData::run() {
_run();
qDebug("ThrData thread run() end.");
}
void ThrData::_run() {
QString _tmp_res = m_res.toLower();
if(_tmp_res.startsWith("http")) {
m_need_download = true;
_notify_message(LOCAL8BIT("正在准备录像数据,请稍候..."));
if(!m_thr_download.init(m_data_path_base, m_res)) {
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("无法下载录像文件!\n\n"), m_res));
return;
}
m_thr_download.start();
msleep(100);
for(;;) {
if(m_need_stop)
return;
if(!m_thr_download.is_running() || m_thr_download.is_tpk_downloaded())
break;
msleep(100);
}
if(!m_thr_download.is_tpk_downloaded()) {
_notify_error(QString("%1\n%2").arg(LOCAL8BIT("无法下载录像文件!"), m_res));
return;
}
m_thr_download.get_data_path(m_data_path);
}
else {
QFileInfo fi_chk_link(m_res);
if(fi_chk_link.isSymLink())
m_res = fi_chk_link.symLinkTarget();
QFileInfo fi(m_res);
if(!fi.exists()) {
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("指定的文件或目录不存在!"), m_res));
return;
}
if(fi.isFile()) {
m_data_path = fi.path();
}
else if(fi.isDir()) {
m_data_path = m_res;
}
m_data_path = QDir::toNativeSeparators(m_data_path);
}
// 到这里,.tpr和.tpk文件均已经下载完成了。
if(!_load_header())
return;
if(!_load_keyframe())
return;
UpdateData* dat = new UpdateData(m_hdr);
emit signal_update_data(dat);
QFile* fdata = nullptr;
qint64 file_size = 0;
qint64 file_processed = 0;
qint64 read_len = 0;
QString str_fidx;
for(;;) {
// 任何时候确保第一时间响应退出操作
if(m_need_stop)
return;
if(m_need_restart) {
if(fdata) {
fdata->close();
delete fdata;
fdata = nullptr;
}
m_wait_restart = true;
msleep(50);
continue;
}
// 如果所有文件都已经处理完了,则等待(可能用户会拖动滚动条,或者重新播放)
if(m_file_idx >= m_hdr.info.dat_file_count) {
msleep(500);
continue;
}
// 看看待播放队列中还有多少个数据包
int pkg_count_in_queue = 0;
int pkg_need_add = 0;
m_locker.lock();
pkg_count_in_queue = m_data.size();
m_locker.unlock();
// 少于1000个的话补足到2000个
if(m_data.size() < 1000)
pkg_need_add = 2000 - pkg_count_in_queue;
if(pkg_need_add == 0) {
msleep(100);
continue;
}
for(int i = 0; i < pkg_need_add; ++i) {
if(m_need_stop)
return;
if(m_need_restart)
break;
// 如果数据文件尚未打开,则打开它
if(fdata == nullptr) {
str_fidx.sprintf("%d", m_file_idx+1);
QString tpd_fname = QString("%1/tp-rdp-%2.tpd").arg(m_data_path, str_fidx);
tpd_fname = QDir::toNativeSeparators(tpd_fname);
QFileInfo fi_tpd(tpd_fname);
if(!fi_tpd.exists()) {
if(m_need_download) {
// 此文件尚未下载完成,等待
for(;;) {
if(m_need_stop)
return;
if(!m_thr_download.is_running() || m_thr_download.is_tpd_downloaded(m_file_idx))
break;
msleep(100);
}
// 下载失败了
if(!m_thr_download.is_tpd_downloaded(m_file_idx))
return;
}
}
fdata = new QFile(tpd_fname);
if(!fdata->open(QFile::ReadOnly)) {
qDebug() << "Can not open " << tpd_fname << " for read.";
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("无法打开录像数据文件!"), tpd_fname));
return;
}
file_size = fdata->size();
file_processed = 0;
qDebug("Open file tp-rdp-%d.tpd, processed: %" PRId64 ", size: %" PRId64, m_file_idx+1, file_processed, file_size);
}
// 如果指定了起始偏移,则跳过这部分数据
if(m_offset > 0) {
fdata->seek(m_offset);
file_processed = m_offset;
m_offset = 0;
}
//----------------------------------
// 读取一个数据包
//----------------------------------
if(file_size - file_processed < sizeof(TS_RECORD_PKG)) {
qDebug("invaid tp-rdp-%d.tpd file, filesize=%" PRId64 ", processed=%" PRId64 ", need=%d.", m_file_idx+1, file_size, file_processed, sizeof(TS_RECORD_PKG));
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
TS_RECORD_PKG pkg;
read_len = fdata->read(reinterpret_cast<char*>(&pkg), sizeof(TS_RECORD_PKG));
if(read_len != sizeof(TS_RECORD_PKG)) {
qDebug("invaid tp-rdp-%d.tpd file, read_len=%" PRId64 " (1).", m_file_idx+1, read_len);
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
file_processed += sizeof(TS_RECORD_PKG);
if(file_size - file_processed < pkg.size) {
qDebug("invaid tp-rdp-%d.tpd file (2).", m_file_idx+1);
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
if(pkg.size == 0) {
qDebug("################## too bad.");
}
QByteArray pkg_data = fdata->read(pkg.size);
if(pkg_data.size() != static_cast<int>(pkg.size)) {
qDebug("invaid tp-rdp-%d.tpd file, read_len=%" PRId64 " (3).", m_file_idx+1, read_len);
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
file_processed += pkg.size;
UpdateData* dat = _parse(pkg, pkg_data);
if(dat == nullptr) {
qDebug("invaid tp-rdp-%d.tpd file (4).", m_file_idx+1);
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
// 遇到关键帧,需要清除自上一个关键帧以来保存的缓存图像数据
if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
for(size_t ci = 0; ci < m_cache_imgs.size(); ++ci) {
if(m_cache_imgs[ci] != nullptr)
delete m_cache_imgs[ci];
}
m_cache_imgs.clear();
}
// 拖动滚动条后,需要显示一次关键帧数据,然后跳过后续关键帧。
if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
qDebug("----key frame: %ld, processed=%" PRId64 ", pkg.size=%d", pkg.time_ms, file_processed, pkg.size);
if(m_need_show_kf) {
m_need_show_kf = false;
qDebug("++ show keyframe.");
}
else {
qDebug("-- skip keyframe.");
delete dat;
dat = nullptr;
}
}
// 数据放到待播放列表中
if(dat) {
m_locker.lock();
m_data.enqueue(dat);
m_locker.unlock();
}
// 让线程调度器让播放线程有机会执行
// msleep(1);
// 如果此文件已经处理完毕,则关闭文件,这样下次处理一个新的文件
if(file_processed >= file_size) {
fdata->close();
delete fdata;
fdata = nullptr;
m_file_idx++;
}
if(m_file_idx >= m_hdr.info.dat_file_count) {
UpdateData* dat = new UpdateData(TYPE_END);
m_locker.lock();
m_data.enqueue(dat);
m_locker.unlock();
break;
}
}
}
}
UpdateData* ThrData::_parse(const TS_RECORD_PKG& pkg, const QByteArray& data) {
if(pkg.type == TS_RECORD_TYPE_RDP_POINTER) {
if(data.size() != sizeof(TS_RECORD_RDP_POINTER))
return nullptr;
UpdateData* ud = new UpdateData();
ud->set_pointer(pkg.time_ms, reinterpret_cast<const TS_RECORD_RDP_POINTER*>(data.data()));
return ud;
}
else if(pkg.type == TS_RECORD_TYPE_RDP_IMAGE) {
UpdateData* ud = new UpdateData(TYPE_IMAGE, pkg.time_ms);
if(data.size() < static_cast<int>(sizeof(uint16_t) + sizeof(TS_RECORD_RDP_IMAGE_INFO))) {
delete ud;
return nullptr;
}
const uint8_t* dat_ptr = reinterpret_cast<const uint8_t*>(data.data());
uint16_t count = (reinterpret_cast<const uint16_t*>(dat_ptr))[0];
uint32_t offset = sizeof(uint16_t);
UpdateImages& imgs = ud->get_images();
for(uint16_t i = 0; i < count; ++i) {
const TS_RECORD_RDP_IMAGE_INFO* info = reinterpret_cast<const TS_RECORD_RDP_IMAGE_INFO*>(dat_ptr+offset);
offset += sizeof(TS_RECORD_RDP_IMAGE_INFO);
if(info->format != TS_RDP_IMG_ALT) {
const uint8_t* img_dat = dat_ptr + offset;
const uint8_t* real_img_dat = nullptr;
QByteArray unzip_data;
if(info->zip_len > 0) {
// 数据被压缩了,需要解压缩
unzip_data.resize(static_cast<int>(info->dat_len));
uLong u_len = info->dat_len;
int err = uncompress(reinterpret_cast<uint8_t*>(unzip_data.data()), &u_len, img_dat, info->zip_len);
if(err != Z_OK || u_len != info->dat_len) {
qDebug("image uncompress failed. err=%d.", err);
}
else {
real_img_dat = reinterpret_cast<const uint8_t*>(unzip_data.data());
}
offset += info->zip_len;
}
else {
real_img_dat = img_dat;
offset += info->dat_len;
}
UPDATE_IMAGE uimg;
uimg.x = info->destLeft;
uimg.y = info->destTop;
uimg.w = info->destRight - info->destLeft + 1;
uimg.h = info->destBottom - info->destTop + 1;
if(real_img_dat)
uimg.img = _rdpimg2QImage(info->width, info->height, info->bitsPerPixel, (info->format == TS_RDP_IMG_BMP) ? true : false, real_img_dat, info->dat_len);
else
uimg.img = nullptr;
imgs.push_back(uimg);
QImage* cache_img = nullptr;
if(uimg.img != nullptr)
cache_img = new QImage(*uimg.img);
m_cache_imgs.push_back(cache_img);
}
else {
UPDATE_IMAGE uimg;
uimg.x = info->destLeft;
uimg.y = info->destTop;
uimg.w = info->destRight - info->destLeft + 1;
uimg.h = info->destBottom - info->destTop + 1;
size_t cache_idx = info->dat_len;
if(cache_idx >= m_cache_imgs.size() || m_cache_imgs[cache_idx] == nullptr) {
uimg.img = nullptr;
}
else {
uimg.img = new QImage(*m_cache_imgs[cache_idx]);
}
imgs.push_back(uimg);
}
}
return ud;
}
else if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
UpdateData* ud = new UpdateData(TYPE_IMAGE, pkg.time_ms);
const TS_RECORD_RDP_KEYFRAME_INFO* info = reinterpret_cast<const TS_RECORD_RDP_KEYFRAME_INFO*>(data.data());
const uint8_t* data_buf = reinterpret_cast<const uint8_t*>(data.data() + sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
uint32_t data_len = data.size() - sizeof(TS_RECORD_RDP_KEYFRAME_INFO);
UpdateImages& imgs = ud->get_images();
UPDATE_IMAGE uimg;
uimg.x = 0;
uimg.y = 0;
uimg.w = m_hdr.basic.width;
uimg.h = m_hdr.basic.height;
const uint8_t* real_img_dat = nullptr;
uint32_t real_img_len = m_hdr.basic.width * m_hdr.basic.height * 2;
QByteArray unzip_data;
if(data_len != real_img_len) {
// 数据被压缩了,需要解压缩
unzip_data.resize(static_cast<int>(real_img_len));
uLong u_len = real_img_len;
int err = uncompress(reinterpret_cast<uint8_t*>(unzip_data.data()), &u_len, data_buf, data_len);
if(err != Z_OK || u_len != real_img_len) {
qDebug("keyframe uncompress failed. err=%d.", err);
}
else {
real_img_dat = reinterpret_cast<const uint8_t*>(unzip_data.data());
}
}
else {
real_img_dat = data_buf;
}
if(real_img_dat != nullptr)
uimg.img = _raw2QImage(m_hdr.basic.width, m_hdr.basic.height, real_img_dat, real_img_len);
else
uimg.img = nullptr;
imgs.push_back(uimg);
return ud;
}
return nullptr;
}
void ThrData::restart(uint32_t start_ms) {
qDebug("restart at %ld ms", start_ms);
// 让处理线程处理完当前循环,然后等待
m_need_restart = true;
// 确保处理线程已经处理完当前循环
for(;;) {
msleep(50);
if(m_need_stop)
return;
if(m_wait_restart)
break;
}
// 清空待播放队列
_clear_data();
if(start_ms == 0) {
m_offset = 0;
m_file_idx = 0;
m_need_show_kf = false;
}
else {
// 找到最接近 start_ms 但小于它的关键帧
size_t i = 0;
for(i = 0; i < m_kf.size(); ++i) {
if(m_kf[i].time_ms > start_ms) {
break;
}
}
if(i > 0)
i--;
qDebug("restart acturelly at %ld ms, kf: %d", m_kf[i].time_ms, i);
// 指定要播放的数据的开始位置
m_offset = m_kf[i].offset;
m_file_idx = m_kf[i].file_index;
if(m_file_idx == (uint32_t)-1)
m_file_idx = 0;
m_need_show_kf = true;
}
qDebug("RESTART: offset=%d, file_idx=%d", m_offset, m_file_idx);
// 让处理线程继续
m_wait_restart = false;
m_need_restart = false;
}
bool ThrData::_load_header() {
QString msg;
qDebug() << "PATH_BASE: " << m_data_path;
QString filename = QString("%1/tp-rdp.tpr").arg(m_data_path);
filename = QDir::toNativeSeparators(filename);
qDebug() << "TPR: " << filename;
QFile f(filename);
if(!f.open(QFile::ReadOnly)) {
qDebug() << "Can not open " << filename << " for read.";
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("无法打开录像信息文件!"), filename));
return false;
}
memset(&m_hdr, 0, sizeof(TS_RECORD_HEADER));
qint64 read_len = 0;
read_len = f.read(reinterpret_cast<char*>(&m_hdr), sizeof(TS_RECORD_HEADER));
if(read_len != sizeof(TS_RECORD_HEADER)) {
qDebug() << "invaid .tpr file.";
_notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("错误的录像信息文件!"), filename));
return false;
}
if(m_hdr.info.ver != 4) {
qDebug() << "invaid .tpr file.";
_notify_error(QString("%1 %2%3").arg(LOCAL8BIT("不支持的录像文件版本 "), QString(m_hdr.info.ver), LOCAL8BIT("\n\n此播放器支持录像文件版本 4。")));
return false;
}
if(m_hdr.basic.width == 0 || m_hdr.basic.height == 0) {
_notify_error(LOCAL8BIT("错误的录像信息,未记录窗口尺寸!"));
return false;
}
if(m_hdr.info.dat_file_count == 0) {
_notify_error(LOCAL8BIT("错误的录像信息,未记录数据文件数量!"));
return false;
}
return true;
}
bool ThrData::_load_keyframe() {
QString tpk_fname = QString("%1/tp-rdp.tpk").arg(m_data_path);
tpk_fname = QDir::toNativeSeparators(tpk_fname);
qDebug() << "TPK: " << tpk_fname;
QFile f_kf(tpk_fname);
if(!f_kf.open(QFile::ReadOnly)) {
qDebug() << "Can not open " << tpk_fname << " for read.";
_notify_error(QString("%1\n\n%3").arg(LOCAL8BIT("无法打开关键帧信息文件!"), tpk_fname));
return false;
}
qint64 fsize = f_kf.size();
if(!fsize || fsize % sizeof(TS_RECORD_RDP_KEYFRAME_INFO) != 0) {
qDebug() << "Can not open " << tpk_fname << " for read.";
_notify_error(LOCAL8BIT("关键帧信息文件格式错误!"));
return false;
}
qint64 read_len = 0;
int kf_count = static_cast<int>(fsize / sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
for(int i = 0; i < kf_count; ++i) {
TS_RECORD_RDP_KEYFRAME_INFO kf;
memset(&kf, 0, sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
read_len = f_kf.read(reinterpret_cast<char*>(&kf), sizeof(TS_RECORD_RDP_KEYFRAME_INFO));
if(read_len != sizeof(TS_RECORD_RDP_KEYFRAME_INFO)) {
qDebug() << "invaid .tpk file.";
_notify_error(LOCAL8BIT("关键帧信息文件格式错误!"));
return false;
}
m_kf.push_back(kf);
}
return true;
}
UpdateData* ThrData::get_data() {
UpdateData* d = nullptr;
m_locker.lock();
if(m_data.size() > 0) {
d = m_data.dequeue();
}
m_locker.unlock();
return d;
}
void ThrData::_clear_data() {
m_locker.lock();
while(m_data.size() > 0) {
UpdateData* d = m_data.dequeue();
delete d;
}
m_locker.unlock();
}

107
client/tp-player/thr_data.h Normal file
View File

@ -0,0 +1,107 @@
#ifndef THR_DATA_H
#define THR_DATA_H
#include <QThread>
#include <QQueue>
#include <QMutex>
#include <QNetworkReply>
#include <QFile>
#include <QEventLoop>
#include <QImage>
#include "update_data.h"
#include "record_format.h"
#include "thr_download.h"
/*
4MB
tp-rdp.tpr
tp-rdp.tpk (v3.5.1)
tp-rdp-1.tpd, tp-rdp-2.tpd, tp-rdp-3.tpd, ...
线
线
.tpk
tp-rdp.tpk.tmp, tp-rdp.tpk.len
tp-rdp-1.tpd.tmp, tp-rdp-1.tpd.len, ...
*/
typedef std::vector<TS_RECORD_RDP_KEYFRAME_INFO> KeyFrames;
typedef std::vector<QImage*> CachedImages;
class MainWindow;
// 下载必要的文件解析文件数据生成图像数据QImage*),将数据包放入待显示队列中,等待 ThrPlay 线程使用
// 注意无需将所有数据解析并放入待显示队列此队列有数量限制例如1000个避免过多占用内存
class ThrData : public QThread {
Q_OBJECT
public:
ThrData(MainWindow* mainwin, const QString& url);
~ThrData();
virtual void run();
void stop();
void restart(uint32_t start_ms); // 重新从指定时间开始播放
bool have_more_data();
UpdateData* get_data();
private:
void _run();
bool _load_header();
bool _load_keyframe();
void _clear_data();
// void _prepare();
UpdateData* _parse(const TS_RECORD_PKG& pkg, const QByteArray& data);
void _notify_message(const QString& msg);
void _notify_error(const QString& err_msg);
signals:
void signal_update_data(UpdateData*);
private:
MainWindow* m_mainwin;
QQueue<UpdateData*> m_data;
QMutex m_locker;
ThrDownload m_thr_download;
bool m_need_stop;
bool m_need_download;
QString m_res;
QString m_data_path_base;
QString m_url_base;
QString m_sid;
QString m_rid;
QString m_data_path;
TS_RECORD_HEADER m_hdr;
KeyFrames m_kf;
bool m_need_restart;
bool m_wait_restart;
bool m_need_show_kf;
uint32_t m_file_idx;
uint32_t m_offset;
CachedImages m_cache_imgs;
};
#endif // THR_DATA_H

View File

@ -0,0 +1,292 @@
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QNetworkCookie>
#include "thr_download.h"
#include "util.h"
#include "downloader.h"
#include "record_format.h"
//=================================================================
// ThrDownload
//=================================================================
ThrDownload::ThrDownload() {
m_need_stop = false;
m_have_tpr = false;
m_have_tpk = false;
m_have_tpd = nullptr;
m_need_tpk = false;
m_running = true;
}
ThrDownload::~ThrDownload() {
if(m_have_tpd)
delete[] m_have_tpd;
}
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意并不直接访问此URI实际上其并不存在)
// TP服务器地址(可能包含子路径哦,例如上例中的{sub/path/}部分)/session-id(用于判断当前授权用户)/录像会话编号
bool ThrDownload::init(const QString& local_data_path_base, const QString &res) {
m_data_path_base = local_data_path_base;
QString _tmp_res = res.toLower();
if(!_tmp_res.startsWith("http")) {
return false;
}
QStringList _uris = res.split('/');
if(_uris.size() < 3) {
return false;
}
m_sid = _uris[_uris.size()-2];
m_rid = _uris[_uris.size()-1];
m_url_base = res.left(res.length() - m_sid.length() - m_rid.length() - 2);
if(m_sid.length() == 0 || m_rid.length() == 0 || m_url_base.length() == 0)
return false;
return true;
}
void ThrDownload::stop() {
if(!m_running)
return;
// if(!isRunning())
// return;
m_need_stop = true;
wait();
qDebug("data thread stop() end.");
}
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意并不直接访问此URI实际上其并不存在)
// TP服务器地址(可能包含子路径哦,例如上例中的{sub/path/}部分)/session-id(用于判断当前授权用户)/录像会话编号
void ThrDownload::run() {
_run();
m_running = false;
qDebug("ThrDownload thread run() end.");
}
void ThrDownload::_run() {
// m_state = statDownloading;
if(!_download_tpr()) {
// m_state = statFailDone;
return;
}
m_have_tpr = true;
m_have_tpd = new bool[m_tpd_count];
for(uint32_t i = 0; i < m_tpd_count; ++i) {
m_have_tpd[i] = false;
}
if(m_need_tpk) {
if(!_download_tpk()) {
// m_state = statFailDone;
return;
}
m_have_tpk = true;
}
uint32_t file_idx = 0;
for(;;) {
if(m_need_stop)
break;
QString str_fidx;
str_fidx.sprintf("%d", file_idx+1);
QString tpd_fname = QString("%1/tp-rdp-%2.tpd").arg(m_data_path, str_fidx);
tpd_fname = QDir::toNativeSeparators(tpd_fname);
QString tmp_fname = QString("%1/tp-rdp-%2.tpd.downloading").arg(m_data_path, str_fidx);
tmp_fname = QDir::toNativeSeparators(tmp_fname);
QFileInfo fi_tmp(tmp_fname);
if(fi_tmp.isFile()) {
QFile::remove(tmp_fname);
}
QFileInfo fi_tpd(tpd_fname);
if(!fi_tpd.exists()) {
QString url = QString("%1/audit/get-file?act=read&type=rdp&rid=%2&f=tp-rdp-%3.tpd").arg(m_url_base, m_rid, str_fidx);
qDebug() << "URL : " << url;
qDebug() << "TPD : " << tmp_fname;
if(!_download_file(url, tmp_fname)) {
// m_state = statFailDone;
return;
}
if(!QFile::rename(tmp_fname, tpd_fname)) {
// m_state = statFailDone;
return;
}
}
m_have_tpd[file_idx] = true;
file_idx += 1;
if(file_idx >= m_tpd_count)
break;
}
// m_state = statSuccessDone;
}
bool ThrDownload::_download_tpr() {
QString url = QString("%1/audit/get-file?act=read&type=rdp&rid=%2&f=tp-rdp.tpr").arg(m_url_base, m_rid);
QByteArray data;
if(!_download_file(url, data))
return false;
if(data.size() != sizeof(TS_RECORD_HEADER)) {
qDebug("invalid header data. %d", data.size());
m_error = QString(LOCAL8BIT("录像信息文件数据错误!"));
return false;
}
TS_RECORD_HEADER* hdr = reinterpret_cast<TS_RECORD_HEADER*>(data.data());
// if(hdr->info.ver != 4) {
// qDebug() << "invaid .tpr file.";
// m_last_error = QString("%1 %2%3").arg(LOCAL8BIT("不支持的录像文件版本 "), QString(hdr->info.ver), LOCAL8BIT("\n\n此播放器支持录像文件版本 4。"));
// return false;
// }
// if(m_hdr.basic.width == 0 || m_hdr.basic.height == 0) {
// _notify_error(LOCAL8BIT("错误的录像信息,未记录窗口尺寸!"));
// return false;
// }
// if(m_hdr.info.dat_file_count == 0) {
// _notify_error(LOCAL8BIT("错误的录像信息,未记录数据文件数量!"));
// return false;
// }
// 下载得到的数据应该是一个TS_RECORD_HEADER解析此数据生成本地文件路径并保存之。
QDateTime timeUTC;
// timeUTC.setTimeSpec(Qt::UTC);
// timeUTC.setTime_t(m_hdr.basic.timestamp);
timeUTC.setSecsSinceEpoch(hdr->basic.timestamp);
QString strUTC = timeUTC.toString("yyyyMMdd-hhmmss");
QString strAcc(hdr->basic.acc_username);
int idx = strAcc.indexOf('\\');
if(-1 != idx) {
QString _domain = strAcc.left(idx);
QString _user = strAcc.right(strAcc.length() - idx - 1);
strAcc = _user + "@" + _domain;
}
QString strType;
if(hdr->info.type == TS_TPPR_TYPE_SSH) {
strType = "SSH";
}
else if(hdr->info.type == TS_TPPR_TYPE_RDP) {
strType = "RDP";
m_need_tpk = true;
}
else {
strType = "UNKNOWN";
}
// .../record/RDP-211-admin-user@domain-192.168.0.68-20191015-020243
m_data_path = QString("%1/%2-%3-%4-%5-%6-%7").arg(m_data_path_base, strType, m_rid, hdr->basic.user_username, strAcc, hdr->basic.host_ip, strUTC);
m_data_path = QDir::toNativeSeparators(m_data_path);
qDebug() << "PATH_BASE: " << m_data_path;
QDir dir;
dir.mkpath(m_data_path);
QFileInfo fi;
fi.setFile(m_data_path);
if(!fi.isDir()) {
qDebug("can not create folder to save downloaded file.");
return false;
}
QString filename = QString("%1/tp-rdp.tpr").arg(m_data_path);
filename = QDir::toNativeSeparators(filename);
qDebug() << "TPR: " << filename;
QFile f;
f.setFileName(filename);
if(!f.open(QIODevice::WriteOnly | QFile::Truncate)){
qDebug("open file for write failed.");
return false;
}
qint64 written = f.write(reinterpret_cast<const char*>(hdr), sizeof(TS_RECORD_HEADER));
f.flush();
f.close();
if(written != sizeof(TS_RECORD_HEADER)) {
qDebug("save header file failed.");
return false;
}
m_tpd_count = hdr->info.dat_file_count;
return true;
}
bool ThrDownload::_download_tpk() {
QString tpk_fname = QString("%1/tp-rdp.tpk").arg(m_data_path);
tpk_fname = QDir::toNativeSeparators(tpk_fname);
QString tmp_fname = QString("%1/tp-rdp.tpk.downloading").arg(m_data_path);
tmp_fname = QDir::toNativeSeparators(tmp_fname);
QFileInfo fi_tmp(tmp_fname);
if(fi_tmp.isFile()) {
QFile::remove(tmp_fname);
}
QFileInfo fi_tpk(tpk_fname);
if(!fi_tpk.exists()) {
QString url = QString("%1/audit/get-file?act=read&type=rdp&rid=%2&f=tp-rdp.tpk").arg(m_url_base, m_rid);
qDebug() << "TPK: " << tmp_fname;
if(!_download_file(url, tmp_fname))
return false;
if(!QFile::rename(tmp_fname, tpk_fname))
return false;
}
return true;
}
bool ThrDownload::_download_file(const QString& url, const QString filename) {
Downloader dl;
if(!dl.request(url, m_sid, filename)) {
qDebug() << "download failed.";
m_error = QString("%1").arg(LOCAL8BIT("下载文件失败!"));
return false;
}
return true;
}
bool ThrDownload::_download_file(const QString& url, QByteArray& data) {
Downloader dl;
if(!dl.request(url, m_sid, &data)) {
qDebug() << "download failed.";
m_error = QString("%1").arg(LOCAL8BIT("下载文件失败!"));
return false;
}
return true;
}
bool ThrDownload::is_tpd_downloaded(uint32_t file_idx) const {
if(!m_have_tpd)
return false;
if(file_idx >= m_tpd_count)
return false;
return m_have_tpd[file_idx];
}

View File

@ -0,0 +1,72 @@
#ifndef THR_DOWNLOAD_H
#define THR_DOWNLOAD_H
#include <QThread>
#include <QNetworkReply>
#include <QFile>
#include <QEventLoop>
class ThrDownload : public QThread {
Q_OBJECT
//public:
// enum State {
// statStarting,
// statDownloading,
// statInvalidParam,
// statFailDone,
// statSuccessDone
// };
public:
ThrDownload();
~ThrDownload();
bool init(const QString& local_data_path_base, const QString& res);
virtual void run();
void stop();
bool is_running() const {return m_running;}
bool is_tpr_downloaded() const {return m_have_tpr;}
bool is_tpk_downloaded() const {return m_have_tpk;}
bool is_tpd_downloaded(uint32_t file_idx) const;
bool get_data_path(QString& path) const {
if(m_data_path.isEmpty())
return false;
path = m_data_path;
return true;
}
private:
void _run();
bool _download_tpr();
bool _download_tpk();
bool _download_file(const QString& url, const QString filename);
bool _download_file(const QString& url, QByteArray& data);
private:
bool m_need_stop;
QString m_data_path_base;
QString m_url_base;
QString m_sid;
QString m_rid;
QString m_data_path;
bool m_running;
bool m_have_tpr;
bool m_have_tpk;
bool m_need_tpk;
uint32_t m_tpd_count;
bool* m_have_tpd;
QString m_error;
};
#endif // THR_DOWNLOAD_H

View File

@ -0,0 +1,182 @@
#include <QCoreApplication>
#include <QDateTime>
#include <QDebug>
#include "thr_play.h"
#include "thr_data.h"
#include "mainwindow.h"
#include "record_format.h"
#include "util.h"
/*
*
* - 线线
* + 线500100020ms500
* - 线UI
* + if( * ( - ) >= ( - ))
* + 33
*/
ThrPlay::ThrPlay(MainWindow* mainwnd) {
m_mainwnd = mainwnd;
m_need_stop = false;
m_need_pause = false;
m_speed = 1;
m_skip = false;
m_start_ms = 0;
}
ThrPlay::~ThrPlay() {
stop();
}
void ThrPlay::stop() {
if(!isRunning())
return;
m_need_stop = true;
wait();
qDebug() << "play-thread end.";
}
void ThrPlay::_notify_message(const QString& msg) {
UpdateData* _msg = new UpdateData(TYPE_MESSAGE);
_msg->message(msg);
emit signal_update_data(_msg);
}
void ThrPlay::_notify_error(const QString& msg) {
UpdateData* _msg = new UpdateData(TYPE_ERROR);
_msg->message(msg);
emit signal_update_data(_msg);
}
void ThrPlay::resume(bool relocate, uint32_t start_ms) {
if(relocate) {
m_start_ms = start_ms;
m_first_run = true;
}
m_need_pause = false;
}
void ThrPlay::run() {
ThrData* thr_data = m_mainwnd->get_thr_data();
m_first_run = true;
uint32_t last_time_ms = 0;
uint32_t last_pass_ms = 0;
UpdateData* dat = nullptr;
for(;;) {
if(m_need_stop)
break;
// 1. 从ThrData的待播放队列中取出一个数据
dat = thr_data->get_data();
if(dat == nullptr) {
msleep(20);
continue;
}
if(m_first_run) {
m_first_run = false;
_notify_message("");
}
if(m_start_ms > 0) {
if(dat->get_time() < m_start_ms) {
emit signal_update_data(dat);
continue;
}
last_time_ms = m_start_ms;
m_start_ms = 0;
UpdateData* _enable = new UpdateData(TYPE_ENABLE_DRAW);
emit signal_update_data(_enable);
}
// 2. 根据数据包的信息,等待到播放时间点
uint32_t need_wait_ms = 0;
uint32_t this_time_ms = dat->get_time();
uint32_t this_pass_ms = last_time_ms;
if(this_time_ms > 0) {
if(this_time_ms >= last_time_ms)
need_wait_ms = this_time_ms - last_time_ms;
else
need_wait_ms = 0;
if(need_wait_ms > 0) {
uint32_t time_wait = 0;
// 如果设置了跳过无操作区间将超过1秒的等待时间压缩至1秒。
if(m_skip) {
if(need_wait_ms > 1000)
need_wait_ms = 1000;
}
for(;;) {
time_wait = need_wait_ms > 10 ? 10 : need_wait_ms;
msleep(time_wait);
if(m_need_pause) {
while(m_need_pause) {
msleep(50);
if(m_need_stop)
break;
}
}
if(m_need_stop)
break;
if(m_start_ms > 0) {
delete dat;
dat = nullptr;
UpdateData* _disable = new UpdateData(TYPE_DISABLE_DRAW);
msleep(500);
emit signal_update_data(_disable);
break;
}
time_wait *= m_speed;
// 如果已经在等待长时间无操作区间内用户设置了跳过无操作区间则将超过0.5秒的等待时间压缩至0.5秒。
if(m_skip) {
if(need_wait_ms > 500)
need_wait_ms = 500;
}
this_pass_ms += time_wait;
if(this_pass_ms - last_pass_ms > 100) {
UpdateData* _passed_ms = new UpdateData(TYPE_PLAYED_MS);
_passed_ms->played_ms(this_pass_ms);
emit signal_update_data(_passed_ms);
last_pass_ms = this_pass_ms;
}
if(need_wait_ms <= time_wait)
break;
else
need_wait_ms -= time_wait;
}
if(m_need_stop)
break;
}
}
last_time_ms = this_time_ms;
// 3. 将数据包发送给主UI界面进行显示
if(dat != nullptr) {
if(dat->data_type() == TYPE_END) {
_notify_message(LOCAL8BIT("播放结束"));
}
emit signal_update_data(dat);
}
}
if(dat != nullptr)
delete dat;
}

View File

@ -0,0 +1,43 @@
#ifndef THR_PLAY_H
#define THR_PLAY_H
#include <QThread>
#include "update_data.h"
#include "downloader.h"
class MainWindow;
// 根据播放规则将要播放的图像发送给主UI线程进行显示
class ThrPlay : public QThread
{
Q_OBJECT
friend class ThrData;
public:
ThrPlay(MainWindow* mainwnd);
~ThrPlay();
virtual void run();
void stop();
void pause() {m_need_pause = true;}
void resume(bool relocate, uint32_t start_ms);
void speed(int s) {if(s >= 1 && s <= 16) m_speed = s;}
void skip(bool s) {m_skip = s;}
private:
void _notify_message(const QString& msg);
void _notify_error(const QString& err_msg);
signals:
void signal_update_data(UpdateData*);
private:
MainWindow* m_mainwnd;
bool m_need_stop;
bool m_need_pause;
int m_speed;
bool m_skip;
bool m_first_run;
uint32_t m_start_ms;
};
#endif // THR_PLAY_H

View File

@ -0,0 +1,58 @@
TEMPLATE = app
TARGET = tp-player
QT += core gui widgets network
HEADERS += \
mainwindow.h \
bar.h \
thr_play.h \
thr_data.h \
update_data.h \
record_format.h \
rle.h \
util.h \
downloader.h \
thr_download.h
SOURCES += \
main.cpp \
mainwindow.cpp \
bar.cpp \
thr_play.cpp \
thr_data.cpp \
update_data.cpp \
rle.c \
util.cpp \
downloader.cpp \
thr_download.cpp
RESOURCES += \
tp-player.qrc
RC_FILE += \
tp-player.rc
FORMS += \
mainwindow.ui
win32:CONFIG(release, debug|release): {
DEFINES += QT_NO_DEBUG_OUTPUT
LIBS += -L$$PWD/../../external/zlib/build/release/ -lzlib
DESTDIR = $$PWD/../../out/client/x86/Release
}
else:win32:CONFIG(debug, debug|release): {
LIBS += -L$$PWD/../../external/zlib/build/debug/ -lzlibd
DESTDIR = $$PWD/../../out/client/x86/Debug
}
INCLUDEPATH += $$PWD/../../external/zlib
INCLUDEPATH += $$PWD/../../external/zlib/build
DEPENDPATH += $$PWD/../../external/zlib
DEPENDPATH += $$PWD/../../external/zlib/build
#win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/release/libzlibstatic.a
#else:win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/debug/libzlibstaticd.a
#else:win32:!win32-g++:CONFIG(release, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/release/zlibstatic.lib
#else:win32:!win32-g++:CONFIG(debug, debug|release): PRE_TARGETDEPS += $$PWD/../../external/zlib/build/debug/zlibstaticd.lib

View File

@ -0,0 +1,38 @@
<RCC>
<qresource prefix="/tp-player">
<file>res/bg.png</file>
<file>res/cursor.png</file>
<file>res/bar/bg-left.png</file>
<file>res/bar/bg-mid.png</file>
<file>res/bar/bg-right.png</file>
<file>res/bar/btn-normal-left.png</file>
<file>res/bar/btn-normal-mid.png</file>
<file>res/bar/btn-normal-right.png</file>
<file>res/bar/btn-sel-left.png</file>
<file>res/bar/btn-sel-mid.png</file>
<file>res/bar/btn-sel-right.png</file>
<file>res/bar/btn-hover-left.png</file>
<file>res/bar/btn-hover-mid.png</file>
<file>res/bar/btn-hover-right.png</file>
<file>res/bar/play-hover.png</file>
<file>res/bar/play-normal.png</file>
<file>res/bar/pause-hover.png</file>
<file>res/bar/pause-normal.png</file>
<file>res/bar/prgbar-mid.png</file>
<file>res/bar/prgbar-right.png</file>
<file>res/bar/prgbarh-left.png</file>
<file>res/bar/prgbarh-mid.png</file>
<file>res/bar/prgpt-normal.png</file>
<file>res/bar/prgpt-hover.png</file>
<file>res/bar/chkbox-normal.png</file>
<file>res/bar/chkbox-hover.png</file>
<file>res/bar/chkbox-sel-normal.png</file>
<file>res/bar/chkbox-sel-hover.png</file>
</qresource>
</RCC>

View File

@ -0,0 +1,2 @@
IDI_ICON1 ICON DISCARDABLE "res\\tp-player.ico"

View File

@ -0,0 +1,79 @@
#include "update_data.h"
#include <QImage>
#include <QDebug>
UpdateData::UpdateData() : QObject(nullptr)
{
_init();
}
UpdateData::UpdateData(int data_type) : QObject(nullptr)
{
_init();
m_data_type = data_type;
}
UpdateData::UpdateData(int data_type, uint32_t time_ms) : QObject(nullptr)
{
_init();
m_data_type = data_type;
m_time_ms = time_ms;
}
UpdateData::UpdateData(const TS_RECORD_HEADER& hdr) : QObject(nullptr)
{
_init();
m_data_type = TYPE_HEADER_INFO;
m_hdr = new TS_RECORD_HEADER;
memcpy(m_hdr, &hdr, sizeof(TS_RECORD_HEADER));
}
void UpdateData::_init() {
m_data_type = TYPE_UNKNOWN;
m_hdr = nullptr;
m_pointer = nullptr;
m_data_buf = nullptr;
m_data_len = 0;
m_time_ms = 0;
}
UpdateData::~UpdateData() {
if(m_hdr)
delete m_hdr;
if(m_pointer)
delete m_pointer;
for(int i = 0; i < m_images.size(); ++i) {
delete m_images[i].img;
}
m_images.clear();
if(m_data_buf)
delete m_data_buf;
}
void UpdateData::set_pointer(uint32_t ts, const TS_RECORD_RDP_POINTER* p) {
m_data_type = TYPE_POINTER;
m_time_ms = ts;
m_pointer = new TS_RECORD_RDP_POINTER;
memcpy(m_pointer, p, sizeof(TS_RECORD_RDP_POINTER));
}
void UpdateData::alloc_data(uint32_t len) {
if(m_data_buf)
delete m_data_buf;
m_data_buf = new uint8_t[len];
memset(m_data_buf, 0, len);
m_data_len = len;
}
void UpdateData::attach_data(const uint8_t* dat, uint32_t len) {
if(m_data_buf)
delete m_data_buf;
m_data_buf = new uint8_t[len];
memcpy(m_data_buf, dat, len);
m_data_len = len;
}

View File

@ -0,0 +1,106 @@
#ifndef UPDATE_DATA_H
#define UPDATE_DATA_H
#include <QObject>
#include <QVector>
#include "record_format.h"
#define TYPE_UNKNOWN 0
#define TYPE_HEADER_INFO 1
#define TYPE_DISABLE_DRAW 5
#define TYPE_ENABLE_DRAW 6
#define TYPE_POINTER 10
#define TYPE_IMAGE 11
#define TYPE_KEYFRAME 12
#define TYPE_PLAYED_MS 20
#define TYPE_DOWNLOAD_PERCENT 21
#define TYPE_END 50
#define TYPE_MESSAGE 90
#define TYPE_ERROR 91
typedef struct UPDATE_IMAGE {
int x;
int y;
int w;
int h;
QImage* img;
}UPDATE_IMAGE;
typedef QVector<UPDATE_IMAGE> UpdateImages;
class UpdateData : public QObject
{
Q_OBJECT
public:
explicit UpdateData();
explicit UpdateData(int data_type);
explicit UpdateData(int data_type, uint32_t time_ms);
explicit UpdateData(const TS_RECORD_HEADER& hdr);
virtual ~UpdateData();
void set_pointer(uint32_t ts, const TS_RECORD_RDP_POINTER* p);
TS_RECORD_HEADER* get_header() {return m_hdr;}
TS_RECORD_RDP_POINTER* get_pointer() {return m_pointer;}
UpdateImages& get_images() {return m_images;}
const UpdateImages& get_images() const {return m_images;}
uint32_t get_time() {return m_time_ms;}
void alloc_data(uint32_t len);
void attach_data(const uint8_t* dat, uint32_t len);
int data_type() const {return m_data_type;}
uint8_t* data_buf() {return m_data_buf;}
uint32_t data_len() const {return m_data_len;}
void played_ms(uint32_t ms) {m_played_ms = ms;}
uint32_t played_ms() {return m_played_ms;}
void message(const QString& msg) {m_msg = msg;}
const QString message(){return m_msg;}
private:
void _init(void);
signals:
public slots:
private:
int m_data_type;
uint32_t m_time_ms;
uint8_t* m_data_buf;
uint32_t m_data_len;
uint32_t m_played_ms;
QString m_msg;
// for HEADER
TS_RECORD_HEADER* m_hdr;
// for POINTER
TS_RECORD_RDP_POINTER* m_pointer;
// for IMAGE
UpdateImages m_images;
};
class UpdateDataHelper {
public:
UpdateDataHelper(UpdateData* data) {
m_data = data;
}
~UpdateDataHelper() {
if(m_data)
delete m_data;
}
private:
UpdateData* m_data;
};
#endif // UPDATE_DATA_H

View File

33
client/tp-player/util.h Normal file
View File

@ -0,0 +1,33 @@
#ifndef TP_PLAYER_UTIL_H
#define TP_PLAYER_UTIL_H
#include <QTime>
class TimeUseTest {
public:
TimeUseTest() {
m_used_ms = 0;
m_count = 0;
}
~TimeUseTest() {}
void begin() {
m_time.start();
}
void end() {
m_count++;
m_used_ms += m_time.elapsed();
}
uint32_t used() const {return m_used_ms;}
uint32_t count() const {return m_count;}
private:
QTime m_time;
uint32_t m_used_ms;
uint32_t m_count;
};
#define LOCAL8BIT(x) QString::fromLocal8Bit(x)
#endif // TP_PLAYER_UTIL_H

View File

@ -17,11 +17,11 @@
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleShortVersionString</key>
<string>3.3.1</string>
<string>3.5.1</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>
<string>3.3.1</string>
<string>3.5.1</string>
<key>LSApplicationCategoryType</key>
<string>public.app-category.productivity</string>
<key>LSMinimumSystemVersion</key>

View File

@ -1,6 +1,6 @@
#ifndef __TS_ASSIST_VER_H__
#define __TS_ASSIST_VER_H__
#define TP_ASSIST_VER L"3.3.1"
#define TP_ASSIST_VER L"3.5.1"
#endif // __TS_ASSIST_VER_H__

View File

@ -1 +1,14 @@
#include "stdafx.h"
#include "stdafx.h"
#include <ex.h>
// #ifdef EX_DEBUG
// // # pragma comment(lib, "libssl32MTd.lib")
// // # pragma comment(lib, "libcrypto32MTd.lib")
// #else
// # pragma comment(lib, "libssl32MT.lib")
// # pragma comment(lib, "libcrypto32MT.lib")
// #endif
# pragma comment(lib, "libssl.lib")
# pragma comment(lib, "libcrypto.lib")

Binary file not shown.

View File

@ -46,8 +46,8 @@
<LinkIncremental>true</LinkIncremental>
<OutDir>..\..\out\client\$(PlatformTarget)\$(Configuration)\</OutDir>
<IntDir>..\..\out\_tmp_\$(ProjectName)\$(PlatformTarget)\$(Configuration)\</IntDir>
<IncludePath>C:\Program Files %28x86%29\Visual Leak Detector\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\Program Files %28x86%29\Visual Leak Detector\lib\Win32;$(LibraryPath)</LibraryPath>
<IncludePath>C:\apps\vld\include;$(IncludePath)</IncludePath>
<LibraryPath>C:\apps\vld\lib\Win32;$(LibraryPath)</LibraryPath>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
<LinkIncremental>false</LinkIncremental>
@ -61,13 +61,14 @@
<Optimization>Disabled</Optimization>
<PreprocessorDefinitions>WIN32;MG_ENABLE_SSL;_DEBUG;_WINDOWS;_WINSOCK_DEPRECATED_NO_WARNINGS;MG_ENABLE_THREADS;MG_DISABLE_HTTP_DIGEST_AUTH;MG_DISABLE_MQTT;MG_DISABLE_SSI;MG_DISABLE_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\inc32</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreadedDebug</RuntimeLibrary>
</ClCompile>
<Link>
<SubSystem>Windows</SubSystem>
<GenerateDebugInformation>true</GenerateDebugInformation>
<AdditionalDependencies>..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\external\openssl\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
@ -79,7 +80,7 @@
<IntrinsicFunctions>true</IntrinsicFunctions>
<PreprocessorDefinitions>WIN32;MG_ENABLE_SSL;NDEBUG;_WINDOWS;_WINSOCK_DEPRECATED_NO_WARNINGS;MG_ENABLE_THREADS;MG_DISABLE_HTTP_DIGEST_AUTH;MG_DISABLE_MQTT;MG_DISABLE_SSI;MG_DISABLE_FILESYSTEM;%(PreprocessorDefinitions)</PreprocessorDefinitions>
<SDLCheck>true</SDLCheck>
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\inc32</AdditionalIncludeDirectories>
<AdditionalIncludeDirectories>..\..\common\teleport;..\..\common\libex\include;..\..\external\jsoncpp\include;..\..\external\openssl\include</AdditionalIncludeDirectories>
<RuntimeLibrary>MultiThreaded</RuntimeLibrary>
</ClCompile>
<Link>
@ -87,7 +88,8 @@
<GenerateDebugInformation>true</GenerateDebugInformation>
<EnableCOMDATFolding>true</EnableCOMDATFolding>
<OptimizeReferences>true</OptimizeReferences>
<AdditionalDependencies>..\..\external\openssl\out32\ssleay32.lib;..\..\external\openssl\out32\libeay32.lib;%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalDependencies>%(AdditionalDependencies)</AdditionalDependencies>
<AdditionalLibraryDirectories>..\..\external\openssl\lib;%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
</Link>
</ItemDefinitionGroup>
<ItemGroup>

View File

@ -1,266 +1,274 @@
#include "stdafx.h"
#include "ts_cfg.h"
#include "ts_env.h"
TsCfg g_cfg;
TsCfg::TsCfg()
{}
TsCfg::~TsCfg()
{}
bool TsCfg::init(void) {
ex_astr file_content;
if (!ex_read_text_file(g_env.m_cfg_file, file_content)) {
EXLOGE("can not load config file.\n");
return false;
}
if (!_load(file_content))
return false;
return true;
}
bool TsCfg::save(const ex_astr& new_value)
{
if (!_load(new_value))
return false;
Json::StyledWriter jwriter;
ex_astr val = jwriter.write(m_root);
if (!ex_write_text_file(g_env.m_cfg_file, val)) {
EXLOGE("can not save config file.\n");
return false;
}
return true;
}
bool TsCfg::_load(const ex_astr& str_json) {
Json::Reader jreader;
if (!jreader.parse(str_json.c_str(), m_root)) {
EXLOGE("can not parse new config data, not in json format? %s\n", jreader.getFormattedErrorMessages().c_str());
return false;
}
ex_astr sel_name;
size_t i = 0;
ex_astr tmp;
//===================================
// check ssh config
//===================================
if (!m_root["ssh"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["ssh"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["ssh"]["selected"].asCString();
if (!m_root["ssh"]["available"].isArray() || m_root["ssh"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["ssh"]["available"].size(); ++i) {
if (
!m_root["ssh"]["available"][i]["name"].isString()
|| !m_root["ssh"]["available"][i]["app"].isString()
|| !m_root["ssh"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["ssh"]["available"][i]["display"].isNull()) {
m_root["ssh"]["available"][i]["display"] = m_root["ssh"]["available"][i]["name"];
}
if (m_root["ssh"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["ssh"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, ssh_app, EX_CODEPAGE_UTF8);
tmp = m_root["ssh"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, ssh_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (ssh_app.length() == 0 || ssh_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check sftp config
//===================================
if (!m_root["scp"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["scp"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["scp"]["selected"].asCString();
if (!m_root["scp"]["available"].isArray() || m_root["scp"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["scp"]["available"].size(); ++i) {
if (
!m_root["scp"]["available"][i]["name"].isString()
|| !m_root["scp"]["available"][i]["app"].isString()
|| !m_root["scp"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["scp"]["available"][i]["display"].isNull()) {
m_root["scp"]["available"][i]["display"] = m_root["scp"]["available"][i]["name"];
}
if (m_root["scp"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["scp"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, scp_app, EX_CODEPAGE_UTF8);
tmp = m_root["scp"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, scp_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (scp_app.length() == 0 || scp_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check telnet config
//===================================
if (!m_root["telnet"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["telnet"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["telnet"]["selected"].asCString();
if (!m_root["telnet"]["available"].isArray() || m_root["telnet"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["telnet"]["available"].size(); ++i) {
if (
!m_root["telnet"]["available"][i]["name"].isString()
|| !m_root["telnet"]["available"][i]["app"].isString()
|| !m_root["telnet"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["telnet"]["available"][i]["display"].isNull()) {
m_root["telnet"]["available"][i]["display"] = m_root["telnet"]["available"][i]["name"];
}
if (m_root["telnet"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["telnet"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, telnet_app, EX_CODEPAGE_UTF8);
tmp = m_root["telnet"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, telnet_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (telnet_app.length() == 0 || telnet_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check rdp config
//===================================
if (!m_root["rdp"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["rdp"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["rdp"]["selected"].asCString();
if (!m_root["rdp"]["available"].isArray() || m_root["rdp"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["rdp"]["available"].size(); ++i) {
if (
!m_root["rdp"]["available"][i]["name"].isString()
|| !m_root["rdp"]["available"][i]["app"].isString()
|| !m_root["rdp"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["rdp"]["available"][i]["display"].isNull()) {
m_root["rdp"]["available"][i]["display"] = m_root["rdp"]["available"][i]["name"];
}
if (m_root["rdp"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["rdp"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, rdp_app, EX_CODEPAGE_UTF8);
tmp = m_root["rdp"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, rdp_cmdline, EX_CODEPAGE_UTF8);
tmp = m_root["rdp"]["available"][i]["name"].asCString();
ex_astr2wstr(tmp, rdp_name, EX_CODEPAGE_UTF8);
break;
}
if (rdp_app.length() == 0 || rdp_cmdline.length() == 0 || rdp_name.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
return true;
}
#include "stdafx.h"
#include "ts_cfg.h"
#include "ts_env.h"
TsCfg g_cfg;
TsCfg::TsCfg()
{}
TsCfg::~TsCfg()
{}
bool TsCfg::init(void) {
ex_astr file_content;
if (!ex_read_text_file(g_env.m_cfg_file, file_content)) {
EXLOGE("can not load config file.\n");
return false;
}
if (!_load(file_content))
return false;
return true;
}
bool TsCfg::save(const ex_astr& new_value)
{
if (!_load(new_value))
return false;
//Json::StyledWriter jwriter;
Json::StreamWriterBuilder jwb;
std::unique_ptr<Json::StreamWriter> jwriter(jwb.newStreamWriter());
ex_aoss os;
jwriter->write(m_root, &os);
ex_astr val = os.str();
if (!ex_write_text_file(g_env.m_cfg_file, val)) {
EXLOGE("can not save config file.\n");
return false;
}
return true;
}
bool TsCfg::_load(const ex_astr& str_json) {
//Json::Reader jreader;
Json::CharReaderBuilder jcrb;
std::unique_ptr<Json::CharReader> const jreader(jcrb.newCharReader());
const char *str_json_begin = str_json.c_str();
ex_astr err;
if (!jreader->parse(str_json_begin, str_json_begin + str_json.length(), &m_root, &err)) {
EXLOGE("can not parse new config data, not in json format? %s\n", err.c_str());
return false;
}
ex_astr sel_name;
size_t i = 0;
ex_astr tmp;
//===================================
// check ssh config
//===================================
if (!m_root["ssh"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["ssh"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["ssh"]["selected"].asCString();
if (!m_root["ssh"]["available"].isArray() || m_root["ssh"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["ssh"]["available"].size(); ++i) {
if (
!m_root["ssh"]["available"][i]["name"].isString()
|| !m_root["ssh"]["available"][i]["app"].isString()
|| !m_root["ssh"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["ssh"]["available"][i]["display"].isNull()) {
m_root["ssh"]["available"][i]["display"] = m_root["ssh"]["available"][i]["name"];
}
if (m_root["ssh"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["ssh"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, ssh_app, EX_CODEPAGE_UTF8);
tmp = m_root["ssh"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, ssh_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (ssh_app.length() == 0 || ssh_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check sftp config
//===================================
if (!m_root["scp"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["scp"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["scp"]["selected"].asCString();
if (!m_root["scp"]["available"].isArray() || m_root["scp"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["scp"]["available"].size(); ++i) {
if (
!m_root["scp"]["available"][i]["name"].isString()
|| !m_root["scp"]["available"][i]["app"].isString()
|| !m_root["scp"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["scp"]["available"][i]["display"].isNull()) {
m_root["scp"]["available"][i]["display"] = m_root["scp"]["available"][i]["name"];
}
if (m_root["scp"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["scp"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, scp_app, EX_CODEPAGE_UTF8);
tmp = m_root["scp"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, scp_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (scp_app.length() == 0 || scp_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check telnet config
//===================================
if (!m_root["telnet"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["telnet"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["telnet"]["selected"].asCString();
if (!m_root["telnet"]["available"].isArray() || m_root["telnet"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["telnet"]["available"].size(); ++i) {
if (
!m_root["telnet"]["available"][i]["name"].isString()
|| !m_root["telnet"]["available"][i]["app"].isString()
|| !m_root["telnet"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["telnet"]["available"][i]["display"].isNull()) {
m_root["telnet"]["available"][i]["display"] = m_root["telnet"]["available"][i]["name"];
}
if (m_root["telnet"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["telnet"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, telnet_app, EX_CODEPAGE_UTF8);
tmp = m_root["telnet"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, telnet_cmdline, EX_CODEPAGE_UTF8);
break;
}
if (telnet_app.length() == 0 || telnet_cmdline.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
//===================================
// check rdp config
//===================================
if (!m_root["rdp"].isObject()) {
EXLOGE("invalid config, error 1.\n");
return false;
}
if (!m_root["rdp"]["selected"].isString()) {
EXLOGE("invalid config, error 2.\n");
return false;
}
sel_name = m_root["rdp"]["selected"].asCString();
if (!m_root["rdp"]["available"].isArray() || m_root["rdp"]["available"].size() == 0) {
EXLOGE("invalid config, error 3.\n");
return false;
}
for (i = 0; i < m_root["rdp"]["available"].size(); ++i) {
if (
!m_root["rdp"]["available"][i]["name"].isString()
|| !m_root["rdp"]["available"][i]["app"].isString()
|| !m_root["rdp"]["available"][i]["cmdline"].isString()
) {
EXLOGE("invalid config, error 4.\n");
return false;
}
if (m_root["rdp"]["available"][i]["display"].isNull()) {
m_root["rdp"]["available"][i]["display"] = m_root["rdp"]["available"][i]["name"];
}
if (m_root["rdp"]["available"][i]["name"].asCString() != sel_name)
continue;
tmp = m_root["rdp"]["available"][i]["app"].asCString();
ex_astr2wstr(tmp, rdp_app, EX_CODEPAGE_UTF8);
tmp = m_root["rdp"]["available"][i]["cmdline"].asCString();
ex_astr2wstr(tmp, rdp_cmdline, EX_CODEPAGE_UTF8);
tmp = m_root["rdp"]["available"][i]["name"].asCString();
ex_astr2wstr(tmp, rdp_name, EX_CODEPAGE_UTF8);
break;
}
if (rdp_app.length() == 0 || rdp_cmdline.length() == 0 || rdp_name.length() == 0) {
EXLOGE("invalid config, error 6.\n");
return false;
}
return true;
}

View File

@ -1,71 +1,74 @@
#include "stdafx.h"
#include "ts_env.h"
#include <time.h>
#ifdef EX_OS_WIN32
# include <direct.h>
//# include <ShlObj.h>
#endif
TsEnv g_env;
//=======================================================
//
//=======================================================
TsEnv::TsEnv()
{}
TsEnv::~TsEnv()
{}
bool TsEnv::init(void)
{
if (!ex_exec_file(m_exec_file))
return false;
m_exec_path = m_exec_file;
if (!ex_dirname(m_exec_path))
return false;
m_cfg_file = m_exec_path;
ex_path_join(m_cfg_file, false, L"cfg", L"tp-assist.json", NULL);
m_log_path = m_exec_path;
ex_path_join(m_log_path, false, L"log", NULL);
ex_wstr cfg_default;
#ifdef _DEBUG
m_site_path = m_exec_path;
ex_path_join(m_site_path, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"site", NULL);
m_tools_path = m_exec_path;
ex_path_join(m_tools_path, true, L"..", L"..", L"..", L"..", L"client", L"tools", NULL);
cfg_default = m_exec_path;
ex_path_join(cfg_default, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"cfg", L"tp-assist.default.json", NULL);
#else
m_site_path = m_exec_path;
ex_path_join(m_site_path, false, L"site", NULL);
m_tools_path = m_exec_path;
ex_path_join(m_tools_path, false, L"tools", NULL);
cfg_default = m_exec_path;
ex_path_join(cfg_default, false, L"tp-assist.default.json", NULL);
#endif
if (!ex_is_file_exists(m_cfg_file.c_str())) {
ex_wstr cfg_path = m_exec_path;
ex_path_join(cfg_path, false, L"cfg", NULL);
ex_mkdirs(cfg_path);
if (!ex_copy_file(cfg_default.c_str(), m_cfg_file.c_str()))
return false;
}
return true;
}
#include "stdafx.h"
#include "ts_env.h"
#include <time.h>
#ifdef EX_OS_WIN32
# include <direct.h>
//# include <ShlObj.h>
#endif
TsEnv g_env;
//=======================================================
//
//=======================================================
TsEnv::TsEnv()
{}
TsEnv::~TsEnv()
{}
bool TsEnv::init(void)
{
if (!ex_exec_file(m_exec_file))
return false;
m_exec_path = m_exec_file;
if (!ex_dirname(m_exec_path))
return false;
m_cfg_file = m_exec_path;
ex_path_join(m_cfg_file, false, L"cfg", L"tp-assist.json", NULL);
m_log_path = m_exec_path;
ex_path_join(m_log_path, false, L"log", NULL);
ex_wstr cfg_default;
#ifdef _DEBUG
m_site_path = m_exec_path;
ex_path_join(m_site_path, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"site", NULL);
// m_tools_path = m_exec_path;
// ex_path_join(m_tools_path, true, L"..", L"..", L"..", L"..", L"client", L"tools", NULL);
cfg_default = m_exec_path;
ex_path_join(cfg_default, true, L"..", L"..", L"..", L"..", L"client", L"tp_assist_win", L"cfg", L"tp-assist.default.json", NULL);
#else
m_site_path = m_exec_path;
ex_path_join(m_site_path, false, L"site", NULL);
// m_tools_path = m_exec_path;
// ex_path_join(m_tools_path, false, L"tools", NULL);
cfg_default = m_exec_path;
ex_path_join(cfg_default, false, L"tp-assist.default.json", NULL);
#endif
m_tools_path = m_exec_path;
ex_path_join(m_tools_path, false, L"tools", NULL);
if (!ex_is_file_exists(m_cfg_file.c_str())) {
ex_wstr cfg_path = m_exec_path;
ex_path_join(cfg_path, false, L"cfg", NULL);
ex_mkdirs(cfg_path);
if (!ex_copy_file(cfg_default.c_str(), m_cfg_file.c_str()))
return false;
}
return true;
}

File diff suppressed because it is too large Load Diff

View File

@ -1,6 +1,6 @@
#ifndef __TS_ASSIST_VER_H__
#define __TS_ASSIST_VER_H__
#define TP_ASSIST_VER L"3.3.1"
#define TP_ASSIST_VER L"3.5.1"
#endif // __TS_ASSIST_VER_H__

View File

@ -1,4 +1,4 @@
#ifndef __LIB_EX_H__
#ifndef __LIB_EX_H__
#define __LIB_EX_H__
#ifdef EX_HAVE_CONFIG

View File

@ -1,4 +1,4 @@
#ifndef __LIB_EX_CONST_H__
#ifndef __LIB_EX_CONST_H__
#define __LIB_EX_CONST_H__
#include "ex_platform.h"
@ -43,8 +43,8 @@
// error code.
//====================================================
#define EXRV_OK 0
#define EXRV_SYS_ERR 1 // 系统错误可以使用GetLastError或者errno来获取具体错误值
#define EXRV_FAILED 2 // 操作失败
#define EXRV_SYS_ERR 1 // 系统错误可以使用GetLastError或者errno来获取具体错误值
#define EXRV_FAILED 2 // 操作失败
//#define EXRV_CANNOT_FOUND 9
#define EXRV_CANNOT_CREATE 10

View File

@ -1,4 +1,4 @@
#ifndef __EX_INI_H__
#ifndef __EX_INI_H__
#define __EX_INI_H__
/*

View File

@ -1,4 +1,4 @@
#ifndef __EX_LOG_H__
#ifndef __EX_LOG_H__
#define __EX_LOG_H__
#include "ex_types.h"
@ -27,7 +27,7 @@ public:
protected:
bool _open_file();
bool _rotate_file(void); // 将现有日志文件改名备份,然后新开一个日志文件
bool _rotate_file(void); // 将现有日志文件改名备份,然后新开一个日志文件
public:
ExThreadLock lock;
@ -63,7 +63,7 @@ void EXLOG_USE_LOGGER(ExLogger* logger);
void EXLOG_LEVEL(int min_level);
void EXLOG_DEBUG(bool debug_mode);
// 设定日志文件名及路径如未指定路径则为可执行程序所在目录下的log目录。
// 设定日志文件名及路径如未指定路径则为可执行程序所在目录下的log目录。
void EXLOG_FILE(const wchar_t* log_file, const wchar_t* log_path = NULL, ex_u32 max_filesize = EX_LOG_FILE_MAX_SIZE, ex_u8 max_filecount = EX_LOG_FILE_MAX_COUNT);
void EXLOG_CONSOLE(bool output_to_console);

View File

@ -1,4 +1,4 @@
#ifndef __LIB_EX_PATH_H__
#ifndef __LIB_EX_PATH_H__
#define __LIB_EX_PATH_H__
#include "ex_platform.h"
@ -39,7 +39,7 @@ bool ex_path_join(ex_wstr& inout_path, EX_BOOL auto_abspath, ...);
bool ex_abspath_to(const ex_wstr& base_abs_path, const ex_wstr& relate_path, ex_wstr& out_path);
bool ex_mkdirs(const ex_wstr& in_path);
// 获取文件名中的扩展名部分(不包括.例如abc.py返回 py
// 获取文件名中的扩展名部分(不包括.例如abc.py返回 py
bool ex_path_ext_name(const ex_wstr& in_filename, ex_wstr& out_ext);
#endif

View File

@ -1,4 +1,4 @@
#ifndef __LIB_EX_PLATFORM_H__
#ifndef __LIB_EX_PLATFORM_H__
#define __LIB_EX_PLATFORM_H__
#if defined(_WIN32) || defined(WIN32)

View File

@ -1,86 +1,89 @@
#ifndef __LIB_EX_STR_H__
#define __LIB_EX_STR_H__
#include "ex_types.h"
#define EX_CODEPAGE_ACP 0
#define EX_CODEPAGE_UTF8 1
#ifdef EX_OS_WIN32
# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_ACP
#else
# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_UTF8
#endif
#define EX_RSC_BEGIN 0x01
#define EX_RSC_END 0x02
#define EX_RSC_ALL EX_RSC_BEGIN | EX_RSC_END
//=================================================
// C Interface
//=================================================
// copy a string from `source` to `target`.
// `size` is size of target buffer.
// if buffer is to small, NULL will return, but `size-1` characters have been copied.
char* ex_strcpy(char* target, size_t size, const char* source);
wchar_t* ex_wcscpy(wchar_t* target, size_t size, const wchar_t* source);
// dupilicate a string.
// must use ex_free() to release the returned value.
char* ex_strdup(const char* src);
wchar_t* ex_wcsdup(const wchar_t* src);
// convert between mutli-bytes and wide char string.
// must use ex_free() to release the returned value.
wchar_t* ex_str2wcs_alloc(const char* in_buffer, int code_page);
char* ex_wcs2str_alloc(const wchar_t* in_buffer, int code_page);
// convert char** argv to wchar_t** argv.
// must use ex_free_argv() to release the returned value.
wchar_t** ex_make_wargv(int argc, char** argv);
void ex_free_wargv(int argc, wchar_t** argv);
EX_BOOL ex_str_only_white_space(const wchar_t* src);
EX_BOOL ex_wcs_only_white_space(const char* src);
int ex_strformat(char* out_buf, size_t buf_size, const char* fmt, ...);
int ex_wcsformat(wchar_t* out_buf, size_t buf_size, const wchar_t* fmt, ...);
//=================================================
// C++ Interface
//=================================================
#ifdef __cplusplus
#include <string>
#include <vector>
typedef std::string ex_astr;
typedef std::wstring ex_wstr;
typedef std::vector<ex_astr> ex_astrs;
typedef std::vector<ex_wstr> ex_wstrs;
typedef std::vector<ex_utf16> ex_str_utf16le;
bool ex_wstr2astr(const ex_wstr& in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
bool ex_wstr2astr(const wchar_t* in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
bool ex_astr2wstr(const ex_astr& in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
bool ex_astr2wstr(const char* in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
bool ex_only_white_space(const ex_astr& str_check);
bool ex_only_white_space(const ex_wstr& str_check);
void ex_remove_white_space(ex_astr& str_fix, int ulFlag = EX_RSC_ALL);
void ex_remove_white_space(ex_wstr& str_fix, int ulFlag = EX_RSC_ALL);
ex_astr& ex_replace_all(ex_astr& str, const ex_astr& old_value, const ex_astr& new_value);
ex_wstr& ex_replace_all(ex_wstr& str, const ex_wstr& old_value, const ex_wstr& new_value);
// 将UTF8字符串转换为UTF16-LE字符串输出结果包含\0结束符
bool ex_utf8_to_utf16le(const std::string& from, ex_str_utf16le& to);
#endif
#endif // __LIB_EX_STR_H__
#ifndef __LIB_EX_STR_H__
#define __LIB_EX_STR_H__
#include "ex_types.h"
#define EX_CODEPAGE_ACP 0
#define EX_CODEPAGE_UTF8 1
#ifdef EX_OS_WIN32
# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_ACP
#else
# define EX_CODEPAGE_DEFAULT EX_CODEPAGE_UTF8
#endif
#define EX_RSC_BEGIN 0x01
#define EX_RSC_END 0x02
#define EX_RSC_ALL EX_RSC_BEGIN | EX_RSC_END
//=================================================
// C Interface
//=================================================
// copy a string from `source` to `target`.
// `size` is size of target buffer.
// if buffer is to small, NULL will return, but `size-1` characters have been copied.
char* ex_strcpy(char* target, size_t size, const char* source);
wchar_t* ex_wcscpy(wchar_t* target, size_t size, const wchar_t* source);
// dupilicate a string.
// must use ex_free() to release the returned value.
char* ex_strdup(const char* src);
wchar_t* ex_wcsdup(const wchar_t* src);
// convert between mutli-bytes and wide char string.
// must use ex_free() to release the returned value.
wchar_t* ex_str2wcs_alloc(const char* in_buffer, int code_page);
char* ex_wcs2str_alloc(const wchar_t* in_buffer, int code_page);
// convert char** argv to wchar_t** argv.
// must use ex_free_argv() to release the returned value.
wchar_t** ex_make_wargv(int argc, char** argv);
void ex_free_wargv(int argc, wchar_t** argv);
EX_BOOL ex_str_only_white_space(const wchar_t* src);
EX_BOOL ex_wcs_only_white_space(const char* src);
int ex_strformat(char* out_buf, size_t buf_size, const char* fmt, ...);
int ex_wcsformat(wchar_t* out_buf, size_t buf_size, const wchar_t* fmt, ...);
//=================================================
// C++ Interface
//=================================================
#ifdef __cplusplus
#include <string>
#include <vector>
#include <ostream>
typedef std::string ex_astr;
typedef std::wstring ex_wstr;
typedef std::ostringstream ex_aoss;
typedef std::wostringstream ex_woss;
typedef std::vector<ex_astr> ex_astrs;
typedef std::vector<ex_wstr> ex_wstrs;
typedef std::vector<ex_utf16> ex_str_utf16le;
bool ex_wstr2astr(const ex_wstr& in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
bool ex_wstr2astr(const wchar_t* in_str, ex_astr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
bool ex_astr2wstr(const ex_astr& in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
bool ex_astr2wstr(const char* in_str, ex_wstr& out_str, int code_page = EX_CODEPAGE_DEFAULT);
bool ex_only_white_space(const ex_astr& str_check);
bool ex_only_white_space(const ex_wstr& str_check);
void ex_remove_white_space(ex_astr& str_fix, int ulFlag = EX_RSC_ALL);
void ex_remove_white_space(ex_wstr& str_fix, int ulFlag = EX_RSC_ALL);
ex_astr& ex_replace_all(ex_astr& str, const ex_astr& old_value, const ex_astr& new_value);
ex_wstr& ex_replace_all(ex_wstr& str, const ex_wstr& old_value, const ex_wstr& new_value);
// 将UTF8字符串转换为UTF16-LE字符串输出结果包含\0结束符
bool ex_utf8_to_utf16le(const std::string& from, ex_str_utf16le& to);
#endif
#endif // __LIB_EX_STR_H__

View File

@ -1,4 +1,4 @@
#ifndef __EX_THREAD_H__
#ifndef __EX_THREAD_H__
#define __EX_THREAD_H__
#include "ex_str.h"
@ -23,11 +23,11 @@ public:
bool is_running(void) { return m_is_running; }
// 创建并启动线程执行被重载了的run()函数)
// 创建并启动线程执行被重载了的run()函数)
bool start(void);
// 结束线程等待wait_timeout_ms毫秒如果wait_timeout_ms为0则无限等待
// 结束线程等待wait_timeout_ms毫秒如果wait_timeout_ms为0则无限等待
bool stop(void);
// 直接结束线程(强杀,不建议使用)
// 直接结束线程(强杀,不建议使用)
bool terminate(void);
protected:
@ -52,7 +52,7 @@ protected:
};
// 线程锁(进程内使用)
// 线程锁(进程内使用)
class ExThreadLock
{
public:
@ -70,7 +70,7 @@ private:
#endif
};
// 线程锁辅助类
// 线程锁辅助类
class ExThreadSmartLock
{
public:
@ -109,12 +109,12 @@ private:
};
// 原子操作
// 原子操作
int ex_atomic_add(volatile int* pt, int t);
int ex_atomic_inc(volatile int* pt);
int ex_atomic_dec(volatile int* pt);
// 线程相关操作
// 线程相关操作
ex_u64 ex_get_thread_id(void);
#endif // __EX_THREAD_H__

View File

@ -1,44 +1,44 @@
#ifndef __LIB_EX_TYPE_H__
#define __LIB_EX_TYPE_H__
#include "ex_platform.h"
#include <vector>
typedef signed char ex_i8;
typedef signed short ex_i16;
typedef unsigned char ex_u8;
typedef unsigned short ex_u16;
typedef unsigned int ex_u32;
typedef unsigned long ex_ulong;
#if defined(EX_OS_WIN32)
typedef unsigned __int64 ex_u64;
typedef signed __int64 ex_i64;
typedef wchar_t ex_utf16;
#else
typedef unsigned long long ex_u64;
typedef signed long long ex_i64;
typedef ex_i16 ex_utf16;
#endif
typedef int EX_BOOL;
#define EX_TRUE 1
#define EX_FALSE 0
typedef std::vector<ex_u8> ex_bin;
typedef std::vector<char> ex_chars;
typedef ex_u32 ex_rv;
#if defined(EX_OS_WIN32)
# define EX_DYLIB_HANDLE HINSTANCE
#else
# define EX_DYLIB_HANDLE void*
#endif
#endif // __LIB_EX_TYPE_H__
#ifndef __LIB_EX_TYPE_H__
#define __LIB_EX_TYPE_H__
#include "ex_platform.h"
#include <vector>
typedef signed char ex_i8;
typedef signed short ex_i16;
typedef unsigned char ex_u8;
typedef unsigned short ex_u16;
typedef unsigned int ex_u32;
typedef unsigned long ex_ulong;
#if defined(EX_OS_WIN32)
typedef unsigned __int64 ex_u64;
typedef signed __int64 ex_i64;
typedef wchar_t ex_utf16;
#else
typedef unsigned long long ex_u64;
typedef signed long long ex_i64;
typedef ex_i16 ex_utf16;
#endif
typedef int EX_BOOL;
#define EX_TRUE 1
#define EX_FALSE 0
typedef std::vector<ex_u8> ex_bin;
typedef std::vector<char> ex_chars;
typedef ex_u32 ex_rv;
#if defined(EX_OS_WIN32)
# define EX_DYLIB_HANDLE HINSTANCE
#else
# define EX_DYLIB_HANDLE void*
#endif
#endif // __LIB_EX_TYPE_H__

View File

@ -1,55 +1,55 @@
#ifndef __LIB_EX_UTIL_H__
#define __LIB_EX_UTIL_H__
#include "ex_types.h"
#include "ex_str.h"
#ifdef EX_OS_WIN32
# include <time.h>
//# include <io.h>
//# include <stdio.h>
// #include <direct.h>
#pragma comment(lib, "ws2_32.lib")
#else
// #include <dirent.h>
# include <dlfcn.h>
# include <sys/time.h>
#endif
EX_BOOL ex_initialize(const char* lc_ctype);
void ex_free(void* buffer);
// 在haystack长度为haystacklen字节中查找needle长度为needlelen的起始地址返回NULL表示没有找到
const ex_u8* ex_memmem(const ex_u8* haystack, size_t haystacklen, const ex_u8* needle, size_t needlelen);
void ex_mem_reverse(ex_u8* p, size_t l);
void ex_printf(const char* fmt, ...);
void ex_wprintf(const wchar_t* fmt, ...);
ex_u64 ex_get_tick_count(void);
void ex_sleep_ms(int ms);
EX_BOOL ex_localtime_now(int* t, struct tm* dt);
FILE* ex_fopen(const ex_wstr& filename, const wchar_t* mode);
FILE* ex_fopen(const ex_astr& filename, const char* mode);
// open a text file and read all content.
bool ex_read_text_file(const ex_wstr& file_name, ex_astr& file_content);
// open a file and write content.
bool ex_write_text_file(const ex_wstr& file_name, const ex_astr& file_content);
EX_DYLIB_HANDLE ex_dlopen(const wchar_t* dylib_path);
void ex_dlclose(EX_DYLIB_HANDLE dylib);
// inet...
int ex_ip4_name(const struct sockaddr_in* src, char* dst, size_t size);
#define EX_IPV4_NAME_LEN 16
#define EX_IPV6_NAME_LEN 46
const char* ex_inet_ntop(int af, const void *src, char *dst, size_t size);
#endif // __LIB_EX_UTIL_H__
#ifndef __LIB_EX_UTIL_H__
#define __LIB_EX_UTIL_H__
#include "ex_types.h"
#include "ex_str.h"
#ifdef EX_OS_WIN32
# include <time.h>
//# include <io.h>
//# include <stdio.h>
// #include <direct.h>
#pragma comment(lib, "ws2_32.lib")
#else
// #include <dirent.h>
# include <dlfcn.h>
# include <sys/time.h>
#endif
EX_BOOL ex_initialize(const char* lc_ctype);
void ex_free(void* buffer);
// 在haystack长度为haystacklen字节中查找needle长度为needlelen的起始地址返回NULL表示没有找到
const ex_u8* ex_memmem(const ex_u8* haystack, size_t haystacklen, const ex_u8* needle, size_t needlelen);
void ex_mem_reverse(ex_u8* p, size_t l);
void ex_printf(const char* fmt, ...);
void ex_wprintf(const wchar_t* fmt, ...);
ex_u64 ex_get_tick_count(void);
void ex_sleep_ms(int ms);
EX_BOOL ex_localtime_now(int* t, struct tm* dt);
FILE* ex_fopen(const ex_wstr& filename, const wchar_t* mode);
FILE* ex_fopen(const ex_astr& filename, const char* mode);
// open a text file and read all content.
bool ex_read_text_file(const ex_wstr& file_name, ex_astr& file_content);
// open a file and write content.
bool ex_write_text_file(const ex_wstr& file_name, const ex_astr& file_content);
EX_DYLIB_HANDLE ex_dlopen(const wchar_t* dylib_path);
void ex_dlclose(EX_DYLIB_HANDLE dylib);
// inet...
int ex_ip4_name(const struct sockaddr_in* src, char* dst, size_t size);
#define EX_IPV4_NAME_LEN 16
#define EX_IPV6_NAME_LEN 46
const char* ex_inet_ntop(int af, const void *src, char *dst, size_t size);
#endif // __LIB_EX_UTIL_H__

View File

@ -1,4 +1,4 @@
#ifndef __EX_WINSRV_H__
#ifndef __EX_WINSRV_H__
#define __EX_WINSRV_H__
#include "ex_str.h"

View File

@ -1,4 +1,4 @@
#include <ex/ex_ini.h>
#include <ex/ex_ini.h>
#include <ex/ex_log.h>
#include <ex/ex_util.h>
@ -241,7 +241,7 @@ bool ExIniFile::LoadFromFile(const ex_wstr& strFileName, bool bClearOld)
{
pOffset += 3;
}
// 配置文件均使用UTF8编码
// 配置文件均使用UTF8编码
ex_wstr fileData;
if (!ex_astr2wstr(pOffset, fileData, EX_CODEPAGE_UTF8))
return false;
@ -357,7 +357,7 @@ void ExIniFile::Save(int codepage/* = EX_CODEPAGE_UTF8*/)
return;
}
// 如果有不属于任何小节的值对,先保存之
// 如果有不属于任何小节的值对,先保存之
if (m_dumy_sec.Count() > 0)
m_dumy_sec.Save(file, codepage);
@ -415,16 +415,16 @@ ExIniSection* ExIniFile::GetSection(const ex_wstr& strName, bool bCreateIfNotExi
}
// static function.
// 解析一行,返回值为 [节名/值对/注释/什么也不是/出错了]
// 节名 => strKey = [section_name]
// 值对 => strKey = strValue
// 解析一行,返回值为 [节名/值对/注释/什么也不是/出错了]
// 节名 => strKey = [section_name]
// 值对 => strKey = strValue
ExIniFile::PARSE_RV ExIniFile::_ParseLine(const ex_wstr& strOrigLine, ex_wstr& strKey, ex_wstr& strValue)
{
// 首先去掉行首的空格或者 TAB 控制
// 首先去掉行首的空格或者 TAB 控制
ex_wstr strLine(strOrigLine);
ex_remove_white_space(strLine, EX_RSC_BEGIN);
// 判断是否为注释。 .ini 文件以 分号';'/'#' 作为注释行的第一个字符
// 判断是否为注释。 .ini 文件以 分号';'/'#' 作为注释行的第一个字符
if (';' == strLine[0] || '#' == strLine[0])
{
return PARSE_COMMENT;
@ -432,7 +432,7 @@ ExIniFile::PARSE_RV ExIniFile::_ParseLine(const ex_wstr& strOrigLine, ex_wstr& s
if ('[' == strLine[0])
{
// 这是一个节(section)
// 这是一个节(section)
ex_wstr::size_type startPos = strLine.find('[');
ex_wstr::size_type endPos = strLine.rfind(']');
strLine.erase(endPos);
@ -443,23 +443,23 @@ ExIniFile::PARSE_RV ExIniFile::_ParseLine(const ex_wstr& strOrigLine, ex_wstr& s
}
else
{
// 看看能否找到等号(=),这是 key=value 的判别方法
// 看看能否找到等号(=),这是 key=value 的判别方法
ex_wstr::size_type pos = strLine.find('=');
if (ex_wstr::npos == pos)
{
//return PARSE_OTHER; // 没有等号
//return PARSE_OTHER; // 没有等号
ex_remove_white_space(strLine);
strKey = strLine;
strValue.clear();
return PARSE_KEYVALUE;
}
// 将等号前面的与等号后面的分割
// 将等号前面的与等号后面的分割
strKey.assign(strLine, 0, pos);
strValue.assign(strLine, pos + 1, strLine.length() - pos);
ex_remove_white_space(strKey);
// 等号后面的应该原封不动,不应该移除空白字符
// 等号后面的应该原封不动,不应该移除空白字符
ex_remove_white_space(strValue, EX_RSC_BEGIN);
return PARSE_KEYVALUE;
@ -489,7 +489,7 @@ bool ExIniFile::_ProcessLine(const ex_wstr strLine, ExIniSection** pCurSection)
break;
case PARSE_SECTION:
{
// 创建一个节
// 创建一个节
ExIniSection* pSection = GetSection(strKey, true);
if (NULL == pSection)
{
@ -508,7 +508,7 @@ bool ExIniFile::_ProcessLine(const ex_wstr strLine, ExIniSection** pCurSection)
*pCurSection = &m_dumy_sec;
}
// 创建一个值对
// 创建一个值对
if (!(*pCurSection)->SetValue(strKey, strValue, true))
{
bError = true;

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
#include <ex/ex_path.h>
#include <ex/ex_path.h>
#include <ex/ex_const.h>
#include <ex/ex_util.h>

File diff suppressed because it is too large Load Diff

View File

@ -1,224 +1,224 @@
#include <ex/ex_thread.h>
#include <ex/ex_log.h>
//=========================================================
//
//=========================================================
#ifdef EX_OS_WIN32
unsigned int WINAPI ExThreadBase::_thread_func(LPVOID pParam)
#else
void *ExThreadBase::_thread_func(void *pParam)
#endif
{
ExThreadBase *_this = (ExThreadBase *) pParam;
_this->m_is_running = true;
_this->_thread_loop();
_this->m_is_running = false;
_this->m_handle = 0;
EXLOGV(" # thread [%s] exit.\n", _this->m_thread_name.c_str());
_this->_on_stopped();
return 0;
}
ExThreadBase::ExThreadBase(const char *thread_name) :
m_handle(0),
m_is_running(false),
m_need_stop(false) {
m_thread_name = thread_name;
}
ExThreadBase::~ExThreadBase() {
if(m_is_running) {
EXLOGE(" # thread [%s] not stop before destroy.\n", m_thread_name.c_str());
}
}
bool ExThreadBase::start(void) {
m_need_stop = false;
EXLOGV(" . thread [%s] starting.\n", m_thread_name.c_str());
#ifdef WIN32
HANDLE h = (HANDLE)_beginthreadex(NULL, 0, _thread_func, (void*)this, 0, NULL);
if (NULL == h)
{
return false;
}
m_handle = h;
#else
pthread_t ptid = 0;
int ret = pthread_create(&ptid, NULL, _thread_func, (void *) this);
if (ret != 0) {
return false;
}
m_handle = ptid;
#endif
return true;
}
bool ExThreadBase::stop(void) {
if (m_handle == 0) {
EXLOGW("[thread] thread [%s] already stopped.\n", m_thread_name.c_str());
return true;
}
EXLOGV("[thread] try to stop thread [%s].\n", m_thread_name.c_str());
m_need_stop = true;
_on_stop();
EXLOGV("[thread] wait thread [%s] exit.\n", m_thread_name.c_str());
#ifdef EX_OS_WIN32
if (WaitForSingleObject(m_handle, INFINITE) != WAIT_OBJECT_0)
{
return false;
}
#else
if (pthread_join(m_handle, NULL) != 0) {
return false;
}
#endif
return true;
}
bool ExThreadBase::terminate(void) {
#ifdef EX_OS_WIN32
return (TerminateThread(m_handle, 1) == TRUE);
#else
return (pthread_cancel(m_handle) == 0);
#endif
}
//=========================================================
//
//=========================================================
ExThreadManager::ExThreadManager() {}
ExThreadManager::~ExThreadManager() {
if (!m_threads.empty()) {
EXLOGE("when destroy thread manager, there are %d thread not exit.\n", m_threads.size());
stop_all();
}
}
void ExThreadManager::stop_all(void) {
ExThreadSmartLock locker(m_lock);
ex_threads::iterator it = m_threads.begin();
for (; it != m_threads.end(); ++it) {
(*it)->stop();
}
m_threads.clear();
}
void ExThreadManager::add(ExThreadBase *tb) {
ExThreadSmartLock locker(m_lock);
ex_threads::iterator it = m_threads.begin();
for (; it != m_threads.end(); ++it) {
if ((*it) == tb) {
EXLOGE("when add thread to manager, it already exist.\n");
return;
}
}
m_threads.push_back(tb);
}
void ExThreadManager::remove(ExThreadBase *tb) {
ExThreadSmartLock locker(m_lock);
ex_threads::iterator it = m_threads.begin();
for (; it != m_threads.end(); ++it) {
if ((*it) == tb) {
m_threads.erase(it);
return;
}
}
EXLOGE("thread not hold by thread-manager while remove it.\n");
}
//=========================================================
//
//=========================================================
ExThreadLock::ExThreadLock() {
#ifdef EX_OS_WIN32
InitializeCriticalSection(&m_locker);
#else
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&m_locker, &attr);
pthread_mutexattr_destroy(&attr);
#endif
}
ExThreadLock::~ExThreadLock() {
#ifdef EX_OS_WIN32
DeleteCriticalSection(&m_locker);
#else
pthread_mutex_destroy(&m_locker);
#endif
}
void ExThreadLock::lock(void) {
#ifdef EX_OS_WIN32
EnterCriticalSection(&m_locker);
#else
pthread_mutex_lock(&m_locker);
#endif
}
void ExThreadLock::unlock(void) {
#ifdef EX_OS_WIN32
LeaveCriticalSection(&m_locker);
#else
pthread_mutex_unlock(&m_locker);
#endif
}
//=========================================================
//
//=========================================================
int ex_atomic_add(volatile int *pt, int t) {
#ifdef EX_OS_WIN32
return (int)InterlockedExchangeAdd((long*)pt, (long)t);
#else
return __sync_add_and_fetch(pt, t);
#endif
}
int ex_atomic_inc(volatile int *pt) {
#ifdef EX_OS_WIN32
return (int)InterlockedIncrement((long*)pt);
#else
return __sync_add_and_fetch(pt, 1);
#endif
}
int ex_atomic_dec(volatile int *pt) {
#ifdef EX_OS_WIN32
return (int)InterlockedDecrement((long*)pt);
#else
return __sync_add_and_fetch(pt, -1);
#endif
}
ex_u64 ex_get_thread_id(void) {
#ifdef EX_OS_WIN32
return GetCurrentThreadId();
#else
return (ex_u64) pthread_self();
#endif
}
#include <ex/ex_thread.h>
#include <ex/ex_log.h>
//=========================================================
//
//=========================================================
#ifdef EX_OS_WIN32
unsigned int WINAPI ExThreadBase::_thread_func(LPVOID pParam)
#else
void *ExThreadBase::_thread_func(void *pParam)
#endif
{
ExThreadBase *_this = (ExThreadBase *) pParam;
_this->m_is_running = true;
_this->_thread_loop();
_this->m_is_running = false;
_this->m_handle = 0;
EXLOGV(" # thread [%s] exit.\n", _this->m_thread_name.c_str());
_this->_on_stopped();
return 0;
}
ExThreadBase::ExThreadBase(const char *thread_name) :
m_handle(0),
m_is_running(false),
m_need_stop(false) {
m_thread_name = thread_name;
}
ExThreadBase::~ExThreadBase() {
if(m_is_running) {
EXLOGE(" # thread [%s] not stop before destroy.\n", m_thread_name.c_str());
}
}
bool ExThreadBase::start(void) {
m_need_stop = false;
EXLOGV(" . thread [%s] starting.\n", m_thread_name.c_str());
#ifdef WIN32
HANDLE h = (HANDLE)_beginthreadex(NULL, 0, _thread_func, (void*)this, 0, NULL);
if (NULL == h)
{
return false;
}
m_handle = h;
#else
pthread_t ptid = 0;
int ret = pthread_create(&ptid, NULL, _thread_func, (void *) this);
if (ret != 0) {
return false;
}
m_handle = ptid;
#endif
return true;
}
bool ExThreadBase::stop(void) {
if (m_handle == 0) {
EXLOGW("[thread] thread [%s] already stopped.\n", m_thread_name.c_str());
return true;
}
EXLOGV("[thread] try to stop thread [%s].\n", m_thread_name.c_str());
m_need_stop = true;
_on_stop();
EXLOGV("[thread] wait thread [%s] exit.\n", m_thread_name.c_str());
#ifdef EX_OS_WIN32
if (WaitForSingleObject(m_handle, INFINITE) != WAIT_OBJECT_0)
{
return false;
}
#else
if (pthread_join(m_handle, NULL) != 0) {
return false;
}
#endif
return true;
}
bool ExThreadBase::terminate(void) {
#ifdef EX_OS_WIN32
return (TerminateThread(m_handle, 1) == TRUE);
#else
return (pthread_cancel(m_handle) == 0);
#endif
}
//=========================================================
//
//=========================================================
ExThreadManager::ExThreadManager() {}
ExThreadManager::~ExThreadManager() {
if (!m_threads.empty()) {
EXLOGE("when destroy thread manager, there are %d thread not exit.\n", m_threads.size());
stop_all();
}
}
void ExThreadManager::stop_all(void) {
ExThreadSmartLock locker(m_lock);
ex_threads::iterator it = m_threads.begin();
for (; it != m_threads.end(); ++it) {
(*it)->stop();
}
m_threads.clear();
}
void ExThreadManager::add(ExThreadBase *tb) {
ExThreadSmartLock locker(m_lock);
ex_threads::iterator it = m_threads.begin();
for (; it != m_threads.end(); ++it) {
if ((*it) == tb) {
EXLOGE("when add thread to manager, it already exist.\n");
return;
}
}
m_threads.push_back(tb);
}
void ExThreadManager::remove(ExThreadBase *tb) {
ExThreadSmartLock locker(m_lock);
ex_threads::iterator it = m_threads.begin();
for (; it != m_threads.end(); ++it) {
if ((*it) == tb) {
m_threads.erase(it);
return;
}
}
EXLOGE("thread not hold by thread-manager while remove it.\n");
}
//=========================================================
//
//=========================================================
ExThreadLock::ExThreadLock() {
#ifdef EX_OS_WIN32
InitializeCriticalSection(&m_locker);
#else
pthread_mutexattr_t attr;
pthread_mutexattr_init(&attr);
pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE);
pthread_mutex_init(&m_locker, &attr);
pthread_mutexattr_destroy(&attr);
#endif
}
ExThreadLock::~ExThreadLock() {
#ifdef EX_OS_WIN32
DeleteCriticalSection(&m_locker);
#else
pthread_mutex_destroy(&m_locker);
#endif
}
void ExThreadLock::lock(void) {
#ifdef EX_OS_WIN32
EnterCriticalSection(&m_locker);
#else
pthread_mutex_lock(&m_locker);
#endif
}
void ExThreadLock::unlock(void) {
#ifdef EX_OS_WIN32
LeaveCriticalSection(&m_locker);
#else
pthread_mutex_unlock(&m_locker);
#endif
}
//=========================================================
//
//=========================================================
int ex_atomic_add(volatile int *pt, int t) {
#ifdef EX_OS_WIN32
return (int)InterlockedExchangeAdd((long*)pt, (long)t);
#else
return __sync_add_and_fetch(pt, t);
#endif
}
int ex_atomic_inc(volatile int *pt) {
#ifdef EX_OS_WIN32
return (int)InterlockedIncrement((long*)pt);
#else
return __sync_add_and_fetch(pt, 1);
#endif
}
int ex_atomic_dec(volatile int *pt) {
#ifdef EX_OS_WIN32
return (int)InterlockedDecrement((long*)pt);
#else
return __sync_add_and_fetch(pt, -1);
#endif
}
ex_u64 ex_get_thread_id(void) {
#ifdef EX_OS_WIN32
return GetCurrentThreadId();
#else
return (ex_u64) pthread_self();
#endif
}

View File

@ -1,4 +1,4 @@
#include <ex/ex_platform.h>
#include <ex/ex_platform.h>
#include <ex/ex_util.h>
#include <ex/ex_str.h>
#include <ex/ex_log.h>

View File

@ -1,4 +1,4 @@
#include <ex/ex_winsrv.h>
#include <ex/ex_winsrv.h>
#ifdef EX_OS_WIN32
@ -44,14 +44,14 @@ ex_rv ex_winsrv_install(const ex_wstr& srv_name, const ex_wstr& disp_name, const
}
SERVICE_FAILURE_ACTIONS failure_action;
failure_action.dwResetPeriod = 0; // reset failure count to zero 的时间,单位为秒
failure_action.dwResetPeriod = 0; // reset failure count to zero 的时间,单位为秒
failure_action.lpRebootMsg = NULL; // Message to broadcast to server users before rebooting
failure_action.lpCommand = NULL; // Command line of the process for the CreateProcess function to execute in response
failure_action.cActions = 3; // action数组的个数
failure_action.cActions = 3; // action数组的个数
SC_ACTION actionarray[3];
actionarray[0].Type = SC_ACTION_RESTART; // 重新启动服务
actionarray[0].Delay = 60000; // 单位为毫秒
actionarray[0].Type = SC_ACTION_RESTART; // 重新启动服务
actionarray[0].Delay = 60000; // 单位为毫秒
actionarray[1].Type = SC_ACTION_RESTART;
actionarray[1].Delay = 60000;
actionarray[2].Type = SC_ACTION_RESTART;

View File

@ -1,151 +1,151 @@
#ifndef __TELEPORT_CONST_H__
#define __TELEPORT_CONST_H__
// 注意同步更新三个不同语言的const文件
// 本文件设定teleport各个模块之间通讯时的错误值JSON数据包括
// - WEB界面与助手
// - WEB界面与WEB后台
// - WEB后台与CORE核心服务
//=======================================================
// Urlprotocol相关
//=======================================================
#define TP_URLPROTO_APP_NAME "teleport"
//=======================================================
// 远程连接认证方式
//=======================================================
#define TP_AUTH_TYPE_NONE 0
#define TP_AUTH_TYPE_PASSWORD 1
#define TP_AUTH_TYPE_PRIVATE_KEY 2
//=======================================================
// 远程连接协议
//=======================================================
#define TP_PROTOCOL_TYPE_RDP 1
#define TP_PROTOCOL_TYPE_SSH 2
#define TP_PROTOCOL_TYPE_TELNET 3
//=======================================================
// 远程连接子协议
//=======================================================
#define TP_PROTOCOL_TYPE_RDP_DESKTOP 100
#define TP_PROTOCOL_TYPE_SSH_SHELL 200
#define TP_PROTOCOL_TYPE_SSH_SFTP 201
#define TP_PROTOCOL_TYPE_TELNET_SHELL 300
//=======================================================
// 远程主机操作系统
//=======================================================
#define TP_OS_TYPE_WINDOWS 1
#define TP_OS_TYPE_LINUX 2
//=======================================================
// 远程连接会话状态
//=======================================================
#define TP_SESS_STAT_RUNNING 0 // 会话开始了,正在连接
#define TP_SESS_STAT_END 9999 // 会话成功结束
#define TP_SESS_STAT_ERR_AUTH_DENIED 1 // 会话结束,因为认证失败
#define TP_SESS_STAT_ERR_CONNECT 2 // 会话结束,因为无法连接到远程主机
#define TP_SESS_STAT_ERR_BAD_SSH_KEY 3 // 会话结束因为无法识别SSH私钥
#define TP_SESS_STAT_ERR_INTERNAL 4 // 会话结束,因为内部错误
#define TP_SESS_STAT_ERR_UNSUPPORT_PROTOCOL 5 // 会话结束,因为协议不支持(RDP)
#define TP_SESS_STAT_ERR_BAD_PKG 6 // 会话结束,因为收到错误的报文
#define TP_SESS_STAT_ERR_RESET 7 // 会话结束因为teleport核心服务重置了
#define TP_SESS_STAT_ERR_IO 8 // 会话结束,因为网络中断
#define TP_SESS_STAT_ERR_SESSION 9 // 会话结束因为无效的会话ID
#define TP_SESS_STAT_ERR_AUTH_TYPE 10 // 会话结束,因为不被允许的认证方式
#define TP_SESS_STAT_STARTED 100 // 已经连接成功了,开始记录录像了
#define TP_SESS_STAT_ERR_START_INTERNAL 104 // 会话结束,因为内部错误
#define TP_SESS_STAT_ERR_START_BAD_PKG 106 // 会话结束,因为收到错误的报文
#define TP_SESS_STAT_ERR_START_RESET 107 // 会话结束因为teleport核心服务重置了
#define TP_SESS_STAT_ERR_START_IO 108 // 会话结束,因为网络中断
//=======================================================
// 授权标记
//=======================================================
#define TP_FLAG_ALL 0xFFFFFFFF
// 会话记录相关
#define TP_FLAG_RECORD_REPLAY 0x00000001 // 允许记录历史(录像回放)
#define TP_FLAG_RECORD_REAL_TIME 0x00000002 // 允许实时监控
// RDP相关
#define TP_FLAG_RDP_DESKTOP 0x00000001 // 允许远程桌面
#define TP_FLAG_RDP_CLIPBOARD 0x00000002 // 允许剪贴板
#define TP_FLAG_RDP_DISK 0x00000004 // 允许磁盘映射
#define TP_FLAG_RDP_APP 0x00000008 // 允许远程APP尚未实现
#define TP_FLAG_RDP_CONSOLE 0x00001000 //允许连接到管理员会话RDP的console选项
// SSH相关
#define TP_FLAG_SSH_SHELL 0x00000001 // 允许SHELL
#define TP_FLAG_SSH_SFTP 0x00000002 // 允许SFTP
#define TP_FLAG_SSH_X11 0x00000004 // 允许X11转发尚未实现
#define TP_FLAG_SSH_EXEC 0x00000008 // 允许exec执行远程命令尚未实现
#define TP_FLAG_SSH_TUNNEL 0x00000010 // allow ssh tunnel. (not impl.)
//=======================================================
// 错误值
//=======================================================
#define TPE_OK 0 // 成功
//-------------------------------------------------------
// 通用错误值
//-------------------------------------------------------
#define TPE_NEED_MORE_DATA 1 // 需要更多数据(不一定是错误)
#define TPE_NEED_LOGIN 2 // 需要登录
#define TPE_PRIVILEGE 3 // 没有操作权限
#define TPE_NOT_IMPLEMENT 7 // 功能尚未实现
#define TPE_EXISTS 8 // 目标已经存在
#define TPE_NOT_EXISTS 9 // 目标不存在
// 100~299是通用错误值
#define TPE_FAILED 100 // 内部错误
#define TPE_NETWORK 101 // 网络错误
#define TPE_DATABASE 102 // 数据库操作失败
// HTTP请求相关错误
#define TPE_HTTP_METHOD 120 // 无效的请求方法不是GET/POST等或者错误的请求方法例如需要POST却使用GET方式请求
#define TPE_HTTP_URL_ENCODE 121 // URL编码错误无法解码
//#define TPE_HTTP_URI 122 // 无效的URI
#define TPE_UNKNOWN_CMD 124 // 未知的命令
#define TPE_JSON_FORMAT 125 // 错误的JSON格式需要JSON格式数据但是却无法按JSON格式解码
#define TPE_PARAM 126 // 参数错误
#define TPE_DATA 127 // 数据错误
// #define TPE_OPENFILE_ERROR 0x1007 // 无法打开文件
// #define TPE_GETTEMPPATH_ERROR 0x1007
#define TPE_OPENFILE 300
//-------------------------------------------------------
// WEB服务专用错误值
//-------------------------------------------------------
#define TPE_CAPTCHA_EXPIRED 10000 // 验证码已过期
#define TPE_CAPTCHA_MISMATCH 10001 // 验证码错误
#define TPE_OATH_MISMATCH 10002 // 身份验证器动态验证码错误
#define TPE_SYS_MAINTENANCE 10003 // 系统维护中
#define TPE_USER_LOCKED 10100 // 用户已经被锁定(连续多次错误密码)
#define TPE_USER_DISABLED 10101 // 用户已经被禁用
#define TPE_USER_AUTH 10102 // 身份验证失败
//-------------------------------------------------------
// 助手程序专用错误值
//-------------------------------------------------------
#define TPE_NO_ASSIST 100000 // 未能检测到助手程序
#define TPE_OLD_ASSIST 100001 // 助手程序版本太低
#define TPE_START_CLIENT 100002 // 无法启动客户端程序(无法创建进程)
//-------------------------------------------------------
// 核心服务专用错误值
//-------------------------------------------------------
#define TPE_NO_CORE_SERVER 200000 // 未能检测到核心服务
#endif // __TELEPORT_CONST_H__
#ifndef __TELEPORT_CONST_H__
#define __TELEPORT_CONST_H__
// 注意同步更新三个不同语言的const文件
// 本文件设定teleport各个模块之间通讯时的错误值JSON数据包括
// - WEB界面与助手
// - WEB界面与WEB后台
// - WEB后台与CORE核心服务
//=======================================================
// Urlprotocol相关
//=======================================================
#define TP_URLPROTO_APP_NAME "teleport"
//=======================================================
// 远程连接认证方式
//=======================================================
#define TP_AUTH_TYPE_NONE 0
#define TP_AUTH_TYPE_PASSWORD 1
#define TP_AUTH_TYPE_PRIVATE_KEY 2
//=======================================================
// 远程连接协议
//=======================================================
#define TP_PROTOCOL_TYPE_RDP 1
#define TP_PROTOCOL_TYPE_SSH 2
#define TP_PROTOCOL_TYPE_TELNET 3
//=======================================================
// 远程连接子协议
//=======================================================
#define TP_PROTOCOL_TYPE_RDP_DESKTOP 100
#define TP_PROTOCOL_TYPE_SSH_SHELL 200
#define TP_PROTOCOL_TYPE_SSH_SFTP 201
#define TP_PROTOCOL_TYPE_TELNET_SHELL 300
//=======================================================
// 远程主机操作系统
//=======================================================
#define TP_OS_TYPE_WINDOWS 1
#define TP_OS_TYPE_LINUX 2
//=======================================================
// 远程连接会话状态
//=======================================================
#define TP_SESS_STAT_RUNNING 0 // 会话开始了,正在连接
#define TP_SESS_STAT_END 9999 // 会话成功结束
#define TP_SESS_STAT_ERR_AUTH_DENIED 1 // 会话结束,因为认证失败
#define TP_SESS_STAT_ERR_CONNECT 2 // 会话结束,因为无法连接到远程主机
#define TP_SESS_STAT_ERR_BAD_SSH_KEY 3 // 会话结束因为无法识别SSH私钥
#define TP_SESS_STAT_ERR_INTERNAL 4 // 会话结束,因为内部错误
#define TP_SESS_STAT_ERR_UNSUPPORT_PROTOCOL 5 // 会话结束,因为协议不支持(RDP)
#define TP_SESS_STAT_ERR_BAD_PKG 6 // 会话结束,因为收到错误的报文
#define TP_SESS_STAT_ERR_RESET 7 // 会话结束因为teleport核心服务重置了
#define TP_SESS_STAT_ERR_IO 8 // 会话结束,因为网络中断
#define TP_SESS_STAT_ERR_SESSION 9 // 会话结束因为无效的会话ID
#define TP_SESS_STAT_ERR_AUTH_TYPE 10 // 会话结束,因为不被允许的认证方式
#define TP_SESS_STAT_STARTED 100 // 已经连接成功了,开始记录录像了
#define TP_SESS_STAT_ERR_START_INTERNAL 104 // 会话结束,因为内部错误
#define TP_SESS_STAT_ERR_START_BAD_PKG 106 // 会话结束,因为收到错误的报文
#define TP_SESS_STAT_ERR_START_RESET 107 // 会话结束因为teleport核心服务重置了
#define TP_SESS_STAT_ERR_START_IO 108 // 会话结束,因为网络中断
//=======================================================
// 授权标记
//=======================================================
#define TP_FLAG_ALL 0xFFFFFFFF
// 会话记录相关
#define TP_FLAG_RECORD_REPLAY 0x00000001 // 允许记录历史(录像回放)
#define TP_FLAG_RECORD_REAL_TIME 0x00000002 // 允许实时监控
// RDP相关
#define TP_FLAG_RDP_DESKTOP 0x00000001 // 允许远程桌面
#define TP_FLAG_RDP_CLIPBOARD 0x00000002 // 允许剪贴板
#define TP_FLAG_RDP_DISK 0x00000004 // 允许磁盘映射
#define TP_FLAG_RDP_APP 0x00000008 // 允许远程APP尚未实现
#define TP_FLAG_RDP_CONSOLE 0x00001000 //允许连接到管理员会话RDP的console选项
// SSH相关
#define TP_FLAG_SSH_SHELL 0x00000001 // 允许SHELL
#define TP_FLAG_SSH_SFTP 0x00000002 // 允许SFTP
#define TP_FLAG_SSH_X11 0x00000004 // 允许X11转发尚未实现
#define TP_FLAG_SSH_EXEC 0x00000008 // 允许exec执行远程命令尚未实现
#define TP_FLAG_SSH_TUNNEL 0x00000010 // allow ssh tunnel. (not impl.)
//=======================================================
// 错误值
//=======================================================
#define TPE_OK 0 // 成功
//-------------------------------------------------------
// 通用错误值
//-------------------------------------------------------
#define TPE_NEED_MORE_DATA 1 // 需要更多数据(不一定是错误)
#define TPE_NEED_LOGIN 2 // 需要登录
#define TPE_PRIVILEGE 3 // 没有操作权限
#define TPE_NOT_IMPLEMENT 7 // 功能尚未实现
#define TPE_EXISTS 8 // 目标已经存在
#define TPE_NOT_EXISTS 9 // 目标不存在
// 100~299是通用错误值
#define TPE_FAILED 100 // 内部错误
#define TPE_NETWORK 101 // 网络错误
#define TPE_DATABASE 102 // 数据库操作失败
// HTTP请求相关错误
#define TPE_HTTP_METHOD 120 // 无效的请求方法不是GET/POST等或者错误的请求方法例如需要POST却使用GET方式请求
#define TPE_HTTP_URL_ENCODE 121 // URL编码错误无法解码
//#define TPE_HTTP_URI 122 // 无效的URI
#define TPE_UNKNOWN_CMD 124 // 未知的命令
#define TPE_JSON_FORMAT 125 // 错误的JSON格式需要JSON格式数据但是却无法按JSON格式解码
#define TPE_PARAM 126 // 参数错误
#define TPE_DATA 127 // 数据错误
// #define TPE_OPENFILE_ERROR 0x1007 // 无法打开文件
// #define TPE_GETTEMPPATH_ERROR 0x1007
#define TPE_OPENFILE 300
//-------------------------------------------------------
// WEB服务专用错误值
//-------------------------------------------------------
#define TPE_CAPTCHA_EXPIRED 10000 // 验证码已过期
#define TPE_CAPTCHA_MISMATCH 10001 // 验证码错误
#define TPE_OATH_MISMATCH 10002 // 身份验证器动态验证码错误
#define TPE_SYS_MAINTENANCE 10003 // 系统维护中
#define TPE_USER_LOCKED 10100 // 用户已经被锁定(连续多次错误密码)
#define TPE_USER_DISABLED 10101 // 用户已经被禁用
#define TPE_USER_AUTH 10102 // 身份验证失败
//-------------------------------------------------------
// 助手程序专用错误值
//-------------------------------------------------------
#define TPE_NO_ASSIST 100000 // 未能检测到助手程序
#define TPE_OLD_ASSIST 100001 // 助手程序版本太低
#define TPE_START_CLIENT 100002 // 无法启动客户端程序(无法创建进程)
//-------------------------------------------------------
// 核心服务专用错误值
//-------------------------------------------------------
#define TPE_NO_CORE_SERVER 200000 // 未能检测到核心服务
#endif // __TELEPORT_CONST_H__

View File

@ -22,6 +22,8 @@ wget = C:\Program Files (x86)\wget\wget.exe
# if not set msbuild path, default to get it by register.
#msbuild = C:\Program Files (x86)\MSBuild\14.0\bin\MSBuild.exe
# need qt to build tp-player.
qt = C:\Qt\Qt5.12.0\5.12.0\msvc2017
# ============================================
# for Linux and macOS

Binary file not shown.

22
external/version.ini vendored
View File

@ -1,8 +1,16 @@
[external_ver]
openssl = 1.0.2p,1000210f
libuv = 1.28.0
mbedtls = 2.12.0
libssh = 0.9.0
jsoncpp = 0.10.6
mongoose = 6.12
; https://github.com/openssl/openssl/releases
; http://slproweb.com/download/Win32OpenSSL-1_1_1d.exe
openssl = 1.1.1d,1010104f
; https://github.com/libuv/libuv/releases
libuv = 1.33.1
; https://github.com/ARMmbed/mbedtls/releases
mbedtls = 2.16.3
; https://github.com/open-source-parsers/jsoncpp/releases
jsoncpp = 1.9.2
; https://github.com/cesanta/mongoose/releases
mongoose = 6.16
; https://www.zlib.net/zlib1211.zip
zlib = 1.2.11,1211
; https://git.libssh.org/projects/libssh.git/
libssh = 0.9.2

BIN
resource/icon-tp-player.psd Normal file

Binary file not shown.

View File

@ -1,48 +1,48 @@
#include "base_env.h"
TppEnvBase::TppEnvBase()
{}
TppEnvBase::~TppEnvBase()
{}
bool TppEnvBase::init(TPP_INIT_ARGS* args)
{
if (NULL == args)
{
EXLOGE("invalid init args(1).\n");
return false;
}
EXLOG_USE_LOGGER(args->logger);
exec_path = args->exec_path;
etc_path = args->etc_path;
replay_path = args->replay_path;
get_connect_info = args->func_get_connect_info;
free_connect_info = args->func_free_connect_info;
session_begin = args->func_session_begin;
session_update = args->func_session_update;
session_end = args->func_session_end;
if (NULL == get_connect_info || NULL == free_connect_info || NULL == session_begin || NULL == session_update || NULL == session_end)
{
EXLOGE("invalid init args(2).\n");
return false;
}
if (NULL == args->cfg)
{
EXLOGE("invalid init args(3).\n");
return false;
}
if (!_on_init(args))
{
EXLOGE("invalid init args(4).\n");
return false;
}
return true;
}
#include "base_env.h"
TppEnvBase::TppEnvBase()
{}
TppEnvBase::~TppEnvBase()
{}
bool TppEnvBase::init(TPP_INIT_ARGS* args)
{
if (NULL == args)
{
EXLOGE("invalid init args(1).\n");
return false;
}
EXLOG_USE_LOGGER(args->logger);
exec_path = args->exec_path;
etc_path = args->etc_path;
replay_path = args->replay_path;
get_connect_info = args->func_get_connect_info;
free_connect_info = args->func_free_connect_info;
session_begin = args->func_session_begin;
session_update = args->func_session_update;
session_end = args->func_session_end;
if (NULL == get_connect_info || NULL == free_connect_info || NULL == session_begin || NULL == session_update || NULL == session_end)
{
EXLOGE("invalid init args(2).\n");
return false;
}
if (NULL == args->cfg)
{
EXLOGE("invalid init args(3).\n");
return false;
}
if (!_on_init(args))
{
EXLOGE("invalid init args(4).\n");
return false;
}
return true;
}

View File

@ -1,29 +1,29 @@
#ifndef __TS_BASE_ENV_H__
#define __TS_BASE_ENV_H__
#include "protocol_interface.h"
class TppEnvBase
{
public:
TppEnvBase();
virtual ~TppEnvBase();
bool init(TPP_INIT_ARGS* args);
public:
ex_wstr exec_path;
ex_wstr etc_path; // 配置文件、SSH服务器的私钥文件的存放路径
ex_wstr replay_path;
TPP_GET_CONNNECT_INFO_FUNC get_connect_info;
TPP_FREE_CONNECT_INFO_FUNC free_connect_info;
TPP_SESSION_BEGIN_FUNC session_begin;
TPP_SESSION_UPDATE_FUNC session_update;
TPP_SESSION_END_FUNC session_end;
protected:
virtual bool _on_init(TPP_INIT_ARGS* args) = 0;
};
#endif // __TS_BASE_ENV_H__
#ifndef __TS_BASE_ENV_H__
#define __TS_BASE_ENV_H__
#include "protocol_interface.h"
class TppEnvBase
{
public:
TppEnvBase();
virtual ~TppEnvBase();
bool init(TPP_INIT_ARGS* args);
public:
ex_wstr exec_path;
ex_wstr etc_path; // 配置文件、SSH服务器的私钥文件的存放路径
ex_wstr replay_path;
TPP_GET_CONNNECT_INFO_FUNC get_connect_info;
TPP_FREE_CONNECT_INFO_FUNC free_connect_info;
TPP_SESSION_BEGIN_FUNC session_begin;
TPP_SESSION_UPDATE_FUNC session_update;
TPP_SESSION_END_FUNC session_end;
protected:
virtual bool _on_init(TPP_INIT_ARGS* args) = 0;
};
#endif // __TS_BASE_ENV_H__

View File

@ -1,4 +1,4 @@
#include <memory>
#include <memory>
#include "base_record.h"

View File

@ -1,101 +1,109 @@
#ifndef __TS_BASE_RECORD_H__
#define __TS_BASE_RECORD_H__
#include "base_env.h"
#include "ts_membuf.h"
#include "protocol_interface.h"
#include <ex.h>
#define MAX_SIZE_PER_FILE 4194304 // 4M = 1024*1024*4
#pragma pack(push,1)
/*
*
*
*
* 4M5
*
*/
// 录像文件头(随着录像数据写入,会改变的部分)
typedef struct TS_RECORD_HEADER_INFO
{
ex_u32 magic; // "TPPR" 标志 TelePort Protocol Record
ex_u16 ver; // 录像文件版本目前为3
ex_u32 packages; // 总包数
ex_u32 time_ms; // 总耗时(毫秒)
//ex_u32 file_size; // 数据文件大小
}TS_RECORD_HEADER_INFO;
#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO)
// 录像文件头(固定不变部分)
typedef struct TS_RECORD_HEADER_BASIC
{
ex_u16 protocol_type; // 协议1=RDP, 2=SSH, 3=Telnet
ex_u16 protocol_sub_type; // 子协议100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet
ex_u64 timestamp; // 本次录像的起始时间UTC时间戳
ex_u16 width; // 初始屏幕尺寸:宽
ex_u16 height; // 初始屏幕尺寸:高
char user_username[64]; // teleport账号
char acc_username[64]; // 远程主机用户名
char host_ip[40]; // 远程主机IP
char conn_ip[40]; // 远程主机IP
ex_u16 conn_port; // 远程主机端口
char client_ip[40]; // 客户端IP
// RDP专有
ex_u8 rdp_security; // 0 = RDP, 1 = TLS
ex_u8 _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - 1 - ts_record_header_info_size];
}TS_RECORD_HEADER_BASIC;
#define ts_record_header_basic_size sizeof(TS_RECORD_HEADER_BASIC)
typedef struct TS_RECORD_HEADER
{
TS_RECORD_HEADER_INFO info;
TS_RECORD_HEADER_BASIC basic;
}TS_RECORD_HEADER;
// header部分header-info + header-basic = 512B
#define ts_record_header_size sizeof(TS_RECORD_HEADER)
// 一个数据包的头
typedef struct TS_RECORD_PKG
{
ex_u8 type; // 包的数据类型
ex_u32 size; // 这个包的总大小(不含包头)
ex_u32 time_ms; // 这个包距起始时间的时间差毫秒意味着一个连接不能持续超过49天
ex_u8 _reserve[3]; // 保留
}TS_RECORD_PKG;
#pragma pack(pop)
class TppRecBase
{
public:
TppRecBase();
virtual ~TppRecBase();
bool begin(const wchar_t* base_path, const wchar_t* base_fname, int record_id, const TPP_CONNECT_INFO* info);
bool end();
protected:
virtual bool _on_begin(const TPP_CONNECT_INFO* info) = 0;
virtual bool _on_end() = 0;
protected:
ex_wstr m_base_path; // 录像文件基础路径,例如 /usr/local/teleport/data/replay/ssh/123数字编号是内部附加的作为本次会话录像文件的目录名称
ex_wstr m_base_fname; // 录像文件的文件名,不含扩展名部分,内部会以此为基础合成文件全名,并将录像文件存放在 m_base_path 指向的目录中
ex_u64 m_start_time;
MemBuffer m_cache;
};
#endif // __TS_BASE_RECORD_H__
#ifndef __TS_BASE_RECORD_H__
#define __TS_BASE_RECORD_H__
#include "base_env.h"
#include "ts_membuf.h"
#include "protocol_interface.h"
#include <ex.h>
#define MAX_CACHE_SIZE 1048576 // 1M = 1024*1024*1
#define MAX_SIZE_PER_FILE 4194304 // 4M = 1024*1024*4
// for test.
// #define MAX_CACHE_SIZE 524288 // 512KB = 512*1024
// #define MAX_SIZE_PER_FILE 1048576 // 1M = 1024*1024*1
#pragma pack(push,1)
/*
*
*
*
* *.tpr512
* *.tpdn tp-rdp-1.tpdtp-rdp-2.tpd4MB
* *.tpkRDP
* *-cmd.txtsshSSH
* 4M5
*
*/
#define TS_TPPR_TYPE_UNKNOWN 0x0000
#define TS_TPPR_TYPE_SSH 0x0001
#define TS_TPPR_TYPE_RDP 0x0101
// 录像文件头(随着录像数据写入,会改变的部分)
typedef struct TS_RECORD_HEADER_INFO {
ex_u32 magic; // "TPPR" 标志 TelePort Protocol Record
ex_u16 ver; // 录像文件版本v3.5.0开始为4
ex_u16 type; // 录像内容SSH or RDP
// ex_u32 packages; // 总包数
ex_u32 time_ms; // 总耗时(毫秒)
ex_u32 dat_file_count; // 数据文件数量
ex_u8 _reserve[64-4-2-2-4-4];
}TS_RECORD_HEADER_INFO;
#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO)
// 录像文件头(固定不变部分)
typedef struct TS_RECORD_HEADER_BASIC {
ex_u16 protocol_type; // 协议1=RDP, 2=SSH, 3=Telnet
ex_u16 protocol_sub_type; // 子协议100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet
ex_u64 timestamp; // 本次录像的起始时间UTC时间戳
ex_u16 width; // 初始屏幕尺寸:宽
ex_u16 height; // 初始屏幕尺寸:高
char user_username[64]; // teleport账号
char acc_username[64]; // 远程主机用户名
char host_ip[40]; // 远程主机IP
char conn_ip[40]; // 远程主机IP
ex_u16 conn_port; // 远程主机端口
char client_ip[40]; // 客户端IP
// // RDP专有 - v3.5.0废弃并移除
// ex_u8 rdp_security; // 0 = RDP, 1 = TLS
ex_u8 _reserve[512 - ts_record_header_info_size - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40];
}TS_RECORD_HEADER_BASIC;
#define ts_record_header_basic_size sizeof(TS_RECORD_HEADER_BASIC)
typedef struct TS_RECORD_HEADER {
TS_RECORD_HEADER_INFO info;
TS_RECORD_HEADER_BASIC basic;
}TS_RECORD_HEADER;
// header部分header-info + header-basic = 512B
#define ts_record_header_size sizeof(TS_RECORD_HEADER)
// 一个数据包的头
typedef struct TS_RECORD_PKG {
ex_u8 type; // 包的数据类型
ex_u8 _reserve[3]; // 保留
ex_u32 size; // 这个包的总大小(不含包头)
ex_u32 time_ms; // 这个包距起始时间的时间差毫秒意味着一个连接不能持续超过49天
//ex_u32 index; // 这个包的序号最后一个包的序号与TS_RECORD_HEADER_INFO::packages数量匹配
}TS_RECORD_PKG;
#pragma pack(pop)
class TppRecBase {
public:
TppRecBase();
virtual ~TppRecBase();
bool begin(const wchar_t* base_path, const wchar_t* base_fname, int record_id, const TPP_CONNECT_INFO* info);
bool end();
protected:
virtual bool _on_begin(const TPP_CONNECT_INFO* info) = 0;
virtual bool _on_end() = 0;
protected:
ex_wstr m_base_path; // 录像文件基础路径,例如 /usr/local/teleport/data/replay/ssh/123数字编号是内部附加的作为本次会话录像文件的目录名称
ex_wstr m_base_fname; // 录像文件的文件名,不含扩展名部分,内部会以此为基础合成文件全名,并将录像文件存放在 m_base_path 指向的目录中
ex_u64 m_start_time;
MemBuffer m_cache;
};
#endif // __TS_BASE_RECORD_H__

Some files were not shown because too many files have changed in this diff Show More