Qt has a private function qt_imageFromVideoFrame
for converting QVideoFrame
to QImage
. If you want to use it, you'll need to:
- Add
QT += multimedia multimedia-private
to your .pro
file
- Add
#include "private/qvideoframe_p.h"
to your .cpp
file
Then you can use qt_imageFromVideoFrame
with the following signature:
QImage qt_imageFromVideoFrame( const QVideoFrame& f );
Be aware of the warning in the header:
//
// W A R N I N G
// -------------
//
// This file is not part of the Qt API. It exists purely as an
// implementation detail. This header file may change from version to
// version without notice, or even be removed.
//
// We mean it.
//
The implementation uses QVideoFrame::map()
and does low-level manipulation with the bits. The converters handle a variety of conditions including YUV.
The qt_imageFromVideoFrame
works on almost all platforms.
i.e. Windows, macOS, Linux and iOS.
The only platform it doesn't work reliably well is Android.
There, you'll need to use OpenGL calls to extract the VideoFrame. Also, on Android, you need to call QImage:rgbSwapped()
at the end because QImage stores images as ARGB whereas OpenGL retrieves them as ABGR.
QImage QVideoFrameToQImage( const QVideoFrame& videoFrame )
{
if ( videoFrame.handleType() == QAbstractVideoBuffer::NoHandle )
{
QImage image = qt_imageFromVideoFrame( videoFrame );
if ( image.isNull() ) return QImage();
if ( image.format() != QImage::Format_ARGB32 ) return image.convertToFormat( QImage::Format_ARGB32 );
return image;
}
if ( videoFrame.handleType() == QAbstractVideoBuffer::GLTextureHandle )
{
QImage image( videoFrame.width(), videoFrame.height(), QImage::Format_ARGB32 );
GLuint textureId = static_cast<GLuint>( videoFrame.handle().toInt() );
QOpenGLContext* ctx = QOpenGLContext::currentContext();
QOpenGLFunctions* f = ctx->functions();
GLuint fbo;
f->glGenFramebuffers( 1, &fbo );
GLint prevFbo;
f->glGetIntegerv( GL_FRAMEBUFFER_BINDING, &prevFbo );
f->glBindFramebuffer( GL_FRAMEBUFFER, fbo );
f->glFramebufferTexture2D( GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, textureId, 0 );
f->glReadPixels( 0, 0, videoFrame.width(), videoFrame.height(), GL_RGBA, GL_UNSIGNED_BYTE, image.bits() );
f->glBindFramebuffer( GL_FRAMEBUFFER, static_cast<GLuint>( prevFbo ) );
return image.rgbSwapped();
}
return QImage();
}
I have a complete working app on GitHub that includes an implementation of the above function:
https://github.com/stephenquan/MyVideoFilterApp/blob/master/QVideoFrameToQImage.cpp