Каков наиболее эффективный способ отображения декодированных видеокадров в Qt? - PullRequest
34 голосов
/ 07 августа 2009

Какой самый быстрый способ отображения изображений в виджете Qt? Я декодировал видео с использованием libavformat и libavcodec, поэтому у меня уже есть необработанные кадры RGB или YCbCr 4: 2: 0. В настоящее время я использую QGraphicsView с объектом QGraphicsScene, содержащим QGraphicsPixmapItem. В настоящее время я получаю данные кадра в QPixmap с помощью конструктора QImage из буфера памяти и преобразую его в QPixmap с помощью QPixmap :: fromImage ().

Мне нравятся результаты этого, и они кажутся относительно быстрыми, но я не могу не думать, что должен быть более эффективный способ. Я также слышал, что преобразование QImage в QPixmap стоит дорого. Я реализовал решение, которое использует оверлей SDL на виджете, но я бы хотел остаться только с Qt, так как я могу легко захватывать клики и другое взаимодействие с пользователем при отображении видео с помощью QGraphicsView.

Я выполняю любые необходимые операции масштабирования видео или преобразования цветового пространства с помощью libswscale, поэтому я просто хотел бы знать, есть ли у кого-нибудь более эффективный способ отображения данных изображения после выполнения всей обработки.

Спасибо.

Ответы [ 3 ]

32 голосов
/ 20 апреля 2010

Спасибо за ответы, но я наконец-то снова обратился к этой проблеме и нашел довольно простое решение, которое дает хорошую производительность. Он включает в себя вывод из QGLWidget и переопределение функции paintEvent(). Внутри функции paintEvent() вы можете вызвать QPainter::drawImage(...), и она выполнит масштабирование до указанного прямоугольника, используя аппаратное обеспечение, если оно доступно. Так это выглядит примерно так:

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);
}

При этом мне все еще нужно конвертировать YUV 420P в RGB32, но ffmpeg имеет очень быструю реализацию этого преобразования в libswscale. Основные выгоды связаны с двумя вещами:

  • Нет необходимости в программном масштабировании. Масштабирование выполняется на видеокарте (если имеется)
  • Преобразование из QImage в QPixmap, которое происходит в функции QPainter::drawImage(), выполняется с исходным разрешением изображения в отличие от увеличенного полноэкранного разрешения.

Я привязывал свой процессор только к дисплею (декодирование выполнялось в другом потоке) с помощью моего предыдущего метода. Теперь мой поток отображения использует только 8-9% ядра для полноэкранного воспроизведения 1920x1200 с частотой 30 кадров в секунду. Я уверен, что могло бы стать еще лучше, если бы я мог отправлять данные YUV прямо на видеокарту, но на данный момент этого достаточно.

3 голосов
/ 27 августа 2009

В зависимости от ваших навыков OpenGL / затенения вы можете попытаться скопировать кадры видео в текстуру, отобразить текстуру в прямоугольник (или что-нибудь еще ... весело!) И отобразить ее в сцене OpenGL. Не самый прямой подход, но быстрый, потому что вы пишете прямо в графическую память (например, SDL). Я также рекомендовал бы использовать YCbCR только потому, что этот формат сжат (цвет, Y = полный Cb, Cr составляют 1/4 кадра), поэтому для отображения кадра требуется меньше памяти + меньше копирования. Я не использую Qts GL напрямую, но косвенно использую GL в Qt (vis OSG) и могу отображать около 7-11 видео в формате Full HD (1440 x 1080) в режиме реального времени.

3 голосов
/ 12 августа 2009

У меня такая же проблема с gtkmm (обёртка gtk + C ++). Лучшим решением помимо использования наложения SDL было непосредственное обновление буфера изображения виджета с последующим запросом перерисовки. Но я не знаю, возможно ли это с Qt ...

мои 2 цента

...