环形缓冲队列技术应用实例

需求描述

  • 在HDMI扩展屏上连续播放32位深度png图片
  • png分辨率1080x800
  • 播放间隔毫秒级(1~100毫秒)

 

解决思路

【方案1】每次播放图片时从磁盘现加载当前图片,绘制播放

 

 

  • 缺点:加载动作耗时,导致播放卡顿,难以达到毫秒级间隔。

 

方案2】提前把所有png图片加载到内存,并且绘制成QPixmap

 

  • 缺点:单张png图片对应QPixmap内存为1080x800x4 byte(3.375M),若总共1000张图片就需要3G内存,且预加载时间长

 

方案3】采用环形队列缓冲区,先预加载队列深度(50)张图片,然后再播放图片的同时,另开一个线程从磁盘加载图片填充缓冲区

环形队列

用vector实现环形队列,定义一个QPixmap类型的QVector容器,用于存储位图图像。

QVector SliceBuffer;

设置队列深度,预先从磁盘加载少量位图图像。

#define SliceDataBufferLength 50

设置图片序号变量,记录当前需要从磁盘加载的图片序号

int wSliceIndexToRead = 0;


预先从磁盘加载位图,填满缓冲区

SliceBuffer.clear();
for (int i = 0; i < SliceDataBufferLength; i++)
{
	QPixmap pixmap = QPixmap(tr("%1/%2.png").arg(ui->lineEdit_PicPath->text()).arg(i, 4, 10, QChar('0')));
	SliceBuffer.push_back(pixmap);
}
wSliceIndexToRead = SliceDataBufferLength;

填充线程

【初始化线程】(重点关注红色加粗内容)

if (m_writeBufferThread)
{
	return;
}
m_writeBufferThread = new QThread();
m_writeBufferObj->moveToThread(m_writeBufferThread);
connect(m_writeBufferThread, &QThread::finished, m_writeBufferThread, &QObject::deleteLater);
connect(m_writeBufferThread, &QThread::finished, m_writeBufferObj, &QObject::deleteLater);
connect(this, &MainWindow::startWriteBuffer, m_writeBufferObj, &WriteSliceBufferThreadObject::writeBuffer);
connect(m_writeBufferObj, &WriteSliceBufferThreadObject::message, this, &MainWindow::receiveMessage);

m_writeBufferThread->start();

【填充操作】

void WriteSliceBufferThreadObject::writeBuffer()
{
	while (1)
	{
		int tempWReadPoint = wReadPoint;
		if ((wWritePoint + 1) % SliceDataBufferLength != tempWReadPoint)//缓冲区未满
		{
			SliceBuffer[wWritePoint % SliceDataBufferLength] = QPixmap(tr("%1/%2.png").arg(m_slicePicPath).arg(wSliceIndexToRead++, 4, 10, QChar('0')));
			wWritePoint = (wWritePoint + 1) % SliceDataBufferLength;
		}
	}
}

 

播放线程

【初始化线程】(重点关注红色加粗内容)

void MainWindow::startBuildThread()
{
	if (m_buildThread)
	{
		return;
	}
	m_buildThread = new QThread();
	m_buildObj->moveToThread(m_buildThread);
	connect(m_buildThread, &QThread::finished, m_buildThread, &QObject::deleteLater);
	connect(m_buildThread, &QThread::finished, m_buildObj, &QObject::deleteLater);
	connect(this, &MainWindow::startPrint, m_buildObj, &PrintBuildThreadObject::buildRun);
	/*connect(m_buildObj, &PrintBuildThreadObject::message, this, &MainWindow::receiveMessage);
	connect(m_buildObj, &PrintBuildThreadObject::progress, this, &MainWindow::updateProgress);
	connect(m_buildObj, &PrintBuildThreadObject::complete, this, &MainWindow::completeBuild);*/
	
	
	m_buildThread->start();
}

 

 
//打印线程设置
if (m_buildObj)
{
	//m_buildObj->resetSerialPort(ui->comboBox_SerialPort->currentText());
	m_buildObj->setGcodePath(ui->lineEdit_GCode->text());
	connect(m_buildObj, &PrintBuildThreadObject::message, this, &MainWindow::receiveMessage);
	connect(m_buildObj, &PrintBuildThreadObject::progress, this, &MainWindow::updateProgress);
	connect(m_buildObj, &PrintBuildThreadObject::complete, this, &MainWindow::completeBuild);
}

 

 

【播放操作】(重点关注红色加粗内容)

