AVFoundation: Enable QImage based frame fallback

QGraphicsVideoItem was not working because the QPainterVideoSurface was
unable to paint BGR32 format OpenGL textures.  Now if the QGraphicsView
window has a QGLWidget viewport, we use the GLTextureHandle to render
the video, otherwise we fallback to the software QImage rendered case.

Task-number: QTBUG-28017
Change-Id: I9304e0a2536f15075ae34cdd509ef24fbc18604e
Reviewed-by: Yoann Lopes <yoann.lopes@digia.com>
This commit is contained in:
Andy Nichols
2012-11-19 16:26:38 +01:00
committed by The Qt Project
parent 21c3915205
commit 4c2346bbdd
5 changed files with 99 additions and 26 deletions

View File

@@ -131,6 +131,7 @@ GLuint AVFVideoFrameRenderer::renderLayerToTexture(AVPlayerLayer *layer)
return 0;
renderLayerToFBO(layer, fbo);
m_glContext->doneCurrent();
return fbo->texture();
}
@@ -148,8 +149,10 @@ QImage AVFVideoFrameRenderer::renderLayerToImage(AVPlayerLayer *layer)
return QImage();
renderLayerToFBO(layer, fbo);
QImage fboImage = fbo->toImage().mirrored();
m_glContext->doneCurrent();
return fbo->toImage();
return fboImage;
}
QOpenGLFramebufferObject *AVFVideoFrameRenderer::initRenderer(AVPlayerLayer *layer)
@@ -179,8 +182,8 @@ QOpenGLFramebufferObject *AVFVideoFrameRenderer::initRenderer(AVPlayerLayer *lay
} else {
#ifdef QT_DEBUG_AVF
qWarning("failed to get Render Thread context");
m_isContextShared = false;
#endif
m_isContextShared = false;
}
if (!m_glContext->create()) {
qWarning("failed to create QOpenGLContext");
@@ -253,6 +256,4 @@ void AVFVideoFrameRenderer::renderLayerToFBO(AVPlayerLayer *layer, QOpenGLFrameb
glFinish(); //Rendering needs to be done before passing texture to video frame
fbo->release();
m_glContext->doneCurrent();
}

View File

@@ -85,6 +85,7 @@ private:
AVFVideoFrameRenderer *m_frameRenderer;
AVFDisplayLink *m_displayLink;
QSize m_nativeSize;
bool m_enableOpenGL;
};
QT_END_NAMESPACE

View File

@@ -75,11 +75,41 @@ private:
GLuint m_textureId;
};
class QImageVideoBuffer : public QAbstractVideoBuffer
{
public:
QImageVideoBuffer(const QImage &image)
: QAbstractVideoBuffer(NoHandle)
, m_image(image)
, m_mode(NotMapped)
{
}
MapMode mapMode() const { return m_mode; }
uchar *map(MapMode mode, int *numBytes, int *bytesPerLine)
{
if (mode != NotMapped && m_mode == NotMapped) {
m_mode = mode;
return m_image.bits();
} else
return 0;
}
void unmap() {
m_mode = NotMapped;
}
private:
QImage m_image;
MapMode m_mode;
};
AVFVideoRendererControl::AVFVideoRendererControl(QObject *parent)
: QVideoRendererControl(parent)
, m_surface(0)
, m_playerLayer(0)
, m_frameRenderer(0)
, m_enableOpenGL(false)
{
m_displayLink = new AVFDisplayLink(this);
@@ -132,6 +162,9 @@ void AVFVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
//Surface changed, so we need a new frame renderer
m_frameRenderer = new AVFVideoFrameRenderer(m_surface, this);
//Check for needed formats to render as OpenGL Texture
m_enableOpenGL = m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).contains(QVideoFrame::Format_BGR32);
//If we already have a layer, but changed surfaces start rendering again
if (m_playerLayer && !m_displayLink->isActive()) {
m_displayLink->start();
@@ -177,31 +210,64 @@ void AVFVideoRendererControl::updateVideoFrame(const CVTimeStamp &ts)
if (!playerLayer.readyForDisplay)
return;
GLuint textureId = m_frameRenderer->renderLayerToTexture(playerLayer);
if (m_enableOpenGL) {
//Make sure we got a valid texture
if (textureId == 0) {
qWarning("renderLayerToTexture failed");
return;
}
GLuint textureId = m_frameRenderer->renderLayerToTexture(playerLayer);
QAbstractVideoBuffer *buffer = new TextureVideoBuffer(textureId);
QVideoFrame frame = QVideoFrame(buffer, m_nativeSize, QVideoFrame::Format_BGR32);
if (m_surface && frame.isValid()) {
if (m_surface->isActive() && m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat())
m_surface->stop();
if (!m_surface->isActive()) {
QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), QAbstractVideoBuffer::GLTextureHandle);
if (!m_surface->start(format)) {
qWarning("Failed to activate video surface");
}
//Make sure we got a valid texture
if (textureId == 0) {
qWarning("renderLayerToTexture failed");
return;
}
QAbstractVideoBuffer *buffer = new TextureVideoBuffer(textureId);
QVideoFrame frame = QVideoFrame(buffer, m_nativeSize, QVideoFrame::Format_BGR32);
if (m_surface && frame.isValid()) {
if (m_surface->isActive() && m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat())
m_surface->stop();
if (!m_surface->isActive()) {
QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), QAbstractVideoBuffer::GLTextureHandle);
format.setScanLineDirection(QVideoSurfaceFormat::BottomToTop);
if (!m_surface->start(format)) {
//Surface doesn't support GLTextureHandle
qWarning("Failed to activate video surface");
}
}
if (m_surface->isActive())
m_surface->present(frame);
}
} else {
//fallback to rendering frames to QImages
QImage frameData = m_frameRenderer->renderLayerToImage(playerLayer);
if (frameData.isNull()) {
qWarning("renterLayerToImage failed");
return;
}
QAbstractVideoBuffer *buffer = new QImageVideoBuffer(frameData);
QVideoFrame frame = QVideoFrame(buffer, m_nativeSize, QVideoFrame::Format_ARGB32_Premultiplied);
if (m_surface && frame.isValid()) {
if (m_surface->isActive() && m_surface->surfaceFormat().pixelFormat() != frame.pixelFormat())
m_surface->stop();
if (!m_surface->isActive()) {
QVideoSurfaceFormat format(frame.size(), frame.pixelFormat(), QAbstractVideoBuffer::NoHandle);
if (!m_surface->start(format)) {
qWarning("Failed to activate video surface");
}
}
if (m_surface->isActive())
m_surface->present(frame);
}
if (m_surface->isActive())
m_surface->present(frame);
}
}