2009-08-06 11 views
29

Qual è il modo più veloce per visualizzare le immagini su un widget Qt? Ho decodificato il video usando libavformat e libavcodec, quindi ho già raw RGB o YCbCr 4: 2: 0 frame. Attualmente sto usando un QGraphicsView con un oggetto QGraphicsScene contenente un QGraphicsPixmapItem. Attualmente sto ottenendo i dati del frame in un QPixmap usando il costruttore QImage da un buffer di memoria e convertendolo in QPixmap usando QPixmap :: fromImage().Qual è il modo più efficiente per visualizzare i frame video decodificati in Qt?

Mi piacciono i risultati di questo e sembra relativamente veloce, ma non posso fare a meno di pensare che ci deve essere un modo più efficiente. Ho anche sentito che la conversione di QImage in QPixmap è costosa. Ho implementato una soluzione che utilizza una sovrapposizione SDL su un widget, ma mi piacerebbe stare con Qt solo perché sono in grado di catturare facilmente i clic e altre interazioni dell'utente con il display video utilizzando QGraphicsView.

Sto eseguendo qualsiasi conversione del ridimensionamento o dello spazio dei colori richiesta con libswscale, quindi vorrei solo sapere se qualcuno ha un modo più efficiente di visualizzare i dati dell'immagine dopo che è stata eseguita tutta l'elaborazione.

Grazie.

risposta

26

Grazie per le risposte, ma alla fine ho rivisitato questo problema e ho trovato una soluzione piuttosto semplice che offre buone prestazioni. Ciò implica derivare da QGLWidget e ignorare la funzione paintEvent(). All'interno della funzione paintEvent(), è possibile chiamare QPainter::drawImage(...) ed eseguire il ridimensionamento su un rettangolo specificato per l'utilizzo dell'hardware, se disponibile. Così sembra qualcosa di simile:

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 questo, ho ancora per convertire il YUV 420P a RGB32, ma ffmpeg ha una molto veloce implementazione di quella conversione in libswscale. I principali vantaggi derivano da due fattori:

  • Nessuna necessità di ridimensionamento software. Il ridimensionamento viene eseguito sulla scheda video (se disponibile)
  • La conversione da QImage a QPixmap, che si sta verificando nella funzione QPainter::drawImage() viene eseguita con la risoluzione dell'immagine originale anziché con la risoluzione a schermo intero ingrandita.

Ero pegging il mio processore solo sul display (la decodifica veniva eseguita in un altro thread) con il mio metodo precedente. Ora il mio thread di visualizzazione utilizza solo l'8-9% di un core per la riproduzione a schermo intero di 1920x1200 a 30 fps. Sono sicuro che potrebbe essere ancora meglio se potessi inviare i dati YUV direttamente alla scheda video, ma per ora è abbastanza buono.

+0

ho convertito i pacchetti YUV a RGB con swscale nel mio thread decoder e utilizzato il metodo. correndo in modo impeccabile. – baci

+0

'p.SetRenderHint (QPainter :: SmoothPixmapTransform, 1);' - setRenderHint deve essere in minuscolo. – ottoshmidt

+0

grazie, @ottoshmidt. Fisso. –

3

Ho lo stesso problema con gtkmm (gtk + C++ wrapping). La soluzione migliore oltre all'utilizzo di una sovrapposizione SDL era quella di aggiornare direttamente il buffer di immagini del widget, quindi chiedere un ridisegno. Ma io non so se è fattibile con Qt ...

i miei 2 centesimi

3

A seconda delle abilità di OpenGL/ombreggiatura si potrebbe tentare di copiare i video frame per una texture, mappare la texture a un rettangolo (o qualsiasi altra cosa..fun!) e visualizzarlo in una scena OpenGL. Non l'approccio più diretto, ma veloce, perché stai scrivendo direttamente nella memoria grafica (come SDL). Vorrei anche ricorrere all'utilizzo di YCbCR solo perché questo formato è compresso (colore, Y = pieno Cb, Cr sono 1/4 del fotogramma) quindi è necessaria meno memoria + meno copia per visualizzare un frame. Non sto usando Qts GL direttamente ma indirettamente usando GL in Qt (vis OSG) e posso visualizzare circa 7-11 video Full HD (1440 x 1080) in tempo reale.

Problemi correlati