void PrintBuildThreadObject::buildRun()
{
	{
		QMutexLocker locker(&m_stopMutex);
		m_isStop = false;
	}
	QFile file(m_gcodefile);
	if (!file.open(QFile::ReadOnly | QFile::Text))
	{
		QMessageBox::warning(NULL,tr("Error"), tr("read gcode file error:%1").arg(file.errorString()));
	}
	QTextStream in(&file);

	LARGE_INTEGER  large_interger;
	double dff;
	__int64  c0,c1, c2, c3 = 0,c4;
	QueryPerformanceFrequency(&large_interger);
	dff = large_interger.QuadPart;
	QueryPerformanceCounter(&large_interger);
	c0 = large_interger.QuadPart;
	Singleton::getInstance()->m_canContinue = true;
	while (!in.atEnd())
	{
		{
			QMutexLocker locker(&m_stopMutex);
			if (m_isStop)
				return;
		}
		QString line;
		if (Singleton::getInstance()->m_canContinue)
			line = in.readLine();
		else
			line = "Idle";
		if (line.startsWith(";"))
		{
			//qDebug() << tr("show pic:%1").arg(line.right(line.length() - QString(";").length()).trimmed());
			emit progress(line.right(line.length() - 8).toInt());
			emit message(tr("show pic:%1").arg(line.right(line.length() - QString(";").length()).trimmed()));
		}
		else if (line.startsWith(";"))
		{
			int delay = line.right(line.length() - QString(";").length()).trimmed().toInt();
			QueryPerformanceCounter(&large_interger);
			c1 = large_interger.QuadPart;
			QueryPerformanceCounter(&large_interger);
			c2 = large_interger.QuadPart;
			while (((c2 - c1) * 1000 / dff) < delay)
			{
				QueryPerformanceCounter(&large_interger);
				c2 = large_interger.QuadPart;
			}
		}
		else if (line.startsWith("G1"))
		{
			/*QByteArray ba = line.trimmed().toLatin1();
			char* chmm = ba.data();
			Singleton::getInstance()->m_canContinue = false;
			Singleton::getInstance()->m_extSerialPort->write(chmm);*/

			char *sendBuffer = (char*)malloc(FrameLengthWithoutData + 3 + line.trimmed().length()+2);
			SerialPortProtocol::CodingSerialport(sendBuffer, MotorControl, G1Motion, Request, line.trimmed().length(), line.trimmed().toLatin1().data());
			if (Singleton::getInstance()->m_extSerialPort != NULL)
			{
				emit message(QString::fromLocal8Bit("%1:发送%2命令").arg(Singleton::getCurrentTime()).arg(line.trimmed()));
				if(Singleton::getInstance()->m_noneedAnswer==false)
					Singleton::getInstance()->m_canContinue = false;
				sendBuffer[FrameLengthWithoutData + 3 + line.trimmed().length()] = 0x0d;
				sendBuffer[FrameLengthWithoutData + 3 + line.trimmed().length() + 1] = 0x0a;
				Singleton::getInstance()->m_extSerialPort->write(sendBuffer, FrameLengthWithoutData + 3 + line.trimmed().length()+2);
			}
			delete sendBuffer;

			//emit message(QString::fromLocal8Bit("%1:已发送G1命令").arg(Singleton::getCurrentTime()));

			QueryPerformanceCounter(&large_interger);
			c2 = large_interger.QuadPart;
			if (c3 != 0)
				//qDebug() << tr("time intevl:%1").arg((c2 - c3) * 1000 / dff);
				emit message(tr("time intevl:%1").arg((c2 - c3) * 1000 / dff));
			c3 = c2;
		}
	}

	QueryPerformanceCounter(&large_interger);
	c4 = large_interger.QuadPart;
	int secs = (c4 - c0) * 1000 / dff / 1000;
	QString slaspetime = tr("total time:%1 min %2 sec").arg(secs / 60).arg(secs % 60);
	emit message(slaspetime);
	emit complete();
	file.close();
}
void MainWindow::updateProgress(int value)
{
	ui->progressBar->setValue(value);
	//m_pic->setPixmap(SliceBuffer.at(value % SliceDataBufferLength));
	int itempWritePoint = wWritePoint;
	if (wReadPoint % SliceDataBufferLength != itempWritePoint)
	{
		//m_pic->setPixmap(SliceBuffer.at(wReadPoint));
		Singleton::getInstance()->m_projector->showPixmap(SliceBuffer.at(wReadPoint));
		wReadPoint = (wReadPoint + 1) % SliceDataBufferLength;
	}
	//m_pic->showMaximized();
}



 

 

你可能感兴趣的:(Qt&C++)