\documentclass[a4paper]{article}

\input{../common-preamble}
\input{../bmstu-preamble}
\input{../fancy-listings-preamble}

\numerationTop

\begin{document}
\fontsize{14pt}{14pt}\selectfont % Вполне очевидно, что мы хотим 14й шрифт, все его хотят
\thispagestyle{empty}
\makeBMSTUHeader

\makeReportTitle{лабораторной}{3}{Разработка мультимедийного приложения}{Мультимедиа технологии}{}{Д.А. Видьманов}
\newpage
\thispagestyle{empty}
\tableofcontents
\newpage
\pagestyle{fancy}
\sloppy
\section{Цель}
Целью работы является приобретение навыков создания мультимедийных приложений. Создать мультимедийное приложение.

\section{Задачи}
\begin{enumerate}
\item Установить среду разработки;
\item Описать критерии создания приложения;  
\item Написать код и отладить работу приложения.
\end{enumerate}

\section{Выполнение работы}
Для написания мультимедйного приложения был выбран Qt Framework версии 5.12, и язык C++. Разрабатываемое приложение - это единый мультимедийный просмотрщик файлов (офисные документы, pdf, видео). Для реализации мультимедийности были выбраны несколько компонентов и создан экран, основной частью которого является область просмотра.

\subsection{Функции. Открытие MPEG}
Для открытия, воспроизведения и перемотки видеофайлов используются компоненты \code{QMediaPlayer} и \code{QVideoWidget}. Для работы с компонентами были описаны следующие слоты и функции:
\begin{itemize}
\item \code{void openMPEG(QString&)}. Достраивает в главном окне необходимые области, добавляет кнопки, инициализирует слайдеры, связывает слоты.
\item \code{void closeMPEG()}. Отвязывает слоты, удаляет динамические объекты, очищает окно от элементов управления.
\item \code{void playVideo()}. Начинает/продолжает проигрывание медиафайла.
\item \code{void stopVideo()}. Завершает проигрывание файла. Позиция проигрывания перемещается в начало. Если после вызова этой функции вызвать \code{playVideo()}, то видео начнется сначала.
\item \code{void pauseVideo()}. Приостанавливает проигрывание медиа. Позиция сохраняется. Если после вызова этой вызвать \code{playVideo()}, то видео продолжится с момента приостановки видео.
\item \code{void changeVolume(int)}. Изменяет громкость звука. Слот вызывается сигналом \code{&QSlider::valueChanged} с передачей значения из компонента.
\item \code{void changePosition()}. Принудительно перематывает видео ползунком. При вызове слота позиции видео присваивается значение с ползунка, отвечающего за перемотку видео.
\item \code{void updatePosition(qint64)}. Слот реагирует на текущее положение курсора в проигрывателе и передает относительное положение ползунку перемотки видео, чтобы положение ползунка менялось в ходе проигрывания видео.
\item \code{void updateDuration(qint64)}. При старте видеопотока общая продолжительность видео считается некоторое время, поэтому сразу идентифицировать не удастся. Слот привязан к сигналу о том, что для данного видеофайла изменилось (вычислилось) значение продолжительности.
\item \code{void updateState(int)}. Справочный слот. Реагирует на Старт, Стоп и Паузу видео. Если необходимо в процессе работы выдавать еще какие-то команды - слот «узнаёт», в каком состоянии проигрывания медиафайл.
\end{itemize}

\subsection{Функции. Открытие офисных документов (XLSX, DOCX, PPTX)}
Функции офиса используют компонент ActiveX и отображают содержимое, фактически открытое в приложениях офисного пакета.

\begin{itemize}
\item \code{void openXLSX(QString&)}. Открывает XLSX файл и передает управление файлом приложению MS Excel. Отображение размещается внутри виджета.
\item \code{void openDOCX(QString&)}. Открывает DOCX файл и передает управление файлом приложению MS Word. Отображение размещается внутри виджета.
\item \code{void openPPTX(QString&)}. Открывает PPTX файл и передает управление файлом приложению MS PowerPoint. Отображение размещается внутри виджета. Дополнительно, здесь происходит подключение к слоту, вызывющему проигрывание в режиме презентации.
\end{itemize}
\subsection{Функции. Открытие документов PDF}
\code{void openPDF(QString&)}. Открывает PDF файл и передает управление файлом приложению Adobe Acrobat. Отображение размещается внутри виджета.

