summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorDoris Verria <doris.verria@qt.io>2021-10-21 12:50:33 +0200
committerDoris Verria <doris.verria@qt.io>2021-10-22 10:23:42 +0200
commit8a08291a16699cc2534121f76e765d3e88fd910f (patch)
tree4e0dd44f2941830dc403dee0ce4244d774dfd098
parent634f211ae69c6fa327d410527b00d3f7d00126e7 (diff)
macOS/iOS: Query for camera and microphone authorization status
In order to gracefully handle cases where permission to use camera or microphone is not granted, query for authorization status beforehand and prompt for permission when the status is not determined. The API to prompt for permission is asynchronous so make sure to set up the AVCaptureInputs only once permission is granted. Task-number: QTBUG-97408 Pick-to: 6.2 Change-Id: Ifeb94003b83626483336e0b8c69cb40e5841edd8 Reviewed-by: Lars Knoll <lars.knoll@qt.io>
-rw-r--r--src/multimedia/platform/darwin/camera/avfcamerasession.mm210
-rw-r--r--src/multimedia/platform/darwin/camera/avfcamerasession_p.h13
-rw-r--r--src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm17
3 files changed, 174 insertions, 66 deletions
diff --git a/src/multimedia/platform/darwin/camera/avfcamerasession.mm b/src/multimedia/platform/darwin/camera/avfcamerasession.mm
index ce0234370..1dedd801b 100644
--- a/src/multimedia/platform/darwin/camera/avfcamerasession.mm
+++ b/src/multimedia/platform/darwin/camera/avfcamerasession.mm
@@ -53,6 +53,7 @@
#include <QtCore/qdatetime.h>
#include <QtCore/qurl.h>
#include <QtCore/qelapsedtimer.h>
+#include <QtCore/qpointer.h>
#include <private/qplatformaudioinput_p.h>
#include <private/qplatformaudiooutput_p.h>
@@ -179,26 +180,9 @@ void AVFCameraSession::setActiveCamera(const QCameraDevice &info)
if (m_activeCameraDevice != info) {
m_activeCameraDevice = info;
- auto recorder = m_service->recorderControl();
- if (recorder && recorder->state() == QMediaRecorder::RecordingState)
- recorder->toggleRecord(false);
-
- [m_captureSession beginConfiguration];
-
- attachVideoInputDevice();
- if (!m_activeCameraDevice.isNull() && !m_videoOutput) {
- setVideoOutput(new AVFCameraRenderer(this));
- connect(m_videoOutput, &AVFCameraRenderer::newViewfinderFrame,
- this, &AVFCameraSession::newViewfinderFrame);
- updateVideoOutput();
- }
- m_videoOutput->deviceOrientationChanged();
-
- [m_captureSession commitConfiguration];
-
- if (recorder && recorder->state() == QMediaRecorder::RecordingState)
- recorder->toggleRecord(true);
- Q_EMIT readyToConfigureConnections();
+ requestCameraPermissionIfNeeded();
+ if (m_cameraAuthorizationStatus == AVAuthorizationStatusAuthorized)
+ updateVideoInput();
}
}
@@ -206,6 +190,12 @@ void AVFCameraSession::setCameraFormat(const QCameraFormat &format)
{
if (m_cameraFormat == format)
return;
+
+ updateCameraFormat(format);
+}
+
+void AVFCameraSession::updateCameraFormat(const QCameraFormat &format)
+{
m_cameraFormat = format;
AVCaptureDevice *captureDevice = videoCaptureDevice();
@@ -382,6 +372,9 @@ AVCaptureDevice *AVFCameraSession::createAudioCaptureDevice()
void AVFCameraSession::attachVideoInputDevice()
{
+ if (m_cameraAuthorizationStatus != AVAuthorizationStatusAuthorized)
+ return;
+
if (m_videoInput) {
[m_captureSession removeInput:m_videoInput];
[m_videoInput release];
@@ -389,30 +382,25 @@ void AVFCameraSession::attachVideoInputDevice()
}
AVCaptureDevice *videoDevice = createVideoCaptureDevice();
- if (videoDevice) {
- NSError *error = nil;
- m_videoInput = [AVCaptureDeviceInput
- deviceInputWithDevice:videoDevice
- error:&error];
-
- if (!m_videoInput) {
- qWarning() << "Failed to create video device input";
- } else {
- if ([m_captureSession canAddInput:m_videoInput]) {
- [m_videoInput retain];
- [m_captureSession addInput:m_videoInput];
- } else {
- qWarning() << "Failed to connect video device input";
- m_activeCameraDevice = QCameraDevice();
- }
- }
+ if (!videoDevice)
+ return;
+
+ m_videoInput = [AVCaptureDeviceInput
+ deviceInputWithDevice:videoDevice
+ error:nil];
+ if (m_videoInput && [m_captureSession canAddInput:m_videoInput]) {
+ [m_videoInput retain];
+ [m_captureSession addInput:m_videoInput];
} else {
- m_activeCameraDevice = QCameraDevice();
+ qWarning() << "Failed to create video device input";
}
}
void AVFCameraSession::attachAudioInputDevice()
{
+ if (m_microphoneAuthorizationStatus != AVAuthorizationStatusAuthorized)
+ return;
+
if (m_audioInput) {
[m_captureSession removeInput:m_audioInput];
[m_audioInput release];
@@ -420,22 +408,18 @@ void AVFCameraSession::attachAudioInputDevice()
}
AVCaptureDevice *audioDevice = createAudioCaptureDevice();
- if (audioDevice) {
- NSError *error = nil;
- m_audioInput = [AVCaptureDeviceInput
- deviceInputWithDevice:audioDevice
- error:&error];
-
- if (!m_audioInput) {
- qWarning() << "Failed to create audio device input";
- } else {
- if ([m_captureSession canAddInput:m_audioInput]) {
- [m_audioInput retain];
- [m_captureSession addInput:m_audioInput];
- } else {
- qWarning() << "Failed to connect audio device input";
- }
- }
+ if (!audioDevice)
+ return;
+
+ m_audioInput = [AVCaptureDeviceInput
+ deviceInputWithDevice:audioDevice
+ error:nil];
+
+ if (m_audioInput && [m_captureSession canAddInput:m_audioInput]) {
+ [m_audioInput retain];
+ [m_captureSession addInput:m_audioInput];
+ } else {
+ qWarning() << "Failed to create audio device input";
}
}
@@ -472,8 +456,37 @@ void AVFCameraSession::setVideoSink(QVideoSink *sink)
updateVideoOutput();
}
+void AVFCameraSession::updateVideoInput()
+{
+ auto recorder = m_service->recorderControl();
+ if (recorder && recorder->state() == QMediaRecorder::RecordingState)
+ recorder->toggleRecord(false);
+
+ [m_captureSession beginConfiguration];
+
+ attachVideoInputDevice();
+ if (!m_activeCameraDevice.isNull() && !m_videoOutput) {
+ setVideoOutput(new AVFCameraRenderer(this));
+ connect(m_videoOutput, &AVFCameraRenderer::newViewfinderFrame,
+ this, &AVFCameraSession::newViewfinderFrame);
+ updateVideoOutput();
+ }
+ if (m_videoOutput)
+ m_videoOutput->deviceOrientationChanged();
+
+ [m_captureSession commitConfiguration];
+
+ if (recorder && recorder->state() == QMediaRecorder::RecordingState)
+ recorder->toggleRecord(true);
+ Q_EMIT readyToConfigureConnections();
+}
+
void AVFCameraSession::updateAudioInput()
{
+ requestMicrophonePermissionIfNeeded();
+ if (m_microphoneAuthorizationStatus != AVAuthorizationStatusAuthorized)
+ return;
+
auto recorder = m_service->recorderControl();
if (recorder && recorder->state() == QMediaRecorder::RecordingState)
recorder->toggleRecord(false);
@@ -519,4 +532,93 @@ void AVFCameraSession::updateVideoOutput()
}
}
+void AVFCameraSession::requestCameraPermissionIfNeeded()
+{
+ if (m_cameraAuthorizationStatus == AVAuthorizationStatusAuthorized)
+ return;
+
+ switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeVideo])
+ {
+ case AVAuthorizationStatusAuthorized:
+ {
+ m_cameraAuthorizationStatus = AVAuthorizationStatusAuthorized;
+ break;
+ }
+ case AVAuthorizationStatusNotDetermined:
+ {
+ m_cameraAuthorizationStatus = AVAuthorizationStatusNotDetermined;
+ QPointer<AVFCameraSession> guard(this);
+ [AVCaptureDevice requestAccessForMediaType:AVMediaTypeVideo completionHandler:^(BOOL granted) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (guard)
+ cameraAuthorizationChanged(granted);
+ });
+ }];
+ break;
+ }
+ case AVAuthorizationStatusDenied:
+ case AVAuthorizationStatusRestricted:
+ {
+ m_cameraAuthorizationStatus = AVAuthorizationStatusDenied;
+ return;
+ }
+ }
+}
+
+void AVFCameraSession::requestMicrophonePermissionIfNeeded()
+{
+ if (m_microphoneAuthorizationStatus == AVAuthorizationStatusAuthorized)
+ return;
+
+ switch ([AVCaptureDevice authorizationStatusForMediaType:AVMediaTypeAudio])
+ {
+ case AVAuthorizationStatusAuthorized:
+ {
+ m_microphoneAuthorizationStatus = AVAuthorizationStatusAuthorized;
+ break;
+ }
+ case AVAuthorizationStatusNotDetermined:
+ {
+ m_microphoneAuthorizationStatus = AVAuthorizationStatusNotDetermined;
+ QPointer<AVFCameraSession> guard(this);
+ [AVCaptureDevice requestAccessForMediaType:AVMediaTypeAudio completionHandler:^(BOOL granted) {
+ dispatch_async(dispatch_get_main_queue(), ^{
+ if (guard)
+ microphoneAuthorizationChanged(granted);
+ });
+ }];
+ break;
+ }
+ case AVAuthorizationStatusDenied:
+ case AVAuthorizationStatusRestricted:
+ {
+ m_microphoneAuthorizationStatus = AVAuthorizationStatusDenied;
+ return;
+ }
+ }
+}
+
+void AVFCameraSession::cameraAuthorizationChanged(bool authorized)
+{
+ if (authorized) {
+ m_cameraAuthorizationStatus = AVAuthorizationStatusAuthorized;
+ updateVideoInput();
+ updateCameraFormat(m_cameraFormat);
+ } else {
+ m_cameraAuthorizationStatus = AVAuthorizationStatusDenied;
+ qWarning() << "User has denied access to camera";
+ }
+}
+
+void AVFCameraSession::microphoneAuthorizationChanged(bool authorized)
+{
+ if (authorized) {
+ m_microphoneAuthorizationStatus = AVAuthorizationStatusAuthorized;
+ updateAudioInput();
+ } else {
+ m_microphoneAuthorizationStatus = AVAuthorizationStatusDenied;
+ qWarning() << "User has denied access to microphone";
+ }
+}
+
#include "moc_avfcamerasession_p.cpp"
diff --git a/src/multimedia/platform/darwin/camera/avfcamerasession_p.h b/src/multimedia/platform/darwin/camera/avfcamerasession_p.h
index e461b809c..05ddf3373 100644
--- a/src/multimedia/platform/darwin/camera/avfcamerasession_p.h
+++ b/src/multimedia/platform/darwin/camera/avfcamerasession_p.h
@@ -85,7 +85,6 @@ public:
AVCaptureAudioDataOutput *audioOutput() const { return m_audioOutput; }
AVFAudioPreviewDelegate *audioPreviewDelegate() const { return m_audioPreviewDelegate; }
-
AVCaptureSession *captureSession() const { return m_captureSession; }
AVCaptureDevice *videoCaptureDevice() const;
AVCaptureDevice *audioCaptureDevice() const;
@@ -99,6 +98,8 @@ public:
void setVideoSink(QVideoSink *sink);
+ void updateVideoInput();
+
void updateAudioInput();
void updateAudioOutput();
@@ -114,6 +115,9 @@ public Q_SLOTS:
void processSessionStarted();
void processSessionStopped();
+ void cameraAuthorizationChanged(bool authorized);
+ void microphoneAuthorizationChanged(bool authorized);
+
Q_SIGNALS:
void readyToConfigureConnections();
void activeChanged(bool);
@@ -121,6 +125,8 @@ Q_SIGNALS:
void newViewfinderFrame(const QVideoFrame &frame);
private:
+ void updateCameraFormat(const QCameraFormat &format);
+
void setVideoOutput(AVFCameraRenderer *output);
void updateVideoOutput();
@@ -130,6 +136,8 @@ private:
AVCaptureDevice *createAudioCaptureDevice();
void attachVideoInputDevice();
void attachAudioInputDevice();
+ void requestCameraPermissionIfNeeded();
+ void requestMicrophonePermissionIfNeeded();
bool applyImageEncoderSettings();
@@ -155,6 +163,9 @@ private:
bool m_inputMuted = false;
FourCharCode m_defaultCodec;
+
+ AVAuthorizationStatus m_cameraAuthorizationStatus = AVAuthorizationStatusDenied;
+ AVAuthorizationStatus m_microphoneAuthorizationStatus = AVAuthorizationStatusDenied;
};
QT_END_NAMESPACE
diff --git a/src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm b/src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm
index ed90dbd07..586e1a60e 100644
--- a/src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm
+++ b/src/multimedia/platform/darwin/camera/avfmediaassetwriter.mm
@@ -62,6 +62,9 @@ bool qt_capture_session_isValid(AVFCameraService *service)
if (!session->captureSession())
return false;
+ if (!session->videoInput() && !session->audioInput())
+ return false;
+
return true;
}
@@ -171,7 +174,7 @@ using AVFAtomicInt64 = QAtomicInteger<qint64>;
return false;
}
- if (session->videoOutput() && session->videoOutput()->videoDataOutput()) {
+ if (session->videoInput() && session->videoOutput() && session->videoOutput()->videoDataOutput()) {
m_videoQueue.reset(dispatch_queue_create("video-output-queue", DISPATCH_QUEUE_SERIAL));
if (!m_videoQueue) {
qDebugCamera() << Q_FUNC_INFO << "failed to create video queue";
@@ -197,10 +200,6 @@ using AVFAtomicInt64 = QAtomicInteger<qint64>;
return false;
}
- bool audioCaptureOn = false;
- if (m_audioQueue)
- audioCaptureOn = session->audioOutput() != nil;
-
if (!m_videoQueue)
m_writeFirstAudioBuffer = true;
@@ -492,16 +491,12 @@ using AVFAtomicInt64 = QAtomicInteger<qint64>;
if (m_videoQueue)
{
- Q_ASSERT(session->videoOutput() && session->videoOutput()->videoDataOutput());
+ Q_ASSERT(session->videoCaptureDevice() && session->videoOutput() && session->videoOutput()->videoDataOutput());
m_cameraWriterInput.reset([[AVAssetWriterInput alloc] initWithMediaType:AVMediaTypeVideo
outputSettings:m_videoSettings
sourceFormatHint:session->videoCaptureDevice().activeFormat.formatDescription]);
- if (!m_cameraWriterInput) {
- qDebugCamera() << Q_FUNC_INFO << "failed to create camera writer input";
- return false;
- }
- if ([m_assetWriter canAddInput:m_cameraWriterInput]) {
+ if (m_cameraWriterInput && [m_assetWriter canAddInput:m_cameraWriterInput]) {
[m_assetWriter addInput:m_cameraWriterInput];
} else {
qDebugCamera() << Q_FUNC_INFO << "failed to add camera writer input";