From e5610b17a2510e1673766314477052181eff2104 Mon Sep 17 00:00:00 2001 From: Apex Liu Date: Mon, 16 Sep 2019 13:53:27 +0800 Subject: [PATCH] .temp. --- client/tp-player/dlgmessage.cpp | 21 ++++ client/tp-player/dlgmessage.h | 24 ++++ client/tp-player/dlgmessage.ui | 32 +++++ client/tp-player/main.cpp | 42 ++++++- client/tp-player/mainwindow.cpp | 131 +++++++++++++------- client/tp-player/mainwindow.h | 13 +- client/tp-player/record_format.h | 18 +-- client/tp-player/thr_download.cpp | 34 +++++ client/tp-player/thr_download.h | 47 +++++++ client/tp-player/thr_play.cpp | 184 ++++++++++++++++++++++++---- client/tp-player/thr_play.h | 14 ++- client/tp-player/tp-player.pro | 63 +++++----- client/tp-player/update_data.cpp | 4 +- client/tp-player/update_data.h | 21 +++- external/version.ini | 2 +- server/tp_core/common/base_record.h | 36 +++--- 16 files changed, 546 insertions(+), 140 deletions(-) create mode 100644 client/tp-player/dlgmessage.cpp create mode 100644 client/tp-player/dlgmessage.h create mode 100644 client/tp-player/dlgmessage.ui create mode 100644 client/tp-player/thr_download.cpp create mode 100644 client/tp-player/thr_download.h diff --git a/client/tp-player/dlgmessage.cpp b/client/tp-player/dlgmessage.cpp new file mode 100644 index 0000000..5df1bb5 --- /dev/null +++ b/client/tp-player/dlgmessage.cpp @@ -0,0 +1,21 @@ +#include "dlgmessage.h" +#include "ui_dlgmessage.h" + +DlgMessage::DlgMessage(QWidget *parent) : + QDialog(parent), + ui(new Ui::DlgMessage) +{ + ui->setupUi(this); +} + +DlgMessage::~DlgMessage() +{ + delete ui; +} + +void DlgMessage::set_text(const QString& text) { + // TODO: 根据文字长度,父窗口宽度,调节对话框宽度,最大不超过父窗口宽度的 2/3。 + // 调节label的宽度和高度,并调节对话框高度,最后将对话框调整到父窗口居中的位置。 + + ui->label->setText(text); +} diff --git a/client/tp-player/dlgmessage.h b/client/tp-player/dlgmessage.h new file mode 100644 index 0000000..8dbbe7d --- /dev/null +++ b/client/tp-player/dlgmessage.h @@ -0,0 +1,24 @@ +#ifndef DLGMESSAGE_H +#define DLGMESSAGE_H + +#include + +namespace Ui { +class DlgMessage; +} + +class DlgMessage : public QDialog +{ + Q_OBJECT + +public: + explicit DlgMessage(QWidget *parent = nullptr); + ~DlgMessage(); + + void set_text(const QString& text); + +private: + Ui::DlgMessage *ui; +}; + +#endif // DLGMESSAGE_H diff --git a/client/tp-player/dlgmessage.ui b/client/tp-player/dlgmessage.ui new file mode 100644 index 0000000..71a5631 --- /dev/null +++ b/client/tp-player/dlgmessage.ui @@ -0,0 +1,32 @@ + + + DlgMessage + + + + 0 + 0 + 400 + 120 + + + + + + + + + 10 + 10 + 59 + 16 + + + + + + + + + + diff --git a/client/tp-player/main.cpp b/client/tp-player/main.cpp index b7b993e..bbf4c1a 100644 --- a/client/tp-player/main.cpp +++ b/client/tp-player/main.cpp @@ -1,5 +1,8 @@ -#include "mainwindow.h" +#include "mainwindow.h" #include +#include +#include +#include // 编译出来的可执行程序复制到单独目录,然后执行 windeployqt 应用程序文件名 // 即可自动将依赖的动态库等复制到此目录中。有些文件是多余的,可以酌情删除。 @@ -11,8 +14,41 @@ int main(int argc, char *argv[]) //#endif QApplication a(argc, argv); - MainWindow w; - w.show(); + QGuiApplication::setApplicationDisplayName("TP-Player"); + + 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(), + //"

