What Is the Most Efficient Way to Display Decoded Video Frames in Qt

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 to QPixmap, which is happening in the QPainter::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



Leave a reply



Submit