Pregunta

Basing my work off of this answer, I'm attempting to use Qt's QGLWidget to render video, but I'm having some issues. When saving a frame right after I've decoded it in my video decoding thread, it comes out just fine:

intact frame

but when drawing it, it comes out horribly mangled:

mangled frame

It almost seems like the image is being copied and the copy doesn't complete, but

  1. I'm using a mutex to make sure the image isn't touched while the drawing code is drawing.

  2. I pass a pointer to the QImage to the drawing code, so it should be the same chunk of memory.

In the decoding thread I have the following:

/* Read in the frame up here, skipped for brevity */

// Set a new image
auto frameImage = make_shared<QImage>(nextFrame->getPixels(),
                                      nextFrame->getWidth(),
                                      nextFrame->getHeight(),
                                      QImage::Format_RGB888);

canvas->setImage(frameImage);
// Post a new order to repaint.
// Done this way because another thread cannot directly call repaint()
QCoreApplication::postEvent(canvas, new QPaintEvent(canvas->rect()));

Then, in the canvas (derived from QGLWidget):

void QGLCanvas::setImage(const std::shared_ptr<QImage>& image)
{
    // Keep the QGL canvas from drawing while we change the image
    lock_guard<mutex> pixelLock(pixelsMutex);
    img = image; // img is just a shared_ptr
}

void QGLCanvas::paintEvent(QPaintEvent*)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::SmoothPixmapTransform, 1);

    // Lock the image so that other threads can't access it at the same time
    lock_guard<mutex> pixelLock(pixelsMutex);
    painter.drawImage(this->rect(), *img);
}

What's going on here?

¿Fue útil?

Solución

I forgot that the QImage, when given pixel data, is a shallow copy, not a deep one. The problem is fixed by keeping the actual frame data allocated as long as the QImage exists, like so:

void QGLCanvas::setFrame(const std::shared_ptr<VideoFrame>& newFrame)
{
    // Keep the QGL canvas from drawing while we change the image
    lock_guard<mutex> pixelLock(pixelsMutex);

    // Keep a shared_ptr reference to our frame data
    frame = newFrame;

    // Create a new QImage, which is just a shallow copy of the frame.
    img.reset(new QImage(frame->getPixels(),
                         frame->getWidth(),
                         frame->getHeight(),
                         QImage::Format_RGB888));
}
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top