" + parser.errorText() + "

"
+                             "

" + parser.errorText() + "

"
+                             + parser.helpText() + "
"); + return 1; + } + + if(parser.isSet(opt_help)) { + QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(), + "
"
+                             + parser.helpText()
+                             + "\n\n"
+                             + "RESOURCE could be:\n"
+                             + "    teleport record file (.tpr).\n"
+                             + "    a directory contains .tpr file.\n"
+                             + "    an URL for download teleport record file."
+                             + "
"); + return 2; + } + + const QStringList args = parser.positionalArguments(); + QString resource = args.at(0); + qDebug() << resource; + + MainWindow w; + w.set_resource(resource); + w.show(); return a.exec(); } diff --git a/client/tp-player/mainwindow.cpp b/client/tp-player/mainwindow.cpp index 0dbd413..3b0b323 100644 --- a/client/tp-player/mainwindow.cpp +++ b/client/tp-player/mainwindow.cpp @@ -7,6 +7,8 @@ #include #include #include +#include +#include bool rdpimg2QImage(QImage& out, int w, int h, int bitsPerPixel, bool isCompressed, uint8_t* dat, uint32_t len) { switch(bitsPerPixel) { @@ -67,7 +69,7 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { - m_shown = false; + //m_shown = false; m_show_default = true; m_bar_shown = false; m_bar_fade_in = false; @@ -78,6 +80,8 @@ MainWindow::MainWindow(QWidget *parent) : m_thr_play = nullptr; m_play_state = PLAY_STATE_UNKNOWN; + m_msg_box = nullptr; + ui->setupUi(this); ui->centralWidget->setMouseTracking(true); @@ -103,28 +107,46 @@ MainWindow::MainWindow(QWidget *parent) : } // connect(&m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(_do_update_data(update_data*))); + 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(); - m_thr_play->wait(); + //m_thr_play->wait(); + //qDebug() << "play thread stoped."; disconnect(m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(_do_update_data(update_data*))); delete m_thr_play; m_thr_play = nullptr; } + + if(m_msg_box) { + delete m_msg_box; + } + delete ui; } +void MainWindow::set_resource(const QString &res) { + m_res = res; +} + +void MainWindow::_do_first_run() { + _start_play_thread(); +} + void MainWindow::_start_play_thread() { if(m_thr_play) { m_thr_play->stop(); - m_thr_play->wait(); + //m_thr_play->wait(); disconnect(m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(_do_update_data(update_data*))); @@ -132,7 +154,7 @@ void MainWindow::_start_play_thread() { m_thr_play = nullptr; } - m_thr_play = new ThreadPlay; + m_thr_play = new ThreadPlay(m_res); connect(m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(_do_update_data(update_data*))); m_thr_play->speed(m_bar.get_speed()); m_thr_play->start(); @@ -169,11 +191,11 @@ void MainWindow::paintEvent(QPaintEvent *e) } } - if(!m_shown) { - m_shown = true; - //m_thr_play.start(); - _start_play_thread(); - } +// if(!m_shown) { +// m_shown = true; +// //m_thr_play.start(); +// _start_play_thread(); +// } } void MainWindow::pause() { @@ -200,6 +222,9 @@ void MainWindow::_do_update_data(update_data* dat) { UpdateDataHelper data_helper(dat); if(dat->data_type() == TYPE_DATA) { + if(m_msg_box) { + m_msg_box->hide(); + } if(dat->data_len() <= sizeof(TS_RECORD_PKG)) { qDebug() << "invalid record package(1)."; @@ -250,13 +275,35 @@ void MainWindow::_do_update_data(update_data* dat) { return; } - if(dat->data_type() == TYPE_TIMER) { - m_bar.update_passed_time(dat->passed_ms()); + else if(dat->data_type() == TYPE_PLAYED_MS) { + m_bar.update_passed_time(dat->played_ms()); + return; + } + + else if(dat->data_type() == TYPE_MESSAGE) { + //QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(), dat->message()); + if(!m_msg_box) { + m_msg_box = new DlgMessage(this); + // 无窗口标题栏,无边框 + m_msg_box->setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::ToolTip | Qt::FramelessWindowHint); + // 设置成非模态 + m_msg_box->setModal(false); + } + + m_msg_box->set_text(dat->message()); + // 显示对话框 + m_msg_box->show(); + return; + } + + else if(dat->data_type() == TYPE_ERROR) { + QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(), dat->message()); + QApplication::instance()->exit(0); return; } // 这是播放开始时收到的第一个数据包 - if(dat->data_type() == TYPE_HEADER_INFO) { + else if(dat->data_type() == TYPE_HEADER_INFO) { if(dat->data_len() != sizeof(TS_RECORD_HEADER)) { qDebug() << "invalid record header."; return; @@ -264,41 +311,39 @@ void MainWindow::_do_update_data(update_data* dat) { memcpy(&m_rec_hdr, dat->data_buf(), sizeof(TS_RECORD_HEADER)); qDebug() << "resize (" << m_rec_hdr.basic.width << "," << m_rec_hdr.basic.height << ")"; - if(m_rec_hdr.basic.width > 0 && m_rec_hdr.basic.height > 0) { - if(m_canvas.width() != m_rec_hdr.basic.width && m_canvas.height() != m_rec_hdr.basic.height) { - m_canvas = QPixmap(m_rec_hdr.basic.width, m_rec_hdr.basic.height); + if(m_canvas.width() != m_rec_hdr.basic.width && m_canvas.height() != m_rec_hdr.basic.height) { + m_canvas = QPixmap(m_rec_hdr.basic.width, m_rec_hdr.basic.height); - //m_win_board_w = frameGeometry().width() - geometry().width(); - //m_win_board_h = frameGeometry().height() - geometry().height(); + //m_win_board_w = frameGeometry().width() - geometry().width(); + //m_win_board_h = frameGeometry().height() - geometry().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((desktop->width() - this->width())/2, (desktop->height() - this->height())/2); - move(10, (desktop->height() - m_rec_hdr.basic.height)/2); + QDesktopWidget *desktop = QApplication::desktop(); // =qApp->desktop();也可以 + qDebug("desktop w:%d,h:%d, this w:%d,h:%d", desktop->width(), desktop->height(), width(), height()); + //move((desktop->width() - this->width())/2, (desktop->height() - this->height())/2); + move(10, (desktop->height() - m_rec_hdr.basic.height)/2); - //setFixedSize(m_rec_hdr.basic.width + m_win_board_w, m_rec_hdr.basic.height + m_win_board_h); - //resize(m_rec_hdr.basic.width + m_win_board_w, m_rec_hdr.basic.height + m_win_board_h); - //resize(m_rec_hdr.basic.width, m_rec_hdr.basic.height); - 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); + //setFixedSize(m_rec_hdr.basic.width + m_win_board_w, m_rec_hdr.basic.height + m_win_board_h); + //resize(m_rec_hdr.basic.width + m_win_board_w, m_rec_hdr.basic.height + m_win_board_h); + //resize(m_rec_hdr.basic.width, m_rec_hdr.basic.height); + 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.sprintf("[%s] %s@%s [Teleport-RDP录像回放]", m_rec_hdr.basic.acc_username, m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip); @@ -311,7 +356,7 @@ void MainWindow::_do_update_data(update_data* dat) { } - if(dat->data_type() == TYPE_END) { + else if(dat->data_type() == TYPE_END) { m_bar.end(); m_play_state = PLAY_STATE_STOP; return; @@ -383,6 +428,8 @@ void MainWindow::mouseMoveEvent(QMouseEvent *e) { } void MainWindow::mousePressEvent(QMouseEvent *e) { +// QApplication::instance()->exit(0); +// return; if(!m_show_default) { QRect rc = m_bar.rc(); if(rc.contains(e->pos())) { diff --git a/client/tp-player/mainwindow.h b/client/tp-player/mainwindow.h index 03aac59..1e14620 100644 --- a/client/tp-player/mainwindow.h +++ b/client/tp-player/mainwindow.h @@ -2,11 +2,13 @@ #define MAINWINDOW_H #include +#include #include #include "bar.h" #include "thr_play.h" #include "update_data.h" #include "record_format.h" +#include "dlgmessage.h" #define PLAY_STATE_UNKNOWN 0 #define PLAY_STATE_RUNNING 1 @@ -25,6 +27,8 @@ public: explicit MainWindow(QWidget *parent = nullptr); ~MainWindow(); + void set_resource(const QString& res); + void pause(); void resume(); void restart(); @@ -38,17 +42,20 @@ private: void _start_play_thread(); private slots: + void _do_first_run(); // 默认界面加载完成后,开始播放操作(可能会进行数据下载) void _do_update_data(update_data*); void _do_bar_fade(); void _do_bar_delay_hide(); private: Ui::MainWindow *ui; - bool m_shown; + + //bool m_shown; bool m_show_default; bool m_bar_shown; QPixmap m_default_bg; + QString m_res; ThreadPlay* m_thr_play; QPixmap m_canvas; @@ -60,6 +67,7 @@ private: 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; @@ -67,6 +75,9 @@ private: qreal m_bar_opacity; int m_play_state; + + //QMessageBox* m_msg_box; + DlgMessage* m_msg_box; }; #endif // MAINWINDOW_H diff --git a/client/tp-player/record_format.h b/client/tp-player/record_format.h index 2a09635..cbb122c 100644 --- a/client/tp-player/record_format.h +++ b/client/tp-player/record_format.h @@ -3,13 +3,6 @@ #include - -#define TYPE_HEADER_INFO 0 -#define TYPE_DATA 1 -#define TYPE_TIMER 2 -#define TYPE_END 3 - - #define TS_RECORD_TYPE_RDP_POINTER 0x12 // 鼠标坐标位置改变,用于绘制虚拟鼠标 #define TS_RECORD_TYPE_RDP_IMAGE 0x13 // 服务端返回的图像,用于展示 @@ -22,11 +15,12 @@ // 录像文件头(随着录像数据写入,会改变的部分) typedef struct TS_RECORD_HEADER_INFO { - uint32_t magic; // "TPPR" 标志 TelePort Protocol Record - uint16_t ver; // 录像文件版本,目前为3 - uint32_t packages; // 总包数 - uint32_t time_ms; // 总耗时(毫秒) - //uint32_t file_size; // 数据文件大小 + uint32_t magic; // "TPPR" 标志 TelePort Protocol Record + uint16_t ver; // 录像文件版本,从3.5.0开始,为4 + uint32_t packages; // 总包数 + uint32_t time_ms; // 总耗时(毫秒) + uint32_t dat_file_count; // 数据文件数量 + uint8_t _reserve[64-4-2-4-4-4]; }TS_RECORD_HEADER_INFO; #define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO) diff --git a/client/tp-player/thr_download.cpp b/client/tp-player/thr_download.cpp new file mode 100644 index 0000000..c338932 --- /dev/null +++ b/client/tp-player/thr_download.cpp @@ -0,0 +1,34 @@ +#include "thr_download.h" +#include + +ThreadDownload::ThreadDownload(const QString& url) +{ + m_url = url; + m_need_stop = false; +} + +void ThreadDownload::stop() { + if(!isRunning()) + return; + m_need_stop = true; + wait(); + qDebug() << "download thread end."; +} + +bool ThreadDownload::prepare(QString& path_base, QString& msg) { + path_base = m_path_base; + return true; +} + + +void ThreadDownload::run() { + for(int i = 0; i < 500; i++) { + if(m_need_stop) + break; + msleep(100); + + if(i == 50) { + m_path_base = "/Users/apex/Desktop/tp-testdata/"; + } + } +} diff --git a/client/tp-player/thr_download.h b/client/tp-player/thr_download.h new file mode 100644 index 0000000..88be069 --- /dev/null +++ b/client/tp-player/thr_download.h @@ -0,0 +1,47 @@ +#ifndef THREADDOWNLOAD_H +#define THREADDOWNLOAD_H + +#include + +/* +为支持“边下载,边播放”、“可拖动进度条”等功能,录像数据会分为多个文件存放,目前每个文件约4MB。 +例如: + tp-rdp.tpr + tp-rdp.tpk (关键帧信息文件,v3.5.0开始引入) + 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, ... +这样,下次需要下载指定文件时,如果发现对应的临时文件存在,可以根据已下载字节数,继续下载。 +*/ + + +class ThreadDownload : public QThread +{ +public: + ThreadDownload(const QString& url); + + virtual void run(); + void stop(); + + // 下载 .tpr 和 .tpf 文件,出错返回false,正在下载或已经下载完成则返回true. + bool prepare(QString& path_base, QString& msg); + +private: + bool m_need_stop; + QString m_url; + + QString m_path_base; +}; + +#endif // THREADDOWNLOAD_H diff --git a/client/tp-player/thr_play.cpp b/client/tp-player/thr_play.cpp index c40e7c6..8aaa311 100644 --- a/client/tp-player/thr_play.cpp +++ b/client/tp-player/thr_play.cpp @@ -3,27 +3,136 @@ #include #include #include +#include #include "thr_play.h" #include "record_format.h" -ThreadPlay::ThreadPlay() +ThreadPlay::ThreadPlay(const QString& res) { m_need_stop = false; m_need_pause = false; m_speed = 2; + m_res = res; + m_thr_download = nullptr; +} + +ThreadPlay::~ThreadPlay() { + stop(); } void ThreadPlay::stop() { + if(!isRunning()) + return; + + // warning: never call stop() inside thread::run() loop. + m_need_stop = true; + wait(); + qDebug() << "play-thread end."; + + if(m_thr_download) { + m_thr_download->stop(); + //m_thr_download->wait(); + delete m_thr_download; + m_thr_download = nullptr; + } } +void ThreadPlay::_notify_message(const QString& msg) { + update_data* _msg = new update_data(TYPE_MESSAGE); + _msg->message(msg); + emit signal_update_data(_msg); +} + +void ThreadPlay::_notify_error(const QString& err_msg) { + update_data* _err = new update_data(TYPE_ERROR); + _err->message(err_msg); + emit signal_update_data(_err); +} + + void ThreadPlay::run() { - sleep(1); +//#ifdef __APPLE__ +// QString currentPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); +// currentPath += "/tp-testdata/"; +//#else +// QString currentPath = QCoreApplication::applicationDirPath() + "/testdata/"; +//#endif + // /Users/apex/Library/Preferences/tp-player + //qDebug() << "appdata:" << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); + + // /private/var/folders/_3/zggrxjdx1lxcdqnfsbgpcwzh0000gn/T + //qDebug() << "tmp:" << QStandardPaths::writableLocation(QStandardPaths::TempLocation); + + // base of data path (include the .tpr file) + QString path_base; + + QString _tmp_res = m_res.toLower(); + if(_tmp_res.startsWith("http")) { + qDebug() << "DOWNLOAD"; + m_need_download = true; + +// path_base = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); +// path_base += "/tprdp/"; + + _notify_message("正在缓存录像数据,请稍候..."); + + m_thr_download = new ThreadDownload(m_res); + m_thr_download->start(); + + QString msg; + for(;;) { + msleep(500); + + if(m_need_stop) { +// m_thr_download->stop(); +// m_thr_download->wait(); +// delete m_thr_download; +// m_thr_download = nullptr; + + return; + } + + if(!m_thr_download->prepare(path_base, msg)) { + msg.sprintf("指定的文件或目录不存在!\n\n%s", _tmp_res.toStdString().c_str()); + _notify_error(msg); + return; + } + + if(path_base.length()) + break; + } + } + else { + { + QFileInfo fi(m_res); + if(fi.isSymLink()) + _tmp_res = fi.symLinkTarget(); + else + _tmp_res = m_res; + } + + QFileInfo fi(_tmp_res); + if(!fi.exists()) { + QString msg; + msg.sprintf("指定的文件或目录不存在!\n\n%s", _tmp_res.toStdString().c_str()); + _notify_error(msg); + return; + } + + if(fi.isFile()) { + path_base = fi.path(); + } + else if(fi.isDir()) { + path_base = m_res; + } + + path_base += "/"; + } - QString currentPath = QCoreApplication::applicationDirPath() + "/testdata/"; qint64 read_len = 0; uint32_t total_pkg = 0; @@ -31,27 +140,47 @@ void ThreadPlay::run() { // 加载录像基本信息数据 - QString hdr_filename(currentPath); - hdr_filename += "tp-rdp.tpr"; + QString tpr_filename(path_base); + tpr_filename += "tp-rdp.tpr"; - QFile f_hdr(hdr_filename); + QFile f_hdr(tpr_filename); if(!f_hdr.open(QFile::ReadOnly)) { - qDebug() << "Can not open " << hdr_filename << " for read."; + qDebug() << "Can not open " << tpr_filename << " for read."; + QString msg; + msg.sprintf("无法打开录像信息文件!\n\n%s", tpr_filename.toStdString().c_str()); + _notify_error(msg); return; } else { - update_data* dat = new update_data; - dat->data_type(TYPE_HEADER_INFO); + update_data* dat = new update_data(TYPE_HEADER_INFO); dat->alloc_data(sizeof(TS_RECORD_HEADER)); read_len = f_hdr.read((char*)(dat->data_buf()), dat->data_len()); if(read_len != sizeof(TS_RECORD_HEADER)) { delete dat; qDebug() << "invaid .tpr file."; + QString msg; + msg.sprintf("错误的录像信息文件!\n\n%s", tpr_filename.toStdString().c_str()); + _notify_error(msg); return; } TS_RECORD_HEADER* hdr = (TS_RECORD_HEADER*)dat->data_buf(); + + if(hdr->info.ver != 4) { + delete dat; + qDebug() << "invaid .tpr file."; + QString msg; + msg.sprintf("不支持的录像文件版本 %d!\n\n此播放器支持录像文件版本 4。", hdr->info.ver); + _notify_error(msg); + return; + } + +// if(hdr->basic.width == 0 || hdr->basic.height == 0) { +// _notify_error("错误的录像信息,未记录窗口尺寸!"); +// return; +// } + total_pkg = hdr->info.packages; total_ms = hdr->info.time_ms; @@ -60,12 +189,15 @@ void ThreadPlay::run() { // 加载录像文件数据 - QString dat_filename(currentPath); - dat_filename += "tp-rdp.dat"; + QString tpd_filename(path_base); + tpd_filename += "tp-rdp.tpd"; - QFile f_dat(dat_filename); + QFile f_dat(tpd_filename); if(!f_dat.open(QFile::ReadOnly)) { - qDebug() << "Can not open " << dat_filename << " for read."; + qDebug() << "Can not open " << tpd_filename << " for read."; + QString msg; + msg.sprintf("无法打开录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); + _notify_error(msg); return; } @@ -89,18 +221,23 @@ void ThreadPlay::run() { TS_RECORD_PKG pkg; read_len = f_dat.read((char*)(&pkg), sizeof(pkg)); if(read_len != sizeof(TS_RECORD_PKG)) { - qDebug() << "invaid .dat file (1)."; + qDebug() << "invaid .tpd file (1)."; + QString msg; + msg.sprintf("错误的录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); + _notify_error(msg); return; } - update_data* dat = new update_data; - dat->data_type(TYPE_DATA); + update_data* dat = new update_data(TYPE_DATA); dat->alloc_data(sizeof(TS_RECORD_PKG) + pkg.size); memcpy(dat->data_buf(), &pkg, sizeof(TS_RECORD_PKG)); read_len = f_dat.read((char*)(dat->data_buf()+sizeof(TS_RECORD_PKG)), pkg.size); if(read_len != pkg.size) { delete dat; - qDebug() << "invaid .dat file."; + qDebug() << "invaid .tpd file."; + QString msg; + msg.sprintf("错误的录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); + _notify_error(msg); return; } @@ -108,9 +245,8 @@ void ThreadPlay::run() { if(time_pass > total_ms) time_pass = total_ms; if(time_pass - time_last_pass > 200) { - update_data* _passed_ms = new update_data; - _passed_ms->data_type(TYPE_TIMER); - _passed_ms->passed_ms(time_pass); + update_data* _passed_ms = new update_data(TYPE_PLAYED_MS); + _passed_ms->played_ms(time_pass); emit signal_update_data(_passed_ms); time_last_pass = time_pass; } @@ -145,9 +281,8 @@ void ThreadPlay::run() { if(_time_pass > total_ms) _time_pass = total_ms; if(_time_pass - time_last_pass > 200) { - update_data* _passed_ms = new update_data; - _passed_ms->data_type(TYPE_TIMER); - _passed_ms->passed_ms(_time_pass); + update_data* _passed_ms = new update_data(TYPE_PLAYED_MS); + _passed_ms->played_ms(_time_pass); emit signal_update_data(_passed_ms); time_last_pass = _time_pass; } @@ -160,7 +295,6 @@ void ThreadPlay::run() { } } - update_data* _end = new update_data; - _end->data_type(TYPE_END); + update_data* _end = new update_data(TYPE_END); emit signal_update_data(_end); } diff --git a/client/tp-player/thr_play.h b/client/tp-player/thr_play.h index c875762..84f23c0 100644 --- a/client/tp-player/thr_play.h +++ b/client/tp-player/thr_play.h @@ -3,13 +3,14 @@ #include #include "update_data.h" - +#include "thr_download.h" class ThreadPlay : public QThread { Q_OBJECT public: - ThreadPlay(); + ThreadPlay(const QString& res); + ~ThreadPlay(); virtual void run(); void stop(); @@ -17,6 +18,10 @@ public: void resume() {m_need_pause = false;} void speed(int s) {if(s >= 1 && s <= 16) m_speed = s;} +private: + void _notify_message(const QString& msg); + void _notify_error(const QString& err_msg); + signals: void signal_update_data(update_data*); @@ -24,6 +29,11 @@ private: bool m_need_stop; bool m_need_pause; int m_speed; + + QString m_res; + bool m_need_download; + + ThreadDownload* m_thr_download; }; #endif // THR_PLAY_H diff --git a/client/tp-player/tp-player.pro b/client/tp-player/tp-player.pro index 11b66b3..a7ca8e7 100644 --- a/client/tp-player/tp-player.pro +++ b/client/tp-player/tp-player.pro @@ -1,29 +1,34 @@ -TEMPLATE = app -TARGET = tp-player - -QT += core gui widgets - -HEADERS += \ - mainwindow.h \ - bar.h \ - thr_play.h \ - update_data.h \ - record_format.h \ - rle.h - -SOURCES += \ - main.cpp \ - mainwindow.cpp \ - bar.cpp \ - thr_play.cpp \ - update_data.cpp \ - rle.c - -RESOURCES += \ - tp-player.qrc - -RC_FILE += \ - tp-player.rc - -FORMS += \ - mainwindow.ui +TEMPLATE = app +TARGET = tp-player + +QT += core gui widgets + +HEADERS += \ + dlgmessage.h \ + mainwindow.h \ + bar.h \ + thr_download.h \ + thr_play.h \ + update_data.h \ + record_format.h \ + rle.h + +SOURCES += \ + dlgmessage.cpp \ + main.cpp \ + mainwindow.cpp \ + bar.cpp \ + thr_download.cpp \ + thr_play.cpp \ + update_data.cpp \ + rle.c + +RESOURCES += \ + tp-player.qrc + +RC_FILE += \ + tp-player.rc + +FORMS += \ + dlgmessage.ui \ + mainwindow.ui diff --git a/client/tp-player/update_data.cpp b/client/tp-player/update_data.cpp index d84f364..c57c2a1 100644 --- a/client/tp-player/update_data.cpp +++ b/client/tp-player/update_data.cpp @@ -1,8 +1,8 @@ #include "update_data.h" -update_data::update_data(QObject *parent) : QObject(parent) +update_data::update_data(int data_type, QObject *parent) : QObject(parent) { - m_data_type = 0xff; + m_data_type = data_type; m_data_buf = nullptr; m_data_len = 0; } diff --git a/client/tp-player/update_data.h b/client/tp-player/update_data.h index e701c95..97b420b 100644 --- a/client/tp-player/update_data.h +++ b/client/tp-player/update_data.h @@ -3,24 +3,34 @@ #include +#define TYPE_HEADER_INFO 0 +#define TYPE_DATA 1 +#define TYPE_PLAYED_MS 2 +#define TYPE_DOWNLOAD_PERCENT 3 +#define TYPE_END 4 +#define TYPE_MESSAGE 5 +#define TYPE_ERROR 6 + class update_data : public QObject { Q_OBJECT public: - explicit update_data(QObject *parent = nullptr); + explicit update_data(int data_type, QObject *parent = nullptr); virtual ~update_data(); void alloc_data(uint32_t len); void attach_data(const uint8_t* dat, uint32_t len); - void data_type(int dt) {m_data_type = dt;} 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 passed_ms(uint32_t ms) {m_passed_ms = ms;} - uint32_t passed_ms() {return m_passed_ms;} + 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;} signals: @@ -31,7 +41,8 @@ private: int m_data_type; uint8_t* m_data_buf; uint32_t m_data_len; - uint32_t m_passed_ms; + uint32_t m_played_ms; + QString m_msg; }; class UpdateDataHelper { diff --git a/external/version.ini b/external/version.ini index 61456f5..78dacf0 100644 --- a/external/version.ini +++ b/external/version.ini @@ -1,5 +1,5 @@ [external_ver] -openssl = 1.0.2p,1000210f +openssl = 1.0.2s,1000210f libuv = 1.28.0 mbedtls = 2.12.0 libssh = 0.9.0 diff --git a/server/tp_core/common/base_record.h b/server/tp_core/common/base_record.h index 5615895..ddd0345 100644 --- a/server/tp_core/common/base_record.h +++ b/server/tp_core/common/base_record.h @@ -22,34 +22,34 @@ // 录像文件头(随着录像数据写入,会改变的部分) 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; // 数据文件大小 + ex_u32 magic; // "TPPR" 标志 TelePort Protocol Record + ex_u16 ver; // 录像文件版本,v3.5.0开始为4 + ex_u32 packages; // 总包数 + ex_u32 time_ms; // 总耗时(毫秒) + uint32_t dat_file_count; // 数据文件数量 + uint8_t _reserve[64-4-2-4-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]; // 远程主机用户名 + 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 host_ip[40]; // 远程主机IP + char conn_ip[40]; // 远程主机IP + ex_u16 conn_port; // 远程主机端口 - char client_ip[40]; // 客户端IP + char client_ip[40]; // 客户端IP -// // RDP专有 +// // RDP专有 - v3.5.0废弃并移除 // 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]; ex_u8 _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)