Подтвердить что ты не робот

Каков наиболее эффективный способ отображения декодированных видеокадров в Qt?

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

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

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

Спасибо.

4b9b3361

Ответ 1

Спасибо за ответы, но я, наконец, пересмотрел эту проблему и придумал довольно простое решение, обеспечивающее хорошую производительность. Он включает в себя вывод из 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 прямо на видеокарту, но на данный момент это достаточно хорошо.

Ответ 2

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

мои 2 цента

Ответ 3

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