\subsection{Закрытие документа}
\code{void closeFile()} - Закрывает открытый документ.


\section{Выводы}
Для разработки мультимедийного приложения необходимо изучить разнообразный инструментарий, позволяющий манипулировать мультимедийными данными. Написание мультимедийнх приложений тесно связано с обработкой больших объёмов данных или использованием внешних библиотек и приложений. Мультимедийные приложения возможно написать для любых платформ и внедрить для любых категорий пользователей.

\newpage
\appendix
\setcounter{secnumdepth}{0}
\section{Приложения}
\renewcommand{\thesubsection}{\Asbuk{subsection}}

\subsection{Полный листинг главного окна приложения}
\begin{lstlisting}[language=C++,style=CCodeStyle]
#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QMenuBar *menuBar = new QMenuBar();
    m_menuFile = menuBar->addMenu("&File");
    m_openAction = new QAction("&Open");
    m_openAction->setStatusTip(tr("Open a document"));
    m_menuFile->addAction(m_openAction);
    m_menuFile->addSeparator();
    m_quitAction = new QAction("&Quit");
    m_quitAction->setShortcut(tr("CTRL+Q"));
    m_quitAction->setStatusTip(tr("Quit application"));
    m_menuFile->addAction(m_quitAction);

    connect(this->m_openAction, &QAction::triggered, this, &MainWindow::slotOpenAction);
    connect(this->m_quitAction, &QAction::triggered, qApp, &QApplication::quit);

    this->setMenuBar(menuBar);
    this->resize(800, 600);
    show();
}

MainWindow::~MainWindow()
{
    delete ui;

}

void MainWindow::delay(int delay) {
    QTime dieTime= QTime::currentTime().addSecs(delay);
    while (QTime::currentTime() < dieTime)
        QCoreApplication::processEvents(QEventLoop::AllEvents, 100);
}

void MainWindow::slotOpenAction() {
    m_filename = QFileDialog::getOpenFileName(
            this, tr("Выберите файл"), "../", tr("MPEG (*.mpeg) ;; PDF (*.pdf) ;; DOCX (*.docx) ;; XLSX (*.xlsx) ;; PPTX (*.pptx)"));
    if (m_filename != "") {
        if (axObj) {
            qDebug() << "Close file!\n";
            closeFile();
        }
        if (m_videoWidget) {
            qDebug() << "Close MPEG!\n";
            closeMPEG();
        }

        if (m_filename.endsWith(".pdf")) {
            openPDF(m_filename);
        } else if (m_filename.endsWith(".pptx")) {
            openPPTX(m_filename);
        } else if (m_filename.endsWith(".docx")) {
            openDOCX(m_filename);
        } else if (m_filename.endsWith(".xlsx")) {
            openXLSX(m_filename);
        } else if (m_filename.endsWith(".mpeg")) {
            openMPEG(m_filename);
        }
    }
}

void MainWindow::closeFile() {
    if (axObj != nullptr) {
        QProcess process;
        QString strCmd;
        strCmd = "taskkill /im winword.exe /f";
        process.execute(strCmd);
        strCmd = "taskkill /im EXCEL.exe /f";
        process.execute(strCmd);
        strCmd = "taskkill /im POWERPNT.exe /f";
        process.execute(strCmd);
        strCmd = "taskkill /im AcroRd32.exe /f";
        process.execute(strCmd);
        process.close();
        axObj->close();
        axObj->clear();
        delete axObj;
        axObj = nullptr;
        qDebug() << "file closed!";
    }
}


void MainWindow::openPDF(QString &filename) {
    axObj = new QAxWidget(this);
    setCentralWidget(axObj);
    if(!axObj->setControl("Adobe PDF Reader"))
        QMessageBox::critical(this, "Error", "Make sure you have Adobe Reader (and its ActiveX) installed!");
    axObj->dynamicCall("LoadFile(QString)", filename);
}

/* run slideshow */
void MainWindow::playPPTX() {
    // Open parameter
    QList<QVariant> param_list;
    axObj->setControl("PowerPoint.Application");
    param_list.append(m_filename);
    param_list.append(1);
    param_list.append(1);
    param_list.append(0);
    // Open a template file
    QAxObject *presentations = axObj->querySubObject("Presentations");
    QAxObject *presentation = presentations->querySubObject("Open(const QString&,int,int,int)", param_list);
    // Start the slides
    QAxObject *slideshow = presentation->querySubObject("SlideShowSettings");
    slideshow->setProperty("RangeType", "ppShowAll");
    slideshow->dynamicCall("run()");

}

void MainWindow::openPPTX(QString &filename) {
    /* инициализация окна виджетом */
    m_wideWidget = new QWidget;
    setCentralWidget(m_wideWidget);
    qDebug() << "1";
    /* здесь будут элементы управления и окно проигрывателя (выравнивание по горизонтали) */
    m_qvBoxLayout = new QVBoxLayout(m_wideWidget);

    axObj = new QAxWidget("PowerPoint.Application", m_wideWidget);
    m_btnPlay = new QPushButton("Начать презентацию");
    qDebug() << "2";
    m_qvBoxLayout->addWidget(axObj);
    m_qvBoxLayout->addWidget(m_btnPlay);

    if (axObj == nullptr) {
        return;
    }
    if (!axObj->setControl(filename)) {
        return;
    }
    axObj->dynamicCall("LoadFile(const QString&)", filename);
    axObj->dynamicCall("SetVisible (bool Visible)", "false");
    axObj->setProperty("DisplayAlerts", false);
    axObj->setProperty("DisplayScrollBars", true);

    delay(1);

    axObj->show();
    axObj->raise();
    qDebug() << "3";
    connect(this->m_btnPlay, &QPushButton::clicked, this, &MainWindow::playPPTX);
}

void MainWindow::openXLSX(QString &filename) {
    axObj = new QAxWidget("Excel.Application", this);
    setCentralWidget(axObj);
    if (axObj == nullptr) {
        return;
    }
    axObj->dynamicCall("SetVisible (bool Visible)", "false");
    axObj->setProperty("DisplayAlerts", false);
    axObj->setProperty("DisplayScrollBars", true); // Show scroll bar
    axObj->setGeometry(this->geometry());
    axObj->setControl(filename);
    axObj->show();
}
void MainWindow::openDOCX(QString &filename) {
    axObj = new QAxWidget("Word.Application", this);
    setCentralWidget(axObj);
    if (axObj == nullptr) {
        return;
    }
    axObj->dynamicCall("SetVisible (bool Visible)", "false");
    axObj->setFocusPolicy(Qt::StrongFocus);
    axObj->setProperty("DisplayAlerts", false);
    axObj->setProperty("DisplayHorizontalScrollBar", true); // Show scroll bar
    axObj->setGeometry(this->geometry());
    axObj->setControl(filename);
    axObj->show();
}

/* продолжить проигрывать видео */
void MainWindow::playVideo() {
    m_player->setPosition(m_position);
    m_player->play();
}

/* остановка видео */
void MainWindow::stopVideo() {
    m_player->stop();
}

/* пауза видео */
void MainWindow::pauseVideo() {
    m_player->pause();
}

/* событие при изменении состояния видеопроигрывателя */
void MainWindow::updateState(int state) {
    switch (state) {
        case QMediaPlayer::PlayingState:
            /* media is played */
            break;

        case QMediaPlayer::PausedState:
            /* media paused */
            break;

        case QMediaPlayer::StoppedState:
            /* media is stopped */
            break;
    }
}

/* принудительно изменить время проигрывания */
void MainWindow::changePosition() {
    m_position = m_sliderVideo->value();
    m_player->setPosition(m_position);
}

/* принудительно изменить время проигрывания */
void MainWindow::updatePosition(qint64 position) {
    m_position = position;
    m_sliderVideo->setValue(m_position);
}

/* изменить величину звука */
void MainWindow::changeVolume(int volume) {
    m_player->setVolume(volume);
}

/* слот для иницализации значением слайдера видео */
void MainWindow::updateDuration(qint64 duration) {
    m_sliderVideo->setRange(0, m_player->duration());
    m_sliderVideo->setEnabled(duration > 0);
    m_sliderVideo->setPageStep(duration / 10);
}

/* инициализация медиа файла */
void MainWindow::openMPEG(QString &filename) {
    /* инициализация окна виджетом */
    m_wideWidget = new QWidget;
    setCentralWidget(m_wideWidget);

    /* здесь будут элементы управления и окно проигрывателя (выравнивание по горизонтали) */
    m_qvBoxLayout = new QVBoxLayout(m_wideWidget);

    /* область для отображения видео */
    m_videoWidget = new QVideoWidget;
    m_player = new QMediaPlayer(m_videoWidget);
    m_player->setVideoOutput(m_videoWidget);
    m_player->setMedia(QUrl::fromLocalFile(filename));

    /* область размещения кнопок управления видео */
    m_hBtnsLayout = new QHBoxLayout;
    m_play = new QPushButton("Play");
    m_stop = new QPushButton("Stop");
    m_pause = new QPushButton("Pause");
    m_sliderVolume = new QSlider(Qt::Horizontal);
    m_sliderVolume->setRange(0, 100); // диапазон значений громкости
    m_sliderVolume->setValue(50); // текущая громкость 50%
    m_sliderVideo = new QSlider(Qt::Horizontal);
    m_hBtnsLayout->addWidget(m_play, 1);
    m_hBtnsLayout->addWidget(m_stop, 1);
    m_hBtnsLayout->addWidget(m_pause, 1);
    m_hBtnsLayout->addWidget(&m_labelVolume);
    m_hBtnsLayout->addWidget(m_sliderVolume, 1);
    m_hBtnsLayout->addWidget(&m_labelScroll);
    m_hBtnsLayout->addWidget(m_sliderVideo, 5);

    /* поместить настроенные объекты в центральный виджет */
    m_qvBoxLayout->addWidget(m_videoWidget);
    m_qvBoxLayout->addLayout(m_hBtnsLayout);

    /* связать слоты */
    connect(this->m_play, &QPushButton::clicked, this, &MainWindow::playVideo);
    connect(this->m_stop, &QPushButton::clicked, this, &MainWindow::stopVideo);
    connect(this->m_pause, &QPushButton::clicked, this, &MainWindow::pauseVideo);
    connect(this->m_sliderVolume, &QSlider::sliderMoved, this, &MainWindow::changeVolume);
    connect(this->m_sliderVideo, &QSlider::sliderMoved, this, &MainWindow::changePosition);
    connect(this->m_sliderVideo, &QSlider::valueChanged, this, &MainWindow::changePosition);
    connect(this->m_player, &QMediaPlayer::durationChanged, this, &MainWindow::updateDuration);
    connect(this->m_player, &QMediaPlayer::positionChanged, this, &MainWindow::updatePosition);
    connect(this->m_player, &QMediaPlayer::stateChanged, this, &MainWindow::updateState);

    /* начать проигрывать видео */
    playVideo();
}

void MainWindow::closeMPEG() {
    /* отключение всех слотов */
    disconnect(this->m_play, &QPushButton::clicked, this, &MainWindow::playVideo);
    disconnect(this->m_stop, &QPushButton::clicked, this, &MainWindow::stopVideo);
    disconnect(this->m_pause, &QPushButton::clicked, this, &MainWindow::pauseVideo);
    disconnect(this->m_sliderVolume, &QSlider::sliderMoved, this, &MainWindow::changeVolume);
    disconnect(this->m_sliderVideo, &QSlider::sliderMoved, this, &MainWindow::changePosition);
    disconnect(this->m_sliderVideo, &QSlider::sliderPressed, this, &MainWindow::changePosition);
    disconnect(this->m_player, &QMediaPlayer::durationChanged, this, &MainWindow::updateDuration);
    disconnect(this->m_player, &QMediaPlayer::positionChanged, this, &MainWindow::updatePosition);
    disconnect(this->m_player, &QMediaPlayer::stateChanged, this, &MainWindow::updateState);

    /* удаление динамических объектов */
    delete m_sliderVideo;
    delete m_sliderVolume;
    delete m_play;
    delete m_stop;
    delete m_pause;
    delete m_hBtnsLayout;
    delete m_player;
    delete m_videoWidget;
    delete m_qvBoxLayout;
}
\end{lstlisting}
\end{document}
