From 1bbc109ae93cf09deb2968c2172c0755b80959f1 Mon Sep 17 00:00:00 2001 From: Apex Liu Date: Tue, 5 Nov 2019 03:05:29 +0800 Subject: [PATCH] =?UTF-8?q?=E6=92=AD=E6=94=BE=E5=99=A8=E6=B5=81=E7=95=85?= =?UTF-8?q?=E5=B7=A5=E4=BD=9C=EF=BC=8C=E8=BF=98=E7=BC=BA=E9=87=8D=E6=92=AD?= =?UTF-8?q?=E5=92=8C=E8=BF=9B=E5=BA=A6=E6=9D=A1=E6=8B=96=E5=8A=A8=E5=8A=9F?= =?UTF-8?q?=E8=83=BD=E3=80=82?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- client/tp-player/bar.cpp | 2 +- client/tp-player/mainwindow.cpp | 157 ++++------------------ client/tp-player/mainwindow.h | 18 +-- client/tp-player/rle.c | 8 +- client/tp-player/rle.h | 10 +- client/tp-player/thr_data.cpp | 44 +++++-- client/tp-player/thr_play.cpp | 218 ++++++++++--------------------- client/tp-player/thr_play.h | 2 + client/tp-player/update_data.cpp | 142 +++++++++++++++++++- client/tp-player/update_data.h | 57 ++++++-- 10 files changed, 329 insertions(+), 329 deletions(-) diff --git a/client/tp-player/bar.cpp b/client/tp-player/bar.cpp index c1cac0f..f5a39d5 100644 --- a/client/tp-player/bar.cpp +++ b/client/tp-player/bar.cpp @@ -423,7 +423,7 @@ void Bar::onMousePress(int x, int y) { if(m_speed_selected != speed_sel && speed_sel != speed_count) { int old_sel = m_speed_selected; m_speed_selected = speed_sel; - m_owner->speed(get_speed()); + m_owner->set_speed(get_speed()); m_owner->update(m_rc.left()+m_rc_btn_speed[old_sel].left(), m_rc.top()+m_rc_btn_speed[old_sel].top(), m_rc_btn_speed[old_sel].width(), m_rc_btn_speed[old_sel].height()); m_owner->update(m_rc.left()+m_rc_btn_speed[m_speed_hover].left(), m_rc.top()+m_rc_btn_speed[m_speed_hover].top(), m_rc_btn_speed[m_speed_hover].width(), m_rc_btn_speed[m_speed_hover].height()); return; diff --git a/client/tp-player/mainwindow.cpp b/client/tp-player/mainwindow.cpp index db7407f..0d09810 100644 --- a/client/tp-player/mainwindow.cpp +++ b/client/tp-player/mainwindow.cpp @@ -1,6 +1,5 @@ #include "mainwindow.h" #include "ui_mainwindow.h" -#include "rle.h" #include #include @@ -10,64 +9,6 @@ #include #include -bool rdpimg2QImage(QImage& out, int w, int h, int bitsPerPixel, bool isCompressed, uint8_t* dat, uint32_t len) { - switch(bitsPerPixel) { - case 15: - if(isCompressed) { - uint8_t* _dat = (uint8_t*)calloc(1, w*h*2); - if(!bitmap_decompress1(_dat, w, h, dat, len)) { - free(_dat); - return false; - } - out = QImage(_dat, w, h, QImage::Format_RGB555); - free(_dat); - } - else { - out = QImage(dat, w, h, QImage::Format_RGB555).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)) ; - } - break; - case 16: - if(isCompressed) { - - uint8_t* _dat = (uint8_t*)calloc(1, w*h*2); - if(!bitmap_decompress2(_dat, w, h, dat, len)) { - free(_dat); - return false; - } - - // TODO: 这里需要进一步优化,直接操作QImage的buffer。 -// QTime t1; -// t1.start(); - - out = QImage(w, h, QImage::Format_RGB16); - for(int y = 0; y < h; y++) { - for(int x = 0; x < w; x++) { - uint16 a = ((uint16*)_dat)[y * w + x]; - uint8 r = ((a & 0xf800) >> 11) * 255 / 31; - uint8 g = ((a & 0x07e0) >> 5) * 255 / 63; - uint8 b = (a & 0x001f) * 255 / 31; - out.setPixelColor(x, y, QColor(r,g,b)); - } - } -// qDebug("parse: %dB, %dms", len, t1.elapsed()); - - free(_dat); - } - else { - out = QImage(dat, w, h, QImage::Format_RGB16).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0)) ; - } - break; - case 24: - qDebug() << "--------NOT support 24"; - break; - case 32: - qDebug() << "--------NOT support 32"; - break; - } - - return true; -} - static inline int min(int a, int b){ return a < b ? a : b; } @@ -80,7 +21,6 @@ MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { - //m_shown = false; m_show_default = true; m_bar_shown = false; m_bar_fade_in = false; @@ -93,8 +33,6 @@ MainWindow::MainWindow(QWidget *parent) : m_play_state = PLAY_STATE_UNKNOWN; m_thr_data = nullptr; -// m_dl = nullptr; - ui->setupUi(this); ui->centralWidget->setMouseTracking(true); @@ -125,7 +63,6 @@ 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())); @@ -194,11 +131,16 @@ void MainWindow::_start_play_thread() { m_thr_play->start(); } -void MainWindow::speed(int s) { +void MainWindow::set_speed(int s) { if(m_thr_play) m_thr_play->speed(s); } +void MainWindow::set_skip(bool s) { + if(m_thr_play) + m_thr_play->skip(s); +} + void MainWindow::paintEvent(QPaintEvent *e) { QPainter painter(this); @@ -252,12 +194,6 @@ void MainWindow::paintEvent(QPaintEvent *e) int to_y = rc.top() + from_y; painter.drawPixmap(to_x, to_y, m_img_message, from_x, from_y, w, h); } - -// if(!m_shown) { -// m_shown = true; -// //m_thr_play.start(); -// _start_play_thread(); -// } } void MainWindow::pause() { @@ -271,7 +207,6 @@ void MainWindow::resume() { if(m_play_state == PLAY_STATE_PAUSE) m_thr_play->resume(); else if(m_play_state == PLAY_STATE_STOP) - //m_thr_play->start(); _start_play_thread(); m_play_state = PLAY_STATE_RUNNING; @@ -283,53 +218,28 @@ void MainWindow::_do_update_data(UpdateData* dat) { UpdateDataHelper data_helper(dat); - if(dat->data_type() == TYPE_DATA) { + if(dat->data_type() == TYPE_POINTER) { + TS_RECORD_RDP_POINTER pt; + memcpy(&pt, &m_pt, sizeof(TS_RECORD_RDP_POINTER)); - if(dat->data_len() <= sizeof(TS_RECORD_PKG)) { - qDebug() << "invalid record package(1)."; + // 更新虚拟鼠标信息,这样下一次绘制界面时就会在新的位置绘制出虚拟鼠标 + memcpy(&m_pt, dat->get_pointer(), sizeof(TS_RECORD_RDP_POINTER)); + update(m_pt.x - m_pt_normal.width()/2, m_pt.y - m_pt_normal.width()/2, m_pt_normal.width(), m_pt_normal.height()); + + update(pt.x - m_pt_normal.width()/2, pt.y - m_pt_normal.width()/2, m_pt_normal.width(), m_pt_normal.height()); + + return; + } + else if(dat->data_type() == TYPE_IMAGE) { + QImage* img_update = nullptr; + int x, y, w, h; + if(!dat->get_image(&img_update, x, y, w, h)) return; - } - TS_RECORD_PKG* pkg = (TS_RECORD_PKG*)dat->data_buf(); + QPainter pp(&m_canvas); + pp.drawImage(x, y, *img_update, 0, 0, w, h, Qt::AutoColor); - if(pkg->type == TS_RECORD_TYPE_RDP_POINTER) { - if(dat->data_len() != sizeof(TS_RECORD_PKG) + sizeof(TS_RECORD_RDP_POINTER)) { - qDebug() << "invalid record package(2)."; - return; - } - - TS_RECORD_RDP_POINTER pt; - memcpy(&pt, &m_pt, sizeof(TS_RECORD_RDP_POINTER)); - - // 更新虚拟鼠标信息,这样下一次绘制界面时就会在新的位置绘制出虚拟鼠标 - memcpy(&m_pt, dat->data_buf() + sizeof(TS_RECORD_PKG), sizeof(TS_RECORD_RDP_POINTER)); - update(m_pt.x - m_pt_normal.width()/2, m_pt.y - m_pt_normal.width()/2, m_pt_normal.width(), m_pt_normal.height()); - - update(pt.x - m_pt_normal.width()/2, pt.y - m_pt_normal.width()/2, m_pt_normal.width(), m_pt_normal.height()); - } - else if(pkg->type == TS_RECORD_TYPE_RDP_IMAGE) { - if(dat->data_len() <= sizeof(TS_RECORD_PKG) + sizeof(TS_RECORD_RDP_IMAGE_INFO)) { - qDebug() << "invalid record package(3)."; - return; - } - - TS_RECORD_RDP_IMAGE_INFO* info = (TS_RECORD_RDP_IMAGE_INFO*)(dat->data_buf() + sizeof(TS_RECORD_PKG)); - uint8_t* img_dat = dat->data_buf() + sizeof(TS_RECORD_PKG) + sizeof(TS_RECORD_RDP_IMAGE_INFO); - uint32_t img_len = dat->data_len() - sizeof(TS_RECORD_PKG) - sizeof(TS_RECORD_RDP_IMAGE_INFO); - - QImage img_update; - rdpimg2QImage(img_update, info->width, info->height, info->bitsPerPixel, (info->format == TS_RDP_IMG_BMP) ? true : false, img_dat, img_len); - - int x = info->destLeft; - int y = info->destTop; - int w = info->destRight - info->destLeft + 1; - int h = info->destBottom - info->destTop + 1; - - QPainter pp(&m_canvas); - pp.drawImage(x, y, img_update, 0, 0, w, h, Qt::AutoColor); - - update(x, y, w, h); - } + update(x, y, w, h); return; } @@ -394,11 +304,10 @@ void MainWindow::_do_update_data(UpdateData* dat) { // 这是播放开始时收到的第一个数据包 else if(dat->data_type() == TYPE_HEADER_INFO) { - if(dat->data_len() != sizeof(TS_RECORD_HEADER)) { - qDebug() << "invalid record header."; + TS_RECORD_HEADER* hdr = dat->get_header(); + if(hdr == nullptr) return; - } - memcpy(&m_rec_hdr, dat->data_buf(), sizeof(TS_RECORD_HEADER)); + memcpy(&m_rec_hdr, hdr, sizeof(TS_RECORD_HEADER)); qDebug() << "resize (" << m_rec_hdr.basic.width << "," << m_rec_hdr.basic.height << ")"; @@ -490,18 +399,6 @@ void MainWindow::_do_bar_fade() { 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) { if(!m_show_default) { QRect rc = m_bar.rc(); diff --git a/client/tp-player/mainwindow.h b/client/tp-player/mainwindow.h index 9c5eed6..240ad41 100644 --- a/client/tp-player/mainwindow.h +++ b/client/tp-player/mainwindow.h @@ -34,10 +34,8 @@ public: void pause(); void resume(); void restart(); - void speed(int s); - -// Downloader* downloader() {return m_dl;} -// void reset_downloader() {if(m_dl){delete m_dl;m_dl= nullptr;}} + void set_speed(int s); + void set_skip(bool s); // TODO: 将thr_data移动到thr_play线程,由play线程进行管理 ThrData* get_thr_data() {return m_thr_data;} @@ -55,13 +53,9 @@ private slots: void _do_bar_fade(); void _do_bar_delay_hide(); -// void _do_download(Downloader*); -// void _do_download(DownloadParam*); - private: Ui::MainWindow *ui; - //bool m_shown; bool m_show_default; bool m_bar_shown; QPixmap m_default_bg; @@ -91,14 +85,6 @@ private: bool m_show_message; QPixmap m_img_message; QRect m_rc_message; - - -// QNetworkAccessManager m_nam; -// Downloader* m_dl; - - // for test - TimeUseTest m_time_imgconvert_normal; - TimeUseTest m_time_imgconvert_compressed; }; #endif // MAINWINDOW_H diff --git a/client/tp-player/rle.c b/client/tp-player/rle.c index ce9fd03..393e333 100644 --- a/client/tp-player/rle.c +++ b/client/tp-player/rle.c @@ -74,7 +74,7 @@ /* 1 byte bitmap decompress */ RD_BOOL -bitmap_decompress1(uint8 * output, int width, int height, uint8 * input, int size) +bitmap_decompress1(uint8 * output, int width, int height, const uint8 * input, int size) { uint8 *end = input + size; uint8 *prevline = NULL, *line = NULL; @@ -272,7 +272,7 @@ bitmap_decompress1(uint8 * output, int width, int height, uint8 * input, int siz /* 2 byte bitmap decompress */ RD_BOOL -bitmap_decompress2(uint8 * output, int width, int height, uint8 * input, int size) +bitmap_decompress2(uint8 * output, int width, int height, const uint8 * input, int size) { uint8 *end = input + size; uint16 *prevline = NULL, *line = NULL; @@ -471,7 +471,7 @@ bitmap_decompress2(uint8 * output, int width, int height, uint8 * input, int siz /* 3 byte bitmap decompress */ RD_BOOL -bitmap_decompress3(uint8 * output, int width, int height, uint8 * input, int size) +bitmap_decompress3(uint8 * output, int width, int height, const uint8 * input, int size) { uint8 *end = input + size; uint8 *prevline = NULL, *line = NULL; @@ -863,7 +863,7 @@ process_plane(uint8 * in, int width, int height, uint8 * out, int size) /* 4 byte bitmap decompress */ RD_BOOL -bitmap_decompress4(uint8 * output, int width, int height, uint8 * input, int size) +bitmap_decompress4(uint8 * output, int width, int height, const uint8 * input, int size) { int code; int bytes_pro; diff --git a/client/tp-player/rle.h b/client/tp-player/rle.h index 9ed737a..6de7624 100644 --- a/client/tp-player/rle.h +++ b/client/tp-player/rle.h @@ -1,4 +1,4 @@ -#ifndef RLE_H +#ifndef RLE_H #define RLE_H #define RD_BOOL int @@ -13,10 +13,10 @@ extern "C" { #endif -RD_BOOL bitmap_decompress1(uint8 * output, int width, int height, uint8 * input, int size); -RD_BOOL bitmap_decompress2(uint8 * output, int width, int height, uint8 * input, int size); -RD_BOOL bitmap_decompress3(uint8 * output, int width, int height, uint8 * input, int size); -RD_BOOL bitmap_decompress4(uint8 * output, int width, int height, uint8 * input, int size); +RD_BOOL bitmap_decompress1(uint8 * output, int width, int height, const uint8 * input, int size); +RD_BOOL bitmap_decompress2(uint8 * output, int width, int height, const uint8 * input, int size); +RD_BOOL bitmap_decompress3(uint8 * output, int width, int height, const uint8 * input, int size); +RD_BOOL bitmap_decompress4(uint8 * output, int width, int height, const uint8 * input, int size); int bitmap_decompress_15(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size); int bitmap_decompress_16(uint8 * output, int output_width, int output_height, int input_width, int input_height, uint8* input, int size); diff --git a/client/tp-player/thr_data.cpp b/client/tp-player/thr_data.cpp index 846ac39..a3070c7 100644 --- a/client/tp-player/thr_data.cpp +++ b/client/tp-player/thr_data.cpp @@ -137,9 +137,9 @@ void ThrData::_run() { return; - UpdateData* dat = new UpdateData(TYPE_HEADER_INFO); - dat->alloc_data(sizeof(TS_RECORD_HEADER)); - memcpy(dat->data_buf(), &m_hdr, sizeof(TS_RECORD_HEADER)); + UpdateData* dat = new UpdateData(m_hdr); +// dat->alloc_data(sizeof(TS_RECORD_HEADER)); +// memcpy(dat->data_buf(), &m_hdr, sizeof(TS_RECORD_HEADER)); emit signal_update_data(dat); @@ -290,18 +290,34 @@ void ThrData::_run() { 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 = fdata->read(reinterpret_cast(dat->data_buf()+sizeof(TS_RECORD_PKG)), pkg.size); - if(read_len != pkg.size) { - delete dat; + QByteArray pkg_data = fdata->read(pkg.size); + if(pkg_data.size() != pkg.size) { 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; + UpdateData* dat = new UpdateData(); + if(!dat->parse(pkg, pkg_data)) { + qDebug("invaid tp-rdp-%d.tpd file (4).", file_idx+1); + _notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx)); + return; + } + + +// 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 = 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, 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) { @@ -315,10 +331,12 @@ void ThrData::_run() { if(dat) { m_locker.lock(); m_data.enqueue(dat); - qDebug("queue data count: %d", m_data.size()); +// qDebug("queue data count: %d", m_data.size()); m_locker.unlock(); } + msleep(1); + // 如果此文件已经处理完毕,则关闭文件,这样下次处理一个新的文件 // qDebug("C processed: %" PRId64 ", size: %" PRId64, file_processed, file_size); if(file_processed >= file_size) { @@ -332,7 +350,7 @@ void ThrData::_run() { UpdateData* dat = new UpdateData(TYPE_END); m_locker.lock(); m_data.enqueue(dat); - qDebug("queue data count: %d", m_data.size()); +// qDebug("queue data count: %d", m_data.size()); m_locker.unlock(); } } @@ -433,8 +451,10 @@ UpdateData* ThrData::get_data() { UpdateData* d = nullptr; m_locker.lock(); - if(m_data.size() > 0) + if(m_data.size() > 0) { +// qDebug("get_data(), left: %d", m_data.size()); d = m_data.dequeue(); + } m_locker.unlock(); return d; diff --git a/client/tp-player/thr_play.cpp b/client/tp-player/thr_play.cpp index d366efb..cdab18e 100644 --- a/client/tp-player/thr_play.cpp +++ b/client/tp-player/thr_play.cpp @@ -23,7 +23,8 @@ ThrPlay::ThrPlay(MainWindow* mainwnd) { m_mainwnd = mainwnd; m_need_stop = false; m_need_pause = false; - m_speed = 2; + m_speed = 1; + m_skip = false; // m_res = res; // m_thr_data = nullptr; } @@ -67,13 +68,16 @@ void ThrPlay::run() { ThrData* thr_data = m_mainwnd->get_thr_data(); bool first_run = true; + uint32_t last_time_ms = 0; + uint32_t last_pass_ms = 0; + UpdateData* dat = nullptr; for(;;) { if(m_need_stop) break; // 1. 从ThrData的待播放队列中取出一个数据 - UpdateData* dat = thr_data->get_data(); + dat = thr_data->get_data(); if(dat == nullptr) { msleep(20); continue; @@ -85,161 +89,73 @@ void ThrPlay::run() { } // 2. 根据数据包的信息,等待到播放时间点 + uint32_t need_wait_ms = 0; + uint32_t this_time_ms = dat->get_time(); + uint32_t this_pass_ms = last_time_ms; + if(this_time_ms > 0) { + need_wait_ms = this_time_ms - last_time_ms; + + if(need_wait_ms > 0) { + uint32_t time_wait = 0; + + // 如果设置了跳过无操作区间,将超过1秒的等待时间压缩至1秒。 + if(m_skip) { + if(need_wait_ms > 1000) + need_wait_ms = 1000; + } + + for(;;) { + time_wait = need_wait_ms > 10 ? 10 : need_wait_ms; + msleep(time_wait); + + if(m_need_pause) { + while(m_need_pause) { + msleep(50); + if(m_need_stop) + break; + } + } + + if(m_need_stop) + break; + + time_wait *= m_speed; + + // 如果已经在等待长时间无操作区间内,用户设置了跳过无操作区间,则将超过0.5秒的等待时间压缩至0.5秒。 + if(m_skip) { + if(need_wait_ms > 500) + need_wait_ms = 500; + } + + this_pass_ms += time_wait; + if(this_pass_ms - last_pass_ms > 100) { + UpdateData* _passed_ms = new UpdateData(TYPE_PLAYED_MS); + _passed_ms->played_ms(this_pass_ms); + emit signal_update_data(_passed_ms); + last_pass_ms = this_pass_ms; + } + + if(need_wait_ms <= time_wait) + break; + else + need_wait_ms -= time_wait; + } + + if(m_need_stop) + break; + } + + last_time_ms = this_time_ms; + } + // 3. 将数据包发送给主UI界面进行显示 -// qDebug("emit one package."); if(dat->data_type() == TYPE_END) { _notify_message(LOCAL8BIT("播放结束")); } emit signal_update_data(dat); - msleep(5); -} - -#if 0 - //====================================== - // 加载录像文件数据并播放 - //====================================== - - uint32_t pkg_count = 0; - uint32_t time_pass = 0; - uint32_t time_last_pass = 0; - qint64 time_begin = QDateTime::currentMSecsSinceEpoch(); - QString msg; - - for(uint32_t fidx = 0; fidx < file_count; ++fidx) { - if(m_need_stop) { - qDebug() << "stop, user cancel 1."; - break; - } - - QString tpd_filename; - tpd_filename.sprintf("%stp-rdp-%d.tpd", path_base.toStdString().c_str(), fidx+1); - -// // for test. -// msg = QString::fromLocal8Bit("无法打开录像数据文件!\n\n"); -// //msg.sprintf("无法打开录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); -// msg += tpd_filename.toStdString().c_str(); -// _notify_message(msg); - - QFile f_dat(tpd_filename); - if(!f_dat.open(QFile::ReadOnly)) { - qDebug() << "Can not open " << tpd_filename << " for read."; - // msg.sprintf("无法打开录像数据文件!\n\n%s", tpd_filename.toStdString().c_str()); - msg = QString::fromLocal8Bit("无法打开录像数据文件!\n\n"); - msg += tpd_filename.toStdString().c_str(); - _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; - } - } - - } } - if(pkg_count < total_pkg) { - qDebug() << "total-pkg:" << total_pkg << ", played:" << pkg_count; - // msg.sprintf("录像数据文件有误!\n\n部分录像数据缺失!"); - msg = QString::fromLocal8Bit("录像数据文件有误!\n\n部分录像数据缺失!"); - _notify_message(msg); - } - -#endif -// qDebug("play end."); -// UpdateData* _end = new UpdateData(TYPE_END); -// emit signal_update_data(_end); + if(dat != nullptr) + delete dat; } diff --git a/client/tp-player/thr_play.h b/client/tp-player/thr_play.h index abb142d..35a1e40 100644 --- a/client/tp-player/thr_play.h +++ b/client/tp-player/thr_play.h @@ -21,6 +21,7 @@ public: void pause() {m_need_pause = true;} void resume() {m_need_pause = false;} void speed(int s) {if(s >= 1 && s <= 16) m_speed = s;} + void skip(bool s) {m_skip = s;} private: void _notify_message(const QString& msg); @@ -34,6 +35,7 @@ private: bool m_need_stop; bool m_need_pause; int m_speed; + bool m_skip; }; #endif // THR_PLAY_H diff --git a/client/tp-player/update_data.cpp b/client/tp-player/update_data.cpp index 31f2625..7faca7a 100644 --- a/client/tp-player/update_data.cpp +++ b/client/tp-player/update_data.cpp @@ -1,17 +1,157 @@ #include "update_data.h" +#include "rle.h" -UpdateData::UpdateData(int data_type, QObject *parent) : QObject(parent) +#include +#include + + +static QImage* _rdpimg2QImage(int w, int h, int bitsPerPixel, bool isCompressed, const uint8_t* dat, uint32_t len) { + QImage* out; + switch(bitsPerPixel) { + case 15: + if(isCompressed) { + uint8_t* _dat = reinterpret_cast(calloc(1, w*h*2)); + if(!bitmap_decompress1(_dat, w, h, dat, len)) { + free(_dat); + return nullptr; + } + out = new QImage(_dat, w, h, QImage::Format_RGB555); + free(_dat); + } + else { + out = new QImage(QImage(dat, w, h, QImage::Format_RGB555).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))); + } + return out; + + case 16: + if(isCompressed) { + uint8_t* _dat = reinterpret_cast(calloc(1, w*h*2)); + if(!bitmap_decompress2(_dat, w, h, dat, len)) { + free(_dat); + return nullptr; + } + + // TODO: 这里需要进一步优化,直接操作QImage的buffer。 + out = new QImage(w, h, QImage::Format_RGB16); + for(int y = 0; y < h; y++) { + for(int x = 0; x < w; x++) { + uint16 a = ((uint16*)_dat)[y * w + x]; + uint8 r = ((a & 0xf800) >> 11) * 255 / 31; + uint8 g = ((a & 0x07e0) >> 5) * 255 / 63; + uint8 b = (a & 0x001f) * 255 / 31; + out->setPixelColor(x, y, QColor(r,g,b)); + } + } + free(_dat); + return out; + } + else { + out = new QImage(QImage(dat, w, h, QImage::Format_RGB16).transformed(QMatrix(1.0, 0.0, 0.0, -1.0, 0.0, 0.0))); + } + return out; + + case 24: + case 32: + default: + qDebug() << "--------NOT support UNKNOWN bitsPerPix" << bitsPerPixel; + return nullptr; + } +} + + +UpdateData::UpdateData() : QObject(nullptr) { + _init(); +} + +UpdateData::UpdateData(int data_type) : QObject(nullptr) +{ + _init(); m_data_type = data_type; +} + +UpdateData::UpdateData(const TS_RECORD_HEADER& hdr) : QObject(nullptr) +{ + _init(); + m_data_type = TYPE_HEADER_INFO; + m_hdr = new TS_RECORD_HEADER; + memcpy(m_hdr, &hdr, sizeof(TS_RECORD_HEADER)); +} + +void UpdateData::_init() { + m_data_type = TYPE_UNKNOWN; + m_hdr = nullptr; + m_pointer = nullptr; + m_img = nullptr; +// m_img_info = nullptr; + m_data_buf = nullptr; m_data_len = 0; + m_time_ms = 0; } UpdateData::~UpdateData() { + if(m_hdr) + delete m_hdr; + if(m_pointer) + delete m_pointer; + if(m_img) + delete m_img; +// if(m_img_info) +// delete m_img_info; + if(m_data_buf) delete m_data_buf; } +bool UpdateData::parse(const TS_RECORD_PKG& pkg, const QByteArray& data) { + m_time_ms = pkg.time_ms; + + if(pkg.type == TS_RECORD_TYPE_RDP_POINTER) { + m_data_type = TYPE_POINTER; + if(data.size() != sizeof(TS_RECORD_RDP_POINTER)) + return false; + m_pointer = new TS_RECORD_RDP_POINTER; + memcpy(m_pointer, data.data(), sizeof(TS_RECORD_RDP_POINTER)); + return true; + } + else if(pkg.type == TS_RECORD_TYPE_RDP_IMAGE) { + m_data_type = TYPE_IMAGE; + if(data.size() <= sizeof(TS_RECORD_RDP_IMAGE_INFO)) + return false; + const TS_RECORD_RDP_IMAGE_INFO* info = reinterpret_cast(data.data()); + const uint8_t* img_dat = reinterpret_cast(data.data() + sizeof(TS_RECORD_RDP_IMAGE_INFO)); + uint32_t img_len = data.size() - sizeof(TS_RECORD_RDP_IMAGE_INFO); + + QImage* img = _rdpimg2QImage(info->width, info->height, info->bitsPerPixel, (info->format == TS_RDP_IMG_BMP) ? true : false, img_dat, img_len); + if(img == nullptr) + return false; + + m_img = img; + m_img_x = info->destLeft; + m_img_y = info->destTop; + m_img_w = info->destRight - info->destLeft + 1; + m_img_h = info->destBottom - info->destTop + 1; + + + +// m_img_info = new TS_RECORD_RDP_IMAGE_INFO; +// memcpy(m_img_info, data.data(), sizeof(TS_RECORD_RDP_IMAGE_INFO)); +// m_data_buf = new uint8_t[img_len]; +// memcpy(m_data_buf, img_dat, img_len); +// m_data_len = img_len; + + + return true; + } + else if(pkg.type == TS_RECORD_TYPE_RDP_KEYFRAME) { + return true; + } + + return false; +} + + void UpdateData::alloc_data(uint32_t len) { if(m_data_buf) delete m_data_buf; diff --git a/client/tp-player/update_data.h b/client/tp-player/update_data.h index c76c299..c2dea27 100644 --- a/client/tp-player/update_data.h +++ b/client/tp-player/update_data.h @@ -1,23 +1,45 @@ -#ifndef UPDATE_DATA_H +#ifndef UPDATE_DATA_H #define UPDATE_DATA_H #include +#include "record_format.h" -#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 +#define TYPE_UNKNOWN 0 +#define TYPE_HEADER_INFO 1 +#define TYPE_POINTER 10 +#define TYPE_IMAGE 11 +#define TYPE_KEYFRAME 12 +#define TYPE_PLAYED_MS 20 +#define TYPE_DOWNLOAD_PERCENT 21 +#define TYPE_END 50 +#define TYPE_MESSAGE 90 +#define TYPE_ERROR 91 class UpdateData : public QObject { Q_OBJECT public: - explicit UpdateData(int data_type, QObject *parent = nullptr); + explicit UpdateData(); + explicit UpdateData(int data_type); + explicit UpdateData(const TS_RECORD_HEADER& hdr); virtual ~UpdateData(); + bool parse(const TS_RECORD_PKG& pkg, const QByteArray& data); + TS_RECORD_HEADER* get_header() {return m_hdr;} + TS_RECORD_RDP_POINTER* get_pointer() {return m_pointer;} + bool get_image(QImage** img, int& x, int& y, int& w, int& h) { + if(m_img == nullptr) + return false; + *img = m_img; + x = m_img_x; + y = m_img_y; + w = m_img_w; + h = m_img_h; + return true; + } + + uint32_t get_time() {return m_time_ms;} + void alloc_data(uint32_t len); void attach_data(const uint8_t* dat, uint32_t len); @@ -32,6 +54,9 @@ public: void message(const QString& msg) {m_msg = msg;} const QString message(){return m_msg;} +private: + void _init(void); + signals: public slots: @@ -39,10 +64,24 @@ public slots: private: int m_data_type; + uint32_t m_time_ms; uint8_t* m_data_buf; uint32_t m_data_len; uint32_t m_played_ms; QString m_msg; + + // for HEADER + TS_RECORD_HEADER* m_hdr; + // for POINTER + TS_RECORD_RDP_POINTER* m_pointer; + // for IMAGE + QImage* m_img; + int m_img_x; + int m_img_y; + int m_img_w; + int m_img_h; + +// TS_RECORD_RDP_IMAGE_INFO* m_img_info; }; class UpdateDataHelper {