#include "mediahandler.h" #include #include #include #include #include #include #include #include #include #include #include MediaHandler::MediaHandler(QObject *parent) : QObject{parent}, m_captureSession{nullptr}, m_audioInput{nullptr}, m_camera{nullptr}, m_videoSink{nullptr}, m_recorder{nullptr} { } MediaHandler::~MediaHandler() { } MediaHandler::State MediaHandler::state() const { return m_state; } MediaHandler::CaptureMode MediaHandler::captureMode() const { return m_captureMode; } QVideoSink *MediaHandler::videoSink() const { return m_videoSink; } void MediaHandler::startPreview(QVideoSink *sink) { if (m_state != OFF) { return; } m_pendingRecord = false; startCapture(sink); } void MediaHandler::startRecording(QVideoSink *sink) { if (m_state == RECORDING || m_state == PREPARING) { return; } if (m_state == ON) { m_cancelledRecording = false; m_recorder->record(); return; } // OFF m_pendingRecord = true; startCapture(sink); } void MediaHandler::startCapture(QVideoSink *sink) { setState(PREPARING); // microphone permission (always needed) QMicrophonePermission microphonePermission; switch (qApp->checkPermission(microphonePermission)) { case Qt::PermissionStatus::Undetermined: qApp->requestPermission(microphonePermission, this, [this, sink]() { startCapture(sink); }); return; case Qt::PermissionStatus::Denied: qDebug("Microphone permission is not granted!"); setState(OFF); emit micPermissionDenied(); return; case Qt::PermissionStatus::Granted: qDebug("Mic permission granted."); break; } // camera permission (skip for AUDIO_ONLY) if (m_captureMode != AUDIO_ONLY) { QCameraPermission cameraPermission; switch (qApp->checkPermission(cameraPermission)) { case Qt::PermissionStatus::Undetermined: qApp->requestPermission(cameraPermission, this, [this, sink]() { startCapture(sink); }); return; case Qt::PermissionStatus::Denied: qDebug("Camera permission is not granted!"); setState(OFF); emit cameraPermissionDenied(); return; case Qt::PermissionStatus::Granted: qDebug("Camera permission granted."); break; } } // Create capture session and audio input if (!m_captureSession) { m_captureSession = new QMediaCaptureSession{this}; } // TODO: handle QMediaDevices:audioInputsChanged() if (!m_audioInput) { m_audioInput = new QAudioInput{this}; m_captureSession->setAudioInput(m_audioInput); } // Camera setup (skip for AUDIO_ONLY) // TODO: handle QMediaDevices:videoInputsChanged() if (m_captureMode != AUDIO_ONLY) { if (!m_camera) { QCameraDevice cameraDevice = QMediaDevices::defaultVideoInput(); if (cameraDevice.videoFormats().empty()) { setState(OFF); emit errorOccurred("Unable to find suitable camera formats"); return; } QCameraFormat cameraFormat = cameraDevice.videoFormats().at(0); for (auto format : cameraDevice.videoFormats()) { qDebug() << "Format: " << format.resolution(); qDebug() << "framerate: " << format.minFrameRate() << " " << format.maxFrameRate(); if (format.isNull()) { continue; } if (format.resolution().width() == 1280 && format.resolution().height() == 720) { cameraFormat = format; } } m_camera = new QCamera(cameraDevice); m_camera->setCameraFormat(cameraFormat); connect(m_camera, &QCamera::activeChanged, this, [this](bool active) { if (active) { setState(ON); if (m_pendingRecord) { m_pendingRecord = false; m_cancelledRecording = false; m_recorder->record(); } } }); connect(m_camera, &QCamera::errorOccurred, this, [this](QCamera::Error error, const QString &message) { if (error != QCamera::NoError) { stop(); emit errorOccurred(message); } }); m_captureSession->setCamera(m_camera); } m_videoSink = sink; m_captureSession->setVideoSink(m_videoSink); } else { // AUDIO_ONLY: no camera m_captureSession->setCamera(nullptr); m_captureSession->setVideoSink(nullptr); m_videoSink = nullptr; } // Recorder setup if (!m_recorder) { QMediaFormat mediaFormat{QMediaFormat::MPEG4}; mediaFormat.setAudioCodec(QMediaFormat::AudioCodec::AAC); if (m_captureMode != AUDIO_ONLY) { mediaFormat.setVideoCodec(QMediaFormat::VideoCodec::H264); } if (!mediaFormat.isSupported(QMediaFormat::Encode)) { setState(OFF); emit errorOccurred("Unsupported codec"); return; } m_recorder = new QMediaRecorder(this); m_captureSession->setRecorder(m_recorder); m_recorder->setQuality(QMediaRecorder::HighQuality); m_recorder->setAutoStop(true); m_recorder->setMediaFormat(mediaFormat); connect(m_recorder, &QMediaRecorder::recorderStateChanged, this, [this](QMediaRecorder::RecorderState state) { switch (state) { case QMediaRecorder::StoppedState: if (m_state == RECORDING) { setState(ON); } if (!m_cancelledRecording) { qint64 duration = m_recorder->duration(); QString mimeType = m_recorder->mediaFormat().mimeType().name(); qDebug() << "Recording location " << m_recorder->actualLocation().toString(); emit recordingComplete(m_recorder->actualLocation(), duration, mimeType); } m_cancelledRecording = false; break; case QMediaRecorder::PausedState: break; case QMediaRecorder::RecordingState: setState(RECORDING); break; } }); } if (m_captureMode != AUDIO_ONLY) { m_camera->start(); } else { // AUDIO_ONLY: no async camera startup, go straight to ON setState(ON); if (m_pendingRecord) { m_pendingRecord = false; m_cancelledRecording = false; m_recorder->record(); } } } void MediaHandler::stop() { if (m_state == OFF) { return; } m_pendingRecord = false; if (m_state == RECORDING && m_recorder) { m_cancelledRecording = true; m_recorder->stop(); } if (m_camera) { m_camera->stop(); delete m_camera; m_camera = nullptr; } if (m_captureSession) { m_captureSession->setVideoSink(nullptr); } m_videoSink = nullptr; if (m_recorder) { delete m_recorder; m_recorder = nullptr; } setState(OFF); } void MediaHandler::recordStop() { if (m_state != RECORDING) { return; } m_cancelledRecording = false; m_recorder->stop(); } void MediaHandler::recordCancel() { if (m_state != RECORDING) { return; } m_cancelledRecording = true; m_recorder->stop(); } void MediaHandler::setCaptureMode(CaptureMode newMode) { if (m_captureMode == newMode) { return; } m_captureMode = newMode; emit captureModeChanged(); } void MediaHandler::setState(State state) { if (m_state == state) { return; } m_state = state; emit stateChanged(); }