Введение
Аудио воспроизводится «невидимо» — для видео нужен дополнительный компонент, отображающий декодированные видеокадры на экране. В этой статье разберём QVideoWidget для классических Qt Widgets приложений и VideoOutput для Qt Quick/QML, а также базовые аспекты воспроизведения сетевого, потокового видео.
Концепция
QVideoWidget — готовый виджет, который достаточно поместить в layout приложения и подключить к QMediaPlayer через setVideoOutput(), после чего он автоматически отображает декодируемые плеером видеокадры без необходимости вручную обрабатывать отдельные кадры. В Qt Quick аналогичную роль играет VideoOutput — QML-элемент, подключаемый к плееру таким же образом, но интегрированный в декларативную сцену QML наравне с другими визуальными элементами, что даёт более простую анимацию и компоновку видео в составе более сложного, современного интерфейса. Воспроизведение сетевого видео (по HTTP/HTTPS) использует тот же QMediaPlayer, принимающий сетевой QUrl вместо локального файлового пути — внутренняя буферизация и постепенная загрузка обрабатываются плеером автоматически, без необходимости вручную управлять загрузкой потока.
Пример кода
// QVideoWidget в классическом Qt Widgets приложении
#include <QVideoWidget>
#include <QMediaPlayer>
#include <QAudioOutput>
class VideoPlayerWidget : public QWidget
{
Q_OBJECT
public:
VideoPlayerWidget(QWidget *parent = nullptr) : QWidget(parent)
{
m_videoWidget = new QVideoWidget(this);
m_player = new QMediaPlayer(this);
m_audioOutput = new QAudioOutput(this);
m_player->setVideoOutput(m_videoWidget); // связь плеера с виджетом отображения видеокадров
m_player->setAudioOutput(m_audioOutput); // отдельная связь для звуковой составляющей видео
auto *layout = new QVBoxLayout(this);
layout->addWidget(m_videoWidget);
}
void playVideo(const QUrl &source)
{
m_player->setSource(source);
m_player->play();
}
private:
QVideoWidget *m_videoWidget;
QMediaPlayer *m_player;
QAudioOutput *m_audioOutput;
};
// VideoOutput в Qt Quick — интеграция видео в декларативную QML-сцену
import QtQuick
import QtMultimedia
Rectangle {
width: 800; height: 600
MediaPlayer {
id: mediaPlayer
source: "https://example.com/sample_video.mp4" // сетевой источник
audioOutput: AudioOutput {}
videoOutput: videoOutput
}
VideoOutput {
id: videoOutput
anchors.fill: parent
fillMode: VideoOutput.PreserveAspectFit // сохранение пропорций видео при масштабировании
}
MouseArea {
anchors.fill: parent
onClicked: mediaPlayer.playbackState === MediaPlayer.PlayingState
? mediaPlayer.pause() : mediaPlayer.play()
}
}
Пояснения к коду
VideoPlayerWidget показывает явное разделение видео- и аудиовыхода — даже для видеофайла со встроенным звуком требуется отдельная связь setAudioOutput(), поскольку QVideoWidget отвечает исключительно за визуальное отображение, не имея отношения к выводу звуковой составляющей того же файла. QML-пример показывает декларативный, в духе остальной QML-сцены, способ описания того же воспроизведения — MediaPlayer, AudioOutput и VideoOutput определяются как обычные QML-элементы, связанные через свойства (audioOutput, videoOutput), и fillMode: VideoOutput.PreserveAspectFit явно решает частую практическую задачу — сохранение исходных пропорций видео при отображении в области, чьё соотношение сторон отличается от соотношения сторон самого видео (без растягивания/искажения изображения).
Подводные камни
- Игнорирование
fillModeдляVideoOutput(или эквивалентных настроек масштабирования дляQVideoWidget) — без явной настройки видео может отображаться искажённым (растянутым не пропорционально) при несовпадении соотношения сторон видео и области отображения, и выбор подходящего режима (сохранение пропорций с возможными чёрными полосами, либо обрезка для заполнения всей области без искажения) должен быть осознанным решением, соответствующим ожидаемому пользовательскому опыту конкретного приложения. - Воспроизведение сетевого видео без обработки возможных сетевых ошибок (отсутствие соединения, недоступность сервера, прерывание потока на середине) — в отличие от локального файла, сетевой источник может стать недоступным в любой момент уже во время воспроизведения, и приложение должно подписаться на
errorOccurredсигнал плеера, чтобы корректно сообщить пользователю о проблеме, а не оставлять интерфейс в неопределённом, «зависшем» состоянии без какой-либо обратной связи о причине прерывания. - Предположение о мгновенной готовности сетевого видео к воспроизведению без буферизации — в отличие от уже полностью доступного локального файла, сетевое видео требует первоначальной буферизации перед стартом плавного воспроизведения, и приложение должно явно отображать индикатор загрузки/буферизации (через отслеживание
bufferProgressсигнала, статья 475), а не показывать пользователю пустой, неинформативный экран во время этой задержки. - Использование
QVideoWidgetвнутри сложных, многослойных композиций виджетов (например, как дочерний элемент полупрозрачного оверлея) без понимания, что видеовиджет на некоторых платформах/бэкендах может использовать аппаратное ускорение через отдельный, не полностью интегрированный с обычным механизмом отрисовки Qt Widgets слой — это может приводить к неожиданным визуальным артефактам при попытке наложить другие виджеты поверх видео или применить к нему стандартные эффекты прозрачности/трансформации Qt Widgets, не предназначенные изначально для применения к аппаратно-ускоренному видеослою.