c++ - ¿Cuál es la forma más eficiente de mostrar marcos de video decodificados en Qt?
image-processing (3)
¿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 ().
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.
Dependiendo de tus habilidades OpenGL / sombreado podrías intentar copiar los marcos de los videos a una textura, asignar la textura a un rectángulo (¡o cualquier otra cosa ... divertida!) Y mostrarla 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.
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
Gracias por las respuestas, pero finalmente revisé este problema y se me ocurrió una solución bastante simple que ofrece un buen rendimiento. Se trata de derivar de QGLWidget
y anular la función paintEvent()
. Dentro de la función paintEvent()
, puede llamar a QPainter::drawImage(...)
y realizará el escalado a un rectángulo específico para usted utilizando hardware si está disponible. Por lo tanto, 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 implementación muy rápida de esa 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
aQPixmap
, que está sucediendo en la funciónQPainter::drawImage()
se realiza con la resolución de imagen original en oposición a la resolución de pantalla completa ampliada.
Estaba pegando mi procesador solo 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.