An easy way to connect an arbitrary video source in Qml
Preamble
All of the following is given in the context of Qt version 5.3.1 (as the most relevant at the moment), but it makes sense in the context of any version of the 5.x branch, and possibly even 4.8.x (did not check as unnecessary).
Operating system - Windows, development environment - QtCreator 3.1.2 in conjunction with MinGW and gcc 4.8.2 The essence does not change from the use of other platforms / IDE / compilers.
As the source of the video data, the simplest of the available options was chosen, namely the desktop. Those. The application will display a copy of everything that happens on the desktop. In multi-monitor configurations, as the source, we will use the main screen.
So let's get started
As a starting point, you can read the documentation: “Video Overview: Working with Low Level Video Frames” .
A few points to take from this article:
- The video source must be a descendant of QObject;
- It must declare a property videoSurface of type QAbstractVideoSurface *;
- It should call QAbstractVideoSurface :: start, with the QVideoSurfaceFormat passed, before starting playback;
- It should call QAbstractVideoSurface :: present, with a QVideoFrame passed, to display each frame;
- It should call QAbstractVideoSurface :: stop, when the movie finishes playing.
Writing a code
We are creating a new project “Qt Quick Application”. It is important to choose this type of application type, since in the future we will create a Qml component using C ++.
Next, create a class, a descendant of QObject, and begin to expand it.
As far as the result turned out, it’s quite simple and laconic, I won’t pour a lot of water, but just give the code, with some comments:
DesktopVideoProducer.h:
#pragma once
#include
#include
class DesktopVideoProducer : public QObject
{
Q_OBJECT
public:
//Для того чтобы класс был доступен для использования в Qml его необходимо регистрировать
static void registerQmlType();
explicit DesktopVideoProducer( QObject *parent = 0 );
~DesktopVideoProducer();
//то самое property, упомянутое выше
Q_PROPERTY( QAbstractVideoSurface* videoSurface READ videoSurface WRITE setVideoSurface )
QAbstractVideoSurface* videoSurface() const;
void setVideoSurface( QAbstractVideoSurface* s );
protected:
void timerEvent( QTimerEvent* );
private:
void closeSurface();
private:
QAbstractVideoSurface* _surface;
QVideoSurfaceFormat _format;
};
DesktopVideoProducer.cpp:
#include "DesktopVideoProducer.h"
#include
#include
#include
#include
void DesktopVideoProducer::registerQmlType()
{
//Регистрируем наш класс в составе пакета DesktopVideoProducer,
//под версией 0.1, под именем DesktopVideoProducer.
//Нижележащая строчка является подсказкой для парсера типов QtCreator,
//и она не обязательна.
// @uri DesktopVideoProducer
qmlRegisterType(
"DesktopVideoProducer", 0, 1,
"DesktopVideoProducer" );
}
DesktopVideoProducer::DesktopVideoProducer( QObject *parent )
: QObject( parent ), _surface( 0 )
{
startTimer( 1000 / 15 ); //15 fps
}
DesktopVideoProducer::~DesktopVideoProducer()
{
closeSurface();
}
QAbstractVideoSurface* DesktopVideoProducer::videoSurface() const
{
return _surface;
}
void DesktopVideoProducer::setVideoSurface( QAbstractVideoSurface* s )
{
closeSurface();
_surface = s;
}
void DesktopVideoProducer::closeSurface()
{
if( _surface && _surface->isActive() )
_surface->stop();
}
void DesktopVideoProducer::timerEvent( QTimerEvent* )
{
if( !_surface )
return;
QScreen* screen = QGuiApplication::primaryScreen();
QDesktopWidget* desktop = QApplication::desktop();
if( !screen || !desktop )
return;
//Получим screenshot и преобразуем в экземпляр класса подходящий для QVideoFrame
QPixmap screenPixmap = screen->grabWindow( desktop->screen()->winId() );
QImage screenImage = screenPixmap.toImage();
QVideoFrame::PixelFormat pixelFormat =
QVideoFrame::pixelFormatFromImageFormat( screenImage.format() );
//если формат кадра по какой-то причине поменялся (или это первый кадр)-
//выполним повторную (первичную) инициализацию surface
if( screenPixmap.size() != _format.frameSize() ||
pixelFormat != _format.pixelFormat() )
{
closeSurface();
_format =
QVideoSurfaceFormat( screenPixmap.size(),
pixelFormat );
_surface->start( _format );
}
//передадим полученный кадр на отрисовку
_surface->present( QVideoFrame( screenImage ) );
}
main.qml:
import QtQuick 2.2
import QtQuick.Window 2.1
import QtMultimedia 5.0
import DesktopVideoProducer 0.1
Window {
visible: true
width: 360
height: 360
DesktopVideoProducer {
id: videoProducer;
}
VideoOutput {
anchors.fill: parent;
source: videoProducer;
}
}
main.cpp:
#include
#include
#include"DesktopVideoProducer.h"
int main(int argc, char *argv[])
{
//зарегистрируем DesktopVideoProducer для использования в Qml
DesktopVideoProducer::registerQmlType();
//для возможности вызова QApplication::desktop() QGuiApplication недостаточно
//QGuiApplication app(argc, argv);
QApplication app(argc, argv);
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return app.exec();
}
PS: The full project is available for download from GitHub .