AVFoundation: fix microphone permission when using the camera.
The microphone permission was always requested when using the camera, even when not actually using the microphone, which can only happen when recording a video. The permission request is triggered by adding an audio AVCaptureDeviceInput to the AVCaptureSession, which was done when setting the camera to LoadedState. This is now done when setting the camera mode to CaptureVideo. Task-number: QTBUG-45659 Change-Id: I3692797128cfb70ba5ccbc7a36b6955471039e80 Reviewed-by: Timur Pocheptsov <Timur.Pocheptsov@digia.com>
This commit is contained in:
committed by
Jani Heikkinen
parent
6fe3061c1f
commit
bcaec9624c
@@ -92,7 +92,7 @@ Q_SIGNALS:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
static void updateCameraDevices();
|
static void updateCameraDevices();
|
||||||
void attachInputDevices();
|
void attachVideoInputDevice();
|
||||||
|
|
||||||
static int m_defaultCameraIndex;
|
static int m_defaultCameraIndex;
|
||||||
static QList<AVFCameraInfo> m_cameraDevices;
|
static QList<AVFCameraInfo> m_cameraDevices;
|
||||||
@@ -105,7 +105,6 @@ private:
|
|||||||
|
|
||||||
AVCaptureSession *m_captureSession;
|
AVCaptureSession *m_captureSession;
|
||||||
AVCaptureDeviceInput *m_videoInput;
|
AVCaptureDeviceInput *m_videoInput;
|
||||||
AVCaptureDeviceInput *m_audioInput;
|
|
||||||
AVFCameraSessionObserver *m_observer;
|
AVFCameraSessionObserver *m_observer;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -147,7 +147,6 @@ AVFCameraSession::AVFCameraSession(AVFCameraService *service, QObject *parent)
|
|||||||
, m_state(QCamera::UnloadedState)
|
, m_state(QCamera::UnloadedState)
|
||||||
, m_active(false)
|
, m_active(false)
|
||||||
, m_videoInput(nil)
|
, m_videoInput(nil)
|
||||||
, m_audioInput(nil)
|
|
||||||
{
|
{
|
||||||
m_captureSession = [[AVCaptureSession alloc] init];
|
m_captureSession = [[AVCaptureSession alloc] init];
|
||||||
m_observer = [[AVFCameraSessionObserver alloc] initWithCameraSession:this];
|
m_observer = [[AVFCameraSessionObserver alloc] initWithCameraSession:this];
|
||||||
@@ -163,11 +162,6 @@ AVFCameraSession::~AVFCameraSession()
|
|||||||
[m_videoInput release];
|
[m_videoInput release];
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_audioInput) {
|
|
||||||
[m_captureSession removeInput:m_audioInput];
|
|
||||||
[m_audioInput release];
|
|
||||||
}
|
|
||||||
|
|
||||||
[m_observer release];
|
[m_observer release];
|
||||||
[m_captureSession release];
|
[m_captureSession release];
|
||||||
}
|
}
|
||||||
@@ -286,10 +280,9 @@ void AVFCameraSession::setState(QCamera::State newState)
|
|||||||
QCamera::State oldState = m_state;
|
QCamera::State oldState = m_state;
|
||||||
m_state = newState;
|
m_state = newState;
|
||||||
|
|
||||||
//attach audio and video inputs during Unloaded->Loaded transition
|
//attach video input during Unloaded->Loaded transition
|
||||||
if (oldState == QCamera::UnloadedState) {
|
if (oldState == QCamera::UnloadedState)
|
||||||
attachInputDevices();
|
attachVideoInputDevice();
|
||||||
}
|
|
||||||
|
|
||||||
if (m_state == QCamera::ActiveState) {
|
if (m_state == QCamera::ActiveState) {
|
||||||
Q_EMIT readyToConfigureConnections();
|
Q_EMIT readyToConfigureConnections();
|
||||||
@@ -331,7 +324,7 @@ void AVFCameraSession::processSessionStopped()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVFCameraSession::attachInputDevices()
|
void AVFCameraSession::attachVideoInputDevice()
|
||||||
{
|
{
|
||||||
//Attach video input device:
|
//Attach video input device:
|
||||||
if (m_service->videoDeviceControl()->isDirty()) {
|
if (m_service->videoDeviceControl()->isDirty()) {
|
||||||
@@ -359,29 +352,6 @@ void AVFCameraSession::attachInputDevices()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//Attach audio input device:
|
|
||||||
if (m_service->audioInputSelectorControl()->isDirty()) {
|
|
||||||
if (m_audioInput) {
|
|
||||||
[m_captureSession removeInput:m_audioInput];
|
|
||||||
[m_audioInput release];
|
|
||||||
m_audioInput = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
AVCaptureDevice *audioDevice = m_service->audioInputSelectorControl()->createCaptureDevice();
|
|
||||||
|
|
||||||
NSError *error = nil;
|
|
||||||
m_audioInput = [AVCaptureDeviceInput
|
|
||||||
deviceInputWithDevice:audioDevice
|
|
||||||
error:&error];
|
|
||||||
|
|
||||||
if (!m_audioInput) {
|
|
||||||
qWarning() << "Failed to create audio device input";
|
|
||||||
} else {
|
|
||||||
[m_audioInput retain];
|
|
||||||
[m_captureSession addInput:m_audioInput];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#include "moc_avfcamerasession.cpp"
|
#include "moc_avfcamerasession.cpp"
|
||||||
|
|||||||
@@ -46,6 +46,7 @@ QT_BEGIN_NAMESPACE
|
|||||||
|
|
||||||
class AVFCameraSession;
|
class AVFCameraSession;
|
||||||
class AVFCameraControl;
|
class AVFCameraControl;
|
||||||
|
class AVFAudioInputSelectorControl;
|
||||||
class AVFCameraService;
|
class AVFCameraService;
|
||||||
|
|
||||||
class AVFMediaRecorderControl : public QMediaRecorderControl
|
class AVFMediaRecorderControl : public QMediaRecorderControl
|
||||||
@@ -78,11 +79,12 @@ public Q_SLOTS:
|
|||||||
void handleRecordingFailed(const QString &message);
|
void handleRecordingFailed(const QString &message);
|
||||||
|
|
||||||
private Q_SLOTS:
|
private Q_SLOTS:
|
||||||
void reconnectMovieOutput();
|
void setupSessionForCapture();
|
||||||
void updateStatus();
|
void updateStatus();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
AVFCameraControl *m_cameraControl;
|
AVFCameraControl *m_cameraControl;
|
||||||
|
AVFAudioInputSelectorControl *m_audioInputControl;
|
||||||
AVFCameraSession *m_session;
|
AVFCameraSession *m_session;
|
||||||
|
|
||||||
bool m_connected;
|
bool m_connected;
|
||||||
@@ -96,6 +98,7 @@ private:
|
|||||||
bool m_muted;
|
bool m_muted;
|
||||||
qreal m_volume;
|
qreal m_volume;
|
||||||
|
|
||||||
|
AVCaptureDeviceInput *m_audioInput;
|
||||||
AVCaptureMovieFileOutput *m_movieOutput;
|
AVCaptureMovieFileOutput *m_movieOutput;
|
||||||
AVFMediaRecorderDelegate *m_recorderDelagate;
|
AVFMediaRecorderDelegate *m_recorderDelagate;
|
||||||
AVFStorageLocation m_storageLocation;
|
AVFStorageLocation m_storageLocation;
|
||||||
|
|||||||
@@ -44,6 +44,7 @@
|
|||||||
#include "avfcamerasession.h"
|
#include "avfcamerasession.h"
|
||||||
#include "avfcameraservice.h"
|
#include "avfcameraservice.h"
|
||||||
#include "avfcameracontrol.h"
|
#include "avfcameracontrol.h"
|
||||||
|
#include "avfaudioinputselectorcontrol.h"
|
||||||
|
|
||||||
#include <QtCore/qurl.h>
|
#include <QtCore/qurl.h>
|
||||||
#include <QtCore/qfileinfo.h>
|
#include <QtCore/qfileinfo.h>
|
||||||
@@ -122,6 +123,7 @@ QT_USE_NAMESPACE
|
|||||||
AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObject *parent)
|
AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObject *parent)
|
||||||
: QMediaRecorderControl(parent)
|
: QMediaRecorderControl(parent)
|
||||||
, m_cameraControl(service->cameraControl())
|
, m_cameraControl(service->cameraControl())
|
||||||
|
, m_audioInputControl(service->audioInputSelectorControl())
|
||||||
, m_session(service->session())
|
, m_session(service->session())
|
||||||
, m_connected(false)
|
, m_connected(false)
|
||||||
, m_state(QMediaRecorder::StoppedState)
|
, m_state(QMediaRecorder::StoppedState)
|
||||||
@@ -130,21 +132,29 @@ AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObj
|
|||||||
, m_recordingFinished(false)
|
, m_recordingFinished(false)
|
||||||
, m_muted(false)
|
, m_muted(false)
|
||||||
, m_volume(1.0)
|
, m_volume(1.0)
|
||||||
|
, m_audioInput(nil)
|
||||||
{
|
{
|
||||||
m_movieOutput = [[AVCaptureMovieFileOutput alloc] init];
|
m_movieOutput = [[AVCaptureMovieFileOutput alloc] init];
|
||||||
m_recorderDelagate = [[AVFMediaRecorderDelegate alloc] initWithRecorder:this];
|
m_recorderDelagate = [[AVFMediaRecorderDelegate alloc] initWithRecorder:this];
|
||||||
|
|
||||||
connect(m_cameraControl, SIGNAL(stateChanged(QCamera::State)), SLOT(updateStatus()));
|
connect(m_cameraControl, SIGNAL(stateChanged(QCamera::State)), SLOT(updateStatus()));
|
||||||
connect(m_cameraControl, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateStatus()));
|
connect(m_cameraControl, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateStatus()));
|
||||||
connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(reconnectMovieOutput()));
|
connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(setupSessionForCapture()));
|
||||||
|
connect(m_session, SIGNAL(readyToConfigureConnections()), SLOT(setupSessionForCapture()));
|
||||||
reconnectMovieOutput();
|
connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(setupSessionForCapture()));
|
||||||
}
|
}
|
||||||
|
|
||||||
AVFMediaRecorderControl::~AVFMediaRecorderControl()
|
AVFMediaRecorderControl::~AVFMediaRecorderControl()
|
||||||
{
|
{
|
||||||
if (m_movieOutput)
|
if (m_movieOutput) {
|
||||||
[m_session->captureSession() removeOutput:m_movieOutput];
|
[m_session->captureSession() removeOutput:m_movieOutput];
|
||||||
|
[m_movieOutput release];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_audioInput) {
|
||||||
|
[m_session->captureSession() removeInput:m_audioInput];
|
||||||
|
[m_audioInput release];
|
||||||
|
}
|
||||||
|
|
||||||
[m_recorderDelagate release];
|
[m_recorderDelagate release];
|
||||||
}
|
}
|
||||||
@@ -315,13 +325,39 @@ void AVFMediaRecorderControl::handleRecordingFailed(const QString &message)
|
|||||||
Q_EMIT error(QMediaRecorder::ResourceError, message);
|
Q_EMIT error(QMediaRecorder::ResourceError, message);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVFMediaRecorderControl::reconnectMovieOutput()
|
void AVFMediaRecorderControl::setupSessionForCapture()
|
||||||
{
|
{
|
||||||
//adding movie output causes high CPU usage even when while recording is not active,
|
//adding movie output causes high CPU usage even when while recording is not active,
|
||||||
//connect it only while video capture mode is enabled
|
//connect it only while video capture mode is enabled.
|
||||||
|
// Similarly, connect the Audio input only in that mode, since it's only necessary
|
||||||
|
// when recording anyway. Adding an Audio input will trigger the microphone permission
|
||||||
|
// request on iOS, but it shoudn't do so until we actually try to record.
|
||||||
AVCaptureSession *captureSession = m_session->captureSession();
|
AVCaptureSession *captureSession = m_session->captureSession();
|
||||||
|
|
||||||
if (!m_connected && m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)) {
|
if (!m_connected
|
||||||
|
&& m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)
|
||||||
|
&& m_session->state() != QCamera::UnloadedState) {
|
||||||
|
|
||||||
|
// Add audio input
|
||||||
|
// Allow recording even if something wrong happens with the audio input initialization
|
||||||
|
AVCaptureDevice *audioDevice = m_audioInputControl->createCaptureDevice();
|
||||||
|
if (!audioDevice) {
|
||||||
|
qWarning("No audio input device available");
|
||||||
|
} else {
|
||||||
|
NSError *error = nil;
|
||||||
|
m_audioInput = [AVCaptureDeviceInput deviceInputWithDevice:audioDevice error:&error];
|
||||||
|
|
||||||
|
if (!m_audioInput) {
|
||||||
|
qWarning() << "Failed to create audio device input";
|
||||||
|
} else if (![captureSession canAddInput:m_audioInput]) {
|
||||||
|
qWarning() << "Could not connect the audio input";
|
||||||
|
m_audioInput = 0;
|
||||||
|
} else {
|
||||||
|
[m_audioInput retain];
|
||||||
|
[captureSession addInput:m_audioInput];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if ([captureSession canAddOutput:m_movieOutput]) {
|
if ([captureSession canAddOutput:m_movieOutput]) {
|
||||||
[captureSession addOutput:m_movieOutput];
|
[captureSession addOutput:m_movieOutput];
|
||||||
m_connected = true;
|
m_connected = true;
|
||||||
@@ -329,8 +365,18 @@ void AVFMediaRecorderControl::reconnectMovieOutput()
|
|||||||
Q_EMIT error(QMediaRecorder::ResourceError, tr("Could not connect the video recorder"));
|
Q_EMIT error(QMediaRecorder::ResourceError, tr("Could not connect the video recorder"));
|
||||||
qWarning() << "Could not connect the video recorder";
|
qWarning() << "Could not connect the video recorder";
|
||||||
}
|
}
|
||||||
} else if (m_connected && !m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)) {
|
} else if (m_connected
|
||||||
|
&& (!m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)
|
||||||
|
|| m_session->state() != QCamera::ActiveState)) {
|
||||||
|
|
||||||
[captureSession removeOutput:m_movieOutput];
|
[captureSession removeOutput:m_movieOutput];
|
||||||
|
|
||||||
|
if (m_audioInput) {
|
||||||
|
[captureSession removeInput:m_audioInput];
|
||||||
|
[m_audioInput release];
|
||||||
|
m_audioInput = nil;
|
||||||
|
}
|
||||||
|
|
||||||
m_connected = false;
|
m_connected = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user