播放器流畅工作,还缺重播和进度条拖动功能。

pull/175/head^2
Apex Liu 2019-11-05 03:05:29 +08:00
parent 3aa4755876
commit 1bbc109ae9
10 changed files with 329 additions and 329 deletions

View File

@ -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;

View File

@ -1,6 +1,5 @@
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "rle.h"
#include <QMatrix>
#include <QDebug>
@ -10,64 +9,6 @@
#include <QMessageBox>
#include <QDialogButtonBox>
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();

View File

@ -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

View File

@ -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;

View File

@ -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);

View File

@ -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<char*>(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<char*>(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;

View File

@ -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;
}

View File

@ -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

View File

@ -1,17 +1,157 @@
#include "update_data.h"
#include "rle.h"
UpdateData::UpdateData(int data_type, QObject *parent) : QObject(parent)
#include <QImage>
#include <QDebug>
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<uint8_t*>(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<uint8_t*>(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<const TS_RECORD_RDP_IMAGE_INFO*>(data.data());
const uint8_t* img_dat = reinterpret_cast<const uint8_t*>(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;

View File

@ -1,23 +1,45 @@
#ifndef UPDATE_DATA_H
#ifndef UPDATE_DATA_H
#define UPDATE_DATA_H
#include <QObject>
#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 {