Введение
Статья 470 разобрала захват аудио — для видео с камеры используется другой, хотя концептуально похожий набор классов, организованный вокруг QMediaCaptureSession — центрального координатора, объединяющего источники захвата (камеру, микрофон) с приёмниками (видоискатель для предпросмотра, рекордер для записи в файл).
Концепция
QCamera представляет конкретное устройство захвата видео и управляет его параметрами (разрешение, частота кадров, фокус) — сам по себе он не выводит и не записывает видео, а лишь является источником видеопотока. QMediaCaptureSession связывает этот источник с одним или несколькими приёмниками одновременно — QVideoWidget/VideoOutput для живого предпросмотра («видоискатель», то, что пользователь видит на экране во время записи) и QMediaRecorder (статья 473) для собственно сохранения видео в файл — оба приёмника могут работать одновременно от одного и того же источника камеры.
Пример кода
// Связка камеры, предпросмотра и рекордера через QMediaCaptureSession
#include <QCamera>
#include <QMediaCaptureSession>
#include <QMediaRecorder>
#include <QVideoWidget>
#include <QMediaDevices>
class CameraRecorder : public QWidget
{
Q_OBJECT
public:
CameraRecorder(QWidget *parent = nullptr) : QWidget(parent)
{
QCameraDevice defaultCamera = QMediaDevices::defaultVideoInput();
if (defaultCamera.isNull()) {
qWarning() << "Камера не найдена в системе";
return;
}
m_camera = new QCamera(defaultCamera, this);
m_recorder = new QMediaRecorder(this);
m_videoWidget = new QVideoWidget(this);
m_captureSession.setCamera(m_camera);
m_captureSession.setVideoOutput(m_videoWidget); // живой предпросмотр на экране
m_captureSession.setRecorder(m_recorder); // и одновременно — путь для записи в файл
auto *layout = new QVBoxLayout(this);
layout->addWidget(m_videoWidget);
m_camera->start(); // предпросмотр начинает работать немедленно, ДО начала реальной записи
}
void startRecording(const QString &outputPath)
{
m_recorder->setOutputLocation(QUrl::fromLocalFile(outputPath));
m_recorder->record(); // запись начинается ИМЕННО сейчас, предпросмотр уже работал ранее
}
void stopRecording() { m_recorder->stop(); }
private:
QCamera *m_camera;
QMediaRecorder *m_recorder;
QVideoWidget *m_videoWidget;
QMediaCaptureSession m_captureSession;
};
// Перечисление доступных камер — полезно, когда подключено несколько устройств
void listAvailableCameras()
{
const QList<QCameraDevice> cameras = QMediaDevices::videoInputs();
for (const QCameraDevice &camera : cameras) {
qDebug() << "Камера:" << camera.description();
for (const QCameraFormat &format : camera.videoFormats()) {
qDebug() << " Поддерживаемый формат:" << format.resolution()
<< "при" << format.maxFrameRate() << "кадр/с";
}
}
}
Пояснения к коду
QMediaCaptureSession явно связывает три отдельных компонента (setCamera, setVideoOutput, setRecorder) — это архитектурное разделение позволяет независимо включать/выключать предпросмотр и запись (как показано — предпросмотр начинается сразу с m_camera->start(), а реальная запись — отдельным, более поздним вызовом m_recorder->record()), что соответствует типичному пользовательскому опыту приложения камеры, где видоискатель работает постоянно, а запись включается только по явному действию пользователя. listAvailableCameras() показывает, что у каждой камеры может быть несколько поддерживаемых комбинаций разрешения и частоты кадров, и явный выбор подходящей комбинации (через QCamera::setCameraFormat(), не показанный в первом примере для краткости) важен для приложений, которым нужен контроль над конкретным качеством записи, а не использование произвольного формата по умолчанию.
Подводные камни
- Запуск записи через
QMediaRecorderбез предварительно запущенной камеры (m_camera->start()) — попытка начать запись от неактивного источника видео не даёт ожидаемого результата (запись может либо не начаться вовсе, либо начаться с задержкой, соответствующей времени инициализации камеры, неожиданно происходящей уже после вызоваrecord()), и явный, заблаговременный запуск камеры (обычно сразу при инициализации компонента предпросмотра, как в примере) обеспечивает мгновенный старт реальной записи без скрытой задержки инициализации. - Игнорирование запроса разрешения на доступ к камере, аналогично подводному камню статьи 470 про микрофон — современные операционные системы требуют явного, согласованного пользователем разрешения на доступ к камере, и приложение должно корректно обработать оба возможных исхода запроса разрешения, а не предполагать безусловную доступность камеры сразу после попытки её использования.
- Выбор неподходящего разрешения/частоты кадров без проверки реальной поддержки конкретной камерой — в отличие от аудиоформатов (статья 470, с явной проверкой
isFormatSupported), попытка установить нереалистичную для конкретной камеры комбинацию параметров (QCameraFormat) может привести либо к ошибке, либо к неявному использованию ближайшего поддерживаемого варианта самой системой, и для предсказуемого поведения стоит явно перебратьcamera.videoFormats()и выбрать реально поддерживаемую комбинацию, а не задавать произвольные значения параметров наугад. - Одновременное использование нескольких
QMediaCaptureSession, пытающихся захватить видео с одной и той же физической камеры одновременно — большинство камер (особенно встроенные веб-камеры ноутбуков) физически поддерживают единственного активного потребителя одновременно, и попытка второй, конкурирующей сессии захвата той же камеры обычно завершается ошибкой или неопределённым поведением, требуя явной координации между разными частями приложения (или разными приложениями), претендующими на использование одного и того же физического устройства камеры.