From 3aa475587698ea8b8c76c2de790b9707c93206d1 Mon Sep 17 00:00:00 2001 From: Apex Liu Date: Mon, 4 Nov 2019 03:34:11 +0800 Subject: [PATCH] .tmp. --- client/tp-player/bar.cpp | 4 +- client/tp-player/mainwindow.cpp | 19 +- client/tp-player/mainwindow.h | 3 + client/tp-player/record_format.h | 5 + client/tp-player/thr_data.cpp | 526 ++++++++++++++---------------- client/tp-player/thr_data.h | 10 +- client/tp-player/thr_download.cpp | 292 +++++++++++++++++ client/tp-player/thr_download.h | 72 ++++ client/tp-player/thr_play.cpp | 167 ++-------- client/tp-player/thr_play.h | 4 +- client/tp-player/tp-player.pro | 6 +- 11 files changed, 675 insertions(+), 433 deletions(-) create mode 100644 client/tp-player/thr_download.cpp create mode 100644 client/tp-player/thr_download.h diff --git a/client/tp-player/bar.cpp b/client/tp-player/bar.cpp index 14044a3..c1cac0f 100644 --- a/client/tp-player/bar.cpp +++ b/client/tp-player/bar.cpp @@ -219,7 +219,7 @@ void Bar::_init_imgages() { 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("无操作则跳过") + CHKBOX_RIGHT_PADDING + m_res[res_chkbox_normal].width(), h); + 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(); @@ -254,7 +254,7 @@ void Bar::_init_imgages() { 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, "无操作则跳过"); + ps.drawText(QRect(text_left, text_top, w-text_left, h-text_top), Qt::AlignCenter, LOCAL8BIT("无操作则跳过")); } } } diff --git a/client/tp-player/mainwindow.cpp b/client/tp-player/mainwindow.cpp index 7999b7e..db7407f 100644 --- a/client/tp-player/mainwindow.cpp +++ b/client/tp-player/mainwindow.cpp @@ -187,7 +187,7 @@ void MainWindow::_start_play_thread() { m_thr_play = nullptr; } - m_thr_play = new ThrPlay(); + 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()); @@ -340,6 +340,11 @@ void MainWindow::_do_update_data(UpdateData* dat) { } 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()); @@ -430,10 +435,14 @@ void MainWindow::_do_update_data(UpdateData* dat) { 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); - else - title.sprintf("[%s] %s@%s:%d [Teleport-RDP录像回放]", m_rec_hdr.basic.acc_username, m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip, m_rec_hdr.basic.conn_port); + if (m_rec_hdr.basic.conn_port == 3389) { + title = QString(LOCAL8BIT("[%1] %2@%3 [Teleport-RDP录像回放]").arg(m_rec_hdr.basic.acc_username, m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip)); + } + else { + QString _port; + _port.sprintf("%d", m_rec_hdr.basic.conn_port); + title = QString(LOCAL8BIT("[%1] %2@%3:%4 [Teleport-RDP录像回放]").arg(m_rec_hdr.basic.acc_username, m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip, _port)); + } setWindowTitle(title); diff --git a/client/tp-player/mainwindow.h b/client/tp-player/mainwindow.h index feabbf4..9c5eed6 100644 --- a/client/tp-player/mainwindow.h +++ b/client/tp-player/mainwindow.h @@ -39,6 +39,9 @@ public: // Downloader* downloader() {return m_dl;} // void reset_downloader() {if(m_dl){delete m_dl;m_dl= nullptr;}} + // TODO: 将thr_data移动到thr_play线程,由play线程进行管理 + ThrData* get_thr_data() {return m_thr_data;} + private: void paintEvent(QPaintEvent *e); void mouseMoveEvent(QMouseEvent *e); diff --git a/client/tp-player/record_format.h b/client/tp-player/record_format.h index 68d7ef9..2f95c7b 100644 --- a/client/tp-player/record_format.h +++ b/client/tp-player/record_format.h @@ -3,6 +3,11 @@ #include +#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 // diff --git a/client/tp-player/thr_data.cpp b/client/tp-player/thr_data.cpp index 25a5f06..846ac39 100644 --- a/client/tp-player/thr_data.cpp +++ b/client/tp-player/thr_data.cpp @@ -4,6 +4,7 @@ #include #include #include +#include #include "thr_play.h" #include "thr_data.h" @@ -25,12 +26,12 @@ ThrData::ThrData(MainWindow* mainwin, const QString& res) { // m_dl = nullptr; #ifdef __APPLE__ - QString data_path_base = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); - data_path_base += "/tp-testdata/"; + m_data_path_base = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); + m_data_path_base += "/tp-testdata/"; #else - m_local_data_path_base = QCoreApplication::applicationDirPath() + "/record"; + m_data_path_base = QCoreApplication::applicationDirPath() + "/record"; #endif - qDebug("data-path-base: %s", m_local_data_path_base.toStdString().c_str()); + qDebug("data-path-base: %s", m_data_path_base.toStdString().c_str()); // qDebug() << "AppConfigLocation:" << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); // qDebug() << "AppDataLocation:" << QStandardPaths::writableLocation(QStandardPaths::AppDataLocation); @@ -50,6 +51,7 @@ GenericCacheLocation: "C:/Users/apex/AppData/Local/cache" } ThrData::~ThrData() { + _clear_data(); } void ThrData::stop() { @@ -72,224 +74,294 @@ void ThrData::_notify_error(const QString& msg) { emit signal_update_data(_msg); } -// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意,并不直接访问此URI,实际上其并不存在) -// TP服务器地址(可能包含子路径哦,例如上例中的{sub/path/}部分)/session-id(用于判断当前授权用户)/录像会话编号 - 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()) + 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; - uint32_t file_idx = 0; + + UpdateData* dat = new UpdateData(TYPE_HEADER_INFO); + dat->alloc_data(sizeof(TS_RECORD_HEADER)); + memcpy(dat->data_buf(), &m_hdr, sizeof(TS_RECORD_HEADER)); + emit signal_update_data(dat); + + + /* + // fake-code: + file_index = 0; + for(;;) { + if(file_index >= file_count) { + msleep(500); + continue; + } + + + if(queue.size < 500) { + need_pkg = 1000 - queue.size; + + for(i = 0; i < need_pkg; i++) { + if(f.not_open) { + f.open(file_index); + } + + pkg = read_pkg() + if(f.to_end) { + f.close(); + file_index += 1; + if(file_index >= file_count) + break; + } + + queue.add(pkg) + } + + if(file_index >= file_count) + break; + } + else { + msleep(100); + } + } + + */ + + QFile* fdata = nullptr; + uint32_t file_idx = 0; + uint32_t start_offset = 0; + qint64 file_size = 0; + qint64 file_processed = 0; + qint64 read_len = 0; + QString str_fidx; + + 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_path_base, str_fidx); - tpd_fname = QDir::toNativeSeparators(tpd_fname); - - if(m_need_download) { - QString tmp_fname = QString("%1/tp-rdp-%2.tpd.downloading").arg(m_path_base, 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)) - return; - - if(!QFile::rename(tmp_fname, tpd_fname)) - return; - } - } - - qDebug() << "TPD: " << tpd_fname; - - QFile f_tpd(tpd_fname); - if(!f_tpd.open(QFile::ReadOnly)) { - qDebug() << "Can not open " << tpd_fname << " for read."; - _notify_error(QString("%1\n\n%3").arg(LOCAL8BIT("无法打开录像数据文件!"), tpd_fname)); return; + + // 如果所有文件都已经处理完了,则等待(可能用户会拖动滚动条,或者重新播放) + if(file_idx >= m_hdr.info.dat_file_count) { + msleep(500); + continue; } - qint64 filesize = f_tpd.size(); - qint64 processed = 0; + // 看看待播放队列中还有多少个数据包 + int pkg_count_in_queue = 0; + int pkg_need_add = 0; - // 加载并解析到待播放队列 - qint64 read_len = 0; - for(;;) { - { -// if(m_need_stop) -// break; + m_locker.lock(); + pkg_count_in_queue = m_data.size(); + m_locker.unlock(); -// m_locker.lock(); + // 少于500个的话,补足到1000个 + if(m_data.size() < 500) + pkg_need_add = 1000 - pkg_count_in_queue; -// if(m_data.size() > 500) { -// msleep(1000); -// continue; -// } + if(pkg_need_add == 0) { + msleep(100); + continue; + } -// m_locker.unlock(); + for(int i = 0; i < pkg_need_add; ++i) { + if(m_need_stop) + return; + + // 如果数据文件尚未打开,则打开它 + if(fdata == nullptr) { + 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); + + 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(file_idx)) + break; + msleep(100); + } + + // 下载失败了 + if(!m_thr_download.is_tpd_downloaded(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, processed: %" PRId64 ", size: %" PRId64, file_processed, file_size); } +// qDebug("B processed: %" PRId64 ", size: %" PRId64, file_processed, file_size); - if(filesize - processed < sizeof(TS_RECORD_PKG)) { - qDebug("invaid tp-rdp-%d.tpd file, filesize=%d, processed=%d, need=%d.", file_idx+1, filesize, processed, sizeof(TS_RECORD_PKG)); - _notify_error(QString("%1\n\n%3").arg(LOCAL8BIT("错误的录像数据文件!"), tpd_fname)); + //---------------------------------- + // 读取一个数据包 + //---------------------------------- + if(file_size - file_processed < sizeof(TS_RECORD_PKG)) { + qDebug("invaid tp-rdp-%d.tpd file, filesize=%" PRId64 ", processed=%" PRId64 ", need=%d.", 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 = f_tpd.read(reinterpret_cast(&pkg), sizeof(pkg)); - if(read_len == 0) - break; + read_len = fdata->read(reinterpret_cast(&pkg), sizeof(TS_RECORD_PKG)); + // if(read_len == 0) + // break; if(read_len != sizeof(TS_RECORD_PKG)) { - qDebug("invaid tp-rdp-%d.tpd file, read_len=%d (1).", file_idx+1, read_len); - _notify_error(QString("%1\n\n%3").arg(LOCAL8BIT("错误的录像数据文件!"), tpd_fname)); + qDebug("invaid tp-rdp-%d.tpd file, read_len=%" PRId64 " (1).", file_idx+1, read_len); + _notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx)); return; } - processed += sizeof(TS_RECORD_PKG); + file_processed += sizeof(TS_RECORD_PKG); - if(filesize - processed < pkg.size) { + if(file_size - file_processed < pkg.size) { qDebug("invaid tp-rdp-%d.tpd file (2).", file_idx+1); - _notify_error(QString("%1\n\n%3").arg(LOCAL8BIT("错误的录像数据文件!"), tpd_fname)); + _notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx)); return; } - if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) { - qDebug("----key frame: %d, pkg.size=%d", pkg.time_ms, pkg.size); - if(pkg.size > 0) { - f_tpd.read(pkg.size); - } - continue; + if(pkg.size == 0) { + qDebug("################## too bad."); } UpdateData* dat = new UpdateData(TYPE_DATA); dat->alloc_data(sizeof(TS_RECORD_PKG) + pkg.size); memcpy(dat->data_buf(), &pkg, sizeof(TS_RECORD_PKG)); - read_len = f_tpd.read(reinterpret_cast(dat->data_buf()+sizeof(TS_RECORD_PKG)), pkg.size); + read_len = fdata->read(reinterpret_cast(dat->data_buf()+sizeof(TS_RECORD_PKG)), pkg.size); if(read_len != pkg.size) { delete dat; - qDebug("invaid tp-rdp-%d.tpd file (3).", file_idx+1); - _notify_error(QString("%1\n\n%3").arg(LOCAL8BIT("错误的录像数据文件!"), tpd_fname)); + qDebug("invaid tp-rdp-%d.tpd file, read_len=%" PRId64 " (3).", file_idx+1, read_len); + _notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx)); return; } + file_processed += pkg.size; + + // 跳过关键帧 + // TODO: 拖动滚动条后,需要显示一次关键帧数据,然后跳过后续关键帧。 + if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) { + qDebug("----key frame: %ld, processed=%" PRId64 ", pkg.size=%d", pkg.time_ms, file_processed, pkg.size); + delete dat; + dat = nullptr; + } - { + // 数据放到待播放列表中 + if(dat) { m_locker.lock(); m_data.enqueue(dat); -// qDebug("data count: %d", m_data.size()); + qDebug("queue data count: %d", m_data.size()); + m_locker.unlock(); + } + + // 如果此文件已经处理完毕,则关闭文件,这样下次处理一个新的文件 +// qDebug("C processed: %" PRId64 ", size: %" PRId64, file_processed, file_size); + if(file_processed >= file_size) { + fdata->close(); + delete fdata; + fdata = nullptr; + file_idx++; + } + + if(file_idx >= m_hdr.info.dat_file_count) { + UpdateData* dat = new UpdateData(TYPE_END); + m_locker.lock(); + m_data.enqueue(dat); + qDebug("queue data count: %d", m_data.size()); m_locker.unlock(); } } - - - - - file_idx += 1; - if(file_idx >= m_hdr.info.dat_file_count) - break; } } bool ThrData::_load_header() { QString msg; - QString _tmp_res = m_res.toLower(); + qDebug() << "PATH_BASE: " << m_data_path; - if(_tmp_res.startsWith("http")) { - m_need_download = true; + 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; } - if(m_need_download) { - _notify_message(LOCAL8BIT("正在准备录像数据,请稍候...")); + memset(&m_hdr, 0, sizeof(TS_RECORD_HEADER)); - QStringList _uris = m_res.split('/'); - if(_uris.size() < 3) { - qDebug() << "invalid param: " << m_res; - return false; - } - - m_sid = _uris[_uris.size()-2]; - m_rid = _uris[_uris.size()-1]; - m_url_base = m_res.left(m_res.length() - m_sid.length() - m_rid.length() - 2); - - qDebug() << "url-base:[" << m_url_base << "], sid:[" << m_sid << "], rid:[" << m_rid << "]"; - - // 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 file. %d", data.size()); - _notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("指定的文件或目录不存在!"), _tmp_res)); - return false; - } - - memcpy(&m_hdr, data.data(), sizeof(TS_RECORD_HEADER)); - } - else { - QFileInfo fi_chk_link(m_res); - if(fi_chk_link.isSymLink()) - _tmp_res = fi_chk_link.symLinkTarget(); - else - _tmp_res = m_res; - - QFileInfo fi(_tmp_res); - if(!fi.exists()) { - _notify_error(QString("%1\n\n%2").arg(LOCAL8BIT("指定的文件或目录不存在!"), _tmp_res)); - return false; - } - - if(fi.isFile()) { - m_path_base = fi.path(); - } - else if(fi.isDir()) { - m_path_base = _tmp_res; - } - - m_path_base = QDir::toNativeSeparators(m_path_base); - - qDebug() << "PATH_BASE: " << m_path_base; - - QString filename = QString("%1/tp-rdp.tpr").arg(m_path_base); - 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(&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; - } + qint64 read_len = 0; + read_len = f.read(reinterpret_cast(&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) { @@ -308,94 +380,13 @@ bool ThrData::_load_header() { return false; } - - // 下载得到的数据应该是一个TS_RECORD_HEADER,解析此数据,生成本地文件路径,并保存之。 - if(m_need_download) { - QDateTime timeUTC; - // timeUTC.setTimeSpec(Qt::UTC); - // timeUTC.setTime_t(m_hdr.basic.timestamp); - timeUTC.setSecsSinceEpoch(m_hdr.basic.timestamp); - QString strUTC = timeUTC.toString("yyyyMMdd-hhmmss"); - - QString strAcc(m_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; - } - - // .../record/RDP-211-admin-user@domain-192.168.0.68-20191015-020243 - m_path_base = QString("%1/RDP-%2-%3-%4-%5-%6").arg(m_local_data_path_base, - m_rid, - m_hdr.basic.user_username, - strAcc, - m_hdr.basic.host_ip, - strUTC - ); - - m_path_base = QDir::toNativeSeparators(m_path_base); - - qDebug() << "PATH_BASE: " << m_path_base; - - QDir dir; - dir.mkpath(m_path_base); - QFileInfo fi; - fi.setFile(m_path_base); - if(!fi.isDir()) { - qDebug("can not create folder to save downloaded file."); - return false; - } - - QString filename = QString("%1/tp-rdp.tpr").arg(m_path_base); - 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(&m_hdr), sizeof(TS_RECORD_HEADER)); - f.flush(); - f.close(); - - if(written != sizeof(TS_RECORD_HEADER)) { - qDebug("save header file failed."); - return false; - } - } - return true; } bool ThrData::_load_keyframe() { - QString tpk_fname = QString("%1/tp-rdp.tpk").arg(m_path_base); + QString tpk_fname = QString("%1/tp-rdp.tpk").arg(m_data_path); tpk_fname = QDir::toNativeSeparators(tpk_fname); - if(m_need_download) { - QString tmp_fname = QString("%1/tp-rdp.tpk.downloading").arg(m_path_base); - 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; - } - } - qDebug() << "TPK: " << tpk_fname; QFile f_kf(tpk_fname); @@ -430,40 +421,6 @@ bool ThrData::_load_keyframe() { return true; } -bool ThrData::_download_file(const QString& url, const QString filename) { - if(!m_need_download) { - qDebug() << "download not necessary."; - return false; - } - - Downloader dl; - if(!dl.request(url, m_sid, filename)) { - qDebug() << "download failed."; - _notify_error(QString("%1").arg(LOCAL8BIT("下载文件失败!"))); - return false; - } - - return true; -} - -bool ThrData::_download_file(const QString& url, QByteArray& data) { - if(!m_need_download) { - qDebug() << "download not necessary."; - return false; - } - - Downloader dl; - - if(!dl.request(url, m_sid, &data)) { - qDebug() << "download failed."; - _notify_error(QString("%1").arg(LOCAL8BIT("下载文件失败!"))); - return false; - } - - return true; -} - - void ThrData::_prepare() { UpdateData* d = new UpdateData(TYPE_HEADER_INFO); @@ -473,10 +430,21 @@ void ThrData::_prepare() { } UpdateData* ThrData::get_data() { + UpdateData* d = nullptr; m_locker.lock(); - UpdateData* d = m_data.dequeue(); + 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(); +} diff --git a/client/tp-player/thr_data.h b/client/tp-player/thr_data.h index 963203e..dab105c 100644 --- a/client/tp-player/thr_data.h +++ b/client/tp-player/thr_data.h @@ -9,6 +9,7 @@ #include #include "update_data.h" #include "record_format.h" +#include "thr_download.h" /* 为支持“边下载,边播放”、“可拖动进度条”等功能,录像数据会分为多个文件存放,目前每个文件约4MB。 @@ -63,9 +64,8 @@ private: bool _load_header(); bool _load_keyframe(); - bool _download_file(const QString& url, const QString filename); - bool _download_file(const QString& url, QByteArray& data); + void _clear_data(); void _prepare(); void _notify_message(const QString& msg); @@ -79,16 +79,18 @@ private: QQueue m_data; QMutex m_locker; + ThrDownload m_thr_download; + bool m_need_stop; bool m_need_download; QString m_res; - QString m_local_data_path_base; + QString m_data_path_base; QString m_url_base; QString m_sid; QString m_rid; - QString m_path_base; + QString m_data_path; TS_RECORD_HEADER m_hdr; KeyFrames m_kf; diff --git a/client/tp-player/thr_download.cpp b/client/tp-player/thr_download.cpp new file mode 100644 index 0000000..cbfe0f7 --- /dev/null +++ b/client/tp-player/thr_download.cpp @@ -0,0 +1,292 @@ +#include +#include +#include +#include + +#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(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(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]; +} + diff --git a/client/tp-player/thr_download.h b/client/tp-player/thr_download.h new file mode 100644 index 0000000..e5478e0 --- /dev/null +++ b/client/tp-player/thr_download.h @@ -0,0 +1,72 @@ +#ifndef THR_DOWNLOAD_H +#define THR_DOWNLOAD_H + +#include +#include +#include +#include + +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 diff --git a/client/tp-player/thr_play.cpp b/client/tp-player/thr_play.cpp index a5a1f3f..d366efb 100644 --- a/client/tp-player/thr_play.cpp +++ b/client/tp-player/thr_play.cpp @@ -4,6 +4,7 @@ #include "thr_play.h" #include "thr_data.h" +#include "mainwindow.h" #include "record_format.h" #include "util.h" @@ -18,7 +19,8 @@ */ -ThrPlay::ThrPlay() { +ThrPlay::ThrPlay(MainWindow* mainwnd) { + m_mainwnd = mainwnd; m_need_stop = false; m_need_pause = false; m_speed = 2; @@ -63,151 +65,37 @@ void ThrPlay::_notify_error(const QString& msg) { void ThrPlay::run() { - // http://127.0.0.1:7190/tp_1491560510_ca67fceb75a78c9d/211 - // E:\work\tp4a\teleport\server\share\replay\rdp\000000211 + ThrData* thr_data = m_mainwnd->get_thr_data(); + bool first_run = true; -//#ifdef __APPLE__ -// QString currentPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); -// currentPath += "/tp-testdata/"; -//#else -// QString currentPath = QCoreApplication::applicationDirPath() + "/testdata/"; -//#endif + for(;;) { + if(m_need_stop) + break; - // /Users/apex/Library/Preferences/tp-player -// qDebug() << "appdata:" << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation); - - // /private/var/folders/_3/zggrxjdx1lxcdqnfsbgpcwzh0000gn/T - //qDebug() << "tmp:" << QStandardPaths::writableLocation(QStandardPaths::TempLocation); - -// m_thr_data = new ThrData(this, m_res); -// m_thr_data->start(); - - // "正在准备录像数据,请稍候..." -// _notify_message(LOCAL8BIT("正在准备录像数据,请稍候...")); - - -#if 0 - // 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; - - // "正在缓存录像数据,请稍候..." - _notify_message("正在缓存录像数据,请稍候..."); - - m_thr_data = new ThreadDownload(m_res); - m_thr_data->start(); - - QString msg; - for(;;) { - msleep(500); - - if(m_need_stop) - return; - - if(!m_thr_data->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; + // 1. 从ThrData的待播放队列中取出一个数据 + UpdateData* dat = thr_data->get_data(); + if(dat == nullptr) { + msleep(20); + continue; } - QFileInfo fi(_tmp_res); - if(!fi.exists()) { - QString msg; - msg.sprintf("指定的文件或目录不存在!\n\n%s", _tmp_res.toStdString().c_str()); - _notify_error(msg); - return; + if(first_run) { + first_run = false; + _notify_message(""); } - if(fi.isFile()) { - path_base = fi.path(); + // 2. 根据数据包的信息,等待到播放时间点 + // 3. 将数据包发送给主UI界面进行显示 +// qDebug("emit one package."); + if(dat->data_type() == TYPE_END) { + _notify_message(LOCAL8BIT("播放结束")); } - else if(fi.isDir()) { - path_base = m_res; - } - - path_base += "/"; - } - - - 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"; - - QFile f_hdr(tpr_filename); - if(!f_hdr.open(QFile::ReadOnly)) { - 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 { - UpdateData* dat = new UpdateData(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; - } - - 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); - } + msleep(5); +} +#if 0 //====================================== // 加载录像文件数据并播放 //====================================== @@ -351,8 +239,7 @@ void ThrPlay::run() { } #endif - - qDebug("play end."); - UpdateData* _end = new UpdateData(TYPE_END); - emit signal_update_data(_end); +// qDebug("play end."); +// UpdateData* _end = new UpdateData(TYPE_END); +// emit signal_update_data(_end); } diff --git a/client/tp-player/thr_play.h b/client/tp-player/thr_play.h index dcfb190..abb142d 100644 --- a/client/tp-player/thr_play.h +++ b/client/tp-player/thr_play.h @@ -5,6 +5,7 @@ #include "update_data.h" #include "downloader.h" +class MainWindow; // 根据播放规则,将要播放的图像发送给主UI线程进行显示 class ThrPlay : public QThread { @@ -12,7 +13,7 @@ class ThrPlay : public QThread friend class ThrData; public: - ThrPlay(); + ThrPlay(MainWindow* mainwnd); ~ThrPlay(); virtual void run(); @@ -29,6 +30,7 @@ signals: void signal_update_data(UpdateData*); private: + MainWindow* m_mainwnd; bool m_need_stop; bool m_need_pause; int m_speed; diff --git a/client/tp-player/tp-player.pro b/client/tp-player/tp-player.pro index 27a2794..0a8af1d 100644 --- a/client/tp-player/tp-player.pro +++ b/client/tp-player/tp-player.pro @@ -14,7 +14,8 @@ HEADERS += \ record_format.h \ rle.h \ util.h \ - downloader.h + downloader.h \ + thr_download.h SOURCES += \ main.cpp \ @@ -25,7 +26,8 @@ SOURCES += \ update_data.cpp \ rle.c \ util.cpp \ - downloader.cpp + downloader.cpp \ + thr_download.cpp RESOURCES += \ tp-player.qrc