diff options
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"; |