Player example histogram: Process frames on a separate thread
Change-Id: I6989f9ea9cb6e45c54ed75079a5b5748e15ee0d8 Reviewed-by: Dmytro Poplavskiy <dmytro.poplavskiy@nokia.com> Reviewed-by: Michael Goddard <michael.goddard@nokia.com>
This commit is contained in:
committed by
Qt by Nokia
parent
b7f4b2decb
commit
b915c5e0e7
@@ -44,63 +44,34 @@
|
|||||||
HistogramWidget::HistogramWidget(QWidget *parent)
|
HistogramWidget::HistogramWidget(QWidget *parent)
|
||||||
: QWidget(parent)
|
: QWidget(parent)
|
||||||
, m_levels(128)
|
, m_levels(128)
|
||||||
|
, m_isBusy(false)
|
||||||
{
|
{
|
||||||
|
m_processor.moveToThread(&m_processorThread);
|
||||||
|
qRegisterMetaType<QVector<qreal> >("QVector<qreal>");
|
||||||
|
connect(&m_processor, SIGNAL(histogramReady(QVector<qreal>)), SLOT(setHistogram(QVector<qreal>)));
|
||||||
|
m_processorThread.start(QThread::LowestPriority);
|
||||||
|
}
|
||||||
|
|
||||||
|
HistogramWidget::~HistogramWidget()
|
||||||
|
{
|
||||||
|
m_processorThread.quit();
|
||||||
|
m_processorThread.wait(10000);
|
||||||
}
|
}
|
||||||
|
|
||||||
void HistogramWidget::processFrame(QVideoFrame frame)
|
void HistogramWidget::processFrame(QVideoFrame frame)
|
||||||
{
|
{
|
||||||
m_histogram.clear();
|
if (m_isBusy)
|
||||||
|
return; //drop frame
|
||||||
|
|
||||||
do {
|
m_isBusy = true;
|
||||||
if (!m_levels)
|
QMetaObject::invokeMethod(&m_processor, "processFrame",
|
||||||
break;
|
Qt::QueuedConnection, Q_ARG(QVideoFrame, frame), Q_ARG(int, m_levels));
|
||||||
|
|
||||||
if (!frame.map(QAbstractVideoBuffer::ReadOnly))
|
|
||||||
break;
|
|
||||||
|
|
||||||
m_histogram.resize(m_levels);
|
|
||||||
|
|
||||||
if (frame.pixelFormat() == QVideoFrame::Format_YUV420P) {
|
|
||||||
// Process YUV420P data
|
|
||||||
uchar *b = frame.bits();
|
|
||||||
for (int y = 0; y < frame.height(); y++) {
|
|
||||||
uchar *lastPixel = b + frame.width();
|
|
||||||
for (uchar *curPixel = b; curPixel < lastPixel; curPixel++)
|
|
||||||
m_histogram[(*curPixel * m_levels) / 256] += 1.0;
|
|
||||||
b += frame.bytesPerLine();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat());
|
|
||||||
if (imageFormat != QImage::Format_Invalid) {
|
|
||||||
// Process RGB data
|
|
||||||
QImage image(frame.bits(), frame.width(), frame.height(), imageFormat);
|
|
||||||
image = image.convertToFormat(QImage::Format_RGB32);
|
|
||||||
|
|
||||||
const QRgb* b = (const QRgb*)image.bits();
|
|
||||||
for (int y = 0; y < image.height(); y++) {
|
|
||||||
const QRgb *lastPixel = b + frame.width();
|
|
||||||
for (const QRgb *curPixel = b; curPixel < lastPixel; curPixel++)
|
|
||||||
m_histogram[(qGray(*curPixel) * m_levels) / 256] += 1.0;
|
|
||||||
b = (const QRgb*)((uchar*)b + image.bytesPerLine());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// find maximum value
|
void HistogramWidget::setHistogram(QVector<qreal> histogram)
|
||||||
qreal maxValue = 0.0;
|
{
|
||||||
for (int i = 0; i < m_histogram.size(); i++) {
|
m_isBusy = false;
|
||||||
if (m_histogram[i] > maxValue)
|
m_histogram = histogram;
|
||||||
maxValue = m_histogram[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (maxValue > 0.0) {
|
|
||||||
for (int i = 0; i < m_histogram.size(); i++)
|
|
||||||
m_histogram[i] /= maxValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.unmap();
|
|
||||||
} while (false);
|
|
||||||
|
|
||||||
update();
|
update();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,3 +96,59 @@ void HistogramWidget::paintEvent(QPaintEvent *event)
|
|||||||
painter.fillRect(barWidth * i, 0, barWidth * (i + 1), height() - h, Qt::black);
|
painter.fillRect(barWidth * i, 0, barWidth * (i + 1), height() - h, Qt::black);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void FrameProcessor::processFrame(QVideoFrame frame, int levels)
|
||||||
|
{
|
||||||
|
QVector<qreal> histogram(levels);
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (!levels)
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (!frame.map(QAbstractVideoBuffer::ReadOnly))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (frame.pixelFormat() == QVideoFrame::Format_YUV420P ||
|
||||||
|
frame.pixelFormat() == QVideoFrame::Format_NV12) {
|
||||||
|
// Process YUV data
|
||||||
|
uchar *b = frame.bits();
|
||||||
|
for (int y = 0; y < frame.height(); y++) {
|
||||||
|
uchar *lastPixel = b + frame.width();
|
||||||
|
for (uchar *curPixel = b; curPixel < lastPixel; curPixel++)
|
||||||
|
histogram[(*curPixel * levels) >> 8] += 1.0;
|
||||||
|
b += frame.bytesPerLine();
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
QImage::Format imageFormat = QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat());
|
||||||
|
if (imageFormat != QImage::Format_Invalid) {
|
||||||
|
// Process RGB data
|
||||||
|
QImage image(frame.bits(), frame.width(), frame.height(), imageFormat);
|
||||||
|
image = image.convertToFormat(QImage::Format_RGB32);
|
||||||
|
|
||||||
|
const QRgb* b = (const QRgb*)image.bits();
|
||||||
|
for (int y = 0; y < image.height(); y++) {
|
||||||
|
const QRgb *lastPixel = b + frame.width();
|
||||||
|
for (const QRgb *curPixel = b; curPixel < lastPixel; curPixel++)
|
||||||
|
histogram[(qGray(*curPixel) * levels) >> 8] += 1.0;
|
||||||
|
b = (const QRgb*)((uchar*)b + image.bytesPerLine());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// find maximum value
|
||||||
|
qreal maxValue = 0.0;
|
||||||
|
for (int i = 0; i < histogram.size(); i++) {
|
||||||
|
if (histogram[i] > maxValue)
|
||||||
|
maxValue = histogram[i];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (maxValue > 0.0) {
|
||||||
|
for (int i = 0; i < histogram.size(); i++)
|
||||||
|
histogram[i] /= maxValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
frame.unmap();
|
||||||
|
} while (false);
|
||||||
|
|
||||||
|
emit histogramReady(histogram);
|
||||||
|
}
|
||||||
|
|||||||
@@ -43,18 +43,31 @@
|
|||||||
|
|
||||||
#include <QWidget>
|
#include <QWidget>
|
||||||
#include <qvideoframe.h>
|
#include <qvideoframe.h>
|
||||||
|
#include <QThread>
|
||||||
|
|
||||||
QT_USE_NAMESPACE
|
QT_USE_NAMESPACE
|
||||||
|
|
||||||
|
class FrameProcessor: public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void processFrame(QVideoFrame frame, int levels);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void histogramReady(QVector<qreal> histogram);
|
||||||
|
};
|
||||||
|
|
||||||
class HistogramWidget : public QWidget
|
class HistogramWidget : public QWidget
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
public:
|
public:
|
||||||
explicit HistogramWidget(QWidget *parent = 0);
|
explicit HistogramWidget(QWidget *parent = 0);
|
||||||
|
~HistogramWidget();
|
||||||
void setLevels(int levels) { m_levels = levels; }
|
void setLevels(int levels) { m_levels = levels; }
|
||||||
|
|
||||||
public slots:
|
public slots:
|
||||||
void processFrame(QVideoFrame frame);
|
void processFrame(QVideoFrame frame);
|
||||||
|
void setHistogram(QVector<qreal> histogram);
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void paintEvent(QPaintEvent *event);
|
void paintEvent(QPaintEvent *event);
|
||||||
@@ -62,6 +75,9 @@ protected:
|
|||||||
private:
|
private:
|
||||||
QVector<qreal> m_histogram;
|
QVector<qreal> m_histogram;
|
||||||
int m_levels;
|
int m_levels;
|
||||||
|
FrameProcessor m_processor;
|
||||||
|
QThread m_processorThread;
|
||||||
|
bool m_isBusy;
|
||||||
};
|
};
|
||||||
|
|
||||||
#endif // HISTOGRAMWIDGET_H
|
#endif // HISTOGRAMWIDGET_H
|
||||||
|
|||||||
Reference in New Issue
Block a user