播放器进度条任意拖动可以工作了。需要改进录像的关键帧处理。

pull/175/head^2
Apex Liu 2019-11-06 02:49:30 +08:00
parent 1bbc109ae9
commit bde794d253
8 changed files with 254 additions and 118 deletions

View File

@ -86,6 +86,10 @@ Bar::Bar() {
m_speed_hover = speed_count; // speed_count=no-hover
m_skip_selected = false;
m_skip_hover = false;
m_progress_hover = false;
m_progress_pressed = false;
m_resume_ms = 0;
}
Bar::~Bar() {
@ -138,7 +142,7 @@ void Bar::start(uint32_t total_ms, int width) {
}
void Bar::end() {
if(m_passed_ms != m_total_ms)
if(m_played_ms != m_total_ms)
update_passed_time(m_total_ms);
m_playing = false;
@ -342,13 +346,14 @@ void Bar::update_passed_time(uint32_t ms) {
}
int percent = 0;
if(ms > m_total_ms) {
if(ms >= m_total_ms) {
percent = 100;
m_passed_ms = m_total_ms;
m_played_ms = m_total_ms;
}
else {
m_passed_ms = ms;
percent = (int)(((double)m_passed_ms / (double)m_total_ms) * 100);
m_played_ms = ms;
//percent = (int)(((double)m_played_ms / (double)m_total_ms) * 100);
percent = m_played_ms * 100 / m_total_ms;
}
if(percent != m_percent) {
@ -361,6 +366,27 @@ void Bar::onMouseMove(int x, int y) {
// 映射鼠标坐标点到本浮动窗内部的相对位置
QPoint pt(x-m_rc.left(), y-m_rc.top());
if(m_progress_pressed) {
// 重新设置进度条指示器位置
int percent = 0;
if(pt.x() < m_rc_progress.left()) {
percent = 0;
m_resume_ms = 0;
}
else if(pt.x() > m_rc_progress.right()) {
percent = 100;
m_resume_ms = m_total_ms;
}
else {
percent = (pt.x() + m_img_progress_pointer[widget_normal].width()/2 - m_rc_progress.left()) * 100 / m_rc_progress.width();
m_resume_ms = m_total_ms * percent / 100;
}
update_passed_time(m_resume_ms);
return;
}
bool play_hover = m_rc_btn_play.contains(pt);
if(play_hover != m_play_hover) {
m_play_hover = play_hover;
@ -393,11 +419,13 @@ void Bar::onMouseMove(int x, int y) {
}
if(skip_hover)
return;
// TODO: more hover detect.
}
void Bar::onMousePress(int x, int y) {
void Bar::onMousePress(int x, int y, Qt::MouseButton button) {
// 我们只关心左键按下
if(button != Qt::LeftButton)
return;
// 映射鼠标坐标点到本浮动窗内部的相对位置
QPoint pt(x-m_rc.left(), y-m_rc.top());
@ -405,7 +433,7 @@ void Bar::onMousePress(int x, int y) {
if(m_playing)
m_owner->pause();
else
m_owner->resume();
m_owner->resume(false, 0);
m_playing = !m_playing;
m_owner->update(m_rc.left()+m_rc_btn_play.left(), m_rc.top()+m_rc_btn_play.top(), m_rc_btn_play.width(), m_rc_btn_play.height());
@ -431,9 +459,29 @@ void Bar::onMousePress(int x, int y) {
if(m_rc_skip.contains(pt)) {
m_skip_selected = !m_skip_selected;
m_owner->set_skip(m_skip_selected);
m_owner->update(m_rc.left()+m_rc_skip.left(), m_rc.top()+m_rc_skip.top(), m_rc_skip.width(), m_rc_skip.height());
return;
}
//
if(m_rc_progress.contains(pt)) {
m_progress_pressed = true;
// TODO: 暂停播放,按比例计算出点击位置占整个录像时长的百分比,定位到此位置准备播放。
// TODO: 如果点击的位置是进度条指示标志,则仅暂停播放
m_owner->pause();
}
}
void Bar::onMouseRelease(int x, int y, Qt::MouseButton button) {
// 我们只关心左键释放
if(button != Qt::LeftButton)
return;
if(m_progress_pressed) {
m_progress_pressed = false;
qDebug("resume at %dms.", m_resume_ms);
m_owner->resume(true, m_resume_ms);
}
}
int Bar::get_speed() {

View File

@ -84,7 +84,8 @@ public:
QRect rc(){return m_rc;}
void onMouseMove(int x, int y);
void onMousePress(int x, int y);
void onMousePress(int x, int y, Qt::MouseButton button);
void onMouseRelease(int x, int y, Qt::MouseButton button);
private:
void _init_imgages();
@ -94,7 +95,7 @@ private:
MainWindow* m_owner;
uint32_t m_total_ms; // 录像的总时长
uint32_t m_passed_ms; // 已经播放了的时长
uint32_t m_played_ms; // 已经播放了的时长
int m_percent; // 已经播放了的百分比0~100
int m_percent_last_draw;
QString m_str_total_time;
@ -139,6 +140,10 @@ private:
int m_speed_hover; // speed__max=no-hover
bool m_skip_selected;
bool m_skip_hover;
bool m_progress_hover;
bool m_progress_pressed;
uint32_t m_resume_ms; // after drag progress-pointer, resume play from here.
};
#endif // BAR_H

View File

@ -110,20 +110,7 @@ void MainWindow::_do_first_run() {
// connect(m_thr_data, SIGNAL(signal_download(DownloadParam*)), this, SLOT(_do_download(DownloadParam*)));
m_thr_data->start();
_start_play_thread();
}
void MainWindow::_start_play_thread() {
if(m_thr_play) {
m_thr_play->stop();
//m_thr_play->wait();
disconnect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
delete m_thr_play;
m_thr_play = nullptr;
}
//_start_play_thread();
m_thr_play = new ThrPlay(this);
connect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
@ -131,6 +118,24 @@ void MainWindow::_start_play_thread() {
m_thr_play->start();
}
//void MainWindow::_start_play_thread() {
// if(m_thr_play) {
// m_thr_play->stop();
// //m_thr_play->wait();
// disconnect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
// delete m_thr_play;
// m_thr_play = nullptr;
// }
// m_thr_play = new ThrPlay(this);
// connect(m_thr_play, SIGNAL(signal_update_data(UpdateData*)), this, SLOT(_do_update_data(UpdateData*)));
// m_thr_play->speed(m_bar.get_speed());
// m_thr_play->start();
//}
void MainWindow::set_speed(int s) {
if(m_thr_play)
m_thr_play->speed(s);
@ -203,11 +208,17 @@ void MainWindow::pause() {
m_play_state = PLAY_STATE_PAUSE;
}
void MainWindow::resume() {
if(m_play_state == PLAY_STATE_PAUSE)
m_thr_play->resume();
else if(m_play_state == PLAY_STATE_STOP)
_start_play_thread();
void MainWindow::resume(bool relocate, uint32_t ms) {
if(m_play_state == PLAY_STATE_PAUSE) {
if(relocate)
m_thr_data->restart(ms);
m_thr_play->resume(relocate, ms);
}
else if(m_play_state == PLAY_STATE_STOP) {
// _start_play_thread();
m_thr_data->restart(0);
m_thr_play->resume(true, 0);
}
m_play_state = PLAY_STATE_RUNNING;
}
@ -432,12 +443,22 @@ 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())) {
m_bar.onMousePress(e->x(), e->y());
m_bar.onMousePress(e->x(), e->y(), e->button());
}
}
}
void MainWindow::mouseReleaseEvent(QMouseEvent *e) {
qDebug("mouse release.");
// if(!m_show_default) {
// QRect rc = m_bar.rc();
// if(rc.contains(e->pos())) {
// m_bar.onMouseRelease(e->x(), e->y(), e->button());
// }
// }
m_bar.onMouseRelease(e->x(), e->y(), e->button());
}

View File

@ -32,7 +32,7 @@ public:
void set_resource(const QString& res);
void pause();
void resume();
void resume(bool relocate, uint32_t ms);
void restart();
void set_speed(int s);
void set_skip(bool s);
@ -44,8 +44,9 @@ private:
void paintEvent(QPaintEvent *e);
void mouseMoveEvent(QMouseEvent *e);
void mousePressEvent(QMouseEvent *e);
void mouseReleaseEvent(QMouseEvent *e);
void _start_play_thread();
// void _start_play_thread();
private slots:
void _do_first_run(); // 默认界面加载完成后,开始播放操作(可能会进行数据下载)

View File

@ -23,7 +23,12 @@ ThrData::ThrData(MainWindow* mainwin, const QString& res) {
m_res = res;
m_need_download = false;
m_need_stop = false;
// m_dl = nullptr;
m_need_restart = false;
m_wait_restart = false;
m_need_show_kf = false;
m_file_idx = 0;
m_offset = 0;
#ifdef __APPLE__
m_data_path_base = QStandardPaths::writableLocation(QStandardPaths::DesktopLocation);
@ -138,54 +143,12 @@ void ThrData::_run() {
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);
/*
// fake-code:
file_index = 0;
for(;;) {
if(file_index >= file_count) {
msleep(500);
continue;
}
if(queue.size < 500) {
need_pkg = 1000 - queue.size;
for(i = 0; i < need_pkg; i++) {
if(f.not_open) {
f.open(file_index);
}
pkg = read_pkg()
if(f.to_end) {
f.close();
file_index += 1;
if(file_index >= file_count)
break;
}
queue.add(pkg)
}
if(file_index >= file_count)
break;
}
else {
msleep(100);
}
}
*/
QFile* fdata = nullptr;
uint32_t file_idx = 0;
uint32_t start_offset = 0;
//uint32_t file_idx = 0;
//uint32_t start_offset = 0;
qint64 file_size = 0;
qint64 file_processed = 0;
qint64 read_len = 0;
@ -196,8 +159,20 @@ void ThrData::_run() {
if(m_need_stop)
return;
if(m_need_restart) {
if(fdata) {
fdata->close();
delete fdata;
fdata = nullptr;
}
m_wait_restart = true;
msleep(50);
continue;
}
// 如果所有文件都已经处理完了,则等待(可能用户会拖动滚动条,或者重新播放)
if(file_idx >= m_hdr.info.dat_file_count) {
if(m_file_idx >= m_hdr.info.dat_file_count) {
msleep(500);
continue;
}
@ -222,10 +197,12 @@ void ThrData::_run() {
for(int i = 0; i < pkg_need_add; ++i) {
if(m_need_stop)
return;
if(m_need_restart)
break;
// 如果数据文件尚未打开,则打开它
if(fdata == nullptr) {
str_fidx.sprintf("%d", file_idx+1);
str_fidx.sprintf("%d", m_file_idx+1);
QString tpd_fname = QString("%1/tp-rdp-%2.tpd").arg(m_data_path, str_fidx);
tpd_fname = QDir::toNativeSeparators(tpd_fname);
@ -236,13 +213,13 @@ void ThrData::_run() {
for(;;) {
if(m_need_stop)
return;
if(!m_thr_download.is_running() || m_thr_download.is_tpd_downloaded(file_idx))
if(!m_thr_download.is_running() || m_thr_download.is_tpd_downloaded(m_file_idx))
break;
msleep(100);
}
// 下载失败了
if(!m_thr_download.is_tpd_downloaded(file_idx))
if(!m_thr_download.is_tpd_downloaded(m_file_idx))
return;
}
}
@ -256,15 +233,22 @@ void ThrData::_run() {
file_size = fdata->size();
file_processed = 0;
qDebug("Open file, processed: %" PRId64 ", size: %" PRId64, file_processed, file_size);
qDebug("Open file tp-rdp-%d.tpd, processed: %" PRId64 ", size: %" PRId64, m_file_idx+1, file_processed, file_size);
}
// qDebug("B processed: %" PRId64 ", size: %" PRId64, file_processed, file_size);
// 如果指定了起始偏移,则跳过这部分数据
if(m_offset > 0) {
fdata->seek(m_offset);
file_processed = m_offset;
m_offset = 0;
}
//----------------------------------
// 读取一个数据包
//----------------------------------
if(file_size - file_processed < sizeof(TS_RECORD_PKG)) {
qDebug("invaid tp-rdp-%d.tpd file, filesize=%" PRId64 ", processed=%" PRId64 ", need=%d.", file_idx+1, file_size, file_processed, sizeof(TS_RECORD_PKG));
qDebug("invaid tp-rdp-%d.tpd file, filesize=%" PRId64 ", processed=%" PRId64 ", need=%d.", m_file_idx+1, file_size, file_processed, sizeof(TS_RECORD_PKG));
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
@ -274,14 +258,14 @@ void ThrData::_run() {
// if(read_len == 0)
// break;
if(read_len != sizeof(TS_RECORD_PKG)) {
qDebug("invaid tp-rdp-%d.tpd file, read_len=%" PRId64 " (1).", file_idx+1, read_len);
qDebug("invaid tp-rdp-%d.tpd file, read_len=%" PRId64 " (1).", m_file_idx+1, read_len);
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
file_processed += sizeof(TS_RECORD_PKG);
if(file_size - file_processed < pkg.size) {
qDebug("invaid tp-rdp-%d.tpd file (2).", file_idx+1);
qDebug("invaid tp-rdp-%d.tpd file (2).", m_file_idx+1);
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
@ -292,7 +276,7 @@ void ThrData::_run() {
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);
qDebug("invaid tp-rdp-%d.tpd file, read_len=%" PRId64 " (3).", m_file_idx+1, read_len);
_notify_error(QString("%1\ntp-rdp-%2.tpd").arg(LOCAL8BIT("错误的录像数据文件!"), str_fidx));
return;
}
@ -300,30 +284,23 @@ void ThrData::_run() {
UpdateData* dat = new UpdateData();
if(!dat->parse(pkg, pkg_data)) {
qDebug("invaid tp-rdp-%d.tpd file (4).", file_idx+1);
qDebug("invaid tp-rdp-%d.tpd file (4).", m_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) {
qDebug("----key frame: %ld, processed=%" PRId64 ", pkg.size=%d", pkg.time_ms, file_processed, pkg.size);
delete dat;
dat = nullptr;
if(m_need_show_kf) {
m_need_show_kf = false;
qDebug("++ show keyframe.");
}
else {
qDebug("-- skip keyframe.");
delete dat;
dat = nullptr;
}
}
@ -331,32 +308,75 @@ void ThrData::_run() {
if(dat) {
m_locker.lock();
m_data.enqueue(dat);
// 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) {
fdata->close();
delete fdata;
fdata = nullptr;
file_idx++;
m_file_idx++;
}
if(file_idx >= m_hdr.info.dat_file_count) {
if(m_file_idx >= m_hdr.info.dat_file_count) {
UpdateData* dat = new UpdateData(TYPE_END);
m_locker.lock();
m_data.enqueue(dat);
// qDebug("queue data count: %d", m_data.size());
m_locker.unlock();
break;
}
}
}
}
void ThrData::restart(uint32_t start_ms) {
// 让处理线程处理完当前循环,然后等待
m_need_restart = true;
// 确保处理线程已经处理完当前循环
for(;;) {
msleep(50);
if(m_need_stop)
return;
if(m_wait_restart)
break;
}
// 清空待播放队列
_clear_data();
if(start_ms == 0) {
m_offset = 0;
m_file_idx = 0;
m_need_show_kf = false;
}
else {
// 找到最接近 start_ms 但小于它的关键帧
size_t i = 0;
for(i = 0; i < m_kf.size(); ++i) {
if(m_kf[i].time_ms > start_ms)
break;
}
if(i > 0)
i--;
// 指定要播放的数据的开始位置
m_offset = m_kf[i].offset;
m_file_idx = m_kf[i].file_index;
m_need_show_kf = true;
}
qDebug("RESTART: offset=%d, file_idx=%d", m_offset, m_file_idx);
// 让处理线程继续
m_wait_restart = false;
m_need_restart = false;
}
bool ThrData::_load_header() {
QString msg;
qDebug() << "PATH_BASE: " << m_data_path;

View File

@ -54,6 +54,7 @@ public:
virtual void run();
void stop();
void restart(uint32_t start_ms); // 重新从指定时间开始播放
bool have_more_data();
@ -94,6 +95,12 @@ private:
TS_RECORD_HEADER m_hdr;
KeyFrames m_kf;
bool m_need_restart;
bool m_wait_restart;
bool m_need_show_kf;
uint32_t m_file_idx;
uint32_t m_offset;
};
#endif // THR_DATA_H

View File

@ -25,8 +25,7 @@ ThrPlay::ThrPlay(MainWindow* mainwnd) {
m_need_pause = false;
m_speed = 1;
m_skip = false;
// m_res = res;
// m_thr_data = nullptr;
m_start_ms = 0;
}
ThrPlay::~ThrPlay() {
@ -64,10 +63,18 @@ void ThrPlay::_notify_error(const QString& msg) {
emit signal_update_data(_msg);
}
void ThrPlay::resume(bool relocate, uint32_t start_ms) {
if(relocate) {
m_start_ms = start_ms;
m_first_run = true;
}
m_need_pause = false;
}
void ThrPlay::run() {
ThrData* thr_data = m_mainwnd->get_thr_data();
bool first_run = true;
m_first_run = true;
uint32_t last_time_ms = 0;
uint32_t last_pass_ms = 0;
@ -83,11 +90,20 @@ void ThrPlay::run() {
continue;
}
if(first_run) {
first_run = false;
if(m_first_run) {
m_first_run = false;
_notify_message("");
}
if(m_start_ms > 0) {
if(dat->get_time() < m_start_ms) {
emit signal_update_data(dat);
continue;
}
last_time_ms = m_start_ms;
m_start_ms = 0;
}
// 2. 根据数据包的信息,等待到播放时间点
uint32_t need_wait_ms = 0;
uint32_t this_time_ms = dat->get_time();
@ -119,6 +135,14 @@ void ThrPlay::run() {
if(m_need_stop)
break;
if(m_start_ms > 0) {
// if(dat) {
// delete dat;
// dat = nullptr;
// }
break;
}
time_wait *= m_speed;
// 如果已经在等待长时间无操作区间内用户设置了跳过无操作区间则将超过0.5秒的等待时间压缩至0.5秒。
@ -143,6 +167,14 @@ void ThrPlay::run() {
if(m_need_stop)
break;
// if(m_start_ms > 0) {
// if(dat) {
// delete dat;
// dat = nullptr;
// }
// break;
// }
}
last_time_ms = this_time_ms;

View File

@ -19,7 +19,7 @@ public:
virtual void run();
void stop();
void pause() {m_need_pause = true;}
void resume() {m_need_pause = false;}
void resume(bool relocate, uint32_t start_ms);
void speed(int s) {if(s >= 1 && s <= 16) m_speed = s;}
void skip(bool s) {m_skip = s;}
@ -36,6 +36,8 @@ private:
bool m_need_pause;
int m_speed;
bool m_skip;
bool m_first_run;
uint32_t m_start_ms;
};
#endif // THR_PLAY_H