pull/175/head^2
Apex Liu 2019-11-01 01:45:17 +08:00
parent ad5534fd94
commit 2f7fc16235
21 changed files with 1189 additions and 154 deletions

View File

@ -1,4 +1,4 @@
#include "bar.h" #include "bar.h"
#include <QPainter> #include <QPainter>
#include <QDebug> #include <QDebug>
#include "mainwindow.h" #include "mainwindow.h"

View File

@ -1,4 +1,4 @@
#ifndef BAR_H #ifndef BAR_H
#define BAR_H #define BAR_H
#include <QPainter> #include <QPainter>

View File

@ -0,0 +1,106 @@
#include "downloader.h"
#include "record_format.h"
#include <QNetworkReply>
#include <qelapsedtimer.h>
// TODO: 将Downloader的实现代码迁移到ThrData线程中
// 使用局部event循环的方式进行下载
/*
QEventLoop eventLoop;
connect(netWorker, &NetWorker::finished,
&eventLoop, &QEventLoop::quit);
QNetworkReply *reply = netWorker->get(url);
replyMap.insert(reply, FetchWeatherInfo);
eventLoop.exec();
*/
//=================================================================
// Downloader
//=================================================================
Downloader::Downloader() {
m_reply = nullptr;
m_code = codeUnknown;
}
Downloader::~Downloader() {
}
void Downloader::run(QNetworkAccessManager* nam, QString& url, QString& sid, QString& filename) {
m_filename = filename;
if(!m_filename.isEmpty()) {
m_file.setFileName(m_filename);
if(!m_file.open(QIODevice::WriteOnly | QFile::Truncate)){
qDebug("open file for write failed.");
return;
}
}
m_code = codeDownloading;
QString cookie = QString("_sid=%1\r\n").arg(sid);
QNetworkRequest req;
req.setUrl(QUrl(url));
req.setRawHeader("Cookie", cookie.toLatin1());
m_reply = nam->get(req);
connect(m_reply, &QNetworkReply::finished, this, &Downloader::_on_finished);
connect(m_reply, &QIODevice::readyRead, this, &Downloader::_on_data_ready);
}
void Downloader::_on_data_ready() {
QNetworkReply *reply = reinterpret_cast<QNetworkReply*>(sender());
if(m_filename.isEmpty()) {
m_data += reply->readAll();
}
else {
m_file.write(reply->readAll());
}
}
void Downloader::reset() {
m_code = codeUnknown;
}
void Downloader::abort() {
if(m_reply)
m_reply->abort();
}
void Downloader::_on_finished() {
qDebug("download finished");
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() << strError;
m_file.flush();
m_file.close();
m_code = codeFailed;
return;
}
disconnect(m_reply, &QNetworkReply::finished, this, &Downloader::_on_finished);
disconnect(m_reply, &QIODevice::readyRead, this, &Downloader::_on_data_ready);
// reply->deleteLater();
if(m_filename.isEmpty()) {
m_data += reply->readAll();
}
else {
m_file.write(reply->readAll());
m_file.flush();
m_file.close();
}
reply->deleteLater();
m_code = codeSuccess;
}

View File

@ -0,0 +1,50 @@
#ifndef DOWNLOADER_H
#define DOWNLOADER_H
#include <QFile>
#include <QNetworkAccessManager>
class Downloader : public QObject {
Q_OBJECT
public:
enum EndCode{
codeUnknown = 0,
codeSuccess,
codeDownloading,
codeFailed
};
public:
// 从url下载数据写入到filename文件中如果filename为空字符串则保存在内存中可通过 data() 获取。
Downloader();
~Downloader();
void run(QNetworkAccessManager* nam, QString& url, QString& sid, QString& filename);
void abort();
void reset();
QByteArray& data(){return m_data;}
EndCode code() {return m_code;}
private slots:
void _on_data_ready(); // 有数据可读了,读取并写入文件
void _on_finished(); // 下载结束了
private:
QString m_filename;
QFile m_file;
QByteArray m_data;
QNetworkReply* m_reply;
EndCode m_code;
};
typedef struct DownloadParam {
QString url;
QString sid;
QString fname;
}DownloadParam;
#endif

View File

@ -8,14 +8,63 @@
// 编译出来的可执行程序复制到单独目录,然后执行 windeployqt 应用程序文件名 // 编译出来的可执行程序复制到单独目录,然后执行 windeployqt 应用程序文件名
// 即可自动将依赖的动态库等复制到此目录中。有些文件是多余的,可以酌情删除。 // 即可自动将依赖的动态库等复制到此目录中。有些文件是多余的,可以酌情删除。
// 命令行参数格式:
// ## 本地文件或目录
// tp-player.exe path/of/tp-rdp.tpr 一个 .tpr 文件的文件名
// tp-player.exe path/contains/tp-rdp.tpr 包含 .tpr 文件的路径
//
// ## 从TP服务器上下载
// (废弃) tp-player.exe "http://127.0.0.1:7190" 1234 "tp_1491560510_ca67fceb75a78c9d" "000000256-admin-administrator-218.244.140.14-20171209-020047"
// (废弃) TP服务器地址 记录编号 session-id仅授权用户可下载 合成的名称,用于本地生成路径来存放下载的文件
//
// ## 从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-218.244.140.14-20171209-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[]) int main(int argc, char *argv[])
{ {
//#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0)) //#if (QT_VERSION >= QT_VERSION_CHECK(5, 6, 0))
// QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); // QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);
//#endif //#endif
QApplication a(argc, argv); 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("TP-Player"); QGuiApplication::setApplicationDisplayName("TP-Player");
QCommandLineParser parser; QCommandLineParser parser;
@ -32,19 +81,25 @@ int main(int argc, char *argv[])
} }
if(parser.isSet(opt_help)) { if(parser.isSet(opt_help)) {
QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(), show_usage(parser);
"<html><head/><body><pre>" // QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(),
+ parser.helpText() // "<html><head/><body><pre>"
+ "\n\n" // + parser.helpText()
+ "RESOURCE could be:\n" // + "\n\n"
+ " teleport record file (.tpr).\n" // + "RESOURCE could be:\n"
+ " a directory contains .tpr file.\n" // + " teleport record file (.tpr).\n"
+ " an URL for download teleport record file." // + " a directory contains .tpr file.\n"
+ "</pre></body></html>"); // + " an URL for download teleport record file."
// + "</pre></body></html>");
return 2; return 2;
} }
const QStringList args = parser.positionalArguments(); const QStringList args = parser.positionalArguments();
if(0 == args.size()) {
show_usage(parser);
return 2;
}
QString resource = args.at(0); QString resource = args.at(0);
qDebug() << resource; qDebug() << resource;

View File

@ -1,4 +1,4 @@
#include "mainwindow.h" #include "mainwindow.h"
#include "ui_mainwindow.h" #include "ui_mainwindow.h"
#include "rle.h" #include "rle.h"
@ -86,10 +86,14 @@ MainWindow::MainWindow(QWidget *parent) :
m_bar_fade_in = false; m_bar_fade_in = false;
m_bar_fading = false; m_bar_fading = false;
m_bar_opacity = 1.0; m_bar_opacity = 1.0;
m_show_message = false;
memset(&m_pt, 0, sizeof(TS_RECORD_RDP_POINTER)); memset(&m_pt, 0, sizeof(TS_RECORD_RDP_POINTER));
m_thr_play = nullptr; m_thr_play = nullptr;
m_play_state = PLAY_STATE_UNKNOWN; m_play_state = PLAY_STATE_UNKNOWN;
m_thr_data = nullptr;
m_dl = nullptr;
ui->setupUi(this); ui->setupUi(this);
@ -107,6 +111,11 @@ MainWindow::MainWindow(QWidget *parent) :
m_pt_normal.load(":/tp-player/res/cursor.png"); m_pt_normal.load(":/tp-player/res/cursor.png");
m_default_bg.load(":/tp-player/res/bg.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); // 禁止最大化按钮 setWindowFlags(windowFlags()&~Qt::WindowMaximizeButtonHint); // 禁止最大化按钮
setFixedSize(m_default_bg.width(), m_default_bg.height()); // 禁止拖动窗口大小 setFixedSize(m_default_bg.width(), m_default_bg.height()); // 禁止拖动窗口大小
@ -115,6 +124,7 @@ MainWindow::MainWindow(QWidget *parent) :
return; return;
} }
// connect(&m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(_do_update_data(update_data*))); // 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_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_fade, SIGNAL(timeout()), this, SLOT(_do_bar_fade()));
@ -131,12 +141,25 @@ MainWindow::~MainWindow()
//m_thr_play->wait(); //m_thr_play->wait();
//qDebug() << "play thread stoped."; //qDebug() << "play thread stoped.";
disconnect(m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(_do_update_data(update_data*))); disconnect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
delete m_thr_play; delete m_thr_play;
m_thr_play = nullptr; 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*)));
disconnect(m_thr_data, SIGNAL(signal_download(DownloadParam*)), this, SLOT(_do_download(DownloadParam*)));
delete m_thr_data;
m_thr_data = nullptr;
}
if(m_dl) {
delete m_dl;
m_dl = nullptr;
}
delete ui; delete ui;
} }
@ -145,6 +168,11 @@ void MainWindow::set_resource(const QString &res) {
} }
void MainWindow::_do_first_run() { 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*)));
connect(m_thr_data, SIGNAL(signal_download(DownloadParam*)), this, SLOT(_do_download(DownloadParam*)));
m_thr_data->start();
_start_play_thread(); _start_play_thread();
} }
@ -153,14 +181,15 @@ void MainWindow::_start_play_thread() {
m_thr_play->stop(); 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*))); disconnect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
delete m_thr_play; delete m_thr_play;
m_thr_play = nullptr; m_thr_play = nullptr;
} }
m_thr_play = new ThreadPlay(m_res); m_thr_play = new ThrPlay();
connect(m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(_do_update_data(update_data*))); 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->speed(m_bar.get_speed());
m_thr_play->start(); m_thr_play->start();
} }
@ -186,7 +215,31 @@ 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); 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(m_rc_message);
// //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);
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_draw = e->rect();
QRect rc(m_rc_message); QRect rc(m_rc_message);
//rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top()); //rc.moveTo(m_rc.left()+rc.left(), m_rc.top() + rc.top());
@ -200,16 +253,6 @@ void MainWindow::paintEvent(QPaintEvent *e)
painter.drawPixmap(to_x, to_y, m_img_message, from_x, from_y, w, h); painter.drawPixmap(to_x, to_y, m_img_message, from_x, from_y, w, h);
} }
// 绘制浮动控制窗
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_shown) { // if(!m_shown) {
// m_shown = true; // m_shown = true;
// //m_thr_play.start(); // //m_thr_play.start();
@ -234,7 +277,7 @@ void MainWindow::resume() {
m_play_state = PLAY_STATE_RUNNING; m_play_state = PLAY_STATE_RUNNING;
} }
void MainWindow::_do_update_data(update_data* dat) { void MainWindow::_do_update_data(UpdateData* dat) {
if(!dat) if(!dat)
return; return;
@ -297,11 +340,19 @@ void MainWindow::_do_update_data(update_data* dat) {
} }
else if(dat->data_type() == TYPE_MESSAGE) { else if(dat->data_type() == TYPE_MESSAGE) {
m_show_message = true;
qDebug("1message, w=%d, h=%d", m_canvas.width(), m_canvas.height());
// if(0 == m_canvas.width()) {
// QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(), dat->message());
// return;
// }
QPainter pp(&m_canvas); QPainter pp(&m_canvas);
QRect rcWin(0, 0, m_canvas.width(), m_canvas.height()); QRect rcWin(0, 0, m_canvas.width(), m_canvas.height());
pp.drawText(rcWin, Qt::AlignLeft|Qt::TextDontPrint, dat->message(), &m_rc_message); pp.drawText(rcWin, Qt::AlignLeft|Qt::TextDontPrint, dat->message(), &m_rc_message);
qDebug("message, w=%d, h=%d", m_rc_message.width(), m_rc_message.height()); 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.setWidth(m_rc_message.width()+60);
m_rc_message.setHeight(m_rc_message.height()+60); m_rc_message.setHeight(m_rc_message.height()+60);
@ -311,6 +362,11 @@ void MainWindow::_do_update_data(update_data* dat) {
pm.setPen(QColor(255,255,255,153)); pm.setPen(QColor(255,255,255,153));
pm.fillRect(m_rc_message, QColor(0,0,0,190)); 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); QRect rcText(m_rc_message);
rcText.setLeft(30); rcText.setLeft(30);
rcText.setTop(30); rcText.setTop(30);
@ -319,11 +375,14 @@ void MainWindow::_do_update_data(update_data* dat) {
(m_canvas.width() - m_rc_message.width())/2, (m_canvas.width() - m_rc_message.width())/2,
(m_canvas.height() - m_rc_message.height())/3 (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; return;
} }
else if(dat->data_type() == TYPE_ERROR) { else if(dat->data_type() == TYPE_ERROR) {
QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(), dat->message()); QMessageBox::critical(this, QGuiApplication::applicationDisplayName(), dat->message());
QApplication::instance()->exit(0); QApplication::instance()->exit(0);
return; return;
} }
@ -338,7 +397,7 @@ void MainWindow::_do_update_data(update_data* dat) {
qDebug() << "resize (" << m_rec_hdr.basic.width << "," << m_rec_hdr.basic.height << ")"; qDebug() << "resize (" << 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) { //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_canvas = QPixmap(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
//m_win_board_w = frameGeometry().width() - geometry().width(); //m_win_board_w = frameGeometry().width() - geometry().width();
@ -353,7 +412,7 @@ void MainWindow::_do_update_data(update_data* dat) {
//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_win_board_w, m_rec_hdr.basic.height + m_win_board_h);
//resize(m_rec_hdr.basic.width, m_rec_hdr.basic.height); //resize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
setFixedSize(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_canvas.fill(QColor(38, 73, 111));
@ -422,6 +481,18 @@ void MainWindow::_do_bar_fade() {
update(m_bar.rc()); update(m_bar.rc());
} }
void MainWindow::_do_download(DownloadParam* param) {
qDebug("MainWindow::_do_download(). %s %s %s", param->url.toStdString().c_str(), param->sid.toStdString().c_str(), param->fname.toStdString().c_str());
if(m_dl) {
delete m_dl;
m_dl = nullptr;
}
m_dl = new Downloader();
m_dl->run(&m_nam, param->url, param->sid, param->fname);
}
void MainWindow::mouseMoveEvent(QMouseEvent *e) { void MainWindow::mouseMoveEvent(QMouseEvent *e) {
if(!m_show_default) { if(!m_show_default) {
QRect rc = m_bar.rc(); QRect rc = m_bar.rc();

View File

@ -1,4 +1,4 @@
#ifndef MAINWINDOW_H #ifndef MAINWINDOW_H
#define MAINWINDOW_H #define MAINWINDOW_H
#include <QMainWindow> #include <QMainWindow>
@ -6,9 +6,11 @@
#include <QTimer> #include <QTimer>
#include "bar.h" #include "bar.h"
#include "thr_play.h" #include "thr_play.h"
#include "thr_data.h"
#include "update_data.h" #include "update_data.h"
#include "record_format.h" #include "record_format.h"
#include "util.h" #include "util.h"
#include "downloader.h"
#define PLAY_STATE_UNKNOWN 0 #define PLAY_STATE_UNKNOWN 0
#define PLAY_STATE_RUNNING 1 #define PLAY_STATE_RUNNING 1
@ -34,6 +36,9 @@ public:
void restart(); void restart();
void speed(int s); void speed(int s);
Downloader* downloader() {return m_dl;}
void reset_downloader() {if(m_dl){delete m_dl;m_dl= nullptr;}}
private: private:
void paintEvent(QPaintEvent *e); void paintEvent(QPaintEvent *e);
void mouseMoveEvent(QMouseEvent *e); void mouseMoveEvent(QMouseEvent *e);
@ -43,10 +48,13 @@ private:
private slots: private slots:
void _do_first_run(); // 默认界面加载完成后,开始播放操作(可能会进行数据下载) void _do_first_run(); // 默认界面加载完成后,开始播放操作(可能会进行数据下载)
void _do_update_data(update_data*); void _do_update_data(UpdateData*);
void _do_bar_fade(); void _do_bar_fade();
void _do_bar_delay_hide(); void _do_bar_delay_hide();
// void _do_download(Downloader*);
void _do_download(DownloadParam*);
private: private:
Ui::MainWindow *ui; Ui::MainWindow *ui;
@ -56,7 +64,8 @@ private:
QPixmap m_default_bg; QPixmap m_default_bg;
QString m_res; QString m_res;
ThreadPlay* m_thr_play; ThrPlay* m_thr_play;
ThrData* m_thr_data;
QPixmap m_canvas; QPixmap m_canvas;
@ -76,10 +85,14 @@ private:
int m_play_state; int m_play_state;
bool m_show_message;
QPixmap m_img_message; QPixmap m_img_message;
QRect m_rc_message; QRect m_rc_message;
QNetworkAccessManager m_nam;
Downloader* m_dl;
// for test // for test
TimeUseTest m_time_imgconvert_normal; TimeUseTest m_time_imgconvert_normal;
TimeUseTest m_time_imgconvert_compressed; TimeUseTest m_time_imgconvert_compressed;

View File

@ -61,9 +61,10 @@ typedef struct TS_RECORD_HEADER {
// 一个数据包的头 // 一个数据包的头
typedef struct TS_RECORD_PKG { typedef struct TS_RECORD_PKG {
uint8_t type; // 包的数据类型 uint8_t type; // 包的数据类型
uint8_t _reserve[3]; // 保留
uint32_t size; // 这个包的总大小(不含包头) uint32_t size; // 这个包的总大小(不含包头)
uint32_t time_ms; // 这个包距起始时间的时间差毫秒意味着一个连接不能持续超过49天 uint32_t time_ms; // 这个包距起始时间的时间差毫秒意味着一个连接不能持续超过49天
uint8_t _reserve[3]; // 保留 uint32_t index; // 这个包的序号最后一个包的序号与TS_RECORD_HEADER_INFO::packages数量匹配
}TS_RECORD_PKG; }TS_RECORD_PKG;

View File

@ -1,4 +1,4 @@
/* -*- c-basic-offset: 8 -*- /* -*- c-basic-offset: 8 -*-
rdesktop: A Remote Desktop Protocol client. rdesktop: A Remote Desktop Protocol client.
Bitmap decompression routines Bitmap decompression routines
Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008 Copyright (C) Matthew Chapman <matthewc.unsw.edu.au> 1999-2008

View File

@ -0,0 +1,665 @@
#include <QDebug>
#include <QDir>
#include <QFile>
#include <QNetworkCookie>
#include <QStandardPaths>
#include <qcoreapplication.h>
#include "thr_play.h"
#include "thr_data.h"
#include "util.h"
#include "record_format.h"
#include "mainwindow.h"
//=================================================================
// ThrData
//=================================================================
ThrData::ThrData(MainWindow* mainwin, const QString& res) {
m_mainwin = mainwin;
m_res = res;
m_need_download = false;
m_need_stop = false;
#ifdef __APPLE__
QString data_path_base = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
data_path_base += "/tp-testdata/";
#else
m_local_data_path_base = QCoreApplication::applicationDirPath() + "/record";
#endif
qDebug("data-path-base: %s", m_local_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() {
}
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::_notify_download(DownloadParam* param) {
emit signal_download(param);
}
// tp-player.exe http://teleport.domain.com:7190/{sub/path/}tp_1491560510_ca67fceb75a78c9d/1234 (注意并不直接访问此URI实际上其并不存在)
// TP服务器地址(可能包含子路径哦,例如上例中的{sub/path/}部分)/session-id(用于判断当前授权用户)/录像会话编号
void ThrData::run() {
if(!_load_header())
return;
if(!_load_keyframe())
return;
for(;;) {
if(m_need_stop)
break;
msleep(500);
}
qDebug("ThrData thread run() end.");
}
bool ThrData::_load_header() {
QString msg;
QString _tmp_res = m_res.toLower();
if(_tmp_res.startsWith("http")) {
m_need_download = true;
}
if(m_need_download) {
_notify_message(LOCAL8BIT("正在准备录像数据,请稍候..."));
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(m_url_base);
url += "/audit/get-file?act=read&type=rdp&rid=";
url += m_rid;
url += "&f=tp-rdp.tpr";
QString fname;
if(!_download_file(url, fname))
return false;
// Downloader& dl = m_mainwin->downloader();
// dl.reset();
// DownloadParam param;
// param.url = url;
// param.sid = m_sid;
// param.fname = fname;
// _notify_download(&param);
// for(;;) {
// if(dl.code() == Downloader::codeUnknown || dl.code() == Downloader::codeDownloading) {
// msleep(100);
// continue;
// }
// break;
// }
// if(dl.code() != Downloader::codeSuccess) {
// _notify_error(QString("%1").arg(LOCAL8BIT("下载文件失败!")));
// return false;
// }
Downloader* dl = m_mainwin->downloader();
QByteArray& data = dl->data();
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<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;
}
// 下载得到的数据应该是一个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<const char*>(&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() {
// _notify_error(QString("%1").arg(LOCAL8BIT("测试!")));
QString tpk_fname = QString("%1/tp-rdp.tpk").arg(m_path_base);
tpk_fname = QDir::toNativeSeparators(tpk_fname);
if(m_need_download) {
// download .tpr
QString url(m_url_base);
url += "/audit/get-file?act=read&type=rdp&rid=";
url += m_rid;
url += "&f=tp-rdp.tpk";
QString tmp_fname = QString("%1/tp-rdp.tpk.downloading").arg(m_path_base);
tmp_fname = QDir::toNativeSeparators(tmp_fname);
qDebug() << "TPK(tmp): " << tmp_fname;
qDebug() << "TPK(out): " << tpk_fname;
if(!_download_file(url, tmp_fname))
return false;
QFile::rename(tmp_fname, 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(KEYFRAME_INFO) != 0) {
qDebug() << "Can not open " << tpk_fname << " for read.";
_notify_error(LOCAL8BIT("关键帧信息文件格式错误!"));
return false;
}
qint64 read_len = 0;
int kf_count = fsize / sizeof(KEYFRAME_INFO);
for(int i = 0; i < kf_count; ++i) {
KEYFRAME_INFO kf;
memset(&kf, 0, sizeof(KEYFRAME_INFO));
read_len = f_kf.read(reinterpret_cast<char*>(&kf), sizeof(KEYFRAME_INFO));
if(read_len != sizeof(KEYFRAME_INFO)) {
qDebug() << "invaid .tpk file.";
_notify_error(LOCAL8BIT("关键帧信息文件格式错误!"));
return false;
}
m_kf.push_back(kf);
}
return true;
}
bool ThrData::_download_file(const QString& url, const QString filename) {
if(!m_need_download) {
qDebug() << "download not necessary.";
return false;
}
m_mainwin->reset_downloader();
msleep(100);
DownloadParam param;
param.url = url;
param.sid = m_sid;
param.fname = filename;
_notify_download(&param);
for(;;) {
Downloader* dl = m_mainwin->downloader();
if(!dl || dl->code() == Downloader::codeUnknown || dl->code() == Downloader::codeDownloading) {
msleep(100);
continue;
}
if(dl->code() != Downloader::codeSuccess) {
qDebug() << "download failed.";
_notify_error(QString("%1").arg(LOCAL8BIT("下载文件失败!")));
return false;
}
else {
qDebug() << "download ok.";
return true;
}
}
}
#if 0
void ThrData::run() {
QString msg;
QString path_base;
QString _tmp_res = m_res.toLower();
if(_tmp_res.startsWith("http")) {
qDebug() << "DOWNLOAD";
m_need_download = true;
// "正在缓存录像数据,请稍候..."
m_thr_play->_notify_message(LOCAL8BIT("正在下载录像数据,请稍候..."));
// 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_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()) {
msg.sprintf(LOCAL8BIT("指定的文件或目录不存在!\n\n%s").toStdString().c_str(), _tmp_res.toStdString().c_str());
m_thr_play->_notify_error(msg);
return;
}
if(fi.isFile()) {
path_base = fi.path();
}
else if(fi.isDir()) {
path_base = m_res;
}
path_base += "/";
}
//======================================
// 加载录像基本信息数据
//======================================
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.";
msg.sprintf(LOCAL8BIT("无法打开录像信息文件!\n\n%s").toStdString().c_str(), tpr_filename.toStdString().c_str());
m_thr_play->_notify_error(msg);
return;
}
TS_RECORD_HEADER hdr;
memset(&hdr, 0, sizeof(TS_RECORD_HEADER));
qint64 read_len = 0;
read_len = f_hdr.read((char*)(&hdr), sizeof(TS_RECORD_HEADER));
if(read_len != sizeof(TS_RECORD_HEADER)) {
qDebug() << "invaid .tpr file.";
msg.sprintf(LOCAL8BIT("错误的录像信息文件!\n\n%s").toStdString().c_str(), tpr_filename.toStdString().c_str());
m_thr_play->_notify_error(msg);
return;
}
if(hdr.info.ver != 4) {
qDebug() << "invaid .tpr file.";
msg.sprintf(LOCAL8BIT("不支持的录像文件版本 %d\n\n此播放器支持录像文件版本 4。").toStdString().c_str(), hdr.info.ver);
m_thr_play->_notify_error(msg);
return;
}
if(hdr.basic.width == 0 || hdr.basic.height == 0) {
m_thr_play->_notify_error(LOCAL8BIT("错误的录像信息,未记录窗口尺寸!"));
return;
}
if(hdr.info.dat_file_count == 0) {
m_thr_play->_notify_error(LOCAL8BIT("错误的录像信息,未记录数据文件数量!"));
return;
}
//======================================
// 加载关键帧数据
//======================================
QString tpk_filename(path_base);
tpk_filename += "tp-rdp.tpk";
QFile f_kf(tpk_filename);
if(!f_kf.open(QFile::ReadOnly)) {
qDebug() << "Can not open " << tpk_filename << " for read.";
msg.sprintf(LOCAL8BIT("无法打开关键帧信息文件!\n\n%s").toStdString().c_str(), tpk_filename.toStdString().c_str());
m_thr_play->_notify_error(msg);
return;
}
qint64 fsize = f_kf.size();
if(!fsize || fsize % sizeof(KEYFRAME_INFO) != 0) {
qDebug() << "Can not open " << tpk_filename << " for read.";
msg.sprintf(LOCAL8BIT("关键帧信息文件格式错误!\n\n").toStdString().c_str());
m_thr_play->_notify_error(msg);
return;
}
int kf_count = fsize / sizeof(KEYFRAME_INFO);
for(int i = 0; i < kf_count; ++i) {
KEYFRAME_INFO kf;
memset(&kf, 0, sizeof(KEYFRAME_INFO));
read_len = f_kf.read((char*)(&kf), sizeof(KEYFRAME_INFO));
if(read_len != sizeof(KEYFRAME_INFO)) {
qDebug() << "invaid .tpk file.";
msg.sprintf(LOCAL8BIT("关键帧信息文件格式错误!\n\n").toStdString().c_str());
m_thr_play->_notify_error(msg);
return;
}
m_kf.push_back(kf);
}
//======================================
// 读取并解析录像数据文件
//======================================
uint32_t fidx = 0;
while(!m_need_stop) {
for(fidx = 0; fidx < hdr.info.dat_file_count; ++fidx) {
QString tpd_filename(path_base);
QString str_tmp;
str_tmp.sprintf("tp-rdp-%d.tpd", fidx+1);
tpd_filename += str_tmp;
QFileInfo fi(tpd_filename);
if(!fi.isFile()) {
// 文件不存在,如需下载,则启动下载函数并等待下载结束。(下载是异步的吗?)
}
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());
msg = QString::fromLocal8Bit("无法打开录像数据文件!\n\n");
msg += tpd_filename;
m_thr_play->_notify_error(msg);
return;
}
for(;;) {
if(m_need_stop) {
qDebug() << "stop, user cancel 2.";
break;
}
if(m_need_pause) {
msleep(50);
time_begin += 50;
continue;
}
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());
msg = QString::fromLocal8Bit("错误的录像数据文件!\n\n");
msg += tpd_filename.toStdString().c_str();
_notify_error(msg);
return;
}
if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) {
qDebug("----key frame: %d", pkg.time_ms);
}
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_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());
msg = QString::fromLocal8Bit("错误的录像数据文件!\n\n");
msg += tpd_filename.toStdString().c_str();
_notify_error(msg);
return;
}
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) {
UpdateData* _passed_ms = new UpdateData(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_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) {
UpdateData* _passed_ms = new UpdateData(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;
}
}
}
}
}
// msg = LOCAL8BIT("开始播放...");
// m_thr_play->_notify_error(msg);
}
#endif
void ThrData::_prepare() {
UpdateData* d = new UpdateData(TYPE_HEADER_INFO);
m_locker.lock();
m_data.enqueue(d);
m_locker.unlock();
}
UpdateData* ThrData::get_data() {
m_locker.lock();
UpdateData* d = m_data.dequeue();
m_locker.unlock();
return d;
}

View File

@ -1,13 +1,20 @@
#ifndef THREADDOWNLOAD_H #ifndef THR_DATA_H
#define THREADDOWNLOAD_H #define THR_DATA_H
#include <QThread> #include <QThread>
#include <QQueue>
#include <QMutex>
#include <QNetworkReply>
#include <QFile>
#include "downloader.h"
#include "update_data.h"
#include "record_format.h"
/* /*
4MB 4MB
tp-rdp.tpr tp-rdp.tpr
tp-rdp.tpk (v3.5.0) tp-rdp.tpk (v3.5.1)
tp-rdp-1.tpd, tp-rdp-2.tpd, tp-rdp-3.tpd, ... tp-rdp-1.tpd, tp-rdp-2.tpd, tp-rdp-3.tpd, ...
线 线
@ -25,23 +32,66 @@
*/ */
typedef struct KEYFRAME_INFO {
uint32_t time_ms; // 此关键帧的时间点
uint32_t file_index; // 此关键帧图像数据位于哪一个数据文件中
uint32_t offset; // 此关键帧图像数据在数据文件中的偏移
}KEYFRAME_INFO;
class ThreadDownload : public QThread typedef std::vector<KEYFRAME_INFO> KeyFrames;
{
class MainWindow;
// 下载必要的文件解析文件数据生成图像数据QImage*),将数据包放入待显示队列中,等待 ThrPlay 线程使用
// 注意无需将所有数据解析并放入待显示队列此队列有数量限制例如1000个避免过多占用内存
class ThrData : public QThread {
Q_OBJECT
public: public:
ThreadDownload(const QString& url); ThrData(MainWindow* mainwin, const QString& url);
~ThrData();
virtual void run(); virtual void run();
void stop(); void stop();
// 下载 .tpr 和 .tpf 文件出错返回false正在下载或已经下载完成则返回true.
bool prepare(QString& path_base, QString& msg); bool have_more_data();
UpdateData* get_data();
private: private:
bool m_need_stop;
QString m_url;
bool _load_header();
bool _load_keyframe();
bool _download_file(const QString& url, const QString filename);
void _prepare();
void _notify_message(const QString& msg);
void _notify_error(const QString& err_msg);
void _notify_download(DownloadParam* param);
signals:
void signal_update_data(UpdateData*);
void signal_download(DownloadParam*);
private:
MainWindow* m_mainwin;
QQueue<UpdateData*> m_data;
QMutex m_locker;
bool m_need_stop;
bool m_need_download;
QString m_res;
QString m_local_data_path_base;
QString m_url_base;
QString m_sid;
QString m_rid;
QString m_path_base; QString m_path_base;
TS_RECORD_HEADER m_hdr;
KeyFrames m_kf;
}; };
#endif // THREADDOWNLOAD_H #endif // THR_DATA_H

View File

@ -1,34 +0,0 @@
#include "thr_download.h"
#include <QDebug>
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/";
}
}
}

View File

@ -1,27 +1,36 @@
#include <QCoreApplication> #include <QCoreApplication>
#include <QDateTime> #include <QDateTime>
#include <QDebug> #include <QDebug>
#include <QDir>
#include <QFile>
#include <QStandardPaths>
#include "thr_play.h" #include "thr_play.h"
#include "thr_data.h"
#include "record_format.h" #include "record_format.h"
#include "util.h"
ThreadPlay::ThreadPlay(const QString& res)
{ /*
*
* - 线线
* + 线500100020ms500
* - 线UI
* + if( * ( - ) >= ( - ))
* + 33
*/
ThrPlay::ThrPlay() {
m_need_stop = false; m_need_stop = false;
m_need_pause = false; m_need_pause = false;
m_speed = 2; m_speed = 2;
m_res = res; // m_res = res;
m_thr_download = nullptr; // m_thr_data = nullptr;
} }
ThreadPlay::~ThreadPlay() { ThrPlay::~ThrPlay() {
stop(); stop();
} }
void ThreadPlay::stop() { void ThrPlay::stop() {
if(!isRunning()) if(!isRunning())
return; return;
@ -31,28 +40,31 @@ void ThreadPlay::stop() {
wait(); wait();
qDebug() << "play-thread end."; qDebug() << "play-thread end.";
if(m_thr_download) { // if(m_thr_data) {
m_thr_download->stop(); // m_thr_data->stop();
//m_thr_download->wait(); // qDebug("delete thrData.");
delete m_thr_download; // //m_thr_download->wait();
m_thr_download = nullptr; // delete m_thr_data;
} // m_thr_data = nullptr;
// }
} }
void ThreadPlay::_notify_message(const QString& msg) { void ThrPlay::_notify_message(const QString& msg) {
update_data* _msg = new update_data(TYPE_MESSAGE); UpdateData* _msg = new UpdateData(TYPE_MESSAGE);
_msg->message(msg); _msg->message(msg);
emit signal_update_data(_msg); emit signal_update_data(_msg);
} }
void ThreadPlay::_notify_error(const QString& err_msg) { void ThrPlay::_notify_error(const QString& msg) {
update_data* _err = new update_data(TYPE_ERROR); UpdateData* _msg = new UpdateData(TYPE_ERROR);
_err->message(err_msg); _msg->message(msg);
emit signal_update_data(_err); emit signal_update_data(_msg);
} }
void ThrPlay::run() {
void ThreadPlay::run() { // http://127.0.0.1:7190/tp_1491560510_ca67fceb75a78c9d/211
// E:\work\tp4a\teleport\server\share\replay\rdp\000000211
//#ifdef __APPLE__ //#ifdef __APPLE__
// QString currentPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation); // QString currentPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
@ -67,6 +79,14 @@ void ThreadPlay::run() {
// /private/var/folders/_3/zggrxjdx1lxcdqnfsbgpcwzh0000gn/T // /private/var/folders/_3/zggrxjdx1lxcdqnfsbgpcwzh0000gn/T
//qDebug() << "tmp:" << QStandardPaths::writableLocation(QStandardPaths::TempLocation); //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) // base of data path (include the .tpr file)
QString path_base; QString path_base;
@ -78,8 +98,8 @@ void ThreadPlay::run() {
// "正在缓存录像数据,请稍候..." // "正在缓存录像数据,请稍候..."
_notify_message("正在缓存录像数据,请稍候..."); _notify_message("正在缓存录像数据,请稍候...");
m_thr_download = new ThreadDownload(m_res); m_thr_data = new ThreadDownload(m_res);
m_thr_download->start(); m_thr_data->start();
QString msg; QString msg;
for(;;) { for(;;) {
@ -88,7 +108,7 @@ void ThreadPlay::run() {
if(m_need_stop) if(m_need_stop)
return; return;
if(!m_thr_download->prepare(path_base, msg)) { if(!m_thr_data->prepare(path_base, msg)) {
msg.sprintf("指定的文件或目录不存在!\n\n%s", _tmp_res.toStdString().c_str()); msg.sprintf("指定的文件或目录不存在!\n\n%s", _tmp_res.toStdString().c_str());
_notify_error(msg); _notify_error(msg);
return; return;
@ -147,7 +167,7 @@ void ThreadPlay::run() {
return; return;
} }
else { else {
update_data* dat = new update_data(TYPE_HEADER_INFO); UpdateData* dat = new UpdateData(TYPE_HEADER_INFO);
dat->alloc_data(sizeof(TS_RECORD_HEADER)); dat->alloc_data(sizeof(TS_RECORD_HEADER));
read_len = f_hdr.read((char*)(dat->data_buf()), dat->data_len()); read_len = f_hdr.read((char*)(dat->data_buf()), dat->data_len());
@ -251,7 +271,7 @@ void ThreadPlay::run() {
qDebug("----key frame: %d", pkg.time_ms); qDebug("----key frame: %d", pkg.time_ms);
} }
update_data* dat = new update_data(TYPE_DATA); UpdateData* dat = new UpdateData(TYPE_DATA);
dat->alloc_data(sizeof(TS_RECORD_PKG) + pkg.size); dat->alloc_data(sizeof(TS_RECORD_PKG) + pkg.size);
memcpy(dat->data_buf(), &pkg, sizeof(TS_RECORD_PKG)); memcpy(dat->data_buf(), &pkg, sizeof(TS_RECORD_PKG));
read_len = f_dat.read((char*)(dat->data_buf()+sizeof(TS_RECORD_PKG)), pkg.size); read_len = f_dat.read((char*)(dat->data_buf()+sizeof(TS_RECORD_PKG)), pkg.size);
@ -271,7 +291,7 @@ void ThreadPlay::run() {
if(time_pass > total_ms) if(time_pass > total_ms)
time_pass = total_ms; time_pass = total_ms;
if(time_pass - time_last_pass > 200) { if(time_pass - time_last_pass > 200) {
update_data* _passed_ms = new update_data(TYPE_PLAYED_MS); UpdateData* _passed_ms = new UpdateData(TYPE_PLAYED_MS);
_passed_ms->played_ms(time_pass); _passed_ms->played_ms(time_pass);
emit signal_update_data(_passed_ms); emit signal_update_data(_passed_ms);
time_last_pass = time_pass; time_last_pass = time_pass;
@ -307,7 +327,7 @@ void ThreadPlay::run() {
if(_time_pass > total_ms) if(_time_pass > total_ms)
_time_pass = total_ms; _time_pass = total_ms;
if(_time_pass - time_last_pass > 200) { if(_time_pass - time_last_pass > 200) {
update_data* _passed_ms = new update_data(TYPE_PLAYED_MS); UpdateData* _passed_ms = new UpdateData(TYPE_PLAYED_MS);
_passed_ms->played_ms(_time_pass); _passed_ms->played_ms(_time_pass);
emit signal_update_data(_passed_ms); emit signal_update_data(_passed_ms);
time_last_pass = _time_pass; time_last_pass = _time_pass;
@ -330,6 +350,9 @@ void ThreadPlay::run() {
_notify_message(msg); _notify_message(msg);
} }
update_data* _end = new update_data(TYPE_END); #endif
qDebug("play end.");
UpdateData* _end = new UpdateData(TYPE_END);
emit signal_update_data(_end); emit signal_update_data(_end);
} }

View File

@ -1,16 +1,19 @@
#ifndef THR_PLAY_H #ifndef THR_PLAY_H
#define THR_PLAY_H #define THR_PLAY_H
#include <QThread> #include <QThread>
#include "update_data.h" #include "update_data.h"
#include "thr_download.h" #include "downloader.h"
class ThreadPlay : public QThread // 根据播放规则将要播放的图像发送给主UI线程进行显示
class ThrPlay : public QThread
{ {
Q_OBJECT Q_OBJECT
friend class ThrData;
public: public:
ThreadPlay(const QString& res); ThrPlay();
~ThreadPlay(); ~ThrPlay();
virtual void run(); virtual void run();
void stop(); void stop();
@ -23,17 +26,12 @@ private:
void _notify_error(const QString& err_msg); void _notify_error(const QString& err_msg);
signals: signals:
void signal_update_data(update_data*); void signal_update_data(UpdateData*);
private: private:
bool m_need_stop; bool m_need_stop;
bool m_need_pause; bool m_need_pause;
int m_speed; int m_speed;
QString m_res;
bool m_need_download;
ThreadDownload* m_thr_download;
}; };
#endif // THR_PLAY_H #endif // THR_PLAY_H

View File

@ -1,27 +1,29 @@
TEMPLATE = app TEMPLATE = app
TARGET = tp-player TARGET = tp-player
QT += core gui widgets QT += core gui widgets network
HEADERS += \ HEADERS += \
mainwindow.h \ mainwindow.h \
bar.h \ bar.h \
thr_download.h \
thr_play.h \ thr_play.h \
thr_data.h \
update_data.h \ update_data.h \
record_format.h \ record_format.h \
rle.h \ rle.h \
util.h util.h \
downloader.h
SOURCES += \ SOURCES += \
main.cpp \ main.cpp \
mainwindow.cpp \ mainwindow.cpp \
bar.cpp \ bar.cpp \
thr_download.cpp \
thr_play.cpp \ thr_play.cpp \
thr_data.cpp \
update_data.cpp \ update_data.cpp \
rle.c \ rle.c \
util.cpp util.cpp \
downloader.cpp
RESOURCES += \ RESOURCES += \
tp-player.qrc tp-player.qrc

View File

@ -1,18 +1,18 @@
#include "update_data.h" #include "update_data.h"
update_data::update_data(int data_type, QObject *parent) : QObject(parent) UpdateData::UpdateData(int data_type, QObject *parent) : QObject(parent)
{ {
m_data_type = data_type; m_data_type = data_type;
m_data_buf = nullptr; m_data_buf = nullptr;
m_data_len = 0; m_data_len = 0;
} }
update_data::~update_data() { UpdateData::~UpdateData() {
if(m_data_buf) if(m_data_buf)
delete m_data_buf; delete m_data_buf;
} }
void update_data::alloc_data(uint32_t len) { void UpdateData::alloc_data(uint32_t len) {
if(m_data_buf) if(m_data_buf)
delete m_data_buf; delete m_data_buf;
@ -21,7 +21,7 @@ void update_data::alloc_data(uint32_t len) {
m_data_len = len; m_data_len = len;
} }
void update_data::attach_data(const uint8_t* dat, uint32_t len) { void UpdateData::attach_data(const uint8_t* dat, uint32_t len) {
if(m_data_buf) if(m_data_buf)
delete m_data_buf; delete m_data_buf;
m_data_buf = new uint8_t[len]; m_data_buf = new uint8_t[len];

View File

@ -11,12 +11,12 @@
#define TYPE_MESSAGE 5 #define TYPE_MESSAGE 5
#define TYPE_ERROR 6 #define TYPE_ERROR 6
class update_data : public QObject class UpdateData : public QObject
{ {
Q_OBJECT Q_OBJECT
public: public:
explicit update_data(int data_type, QObject *parent = nullptr); explicit UpdateData(int data_type, QObject *parent = nullptr);
virtual ~update_data(); virtual ~UpdateData();
void alloc_data(uint32_t len); void alloc_data(uint32_t len);
void attach_data(const uint8_t* dat, uint32_t len); void attach_data(const uint8_t* dat, uint32_t len);
@ -47,7 +47,7 @@ private:
class UpdateDataHelper { class UpdateDataHelper {
public: public:
UpdateDataHelper(update_data* data) { UpdateDataHelper(UpdateData* data) {
m_data = data; m_data = data;
} }
~UpdateDataHelper() { ~UpdateDataHelper() {
@ -56,7 +56,7 @@ public:
} }
private: private:
update_data* m_data; UpdateData* m_data;
}; };

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

@ -77,9 +77,10 @@ typedef struct TS_RECORD_HEADER {
// 一个数据包的头 // 一个数据包的头
typedef struct TS_RECORD_PKG { typedef struct TS_RECORD_PKG {
ex_u8 type; // 包的数据类型 ex_u8 type; // 包的数据类型
ex_u8 _reserve[3]; // 保留
ex_u32 size; // 这个包的总大小(不含包头) ex_u32 size; // 这个包的总大小(不含包头)
ex_u32 time_ms; // 这个包距起始时间的时间差毫秒意味着一个连接不能持续超过49天 ex_u32 time_ms; // 这个包距起始时间的时间差毫秒意味着一个连接不能持续超过49天
ex_u8 _reserve[3]; // 保留 ex_u32 index; // 这个包的序号最后一个包的序号与TS_RECORD_HEADER_INFO::packages数量匹配
}TS_RECORD_PKG; }TS_RECORD_PKG;
#pragma pack(pop) #pragma pack(pop)

View File

@ -671,12 +671,13 @@ class DoGetFileHandler(TPBaseHandler):
# return self.write('need login first.') # return self.write('need login first.')
# self._user = _user # self._user = _user
if not self._user['_is_login']: # when test, disable auth.
self.set_status(401) # 401=未授权, 要求身份验证 # if not self._user['_is_login']:
return self.write('need login first.') # self.set_status(401) # 401=未授权, 要求身份验证
if (self._user['privilege'] & require_privilege) == 0: # return self.write('need login first.')
self.set_status(403) # 403=禁止 # if (self._user['privilege'] & require_privilege) == 0:
return self.write('you have no such privilege.') # self.set_status(403) # 403=禁止
# return self.write('you have no such privilege.')
act = self.get_argument('act', None) act = self.get_argument('act', None)
_type = self.get_argument('type', None) _type = self.get_argument('type', None)