340 lines
7.3 KiB
C++
340 lines
7.3 KiB
C++
#include "mediahandler.h"
|
|
#include <QPermission>
|
|
#include <QMediaCaptureSession>
|
|
#include <QApplication>
|
|
#include <QCameraDevice>
|
|
#include <QCameraFormat>
|
|
#include <QMediaRecorder>
|
|
#include <QMediaFormat>
|
|
#include <QMimeType>
|
|
#include <QAudioInput>
|
|
#include <QCamera>
|
|
#include <QUrl>
|
|
|
|
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();
|
|
}
|