Files
llink/cpp/src/media/mediahandler.cpp
T

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();
}