What is the most efficient way to display decoded video frames in Qt?
Thanks for the answers, but I finally revisited this problem and came up with a rather simple solution that gives good performance. It involves deriving from QGLWidget
and overriding the paintEvent()
function. Inside the paintEvent()
function, you can call QPainter::drawImage(...)
and it will perform the scaling to a specified rectangle for you using hardware if available. So it looks something like this:
class QGLCanvas : public QGLWidget
{
public:
QGLCanvas(QWidget* parent = NULL);
void setImage(const QImage& image);
protected:
void paintEvent(QPaintEvent*);
private:
QImage img;
};
QGLCanvas::QGLCanvas(QWidget* parent)
: QGLWidget(parent)
{
}
void QGLCanvas::setImage(const QImage& image)
{
img = image;
}
void QGLCanvas::paintEvent(QPaintEvent*)
{
QPainter p(this);
//Set the painter to use a smooth scaling algorithm.
p.setRenderHint(QPainter::SmoothPixmapTransform, 1);
p.drawImage(this->rect(), img);
}
With this, I still have to convert the YUV 420P to RGB32, but ffmpeg has a very fast implementation of that conversion in libswscale. The major gains come from two things:
- No need for software scaling. Scaling is done on the video card (if available)
- Conversion from
QImage
toQPixmap
, which is happening in theQPainter::drawImage()
function is performed at the original image resolution as opposed to the upscaled fullscreen resolution.
I was pegging my processor on just the display (decoding was being done in another thread) with my previous method. Now my display thread only uses about 8-9% of a core for fullscreen 1920x1200 30fps playback. I'm sure it could probably get even better if I could send the YUV data straight to the video card, but this is plenty good enough for now.
How to display the frames of a video via Qt gui application ?
Qt can only update the UI when control has returned to the event loop. You can try calling qApp->processEvents()
in your loop but that may not be the optimum approach.
Qt C++ - multithread video frame transformation
Short answer: you can't.
Qt Help: Threading Basics:
GUI Thread and Worker Thread
As mentioned, each program has one thread when it is started. This
thread is called the "main thread" (also known as the "GUI thread" in
Qt applications). The Qt GUI must run in this thread. All widgets and
several related classes, for example QPixmap, don't work in secondary
threads. A secondary thread is commonly referred to as a "worker
thread" because it is used to offload processing work from the main
thread.
What you can do: you can work with ui in main thread, pass QImage
(or other data, not related with ui) into separate thread through signal/slot system, process it there and pass it back.
UPDATE (how can I do that? I mean, pass QImage back and forth between threads and display both videos simultaneously and continually? – Petersaber)
I'm not a video expert, but I can give you I hint. Let's assume, you have a Widget
, which accepts QImage
and have to show it. Processor
is an object, which processes an image and returns it back to gui thread. Processor
should be derived from QObject
.
Widget.h
class Widget : public QWidget, private Ui::Widget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
void enqueueImage(const QImage &img);
signals:
void process(const QImage &img);
private:
void showNextImage();
private slots:
void onProcessed(const QImage &img);
private:
QThread *mThread;
QScopedPointer < Processor > mProcessor;
QList < QImage > mImagesPool;
};
Widget.cpp
Widget::Widget(QWidget *parent) :
QWidget(parent)
, mThread(new QThread(this))
, mProcessor(new Processor)
{
setupUi(this);
mProcessor->moveToThread(mThread);
connect(this, SIGNAL(process(QImage)), mProcessor, SLOT(process(QImage)));
connect(mProcessor, SIGNAL(processed(QImage)), SLOT(onProcessed(QImage)));
}
void Widget::enqueueImage(const QImage &img)
{
mImagesPool.append(img);
showNextImage();
}
void Widget::showNextImage()
{
if (mImagesPool.isEmpty())
return;
emit process(mImagesPool.first());
}
void Widget::onProcessed(const QImage &img)
{
if (mImagesPool.isEmpty())
return;
rawImage->setPixmap(QPixmap::fromImage(mImagesPool.takeFirst()));
processedImage->setPixmap(QPixmap::fromImage(img));
showNextImage();
}
Processor.cpp
void Processor::process(const QImage &img)
{
QImage newImg = img;
// Do stuff
emit processed(newImg);
}
Best / simplest way to display FFmpeg frames in Qt5
The following works for me :
QImage frame = QImage(avFrame->data[0], avFrame->width, avFrame->height,
avFrame->linesize[0], QImage::Format_RGB32)
.copy();
Related Topics
Is It Ok to Use C-Style Cast for Built-In Types
Configuring Compilers on MAC M1 (Big Sur, Monterey) for Rcpp and Other Tools
How to Create a Temporary Directory in C++
How to Read Bmp Pixel Values into an Array
How to Use C++ Preprocessor Stringification on Variadic MACro Arguments
Cannot Convert 'Const Char*' to 'Lpcwstr {Aka Const Wchar_T*}'
How to Download a File with Winhttp in C/C++
Array Decay to Pointers in Templates
C++ Delete Pointer Issue, Can Still Access Data
Why the Sizeof(Bool) Is Not Defined to Be One, by the Standard Itself
What Is Different Between Join() and Detach() for Multi Threading in C++
General Rules of Passing/Returning Reference of Array (Not Pointer) To/From a Function
Why Must Const Members Be Initialized in the Constructor Initializer Rather Than in Its Body
How to Break When a Specific Exception Type Is Thrown in Gdb
What Is the Vtable Layout and Vtable Pointer Location in C++ Objects in Gcc 3.X and 4.X