mirror of https://github.com/tp4a/teleport
.temp.
parent
6f95664ca1
commit
e5610b17a2
|
@ -0,0 +1,21 @@
|
|||
#include "dlgmessage.h"
|
||||
#include "ui_dlgmessage.h"
|
||||
|
||||
DlgMessage::DlgMessage(QWidget *parent) :
|
||||
QDialog(parent),
|
||||
ui(new Ui::DlgMessage)
|
||||
{
|
||||
ui->setupUi(this);
|
||||
}
|
||||
|
||||
DlgMessage::~DlgMessage()
|
||||
{
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void DlgMessage::set_text(const QString& text) {
|
||||
// TODO: 根据文字长度,父窗口宽度,调节对话框宽度,最大不超过父窗口宽度的 2/3。
|
||||
// 调节label的宽度和高度,并调节对话框高度,最后将对话框调整到父窗口居中的位置。
|
||||
|
||||
ui->label->setText(text);
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#ifndef DLGMESSAGE_H
|
||||
#define DLGMESSAGE_H
|
||||
|
||||
#include <QDialog>
|
||||
|
||||
namespace Ui {
|
||||
class DlgMessage;
|
||||
}
|
||||
|
||||
class DlgMessage : public QDialog
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DlgMessage(QWidget *parent = nullptr);
|
||||
~DlgMessage();
|
||||
|
||||
void set_text(const QString& text);
|
||||
|
||||
private:
|
||||
Ui::DlgMessage *ui;
|
||||
};
|
||||
|
||||
#endif // DLGMESSAGE_H
|
|
@ -0,0 +1,32 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<ui version="4.0">
|
||||
<class>DlgMessage</class>
|
||||
<widget class="QDialog" name="DlgMessage">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>0</x>
|
||||
<y>0</y>
|
||||
<width>400</width>
|
||||
<height>120</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="windowTitle">
|
||||
<string/>
|
||||
</property>
|
||||
<widget class="QLabel" name="label">
|
||||
<property name="geometry">
|
||||
<rect>
|
||||
<x>10</x>
|
||||
<y>10</y>
|
||||
<width>59</width>
|
||||
<height>16</height>
|
||||
</rect>
|
||||
</property>
|
||||
<property name="text">
|
||||
<string/>
|
||||
</property>
|
||||
</widget>
|
||||
</widget>
|
||||
<resources/>
|
||||
<connections/>
|
||||
</ui>
|
|
@ -1,5 +1,8 @@
|
|||
#include "mainwindow.h"
|
||||
#include "mainwindow.h"
|
||||
#include <QApplication>
|
||||
#include <QCommandLineParser>
|
||||
#include <QDebug>
|
||||
#include <QMessageBox>
|
||||
|
||||
// 编译出来的可执行程序复制到单独目录,然后执行 windeployqt 应用程序文件名
|
||||
// 即可自动将依赖的动态库等复制到此目录中。有些文件是多余的,可以酌情删除。
|
||||
|
@ -11,8 +14,41 @@ int main(int argc, char *argv[])
|
|||
//#endif
|
||||
|
||||
QApplication a(argc, argv);
|
||||
MainWindow w;
|
||||
w.show();
|
||||
|
||||
QGuiApplication::setApplicationDisplayName("TP-Player");
|
||||
|
||||
QCommandLineParser parser;
|
||||
const QCommandLineOption opt_help = parser.addHelpOption();
|
||||
|
||||
parser.addPositionalArgument("RESOURCE", "teleport record resource to be play.");
|
||||
|
||||
if(!parser.parse(QCoreApplication::arguments())) {
|
||||
QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(),
|
||||
//"<html><head/><body><h2 style=\"color:#ff0000;\">" + parser.errorText() + "</h2><pre>"
|
||||
"<html><head/><body><h2>" + parser.errorText() + "</h2><pre>"
|
||||
+ parser.helpText() + "</pre></body></html>");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if(parser.isSet(opt_help)) {
|
||||
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 for download teleport record file."
|
||||
+ "</pre></body></html>");
|
||||
return 2;
|
||||
}
|
||||
|
||||
const QStringList args = parser.positionalArguments();
|
||||
QString resource = args.at(0);
|
||||
qDebug() << resource;
|
||||
|
||||
MainWindow w;
|
||||
w.set_resource(resource);
|
||||
w.show();
|
||||
return a.exec();
|
||||
}
|
||||
|
|
|
@ -7,6 +7,8 @@
|
|||
#include <QPainter>
|
||||
#include <QDesktopWidget>
|
||||
#include <QPaintEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QDialogButtonBox>
|
||||
|
||||
bool rdpimg2QImage(QImage& out, int w, int h, int bitsPerPixel, bool isCompressed, uint8_t* dat, uint32_t len) {
|
||||
switch(bitsPerPixel) {
|
||||
|
@ -67,7 +69,7 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||
QMainWindow(parent),
|
||||
ui(new Ui::MainWindow)
|
||||
{
|
||||
m_shown = false;
|
||||
//m_shown = false;
|
||||
m_show_default = true;
|
||||
m_bar_shown = false;
|
||||
m_bar_fade_in = false;
|
||||
|
@ -78,6 +80,8 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||
m_thr_play = nullptr;
|
||||
m_play_state = PLAY_STATE_UNKNOWN;
|
||||
|
||||
m_msg_box = nullptr;
|
||||
|
||||
ui->setupUi(this);
|
||||
|
||||
ui->centralWidget->setMouseTracking(true);
|
||||
|
@ -103,28 +107,46 @@ MainWindow::MainWindow(QWidget *parent) :
|
|||
}
|
||||
|
||||
// connect(&m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(_do_update_data(update_data*)));
|
||||
connect(&m_timer_first_run, SIGNAL(timeout()), this, SLOT(_do_first_run()));
|
||||
connect(&m_timer_bar_fade, SIGNAL(timeout()), this, SLOT(_do_bar_fade()));
|
||||
connect(&m_timer_bar_delay_hide, SIGNAL(timeout()), this, SLOT(_do_bar_delay_hide()));
|
||||
|
||||
m_timer_first_run.setSingleShot(true);
|
||||
m_timer_first_run.start(500);
|
||||
}
|
||||
|
||||
MainWindow::~MainWindow()
|
||||
{
|
||||
if(m_thr_play) {
|
||||
m_thr_play->stop();
|
||||
m_thr_play->wait();
|
||||
//m_thr_play->wait();
|
||||
//qDebug() << "play thread stoped.";
|
||||
|
||||
disconnect(m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(_do_update_data(update_data*)));
|
||||
|
||||
delete m_thr_play;
|
||||
m_thr_play = nullptr;
|
||||
}
|
||||
|
||||
if(m_msg_box) {
|
||||
delete m_msg_box;
|
||||
}
|
||||
|
||||
delete ui;
|
||||
}
|
||||
|
||||
void MainWindow::set_resource(const QString &res) {
|
||||
m_res = res;
|
||||
}
|
||||
|
||||
void MainWindow::_do_first_run() {
|
||||
_start_play_thread();
|
||||
}
|
||||
|
||||
void MainWindow::_start_play_thread() {
|
||||
if(m_thr_play) {
|
||||
m_thr_play->stop();
|
||||
m_thr_play->wait();
|
||||
//m_thr_play->wait();
|
||||
|
||||
disconnect(m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(_do_update_data(update_data*)));
|
||||
|
||||
|
@ -132,7 +154,7 @@ void MainWindow::_start_play_thread() {
|
|||
m_thr_play = nullptr;
|
||||
}
|
||||
|
||||
m_thr_play = new ThreadPlay;
|
||||
m_thr_play = new ThreadPlay(m_res);
|
||||
connect(m_thr_play, SIGNAL(signal_update_data(update_data*)), this, SLOT(_do_update_data(update_data*)));
|
||||
m_thr_play->speed(m_bar.get_speed());
|
||||
m_thr_play->start();
|
||||
|
@ -169,11 +191,11 @@ void MainWindow::paintEvent(QPaintEvent *e)
|
|||
}
|
||||
}
|
||||
|
||||
if(!m_shown) {
|
||||
m_shown = true;
|
||||
//m_thr_play.start();
|
||||
_start_play_thread();
|
||||
}
|
||||
// if(!m_shown) {
|
||||
// m_shown = true;
|
||||
// //m_thr_play.start();
|
||||
// _start_play_thread();
|
||||
// }
|
||||
}
|
||||
|
||||
void MainWindow::pause() {
|
||||
|
@ -200,6 +222,9 @@ void MainWindow::_do_update_data(update_data* dat) {
|
|||
UpdateDataHelper data_helper(dat);
|
||||
|
||||
if(dat->data_type() == TYPE_DATA) {
|
||||
if(m_msg_box) {
|
||||
m_msg_box->hide();
|
||||
}
|
||||
|
||||
if(dat->data_len() <= sizeof(TS_RECORD_PKG)) {
|
||||
qDebug() << "invalid record package(1).";
|
||||
|
@ -250,13 +275,35 @@ void MainWindow::_do_update_data(update_data* dat) {
|
|||
return;
|
||||
}
|
||||
|
||||
if(dat->data_type() == TYPE_TIMER) {
|
||||
m_bar.update_passed_time(dat->passed_ms());
|
||||
else if(dat->data_type() == TYPE_PLAYED_MS) {
|
||||
m_bar.update_passed_time(dat->played_ms());
|
||||
return;
|
||||
}
|
||||
|
||||
else if(dat->data_type() == TYPE_MESSAGE) {
|
||||
//QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(), dat->message());
|
||||
if(!m_msg_box) {
|
||||
m_msg_box = new DlgMessage(this);
|
||||
// 无窗口标题栏,无边框
|
||||
m_msg_box->setWindowFlags(Qt::Dialog | Qt::MSWindowsFixedSizeDialogHint | Qt::ToolTip | Qt::FramelessWindowHint);
|
||||
// 设置成非模态
|
||||
m_msg_box->setModal(false);
|
||||
}
|
||||
|
||||
m_msg_box->set_text(dat->message());
|
||||
// 显示对话框
|
||||
m_msg_box->show();
|
||||
return;
|
||||
}
|
||||
|
||||
else if(dat->data_type() == TYPE_ERROR) {
|
||||
QMessageBox::warning(nullptr, QGuiApplication::applicationDisplayName(), dat->message());
|
||||
QApplication::instance()->exit(0);
|
||||
return;
|
||||
}
|
||||
|
||||
// 这是播放开始时收到的第一个数据包
|
||||
if(dat->data_type() == TYPE_HEADER_INFO) {
|
||||
else if(dat->data_type() == TYPE_HEADER_INFO) {
|
||||
if(dat->data_len() != sizeof(TS_RECORD_HEADER)) {
|
||||
qDebug() << "invalid record header.";
|
||||
return;
|
||||
|
@ -264,41 +311,39 @@ void MainWindow::_do_update_data(update_data* dat) {
|
|||
memcpy(&m_rec_hdr, dat->data_buf(), sizeof(TS_RECORD_HEADER));
|
||||
|
||||
qDebug() << "resize (" << m_rec_hdr.basic.width << "," << m_rec_hdr.basic.height << ")";
|
||||
if(m_rec_hdr.basic.width > 0 && m_rec_hdr.basic.height > 0) {
|
||||
|
||||
if(m_canvas.width() != m_rec_hdr.basic.width && m_canvas.height() != m_rec_hdr.basic.height) {
|
||||
m_canvas = QPixmap(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
if(m_canvas.width() != m_rec_hdr.basic.width && m_canvas.height() != m_rec_hdr.basic.height) {
|
||||
m_canvas = QPixmap(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
|
||||
//m_win_board_w = frameGeometry().width() - geometry().width();
|
||||
//m_win_board_h = frameGeometry().height() - geometry().height();
|
||||
//m_win_board_w = frameGeometry().width() - geometry().width();
|
||||
//m_win_board_h = frameGeometry().height() - geometry().height();
|
||||
|
||||
QDesktopWidget *desktop = QApplication::desktop(); // =qApp->desktop();也可以
|
||||
qDebug("desktop w:%d,h:%d, this w:%d,h:%d", desktop->width(), desktop->height(), width(), height());
|
||||
//move((desktop->width() - this->width())/2, (desktop->height() - this->height())/2);
|
||||
move(10, (desktop->height() - m_rec_hdr.basic.height)/2);
|
||||
QDesktopWidget *desktop = QApplication::desktop(); // =qApp->desktop();也可以
|
||||
qDebug("desktop w:%d,h:%d, this w:%d,h:%d", desktop->width(), desktop->height(), width(), height());
|
||||
//move((desktop->width() - this->width())/2, (desktop->height() - this->height())/2);
|
||||
move(10, (desktop->height() - m_rec_hdr.basic.height)/2);
|
||||
|
||||
//setFixedSize(m_rec_hdr.basic.width + m_win_board_w, m_rec_hdr.basic.height + m_win_board_h);
|
||||
//resize(m_rec_hdr.basic.width + m_win_board_w, m_rec_hdr.basic.height + m_win_board_h);
|
||||
//resize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
setFixedSize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
}
|
||||
|
||||
m_canvas.fill(QColor(38, 73, 111));
|
||||
|
||||
m_show_default = false;
|
||||
repaint();
|
||||
|
||||
m_bar.start(m_rec_hdr.info.time_ms, 640);
|
||||
m_bar_shown = true;
|
||||
m_play_state = PLAY_STATE_RUNNING;
|
||||
|
||||
update(m_bar.rc());
|
||||
|
||||
m_bar_fade_in = false;
|
||||
m_bar_fading = true;
|
||||
m_timer_bar_delay_hide.start(2000);
|
||||
//setFixedSize(m_rec_hdr.basic.width + m_win_board_w, m_rec_hdr.basic.height + m_win_board_h);
|
||||
//resize(m_rec_hdr.basic.width + m_win_board_w, m_rec_hdr.basic.height + m_win_board_h);
|
||||
//resize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
setFixedSize(m_rec_hdr.basic.width, m_rec_hdr.basic.height);
|
||||
}
|
||||
|
||||
m_canvas.fill(QColor(38, 73, 111));
|
||||
|
||||
m_show_default = false;
|
||||
repaint();
|
||||
|
||||
m_bar.start(m_rec_hdr.info.time_ms, 640);
|
||||
m_bar_shown = true;
|
||||
m_play_state = PLAY_STATE_RUNNING;
|
||||
|
||||
update(m_bar.rc());
|
||||
|
||||
m_bar_fade_in = false;
|
||||
m_bar_fading = true;
|
||||
m_timer_bar_delay_hide.start(2000);
|
||||
|
||||
QString title;
|
||||
if (m_rec_hdr.basic.conn_port == 3389)
|
||||
title.sprintf("[%s] %s@%s [Teleport-RDP录像回放]", m_rec_hdr.basic.acc_username, m_rec_hdr.basic.user_username, m_rec_hdr.basic.conn_ip);
|
||||
|
@ -311,7 +356,7 @@ void MainWindow::_do_update_data(update_data* dat) {
|
|||
}
|
||||
|
||||
|
||||
if(dat->data_type() == TYPE_END) {
|
||||
else if(dat->data_type() == TYPE_END) {
|
||||
m_bar.end();
|
||||
m_play_state = PLAY_STATE_STOP;
|
||||
return;
|
||||
|
@ -383,6 +428,8 @@ void MainWindow::mouseMoveEvent(QMouseEvent *e) {
|
|||
}
|
||||
|
||||
void MainWindow::mousePressEvent(QMouseEvent *e) {
|
||||
// QApplication::instance()->exit(0);
|
||||
// return;
|
||||
if(!m_show_default) {
|
||||
QRect rc = m_bar.rc();
|
||||
if(rc.contains(e->pos())) {
|
||||
|
|
|
@ -2,11 +2,13 @@
|
|||
#define MAINWINDOW_H
|
||||
|
||||
#include <QMainWindow>
|
||||
#include <QMessageBox>
|
||||
#include <QTimer>
|
||||
#include "bar.h"
|
||||
#include "thr_play.h"
|
||||
#include "update_data.h"
|
||||
#include "record_format.h"
|
||||
#include "dlgmessage.h"
|
||||
|
||||
#define PLAY_STATE_UNKNOWN 0
|
||||
#define PLAY_STATE_RUNNING 1
|
||||
|
@ -25,6 +27,8 @@ public:
|
|||
explicit MainWindow(QWidget *parent = nullptr);
|
||||
~MainWindow();
|
||||
|
||||
void set_resource(const QString& res);
|
||||
|
||||
void pause();
|
||||
void resume();
|
||||
void restart();
|
||||
|
@ -38,17 +42,20 @@ private:
|
|||
void _start_play_thread();
|
||||
|
||||
private slots:
|
||||
void _do_first_run(); // 默认界面加载完成后,开始播放操作(可能会进行数据下载)
|
||||
void _do_update_data(update_data*);
|
||||
void _do_bar_fade();
|
||||
void _do_bar_delay_hide();
|
||||
|
||||
private:
|
||||
Ui::MainWindow *ui;
|
||||
bool m_shown;
|
||||
|
||||
//bool m_shown;
|
||||
bool m_show_default;
|
||||
bool m_bar_shown;
|
||||
QPixmap m_default_bg;
|
||||
|
||||
QString m_res;
|
||||
ThreadPlay* m_thr_play;
|
||||
|
||||
QPixmap m_canvas;
|
||||
|
@ -60,6 +67,7 @@ private:
|
|||
QPixmap m_pt_normal;
|
||||
TS_RECORD_RDP_POINTER m_pt;
|
||||
|
||||
QTimer m_timer_first_run;
|
||||
QTimer m_timer_bar_fade;
|
||||
QTimer m_timer_bar_delay_hide;
|
||||
bool m_bar_fade_in;
|
||||
|
@ -67,6 +75,9 @@ private:
|
|||
qreal m_bar_opacity;
|
||||
|
||||
int m_play_state;
|
||||
|
||||
//QMessageBox* m_msg_box;
|
||||
DlgMessage* m_msg_box;
|
||||
};
|
||||
|
||||
#endif // MAINWINDOW_H
|
||||
|
|
|
@ -3,13 +3,6 @@
|
|||
|
||||
#include <Qt>
|
||||
|
||||
|
||||
#define TYPE_HEADER_INFO 0
|
||||
#define TYPE_DATA 1
|
||||
#define TYPE_TIMER 2
|
||||
#define TYPE_END 3
|
||||
|
||||
|
||||
#define TS_RECORD_TYPE_RDP_POINTER 0x12 // 鼠标坐标位置改变,用于绘制虚拟鼠标
|
||||
#define TS_RECORD_TYPE_RDP_IMAGE 0x13 // 服务端返回的图像,用于展示
|
||||
|
||||
|
@ -22,11 +15,12 @@
|
|||
|
||||
// 录像文件头(随着录像数据写入,会改变的部分)
|
||||
typedef struct TS_RECORD_HEADER_INFO {
|
||||
uint32_t magic; // "TPPR" 标志 TelePort Protocol Record
|
||||
uint16_t ver; // 录像文件版本,目前为3
|
||||
uint32_t packages; // 总包数
|
||||
uint32_t time_ms; // 总耗时(毫秒)
|
||||
//uint32_t file_size; // 数据文件大小
|
||||
uint32_t magic; // "TPPR" 标志 TelePort Protocol Record
|
||||
uint16_t ver; // 录像文件版本,从3.5.0开始,为4
|
||||
uint32_t packages; // 总包数
|
||||
uint32_t time_ms; // 总耗时(毫秒)
|
||||
uint32_t dat_file_count; // 数据文件数量
|
||||
uint8_t _reserve[64-4-2-4-4-4];
|
||||
}TS_RECORD_HEADER_INFO;
|
||||
#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO)
|
||||
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
#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/";
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,47 @@
|
|||
#ifndef THREADDOWNLOAD_H
|
||||
#define THREADDOWNLOAD_H
|
||||
|
||||
#include <QThread>
|
||||
|
||||
/*
|
||||
为支持“边下载,边播放”、“可拖动进度条”等功能,录像数据会分为多个文件存放,目前每个文件约4MB。
|
||||
例如:
|
||||
tp-rdp.tpr
|
||||
tp-rdp.tpk (关键帧信息文件,v3.5.0开始引入)
|
||||
tp-rdp-1.tpd, tp-rdp-2.tpd, tp-rdp-3.tpd, ...
|
||||
这样,下载完一个数据文件,即可播放此数据文件中的内容,同时下载线程可以下载后续数据文件。
|
||||
|
||||
为支持“拖动进度条”,可以在数据文件中插入关键帧的方式,这就要求记录录像数据的同时对图像数据进行解码,
|
||||
并同步合成全屏数据(关键帧),每经过一段时间(或者一定数量的图像数据包)之后,就在录像数据文件中增加一个关键帧。
|
||||
正常播放时,跳过此关键帧。
|
||||
当进度条拖放发生时,找到目标时间点之前的最后一个关键帧,从此处开始无延时播放到目标时间点,然后正常播放。
|
||||
因此,需要能够快速定位到各个关键帧,因为有可能此时尚未下载这个关键帧所在的数据文件。定位到此关键帧
|
||||
所在的数据文件后,下载线程要放弃当前下载任务(如果不是当前正在下载的数据文件),并开始下载新的数据文件。
|
||||
因此,需要引入新的关键帧信息文件(.tpk),记录各个关键帧数据所在的数据文件序号、偏移、时间点等信息。
|
||||
|
||||
另外,为保证数据文件、关键帧信息文件等下载正确,下载时保存到对应的临时文件中,并记录已下载字节数,下载完成后再改名,如:
|
||||
tp-rdp.tpk.tmp, tp-rdp.tpk.len
|
||||
tp-rdp-1.tpd.tmp, tp-rdp-1.tpd.len, ...
|
||||
这样,下次需要下载指定文件时,如果发现对应的临时文件存在,可以根据已下载字节数,继续下载。
|
||||
*/
|
||||
|
||||
|
||||
class ThreadDownload : public QThread
|
||||
{
|
||||
public:
|
||||
ThreadDownload(const QString& url);
|
||||
|
||||
virtual void run();
|
||||
void stop();
|
||||
|
||||
// 下载 .tpr 和 .tpf 文件,出错返回false,正在下载或已经下载完成则返回true.
|
||||
bool prepare(QString& path_base, QString& msg);
|
||||
|
||||
private:
|
||||
bool m_need_stop;
|
||||
QString m_url;
|
||||
|
||||
QString m_path_base;
|
||||
};
|
||||
|
||||
#endif // THREADDOWNLOAD_H
|
|
@ -3,27 +3,136 @@
|
|||
#include <QDebug>
|
||||
#include <QDir>
|
||||
#include <QFile>
|
||||
#include <QStandardPaths>
|
||||
|
||||
#include "thr_play.h"
|
||||
#include "record_format.h"
|
||||
|
||||
ThreadPlay::ThreadPlay()
|
||||
ThreadPlay::ThreadPlay(const QString& res)
|
||||
{
|
||||
m_need_stop = false;
|
||||
m_need_pause = false;
|
||||
m_speed = 2;
|
||||
m_res = res;
|
||||
m_thr_download = nullptr;
|
||||
}
|
||||
|
||||
ThreadPlay::~ThreadPlay() {
|
||||
stop();
|
||||
}
|
||||
|
||||
void ThreadPlay::stop() {
|
||||
if(!isRunning())
|
||||
return;
|
||||
|
||||
// warning: never call stop() inside thread::run() loop.
|
||||
|
||||
m_need_stop = true;
|
||||
wait();
|
||||
qDebug() << "play-thread end.";
|
||||
|
||||
if(m_thr_download) {
|
||||
m_thr_download->stop();
|
||||
//m_thr_download->wait();
|
||||
delete m_thr_download;
|
||||
m_thr_download = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void ThreadPlay::_notify_message(const QString& msg) {
|
||||
update_data* _msg = new update_data(TYPE_MESSAGE);
|
||||
_msg->message(msg);
|
||||
emit signal_update_data(_msg);
|
||||
}
|
||||
|
||||
void ThreadPlay::_notify_error(const QString& err_msg) {
|
||||
update_data* _err = new update_data(TYPE_ERROR);
|
||||
_err->message(err_msg);
|
||||
emit signal_update_data(_err);
|
||||
}
|
||||
|
||||
|
||||
void ThreadPlay::run() {
|
||||
|
||||
sleep(1);
|
||||
//#ifdef __APPLE__
|
||||
// QString currentPath = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
|
||||
// currentPath += "/tp-testdata/";
|
||||
//#else
|
||||
// QString currentPath = QCoreApplication::applicationDirPath() + "/testdata/";
|
||||
//#endif
|
||||
|
||||
// /Users/apex/Library/Preferences/tp-player
|
||||
//qDebug() << "appdata:" << QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
|
||||
|
||||
// /private/var/folders/_3/zggrxjdx1lxcdqnfsbgpcwzh0000gn/T
|
||||
//qDebug() << "tmp:" << QStandardPaths::writableLocation(QStandardPaths::TempLocation);
|
||||
|
||||
// base of data path (include the .tpr file)
|
||||
QString path_base;
|
||||
|
||||
QString _tmp_res = m_res.toLower();
|
||||
if(_tmp_res.startsWith("http")) {
|
||||
qDebug() << "DOWNLOAD";
|
||||
m_need_download = true;
|
||||
|
||||
// path_base = QStandardPaths::writableLocation(QStandardPaths::AppConfigLocation);
|
||||
// path_base += "/tprdp/";
|
||||
|
||||
_notify_message("正在缓存录像数据,请稍候...");
|
||||
|
||||
m_thr_download = new ThreadDownload(m_res);
|
||||
m_thr_download->start();
|
||||
|
||||
QString msg;
|
||||
for(;;) {
|
||||
msleep(500);
|
||||
|
||||
if(m_need_stop) {
|
||||
// m_thr_download->stop();
|
||||
// m_thr_download->wait();
|
||||
// delete m_thr_download;
|
||||
// m_thr_download = nullptr;
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if(!m_thr_download->prepare(path_base, msg)) {
|
||||
msg.sprintf("指定的文件或目录不存在!\n\n%s", _tmp_res.toStdString().c_str());
|
||||
_notify_error(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(path_base.length())
|
||||
break;
|
||||
}
|
||||
}
|
||||
else {
|
||||
{
|
||||
QFileInfo fi(m_res);
|
||||
if(fi.isSymLink())
|
||||
_tmp_res = fi.symLinkTarget();
|
||||
else
|
||||
_tmp_res = m_res;
|
||||
}
|
||||
|
||||
QFileInfo fi(_tmp_res);
|
||||
if(!fi.exists()) {
|
||||
QString msg;
|
||||
msg.sprintf("指定的文件或目录不存在!\n\n%s", _tmp_res.toStdString().c_str());
|
||||
_notify_error(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
if(fi.isFile()) {
|
||||
path_base = fi.path();
|
||||
}
|
||||
else if(fi.isDir()) {
|
||||
path_base = m_res;
|
||||
}
|
||||
|
||||
path_base += "/";
|
||||
}
|
||||
|
||||
QString currentPath = QCoreApplication::applicationDirPath() + "/testdata/";
|
||||
|
||||
qint64 read_len = 0;
|
||||
uint32_t total_pkg = 0;
|
||||
|
@ -31,27 +140,47 @@ void ThreadPlay::run() {
|
|||
|
||||
// 加载录像基本信息数据
|
||||
|
||||
QString hdr_filename(currentPath);
|
||||
hdr_filename += "tp-rdp.tpr";
|
||||
QString tpr_filename(path_base);
|
||||
tpr_filename += "tp-rdp.tpr";
|
||||
|
||||
QFile f_hdr(hdr_filename);
|
||||
QFile f_hdr(tpr_filename);
|
||||
if(!f_hdr.open(QFile::ReadOnly)) {
|
||||
qDebug() << "Can not open " << hdr_filename << " for read.";
|
||||
qDebug() << "Can not open " << tpr_filename << " for read.";
|
||||
QString msg;
|
||||
msg.sprintf("无法打开录像信息文件!\n\n%s", tpr_filename.toStdString().c_str());
|
||||
_notify_error(msg);
|
||||
return;
|
||||
}
|
||||
else {
|
||||
update_data* dat = new update_data;
|
||||
dat->data_type(TYPE_HEADER_INFO);
|
||||
update_data* dat = new update_data(TYPE_HEADER_INFO);
|
||||
dat->alloc_data(sizeof(TS_RECORD_HEADER));
|
||||
|
||||
read_len = f_hdr.read((char*)(dat->data_buf()), dat->data_len());
|
||||
if(read_len != sizeof(TS_RECORD_HEADER)) {
|
||||
delete dat;
|
||||
qDebug() << "invaid .tpr file.";
|
||||
QString msg;
|
||||
msg.sprintf("错误的录像信息文件!\n\n%s", tpr_filename.toStdString().c_str());
|
||||
_notify_error(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
TS_RECORD_HEADER* hdr = (TS_RECORD_HEADER*)dat->data_buf();
|
||||
|
||||
if(hdr->info.ver != 4) {
|
||||
delete dat;
|
||||
qDebug() << "invaid .tpr file.";
|
||||
QString msg;
|
||||
msg.sprintf("不支持的录像文件版本 %d!\n\n此播放器支持录像文件版本 4。", hdr->info.ver);
|
||||
_notify_error(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
// if(hdr->basic.width == 0 || hdr->basic.height == 0) {
|
||||
// _notify_error("错误的录像信息,未记录窗口尺寸!");
|
||||
// return;
|
||||
// }
|
||||
|
||||
total_pkg = hdr->info.packages;
|
||||
total_ms = hdr->info.time_ms;
|
||||
|
||||
|
@ -60,12 +189,15 @@ void ThreadPlay::run() {
|
|||
|
||||
// 加载录像文件数据
|
||||
|
||||
QString dat_filename(currentPath);
|
||||
dat_filename += "tp-rdp.dat";
|
||||
QString tpd_filename(path_base);
|
||||
tpd_filename += "tp-rdp.tpd";
|
||||
|
||||
QFile f_dat(dat_filename);
|
||||
QFile f_dat(tpd_filename);
|
||||
if(!f_dat.open(QFile::ReadOnly)) {
|
||||
qDebug() << "Can not open " << dat_filename << " for read.";
|
||||
qDebug() << "Can not open " << tpd_filename << " for read.";
|
||||
QString msg;
|
||||
msg.sprintf("无法打开录像数据文件!\n\n%s", tpd_filename.toStdString().c_str());
|
||||
_notify_error(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -89,18 +221,23 @@ void ThreadPlay::run() {
|
|||
TS_RECORD_PKG pkg;
|
||||
read_len = f_dat.read((char*)(&pkg), sizeof(pkg));
|
||||
if(read_len != sizeof(TS_RECORD_PKG)) {
|
||||
qDebug() << "invaid .dat file (1).";
|
||||
qDebug() << "invaid .tpd file (1).";
|
||||
QString msg;
|
||||
msg.sprintf("错误的录像数据文件!\n\n%s", tpd_filename.toStdString().c_str());
|
||||
_notify_error(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
update_data* dat = new update_data;
|
||||
dat->data_type(TYPE_DATA);
|
||||
update_data* dat = new update_data(TYPE_DATA);
|
||||
dat->alloc_data(sizeof(TS_RECORD_PKG) + pkg.size);
|
||||
memcpy(dat->data_buf(), &pkg, sizeof(TS_RECORD_PKG));
|
||||
read_len = f_dat.read((char*)(dat->data_buf()+sizeof(TS_RECORD_PKG)), pkg.size);
|
||||
if(read_len != pkg.size) {
|
||||
delete dat;
|
||||
qDebug() << "invaid .dat file.";
|
||||
qDebug() << "invaid .tpd file.";
|
||||
QString msg;
|
||||
msg.sprintf("错误的录像数据文件!\n\n%s", tpd_filename.toStdString().c_str());
|
||||
_notify_error(msg);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -108,9 +245,8 @@ void ThreadPlay::run() {
|
|||
if(time_pass > total_ms)
|
||||
time_pass = total_ms;
|
||||
if(time_pass - time_last_pass > 200) {
|
||||
update_data* _passed_ms = new update_data;
|
||||
_passed_ms->data_type(TYPE_TIMER);
|
||||
_passed_ms->passed_ms(time_pass);
|
||||
update_data* _passed_ms = new update_data(TYPE_PLAYED_MS);
|
||||
_passed_ms->played_ms(time_pass);
|
||||
emit signal_update_data(_passed_ms);
|
||||
time_last_pass = time_pass;
|
||||
}
|
||||
|
@ -145,9 +281,8 @@ void ThreadPlay::run() {
|
|||
if(_time_pass > total_ms)
|
||||
_time_pass = total_ms;
|
||||
if(_time_pass - time_last_pass > 200) {
|
||||
update_data* _passed_ms = new update_data;
|
||||
_passed_ms->data_type(TYPE_TIMER);
|
||||
_passed_ms->passed_ms(_time_pass);
|
||||
update_data* _passed_ms = new update_data(TYPE_PLAYED_MS);
|
||||
_passed_ms->played_ms(_time_pass);
|
||||
emit signal_update_data(_passed_ms);
|
||||
time_last_pass = _time_pass;
|
||||
}
|
||||
|
@ -160,7 +295,6 @@ void ThreadPlay::run() {
|
|||
}
|
||||
}
|
||||
|
||||
update_data* _end = new update_data;
|
||||
_end->data_type(TYPE_END);
|
||||
update_data* _end = new update_data(TYPE_END);
|
||||
emit signal_update_data(_end);
|
||||
}
|
||||
|
|
|
@ -3,13 +3,14 @@
|
|||
|
||||
#include <QThread>
|
||||
#include "update_data.h"
|
||||
|
||||
#include "thr_download.h"
|
||||
|
||||
class ThreadPlay : public QThread
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
ThreadPlay();
|
||||
ThreadPlay(const QString& res);
|
||||
~ThreadPlay();
|
||||
|
||||
virtual void run();
|
||||
void stop();
|
||||
|
@ -17,6 +18,10 @@ public:
|
|||
void resume() {m_need_pause = false;}
|
||||
void speed(int s) {if(s >= 1 && s <= 16) m_speed = s;}
|
||||
|
||||
private:
|
||||
void _notify_message(const QString& msg);
|
||||
void _notify_error(const QString& err_msg);
|
||||
|
||||
signals:
|
||||
void signal_update_data(update_data*);
|
||||
|
||||
|
@ -24,6 +29,11 @@ private:
|
|||
bool m_need_stop;
|
||||
bool m_need_pause;
|
||||
int m_speed;
|
||||
|
||||
QString m_res;
|
||||
bool m_need_download;
|
||||
|
||||
ThreadDownload* m_thr_download;
|
||||
};
|
||||
|
||||
#endif // THR_PLAY_H
|
||||
|
|
|
@ -1,29 +1,34 @@
|
|||
TEMPLATE = app
|
||||
TARGET = tp-player
|
||||
|
||||
QT += core gui widgets
|
||||
|
||||
HEADERS += \
|
||||
mainwindow.h \
|
||||
bar.h \
|
||||
thr_play.h \
|
||||
update_data.h \
|
||||
record_format.h \
|
||||
rle.h
|
||||
|
||||
SOURCES += \
|
||||
main.cpp \
|
||||
mainwindow.cpp \
|
||||
bar.cpp \
|
||||
thr_play.cpp \
|
||||
update_data.cpp \
|
||||
rle.c
|
||||
|
||||
RESOURCES += \
|
||||
tp-player.qrc
|
||||
|
||||
RC_FILE += \
|
||||
tp-player.rc
|
||||
|
||||
FORMS += \
|
||||
mainwindow.ui
|
||||
TEMPLATE = app
|
||||
TARGET = tp-player
|
||||
|
||||
QT += core gui widgets
|
||||
|
||||
HEADERS += \
|
||||
dlgmessage.h \
|
||||
mainwindow.h \
|
||||
bar.h \
|
||||
thr_download.h \
|
||||
thr_play.h \
|
||||
update_data.h \
|
||||
record_format.h \
|
||||
rle.h
|
||||
|
||||
SOURCES += \
|
||||
dlgmessage.cpp \
|
||||
main.cpp \
|
||||
mainwindow.cpp \
|
||||
bar.cpp \
|
||||
thr_download.cpp \
|
||||
thr_play.cpp \
|
||||
update_data.cpp \
|
||||
rle.c
|
||||
|
||||
RESOURCES += \
|
||||
tp-player.qrc
|
||||
|
||||
RC_FILE += \
|
||||
tp-player.rc
|
||||
|
||||
FORMS += \
|
||||
dlgmessage.ui \
|
||||
mainwindow.ui
|
||||
|
|
|
@ -1,8 +1,8 @@
|
|||
#include "update_data.h"
|
||||
|
||||
update_data::update_data(QObject *parent) : QObject(parent)
|
||||
update_data::update_data(int data_type, QObject *parent) : QObject(parent)
|
||||
{
|
||||
m_data_type = 0xff;
|
||||
m_data_type = data_type;
|
||||
m_data_buf = nullptr;
|
||||
m_data_len = 0;
|
||||
}
|
||||
|
|
|
@ -3,24 +3,34 @@
|
|||
|
||||
#include <QObject>
|
||||
|
||||
#define TYPE_HEADER_INFO 0
|
||||
#define TYPE_DATA 1
|
||||
#define TYPE_PLAYED_MS 2
|
||||
#define TYPE_DOWNLOAD_PERCENT 3
|
||||
#define TYPE_END 4
|
||||
#define TYPE_MESSAGE 5
|
||||
#define TYPE_ERROR 6
|
||||
|
||||
class update_data : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
explicit update_data(QObject *parent = nullptr);
|
||||
explicit update_data(int data_type, QObject *parent = nullptr);
|
||||
virtual ~update_data();
|
||||
|
||||
void alloc_data(uint32_t len);
|
||||
void attach_data(const uint8_t* dat, uint32_t len);
|
||||
|
||||
void data_type(int dt) {m_data_type = dt;}
|
||||
int data_type() const {return m_data_type;}
|
||||
|
||||
uint8_t* data_buf() {return m_data_buf;}
|
||||
uint32_t data_len() const {return m_data_len;}
|
||||
|
||||
void passed_ms(uint32_t ms) {m_passed_ms = ms;}
|
||||
uint32_t passed_ms() {return m_passed_ms;}
|
||||
void played_ms(uint32_t ms) {m_played_ms = ms;}
|
||||
uint32_t played_ms() {return m_played_ms;}
|
||||
|
||||
void message(const QString& msg) {m_msg = msg;}
|
||||
const QString message(){return m_msg;}
|
||||
|
||||
signals:
|
||||
|
||||
|
@ -31,7 +41,8 @@ private:
|
|||
int m_data_type;
|
||||
uint8_t* m_data_buf;
|
||||
uint32_t m_data_len;
|
||||
uint32_t m_passed_ms;
|
||||
uint32_t m_played_ms;
|
||||
QString m_msg;
|
||||
};
|
||||
|
||||
class UpdateDataHelper {
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
[external_ver]
|
||||
openssl = 1.0.2p,1000210f
|
||||
openssl = 1.0.2s,1000210f
|
||||
libuv = 1.28.0
|
||||
mbedtls = 2.12.0
|
||||
libssh = 0.9.0
|
||||
|
|
|
@ -22,34 +22,34 @@
|
|||
|
||||
// 录像文件头(随着录像数据写入,会改变的部分)
|
||||
typedef struct TS_RECORD_HEADER_INFO {
|
||||
ex_u32 magic; // "TPPR" 标志 TelePort Protocol Record
|
||||
ex_u16 ver; // 录像文件版本,目前为3
|
||||
ex_u32 packages; // 总包数
|
||||
ex_u32 time_ms; // 总耗时(毫秒)
|
||||
//ex_u32 file_size; // 数据文件大小
|
||||
ex_u32 magic; // "TPPR" 标志 TelePort Protocol Record
|
||||
ex_u16 ver; // 录像文件版本,v3.5.0开始为4
|
||||
ex_u32 packages; // 总包数
|
||||
ex_u32 time_ms; // 总耗时(毫秒)
|
||||
uint32_t dat_file_count; // 数据文件数量
|
||||
uint8_t _reserve[64-4-2-4-4-4];
|
||||
}TS_RECORD_HEADER_INFO;
|
||||
#define ts_record_header_info_size sizeof(TS_RECORD_HEADER_INFO)
|
||||
|
||||
// 录像文件头(固定不变部分)
|
||||
typedef struct TS_RECORD_HEADER_BASIC {
|
||||
ex_u16 protocol_type; // 协议:1=RDP, 2=SSH, 3=Telnet
|
||||
ex_u16 protocol_sub_type; // 子协议:100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet
|
||||
ex_u64 timestamp; // 本次录像的起始时间(UTC时间戳)
|
||||
ex_u16 width; // 初始屏幕尺寸:宽
|
||||
ex_u16 height; // 初始屏幕尺寸:高
|
||||
char user_username[64]; // teleport账号
|
||||
char acc_username[64]; // 远程主机用户名
|
||||
ex_u16 protocol_type; // 协议:1=RDP, 2=SSH, 3=Telnet
|
||||
ex_u16 protocol_sub_type; // 子协议:100=RDP-DESKTOP, 200=SSH-SHELL, 201=SSH-SFTP, 300=Telnet
|
||||
ex_u64 timestamp; // 本次录像的起始时间(UTC时间戳)
|
||||
ex_u16 width; // 初始屏幕尺寸:宽
|
||||
ex_u16 height; // 初始屏幕尺寸:高
|
||||
char user_username[64]; // teleport账号
|
||||
char acc_username[64]; // 远程主机用户名
|
||||
|
||||
char host_ip[40]; // 远程主机IP
|
||||
char conn_ip[40]; // 远程主机IP
|
||||
ex_u16 conn_port; // 远程主机端口
|
||||
char host_ip[40]; // 远程主机IP
|
||||
char conn_ip[40]; // 远程主机IP
|
||||
ex_u16 conn_port; // 远程主机端口
|
||||
|
||||
char client_ip[40]; // 客户端IP
|
||||
char client_ip[40]; // 客户端IP
|
||||
|
||||
// // RDP专有
|
||||
// // RDP专有 - v3.5.0废弃并移除
|
||||
// ex_u8 rdp_security; // 0 = RDP, 1 = TLS
|
||||
|
||||
// ex_u8 _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - 1 - ts_record_header_info_size];
|
||||
ex_u8 _reserve[512 - 2 - 2 - 8 - 2 - 2 - 64 - 64 - 40 - 40 - 2 - 40 - ts_record_header_info_size];
|
||||
}TS_RECORD_HEADER_BASIC;
|
||||
#define ts_record_header_basic_size sizeof(TS_RECORD_HEADER_BASIC)
|
||||
|
|
Loading…
Reference in New Issue