Skip to content

Commit 7d6d8bb

Browse files
authored
Prevent image buffer exhaustion on Android using Qt (#938)
* Prevent image buffer exhaustion on Android using Qt This commit fixes the following exception: java.lang.IllegalStateException: maxImages (10) has already been acquired, call #close before acquiring more. This exception is thrown when the device cannot process the images fast enough. Note that Qt captures the images in a separate (Java) thread (on Android) and sends them asynchronously to the main (GUI) thread. This fix moves the processing to a separate thread and discards all new images while another image (frame) is still being processed. This allows Qt to close the Android resources before the buffer is full. In recent versions of Qt, Qt catches this exception itself (see https:// bugreports.qt.io/browse/QTBUG-116526) by restarting the camera, but frequent restarts may degrade performance. On non-Android platforms, this fix has the advantage that the GUI remains responsive even if the barcode processing is slow, and that always the most recently captured frame is processed. Related to #743 * Support multiple threads to process video frames in Qt * Do not start processing new frames during shutdown.
1 parent 68c5727 commit 7d6d8bb

File tree

1 file changed

+35
-3
lines changed

1 file changed

+35
-3
lines changed

example/ZXingQtReader.h

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
1717
#include <QAbstractVideoFilter>
1818
#else
19+
#include <QThreadPool>
1920
#include <QVideoFrame>
2021
#include <QVideoSink>
2122
#endif
@@ -333,7 +334,16 @@ class BarcodeReader : public QObject, private ReaderOptions
333334
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
334335
BarcodeReader(QObject* parent = nullptr) : QAbstractVideoFilter(parent) {}
335336
#else
336-
BarcodeReader(QObject* parent = nullptr) : QObject(parent) {}
337+
BarcodeReader(QObject* parent = nullptr) : QObject(parent)
338+
{
339+
_pool.setMaxThreadCount(1);
340+
}
341+
~BarcodeReader()
342+
{
343+
_pool.setMaxThreadCount(0);
344+
_pool.waitForDone(-1);
345+
}
346+
337347
#endif
338348

339349
// TODO: find out how to properly expose QFlags to QML
@@ -373,10 +383,11 @@ class BarcodeReader : public QObject, private ReaderOptions
373383
ZQ_PROPERTY(bool, isPure, setIsPure)
374384

375385
// For debugging/development
376-
int runTime = 0;
386+
QAtomicInt runTime = 0;
377387
Q_PROPERTY(int runTime MEMBER runTime)
378388

379389
public slots:
390+
// Function should be thread safe, as it may be called from a separate thread.
380391
ZXingQt::Barcode process(const QVideoFrame& image)
381392
{
382393
QElapsedTimer t;
@@ -403,6 +414,7 @@ public slots:
403414
#else
404415
private:
405416
QVideoSink *_sink = nullptr;
417+
QThreadPool _pool;
406418

407419
public:
408420
void setVideoSink(QVideoSink* sink) {
@@ -413,9 +425,29 @@ public slots:
413425
disconnect(_sink, nullptr, this, nullptr);
414426

415427
_sink = sink;
416-
connect(_sink, &QVideoSink::videoFrameChanged, this, &BarcodeReader::process);
428+
connect(_sink, &QVideoSink::videoFrameChanged, this, &BarcodeReader::onVideoFrameChanged, Qt::DirectConnection);
429+
}
430+
void onVideoFrameChanged(const QVideoFrame& frame)
431+
{
432+
if (_pool.activeThreadCount() >= _pool.maxThreadCount())
433+
return; // we are busy => skip the frame
434+
435+
_pool.start([this, frame](){process(frame);});
417436
}
418437
Q_PROPERTY(QVideoSink* videoSink MEMBER _sink WRITE setVideoSink)
438+
Q_PROPERTY(int maxThreadCount READ maxThreadCount WRITE setMaxThreadCount)
439+
int maxThreadCount () const
440+
{
441+
return _pool.maxThreadCount();
442+
}
443+
void setMaxThreadCount (int maxThreadCount)
444+
{
445+
if (_pool.maxThreadCount() != maxThreadCount) {
446+
_pool.setMaxThreadCount(maxThreadCount);
447+
emit maxThreadCountChanged();
448+
}
449+
}
450+
Q_SIGNAL void maxThreadCountChanged();
419451
#endif
420452

421453
};

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy