2009-08-06 14 views
29

¿Cuál es la forma más rápida de mostrar imágenes en un widget Qt? Descodifiqué el video usando libavformat y libavcodec, por lo que ya tengo marcos sin formato RGB o YCbCr 4: 2: 0. Actualmente estoy usando un QGraphicsView con un objeto QGraphicsScene que contiene un QGraphicsPixmapItem. Actualmente estoy obteniendo los datos del marco en un QPixmap usando el constructor QImage desde un búfer de memoria y convirtiéndolo en QPixmap usando QPixmap :: fromImage().¿Cuál es la forma más eficiente de mostrar marcos de video decodificados en Qt?

Me gustan los resultados de esto y parece relativamente rápido, pero no puedo evitar pensar que debe haber una manera más eficiente. También escuché que la conversión de QImage a QPixmap es costosa. Implementé una solución que usa una superposición de SDL en un widget, pero me gustaría quedar solo con Qt, ya que puedo capturar fácilmente clics y otras interacciones del usuario con la pantalla de video con QGraphicsView.

Estoy haciendo cualquier escala de video requerida o conversiones de espacio de color con libswscale, así que me gustaría saber si alguien tiene una manera más eficiente de mostrar los datos de imagen después de que se haya realizado todo el procesamiento.

Gracias.

Respuesta

26

Gracias por las respuestas, pero finalmente volví a visitar este problema y se me ocurrió una solución bastante simple que ofrece un buen rendimiento. Implica derivar de QGLWidget y anular la función paintEvent(). Dentro de la función paintEvent(), puede llamar al QPainter::drawImage(...) y realizará el escalado a un rectángulo específico para usted utilizando hardware si está disponible. Por lo que se ve algo como esto:

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

Con esto, todavía tengo que convertir el YUV 420P a RGB32, pero ffmpeg tiene una aplicación muy rápida de que la conversión en libswscale. Las principales ganancias provienen de dos cosas:

  • No es necesario escalar el software. El escalado se realiza en la tarjeta de video (si está disponible)
  • La conversión de QImage a QPixmap, que ocurre en la función QPainter::drawImage() se realiza con la resolución de imagen original en comparación con la resolución de pantalla completa ampliada.

Estaba sincronizando mi procesador en la pantalla (la decodificación se estaba realizando en otro hilo) con mi método anterior. Ahora mi hilo de visualización solo usa alrededor del 8-9% de un núcleo para la reproducción a pantalla completa de 1920x1200 a 30 fps. Estoy seguro de que probablemente sea aún mejor si pudiera enviar los datos de YUV directamente a la tarjeta de video, pero esto es bastante bueno por ahora.

+0

Convertí tus paquetes a rgb con swscale en el hilo de mi decodificador y utilicé tu método. funcionando sin problemas. – baci

+0

'p.SetRenderHint (QPainter :: SmoothPixmapTransform, 1);' - setRenderHint debe estar en minúscula. – ottoshmidt

+0

gracias, @ottoshmidt. Fijo. –

3

Tengo el mismo problema con gtkmm (gtk + C++ wrapping). La mejor solución además de usar una superposición de SDL era actualizar directamente el buffer de imagen del widget y luego solicitar un redibujado. Pero no sé si es factible con Qt ...

mis 2 centavos

3

Dependiendo de sus habilidades OpenGL/sombreado se podría tratar de copiar los fotogramas de vídeos a una textura, un mapa de la textura a una rectángulo (o cualquier otra cosa ... ¡divertido!) y mostrarlo en una escena OpenGL. No es el enfoque más directo, pero rápido, porque estás escribiendo directamente en la memoria de gráficos (como SDL). También recomiendo utilizar YCbCR solo ya que este formato está comprimido (color, Y = Cb completo, Cr son 1/4 del fotograma) por lo que se necesita menos memoria + menos copia para mostrar un fotograma. No estoy usando Qts GL directamente pero indirectamente usando GL en Qt (vis OSG) y puedo visualizar de 7-11 videos completos en HD (1440 x 1080) en tiempo real.

Cuestiones relacionadas