diff --git a/client/tp-player/mainwindow.cpp b/client/tp-player/mainwindow.cpp index 3b0b323..f4acfbb 100644 --- a/client/tp-player/mainwindow.cpp +++ b/client/tp-player/mainwindow.cpp @@ -64,6 +64,13 @@ bool rdpimg2QImage(QImage& out, int w, int h, int bitsPerPixel, bool isCompresse return true; } +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), @@ -181,6 +188,20 @@ void MainWindow::paintEvent(QPaintEvent *e) painter.drawPixmap(m_pt.x-m_pt_normal.width()/2, m_pt.y-m_pt_normal.height()/2, m_pt_normal); } + { + QRect rc_draw = e->rect(); + QRect rc(100, 100, m_img_message.width(), m_img_message.height()); + //rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top()); + + 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); + } + // 绘制浮动控制窗 if(m_bar_fading) { painter.setOpacity(m_bar_opacity); @@ -281,18 +302,37 @@ void MainWindow::_do_update_data(update_data* dat) { } 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); - } + QPainter pp(&m_canvas); + QFontMetrics fm = pp.fontMetrics(); + QRect rcWin(0, 0, m_canvas.width(), m_canvas.height()); + QRect rc = fm.boundingRect(rcWin, Qt::AlignLeft|Qt::TextWordWrap, dat->message()); + qDebug("message, w=%d, h=%d", rc.width(), rc.height()); +// int w = fm.width(dat->message()); +// int h = fm.height(); +// qDebug("message, w=%d, h=%d", w, h); - m_msg_box->set_text(dat->message()); - // 显示对话框 - m_msg_box->show(); + m_img_message = QPixmap(rc.width() + 30, rc.height() + 30); + m_img_message.fill(Qt::transparent); + QPainter pm(&m_img_message); + pm.setPen(QColor(255,255,255,153)); + pm.fillRect(rc, QColor(0,0,0,190)); + pm.drawText(rc, Qt::AlignLeft|Qt::TextWordWrap, dat->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; } diff --git a/client/tp-player/mainwindow.h b/client/tp-player/mainwindow.h index 1e14620..dbcdb09 100644 --- a/client/tp-player/mainwindow.h +++ b/client/tp-player/mainwindow.h @@ -78,6 +78,8 @@ private: //QMessageBox* m_msg_box; DlgMessage* m_msg_box; + + QPixmap m_img_message; }; #endif // MAINWINDOW_H diff --git a/client/tp-player/thr_play.cpp b/client/tp-player/thr_play.cpp index 8aaa311..9d133d0 100644 --- a/client/tp-player/thr_play.cpp +++ b/client/tp-player/thr_play.cpp @@ -75,9 +75,6 @@ void ThreadPlay::run() { qDebug() << "DOWNLOAD"; m_need_download = true; -// path_base = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); -// path_base += "/tprdp/"; - _notify_message("正在缓存录像数据,请稍候..."); m_thr_download = new ThreadDownload(m_res); @@ -87,14 +84,8 @@ void ThreadPlay::run() { for(;;) { msleep(500); - if(m_need_stop) { -// m_thr_download->stop(); -// m_thr_download->wait(); -// delete m_thr_download; -// m_thr_download = nullptr; - + if(m_need_stop) return; - } if(!m_thr_download->prepare(path_base, msg)) { msg.sprintf("指定的文件或目录不存在!\n\n%s", _tmp_res.toStdString().c_str()); @@ -137,8 +128,11 @@ void ThreadPlay::run() { qint64 read_len = 0; uint32_t total_pkg = 0; uint32_t total_ms = 0; + uint32_t file_count = 0; + //====================================== // 加载录像基本信息数据 + //====================================== QString tpr_filename(path_base); tpr_filename += "tp-rdp.tpr"; @@ -176,125 +170,153 @@ void ThreadPlay::run() { return; } -// if(hdr->basic.width == 0 || hdr->basic.height == 0) { -// _notify_error("错误的录像信息,未记录窗口尺寸!"); -// return; -// } + if(hdr->basic.width == 0 || hdr->basic.height == 0) { + _notify_error("错误的录像信息,未记录窗口尺寸!"); + return; + } + + if(hdr->info.dat_file_count == 0) { + _notify_error("错误的录像信息,未记录数据文件数量!"); + return; + } total_pkg = hdr->info.packages; total_ms = hdr->info.time_ms; + file_count = hdr->info.dat_file_count; emit signal_update_data(dat); } - // 加载录像文件数据 - - QString tpd_filename(path_base); - tpd_filename += "tp-rdp.tpd"; - - QFile f_dat(tpd_filename); - if(!f_dat.open(QFile::ReadOnly)) { - qDebug() << "Can not open " << tpd_filename << " for read."; - QString msg; - msg.sprintf("无法打开录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); - _notify_error(msg); - return; - } + //====================================== + // 加载录像文件数据并播放 + //====================================== + uint32_t pkg_count = 0; uint32_t time_pass = 0; uint32_t time_last_pass = 0; - qint64 time_begin = QDateTime::currentMSecsSinceEpoch(); + QString msg; - for(uint32_t i = 0; i < total_pkg; ++i) { + for(uint32_t fidx = 0; fidx < file_count; ++fidx) { if(m_need_stop) { - qDebug() << "stop, user cancel."; + qDebug() << "stop, user cancel 1."; break; } - if(m_need_pause) { - msleep(50); - time_begin += 50; - continue; - } + QString tpd_filename; + tpd_filename.sprintf("%stp-rdp-%d.tpd", path_base.toStdString().c_str(), fidx+1); - TS_RECORD_PKG pkg; - read_len = f_dat.read((char*)(&pkg), sizeof(pkg)); - if(read_len != sizeof(TS_RECORD_PKG)) { - qDebug() << "invaid .tpd file (1)."; - QString msg; - msg.sprintf("错误的录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); + // for test. + msg.sprintf("无法打开录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); + _notify_message(msg); + + QFile f_dat(tpd_filename); + if(!f_dat.open(QFile::ReadOnly)) { + qDebug() << "Can not open " << tpd_filename << " for read."; + msg.sprintf("无法打开录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); _notify_error(msg); return; } - 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 .tpd file."; - QString msg; - msg.sprintf("错误的录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); - _notify_error(msg); - return; - } - - time_pass = (uint32_t)(QDateTime::currentMSecsSinceEpoch() - time_begin) * m_speed; - if(time_pass > total_ms) - time_pass = total_ms; - if(time_pass - time_last_pass > 200) { - 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; - } - - if(time_pass >= pkg.time_ms) { - emit signal_update_data(dat); - continue; - } - - // 需要等待 - uint32_t time_wait = pkg.time_ms - time_pass; - uint32_t wait_this_time = 0; for(;;) { + if(m_need_stop) { + qDebug() << "stop, user cancel 2."; + break; + } + if(m_need_pause) { msleep(50); time_begin += 50; continue; } - wait_this_time = time_wait; - if(wait_this_time > 10) - wait_this_time = 10; - - if(m_need_stop) { - qDebug() << "stop, user cancel (2)."; + TS_RECORD_PKG pkg; + read_len = f_dat.read((char*)(&pkg), sizeof(pkg)); + if(read_len == 0) break; + if(read_len != sizeof(TS_RECORD_PKG)) { + qDebug() << "invaid .tpd file (1)."; + msg.sprintf("错误的录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); + _notify_error(msg); + return; } - msleep(wait_this_time); + 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 .tpd file."; + msg.sprintf("错误的录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); + _notify_error(msg); + return; + } - uint32_t _time_pass = (uint32_t)(QDateTime::currentMSecsSinceEpoch() - time_begin) * m_speed; - if(_time_pass > total_ms) - _time_pass = total_ms; - if(_time_pass - time_last_pass > 200) { + pkg_count++; + + time_pass = (uint32_t)(QDateTime::currentMSecsSinceEpoch() - time_begin) * m_speed; + if(time_pass > total_ms) + time_pass = total_ms; + if(time_pass - time_last_pass > 200) { update_data* _passed_ms = new update_data(TYPE_PLAYED_MS); - _passed_ms->played_ms(_time_pass); + _passed_ms->played_ms(time_pass); emit signal_update_data(_passed_ms); - time_last_pass = _time_pass; + time_last_pass = time_pass; } - time_wait -= wait_this_time; - if(time_wait == 0) { + if(time_pass >= pkg.time_ms) { emit signal_update_data(dat); - break; + continue; } + + // 需要等待 + uint32_t time_wait = pkg.time_ms - time_pass; + uint32_t wait_this_time = 0; + for(;;) { + if(m_need_pause) { + msleep(50); + time_begin += 50; + continue; + } + + wait_this_time = time_wait; + if(wait_this_time > 10) + wait_this_time = 10; + + if(m_need_stop) { + qDebug() << "stop, user cancel (2)."; + break; + } + + msleep(wait_this_time); + + uint32_t _time_pass = (uint32_t)(QDateTime::currentMSecsSinceEpoch() - time_begin) * m_speed; + if(_time_pass > total_ms) + _time_pass = total_ms; + if(_time_pass - time_last_pass > 200) { + 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; + } + + time_wait -= wait_this_time; + if(time_wait == 0) { + emit signal_update_data(dat); + break; + } + } + } } + if(pkg_count < total_pkg) { + qDebug() << "total-pkg:" << total_pkg << ", played:" << pkg_count; + msg.sprintf("录像数据文件有误!\n\n部分录像数据缺失!"); + _notify_message(msg); + } + update_data* _end = new update_data(TYPE_END); emit signal_update_data(_end); } diff --git a/server/tp_core/common/base_record.h b/server/tp_core/common/base_record.h index ddd0345..33bf2a9 100644 --- a/server/tp_core/common/base_record.h +++ b/server/tp_core/common/base_record.h @@ -7,14 +7,19 @@ #include -#define MAX_SIZE_PER_FILE 4194304 // 4M = 1024*1024*4 +#define MAX_CACHE_SIZE 1048576 // 1M = 1024*1024*1 +#define MAX_SIZE_PER_FILE 4194304 // 4M = 1024*1024*4 #pragma pack(push,1) /* * 录像 * - * 一个录像分为两个文件,一个信息文件,一个数据文件。 + * 一个录像分为多个文件: + * *.tpr,录像信息文件,一个,固定大小(512字节) + * *.tpd,数据文件,n个,例如 tp-rdp-1.tpd,tp-rdp-2.tpd等等,每个数据文件约4MB + * *.tpk,关键帧信息文件,一个,仅RDP录像,记录各个关键帧数据所在的数据文件序号、偏移、时间点等信息。 + * *-cmd.txt,ssh命令记录文件,仅SSH。 * 服务内部缓存最大4M,或者5秒,就将数据写入数据文件中,并同时更新信息文件。 * */ @@ -26,8 +31,8 @@ typedef struct TS_RECORD_HEADER_INFO { 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]; + ex_u32 dat_file_count; // 数据文件数量 + ex_u8 _reserve[64-4-2-4-4-4]; }TS_RECORD_HEADER_INFO; #define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO)