Введение
В заключительной статье цикла Qt Multimedia собираем итоговый проект — приложение, объединяющее воспроизведение медиафайлов (статьи 467-469), запись экрана (расширение концепций захвата видео статьи 471 на запись содержимого экрана вместо камеры), и визуализацию аудиоспектра (статья 472) в едином, практическом инструменте.
Концепция
Архитектура итогового проекта: основной плеер на базе QMediaPlayer/QAudioOutput/QVideoWidget для воспроизведения медиафайлов с отображением метаданных (статья 476); параллельный модуль записи экрана, использующий QScreenCapture (специализированный источник захвата для QMediaCaptureSession, концептуально аналогичный QCamera, статья 471, но захватывающий содержимое экрана вместо физической камеры) в сочетании с QMediaRecorder (статья 473); и модуль визуализации спектра, анализирующий поток воспроизводимого аудио через FFT (статья 472) для отображения анимированного индикатора частотного спектра в реальном времени.
Пример кода
// Запись экрана через QScreenCapture — аналогично записи с камеры, но другой источник
#include <QScreenCapture>
#include <QMediaCaptureSession>
#include <QMediaRecorder>
#include <QGuiApplication>
class ScreenRecorder : public QObject
{
Q_OBJECT
public:
ScreenRecorder(QObject *parent = nullptr) : QObject(parent)
{
m_screenCapture = new QScreenCapture(this);
m_screenCapture->setScreen(QGuiApplication::primaryScreen());
m_recorder = new QMediaRecorder(this);
m_captureSession.setScreenCapture(m_screenCapture);
m_captureSession.setRecorder(m_recorder);
}
void startRecording(const QString &outputPath)
{
m_screenCapture->start();
m_recorder->setOutputLocation(QUrl::fromLocalFile(outputPath));
m_recorder->record();
}
void stopRecording()
{
m_recorder->stop();
m_screenCapture->stop();
}
private:
QScreenCapture *m_screenCapture;
QMediaRecorder *m_recorder;
QMediaCaptureSession m_captureSession;
};
// Связывание плеера с визуализацией спектра через QAudioBufferOutput (промежуточный перехват аудиоданных)
#include <QAudioBufferOutput>
class VisualizingPlayer : public QObject
{
Q_OBJECT
public:
VisualizingPlayer(QObject *parent = nullptr) : QObject(parent)
{
m_player = new QMediaPlayer(this);
m_audioOutput = new QAudioOutput(this);
m_bufferOutput = new QAudioBufferOutput(this); // даёт доступ к "сырым" буферам ВО ВРЕМЯ воспроизведения
m_player->setAudioOutput(m_audioOutput);
m_player->setAudioBufferOutput(m_bufferOutput);
connect(m_bufferOutput, &QAudioBufferOutput::audioBufferReceived,
this, &VisualizingPlayer::analyzeForVisualization);
}
private slots:
void analyzeForVisualization(const QAudioBuffer &buffer)
{
const qint16 *samples = buffer.constData<qint16>();
int sampleCount = buffer.sampleCount();
// computeSpectrum() из статьи 472 — применяется к данным, поступающим в реальном
// времени параллельно с обычным, нормальным воспроизведением через QAudioOutput
QVector<float> spectrum = computeSpectrum(samples, sampleCount);
emit spectrumUpdated(spectrum);
}
signals:
void spectrumUpdated(const QVector<float> &spectrum);
private:
QMediaPlayer *m_player;
QAudioOutput *m_audioOutput;
QAudioBufferOutput *m_bufferOutput;
};
Пояснения к коду
ScreenRecorder показывает прямую параллель с записью видео с камеры (статья 471) — QScreenCapture заменяет QCamera как источник в той же архитектуре QMediaCaptureSession, демонстрируя последовательность и переиспользуемость общей архитектуры Qt Multimedia для принципиально разных источников видео (физическая камера против содержимого экрана). VisualizingPlayer показывает ключевую техническую деталь итогового проекта — QAudioBufferOutput, подключённый параллельно с обычным QAudioOutput, даёт доступ к тем же самым PCM-данным, что реально воспроизводятся через колонки, для независимого анализа (вычисление спектра через computeSpectrum статьи 472) без необходимости как-либо мешать обычному, нормальному процессу воспроизведения звука — оба пути (воспроизведение и анализ для визуализации) работают параллельно от одного и того же источника декодированных аудиоданных.
Подводные камни
- Запись экрана без запроса/проверки необходимых системных разрешений, аналогично подводным камням статей 470-471 про микрофон и камеру — современные операционные системы (особенно macOS) требуют явного, согласованного пользователем разрешения на запись содержимого экрана, отдельного от разрешений на доступ к камере/микрофону, и приложение должно явно обрабатывать оба возможных исхода этого отдельного запроса разрешения.
- Вычисление спектра (FFT, статья 472) синхронно в том же потоке, что обрабатывает сигнал
audioBufferReceived, без учёта вычислительной стоимости этой операции — если вычисление спектра занимает заметное время, выполнение его непосредственно в обработчике сигнала может задержать обработку последующих аудиобуферов, потенциально влияя на плавность самого воспроизведения звука (если оба пути неудачно используют общий, единственный поток); для надёжной реализации стоит рассмотреть перенос вычисления спектра в отдельный поток, не блокирующий основной поток обработки аудиоданных. - Запись экрана с одновременным воспроизведением звука через колонки того же компьютера без явного учёта возможного попадания этого воспроизводимого звука в саму запись (через микрофон, если запись экрана сопровождается также записью с микрофона) — для записи «чистого» звука системы (без паразитного попадания через микрофон того, что одновременно воспроизводится через колонки) обычно нужен явный захват системного звука как отдельного источника, а не звука, перехваченного физическим микрофоном из помещения.
- Объединение в одном приложении нескольких ресурсоёмких задач одновременно (декодирование видео для воспроизведения, запись экрана, вычисление FFT для визуализации) без оценки совокупной вычислительной нагрузки на типичном, не самом мощном оборудовании пользователя — каждая из этих задач по отдельности может работать приемлемо, но их одновременное выполнение в рамках единого приложения может создать суммарную нагрузку, заметно превышающую возможности менее производительного оборудования, и для надёжного, предсказуемого поведения на разнообразном оборудовании пользователей стоит явно протестировать и, при необходимости, оптимизировать именно сценарий одновременного использования всех функций приложения, а не только каждой из них в отдельности.
—
Цикл «Аудио/видео обработка (Qt Multimedia)» (статьи 467-478) завершён. Общий план теперь включает 478 тем, дополняя десктоп/embedded/сетевые/СУБД/DevOps/алгоритмические циклы практическим направлением мультимедийной разработки на Qt.