Простой спрособ подключения произвольного видеоисточника в Qml

от автора

Преамбула

Все нижеизложенное приводится в контексте 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.

ссылка на оригинал статьи http://habrahabr.ru/post/229747/


Комментарии

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *