Преамбула
Все нижеизложенное приводится в контексте Qt версии 5.3.1 (как наиболее актуальной на данный момент), но имеет смысл в контексте любой версии ветки 5.x, а возможно даже 4.8.x (не проверял за ненадобностью).
Операционная система — Windows, среда разработки — QtCreator 3.1.2 в связке с MinGW и gcc 4.8.2 От использования других платформ/IDE/компиляторов суть не меняется.
В качестве иточника видеоданных был выбран самый простой из доступных вариантов, а именно — рабочий стол. Т.е. приложение будет отображать копию всего что происходит на рабочем столе. В многомониторных конфигурациях, в качестве источника, будем использовать основной экран.
Итак, приступим
В качестве отправной точки можно почитать документацию: «Video Overview:Working with Low Level Video Frames».
Несколько тезисов, которые необходимо почерпнуть из этой статьи:
- Источник видеоданных должен являться потомком QObject;
- Он должен объявлять property videoSurface типа QAbstractVideoSurface*;
- Он должен вызывать QAbstractVideoSurface::start, с передачей QVideoSurfaceFormat, перед началом воспроизведения;
- Он должен вызывать QAbstractVideoSurface::present, с передачей QVideoFrame, для отображения каждого кадра;
- Он должен вызывать QAbstractVideoSurface::stop, по завершении воспроизведения ролика.
Пишем код
Создаем новый проект «Qt Quick Application». Важно выбрать именно этот тип тип приложения, поскольку в дальнейшем мы будем создавать Qml компоненту с использованием C++.
Далее создаем класс, потомок QObject, и начинаем его расширять.
Посколку то, что получилось в итоге, достаточно просто и немногословно, не буду лить много воды, а просто приведу код, с некоторыми комментариями:
DesktopVideoProducer.h:
#pragma once
#include <QAbstractVideoSurface>
#include <QVideoSurfaceFormat>
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 <QtQml/qqml.h>
#include <QApplication>
#include <QScreen>
#include <QDesktopWidget>
void DesktopVideoProducer::registerQmlType()
{
//Регистрируем наш класс в составе пакета DesktopVideoProducer,
//под версией 0.1, под именем DesktopVideoProducer.
//Нижележащая строчка является подсказкой для парсера типов QtCreator,
//и она не обязательна.
// @uri DesktopVideoProducer
qmlRegisterType<DesktopVideoProducer>(
"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 <QApplication>
#include <QQmlApplicationEngine>
#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();
}
P.S.: Полный проект доступен для скачивания с GitHub.
Автор: RSATom