From 6fe3061c1f6b7438d20c7bfb92c7652cedc5b049 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Str=C3=B8mme?= Date: Thu, 16 Apr 2015 15:00:30 +0200 Subject: [PATCH 1/6] Restore ContentCamera.qml The file was removed in 5c3a5cf8106e1b873924b296c792448c33ee4df1 but left the description, documentation and parts of the functionality unchanged. This change adds the camera functionality back into the example. Change-Id: I3bfdd95f8322796d446c571a4e074ce98e5443dd Reviewed-by: Yoann Lopes --- .../qml/qmlvideofx/ContentCamera.qml | 43 +++++++++++++++++++ .../video/qmlvideofx/qmlvideofx.qrc | 1 + 2 files changed, 44 insertions(+) create mode 100644 examples/multimedia/video/qmlvideofx/qml/qmlvideofx/ContentCamera.qml diff --git a/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/ContentCamera.qml b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/ContentCamera.qml new file mode 100644 index 00000000..ef5a3d4a --- /dev/null +++ b/examples/multimedia/video/qmlvideofx/qml/qmlvideofx/ContentCamera.qml @@ -0,0 +1,43 @@ +/**************************************************************************** +** +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). +** Contact: http://www.qt-project.org/legal +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL21$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and Digia. For licensing terms and +** conditions see http://qt.digia.com/licensing. For further information +** use the contact form at http://qt.digia.com/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 or version 3 as published by the Free +** Software Foundation and appearing in the file LICENSE.LGPLv21 and +** LICENSE.LGPLv3 included in the packaging of this file. Please review the +** following information to ensure the GNU Lesser General Public License +** requirements will be met: https://www.gnu.org/licenses/lgpl.html and +** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Digia gives you certain additional +** rights. These rights are described in the Digia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +import QtQuick 2.1 +import QtMultimedia 5.0 + +VideoOutput { + source: camera + + Camera { + id: camera + } +} diff --git a/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc b/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc index e7978e39..f3ad2770 100644 --- a/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc +++ b/examples/multimedia/video/qmlvideofx/qmlvideofx.qrc @@ -3,6 +3,7 @@ images/qt-logo.png qml/qmlvideofx/Button.qml qml/qmlvideofx/Content.qml + qml/qmlvideofx/ContentCamera.qml qml/qmlvideofx/ContentImage.qml qml/qmlvideofx/ContentVideo.qml qml/qmlvideofx/Divider.qml From 483da26351b6b62a73785b592673bce9577d1a88 Mon Sep 17 00:00:00 2001 From: Oswald Buddenhagen Date: Mon, 20 Apr 2015 12:28:05 +0200 Subject: [PATCH 2/6] Bump version Change-Id: I8c772e6048ed08abf98c0aef4b731653b3957ba4 --- .qmake.conf | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.qmake.conf b/.qmake.conf index 35f43d51..60b46503 100644 --- a/.qmake.conf +++ b/.qmake.conf @@ -1,4 +1,4 @@ load(qt_build_config) CONFIG += qt_example_installs -MODULE_VERSION = 5.4.2 +MODULE_VERSION = 5.4.3 From d910b6d63ff0ca74b5af004a83c437fa4db190c0 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 21 Apr 2015 18:49:29 +0200 Subject: [PATCH 3/6] AVFoundation: correctly detect the default audio capture device. Use AVCaptureDevice::defaultDeviceWithMediaType instead of the first device in the list of available devices. Change-Id: I436921f99280a28d7158d345cd977a874cfb8968 Reviewed-by: Timur Pocheptsov Reviewed-by: Christian Stromme --- .../avfoundation/camera/avfaudioinputselectorcontrol.h | 1 + .../avfoundation/camera/avfaudioinputselectorcontrol.mm | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h b/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h index 28188e27..2f499f0a 100644 --- a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h +++ b/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.h @@ -69,6 +69,7 @@ private: QString m_activeInput; bool m_dirty; + QString m_defaultDevice; QStringList m_devices; QMap m_deviceDescriptions; }; diff --git a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm b/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm index 4a2f068f..51a4eb4e 100644 --- a/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm +++ b/src/plugins/avfoundation/camera/avfaudioinputselectorcontrol.mm @@ -60,8 +60,11 @@ AVFAudioInputSelectorControl::AVFAudioInputSelectorControl(AVFCameraService *ser QString::fromUtf8([[device localizedName] UTF8String])); } - if (m_devices.size() > 0) - m_activeInput = m_devices.first(); + AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio]; + if (defaultDevice) { + m_defaultDevice = QString::fromUtf8([defaultDevice.uniqueID UTF8String]); + m_activeInput = m_defaultDevice; + } } AVFAudioInputSelectorControl::~AVFAudioInputSelectorControl() @@ -80,7 +83,7 @@ QString AVFAudioInputSelectorControl::inputDescription(const QString &name) cons QString AVFAudioInputSelectorControl::defaultInput() const { - return m_devices.size() > 0 ? m_devices.first() : QString(); + return m_defaultDevice; } QString AVFAudioInputSelectorControl::activeInput() const From bcaec9624c9124dc256ad963d5735496e219414a Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Tue, 21 Apr 2015 18:07:19 +0200 Subject: [PATCH 4/6] 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 --- .../avfoundation/camera/avfcamerasession.h | 3 +- .../avfoundation/camera/avfcamerasession.mm | 38 ++---------- .../camera/avfmediarecordercontrol.h | 5 +- .../camera/avfmediarecordercontrol.mm | 62 ++++++++++++++++--- 4 files changed, 63 insertions(+), 45 deletions(-) diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h index 8ce7461d..0a2ca214 100644 --- a/src/plugins/avfoundation/camera/avfcamerasession.h +++ b/src/plugins/avfoundation/camera/avfcamerasession.h @@ -92,7 +92,7 @@ Q_SIGNALS: private: static void updateCameraDevices(); - void attachInputDevices(); + void attachVideoInputDevice(); static int m_defaultCameraIndex; static QList m_cameraDevices; @@ -105,7 +105,6 @@ private: AVCaptureSession *m_captureSession; AVCaptureDeviceInput *m_videoInput; - AVCaptureDeviceInput *m_audioInput; AVFCameraSessionObserver *m_observer; }; diff --git a/src/plugins/avfoundation/camera/avfcamerasession.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm index 4d4b2f65..d0bb0e51 100644 --- a/src/plugins/avfoundation/camera/avfcamerasession.mm +++ b/src/plugins/avfoundation/camera/avfcamerasession.mm @@ -147,7 +147,6 @@ AVFCameraSession::AVFCameraSession(AVFCameraService *service, QObject *parent) , m_state(QCamera::UnloadedState) , m_active(false) , m_videoInput(nil) - , m_audioInput(nil) { m_captureSession = [[AVCaptureSession alloc] init]; m_observer = [[AVFCameraSessionObserver alloc] initWithCameraSession:this]; @@ -163,11 +162,6 @@ AVFCameraSession::~AVFCameraSession() [m_videoInput release]; } - if (m_audioInput) { - [m_captureSession removeInput:m_audioInput]; - [m_audioInput release]; - } - [m_observer release]; [m_captureSession release]; } @@ -286,10 +280,9 @@ void AVFCameraSession::setState(QCamera::State newState) QCamera::State oldState = m_state; m_state = newState; - //attach audio and video inputs during Unloaded->Loaded transition - if (oldState == QCamera::UnloadedState) { - attachInputDevices(); - } + //attach video input during Unloaded->Loaded transition + if (oldState == QCamera::UnloadedState) + attachVideoInputDevice(); if (m_state == QCamera::ActiveState) { Q_EMIT readyToConfigureConnections(); @@ -331,7 +324,7 @@ void AVFCameraSession::processSessionStopped() } } -void AVFCameraSession::attachInputDevices() +void AVFCameraSession::attachVideoInputDevice() { //Attach video input device: 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" diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol.h b/src/plugins/avfoundation/camera/avfmediarecordercontrol.h index a4c7b7f4..fb181a67 100644 --- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.h +++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol.h @@ -46,6 +46,7 @@ QT_BEGIN_NAMESPACE class AVFCameraSession; class AVFCameraControl; +class AVFAudioInputSelectorControl; class AVFCameraService; class AVFMediaRecorderControl : public QMediaRecorderControl @@ -78,11 +79,12 @@ public Q_SLOTS: void handleRecordingFailed(const QString &message); private Q_SLOTS: - void reconnectMovieOutput(); + void setupSessionForCapture(); void updateStatus(); private: AVFCameraControl *m_cameraControl; + AVFAudioInputSelectorControl *m_audioInputControl; AVFCameraSession *m_session; bool m_connected; @@ -96,6 +98,7 @@ private: bool m_muted; qreal m_volume; + AVCaptureDeviceInput *m_audioInput; AVCaptureMovieFileOutput *m_movieOutput; AVFMediaRecorderDelegate *m_recorderDelagate; AVFStorageLocation m_storageLocation; diff --git a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm index d65d238d..3e855b46 100644 --- a/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm +++ b/src/plugins/avfoundation/camera/avfmediarecordercontrol.mm @@ -44,6 +44,7 @@ #include "avfcamerasession.h" #include "avfcameraservice.h" #include "avfcameracontrol.h" +#include "avfaudioinputselectorcontrol.h" #include #include @@ -122,6 +123,7 @@ QT_USE_NAMESPACE AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObject *parent) : QMediaRecorderControl(parent) , m_cameraControl(service->cameraControl()) + , m_audioInputControl(service->audioInputSelectorControl()) , m_session(service->session()) , m_connected(false) , m_state(QMediaRecorder::StoppedState) @@ -130,21 +132,29 @@ AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObj , m_recordingFinished(false) , m_muted(false) , m_volume(1.0) + , m_audioInput(nil) { m_movieOutput = [[AVCaptureMovieFileOutput alloc] init]; m_recorderDelagate = [[AVFMediaRecorderDelegate alloc] initWithRecorder:this]; connect(m_cameraControl, SIGNAL(stateChanged(QCamera::State)), SLOT(updateStatus())); connect(m_cameraControl, SIGNAL(statusChanged(QCamera::Status)), SLOT(updateStatus())); - connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(reconnectMovieOutput())); - - reconnectMovieOutput(); + connect(m_cameraControl, SIGNAL(captureModeChanged(QCamera::CaptureModes)), SLOT(setupSessionForCapture())); + connect(m_session, SIGNAL(readyToConfigureConnections()), SLOT(setupSessionForCapture())); + connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(setupSessionForCapture())); } AVFMediaRecorderControl::~AVFMediaRecorderControl() { - if (m_movieOutput) + if (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]; } @@ -315,13 +325,39 @@ void AVFMediaRecorderControl::handleRecordingFailed(const QString &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, - //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(); - 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]) { [captureSession addOutput:m_movieOutput]; m_connected = true; @@ -329,8 +365,18 @@ void AVFMediaRecorderControl::reconnectMovieOutput() Q_EMIT error(QMediaRecorder::ResourceError, tr("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]; + + if (m_audioInput) { + [captureSession removeInput:m_audioInput]; + [m_audioInput release]; + m_audioInput = nil; + } + m_connected = false; } From 20dbf8490e8226e96898d9b0ef6edd5f9a87bff2 Mon Sep 17 00:00:00 2001 From: Yoann Lopes Date: Thu, 23 Apr 2015 14:52:55 +0200 Subject: [PATCH 5/6] Added 5.4.2 change file. Change-Id: Ib9d829e92343d5230875c37bee7b2bf912b1d304 Reviewed-by: Christian Stromme --- dist/changes-5.4.2 | 64 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 dist/changes-5.4.2 diff --git a/dist/changes-5.4.2 b/dist/changes-5.4.2 new file mode 100644 index 00000000..07e55302 --- /dev/null +++ b/dist/changes-5.4.2 @@ -0,0 +1,64 @@ +Qt 5.4.2 is a bug-fix release. It maintains both forward and backward +compatibility (source and binary) with Qt 5.4.0 and 5.4.1. + +For more details, refer to the online documentation included in this +distribution. The documentation is also available online: + + http://qt-project.org/doc/qt-5.4 + +The Qt version 5.4 series is binary compatible with the 5.3.x series. +Applications compiled for 5.3 will continue to run with 5.4. + +Some of the changes listed in this file include issue tracking numbers +corresponding to tasks in the Qt Bug Tracker: + + http://bugreports.qt-project.org/ + +Each of these identifiers can be entered in the bug tracker to obtain more +information about a particular change. + +**************************************************************************** +* Library * +**************************************************************************** + +QtMultimedia +------------ + + - QCameraInfo::availableCameras() and QtMultimedia.availableCameras (QML) + now return an up-to-date list when a camera is added or removed from the + system. + - Fixed memory leak in QMediaPlaylist::load(). + +**************************************************************************** +* Platform Specific Changes * +**************************************************************************** + +Android +------- + + - [QTBUG-37525] Fixed VideoOutput not working on some devices when used + with a Camera. + - [QTBUG-44383] Fixed setting a URL with special characters on a + MediaPlayer. + +iOS +--- + + - [QTBUG-44790] Fixed crash when using QAudioOutput/QAudioInput on iOS 5. + - [QTBUG-45659] Using QCamera doesn't always prompt for microphone + permission anymore, it now only does so when using the CaptureVideo mode. + +Linux +----- + + - [QTBUG-42326] Fixed regression causing software devices to not be + included in QAudioDeviceInfo::availableDevices() when using the ALSA + backend. + +Windows +------- + + - [QTBUG-32746] QMediaPlayer doesn't resume playback anymore when seeking + an audio media while paused. + - [QTBUG-42648] Greatly improved QAudioDeviceInfo supported formats detection. + - [QTBUG-44305] QAudioDeviceInfo: Fixed memory leak in availableDevices(). From 0559f645bbe928e9a54666356409fce444e80c57 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Christian=20Str=C3=B8mme?= Date: Mon, 27 Apr 2015 16:24:18 +0200 Subject: [PATCH 6/6] Android: Don't delete the media recorder object twice. In QAndroidCaptureSession::stop() we call restartViewFinder() which eventually calls QAndroidCaptureSession::stop() again, but this time the media recorder object is already released. Task-number: QTBUG-45637 Change-Id: I943c423398a99d98ccda1063fc16e47cba470deb Reviewed-by: Yoann Lopes --- .../mediacapture/qandroidcapturesession.cpp | 28 ++++++++----------- .../src/mediacapture/qandroidcapturesession.h | 2 +- 2 files changed, 13 insertions(+), 17 deletions(-) diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp index ae6991a3..3e3b17c7 100644 --- a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp @@ -151,23 +151,19 @@ void QAndroidCaptureSession::setState(QMediaRecorder::State state) stop(); break; case QMediaRecorder::RecordingState: - if (!start()) - return; + start(); break; case QMediaRecorder::PausedState: // Not supported by Android API qWarning("QMediaRecorder::PausedState is not supported on Android"); - return; + break; } - - m_state = state; - emit stateChanged(m_state); } -bool QAndroidCaptureSession::start() +void QAndroidCaptureSession::start() { if (m_state == QMediaRecorder::RecordingState || m_status != QMediaRecorder::LoadedStatus) - return false; + return; setStatus(QMediaRecorder::StartingStatus); @@ -225,13 +221,13 @@ bool QAndroidCaptureSession::start() if (!m_mediaRecorder->prepare()) { emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder.")); restartViewfinder(); - return false; + return; } if (!m_mediaRecorder->start()) { emit error(QMediaRecorder::FormatError, QLatin1String("Unable to start the media recorder.")); restartViewfinder(); - return false; + return; } m_elapsedTime.start(); @@ -241,22 +237,21 @@ bool QAndroidCaptureSession::start() if (m_cameraSession) m_cameraSession->setReadyForCapture(false); - return true; + m_state = QMediaRecorder::RecordingState; + emit stateChanged(m_state); } void QAndroidCaptureSession::stop(bool error) { - if (m_state == QMediaRecorder::StoppedState) + if (m_state == QMediaRecorder::StoppedState || m_mediaRecorder == 0) return; setStatus(QMediaRecorder::FinalizingStatus); m_mediaRecorder->stop(); - m_notifyTimer.stop(); updateDuration(); m_elapsedTime.invalidate(); - m_mediaRecorder->release(); delete m_mediaRecorder; m_mediaRecorder = 0; @@ -279,6 +274,9 @@ void QAndroidCaptureSession::stop(bool error) m_actualOutputLocation = m_usedOutputLocation; emit actualLocationChanged(m_actualOutputLocation); } + + m_state = QMediaRecorder::StoppedState; + emit stateChanged(m_state); } void QAndroidCaptureSession::setStatus(QMediaRecorder::Status status) @@ -541,8 +539,6 @@ void QAndroidCaptureSession::onError(int what, int extra) Q_UNUSED(what) Q_UNUSED(extra) stop(true); - m_state = QMediaRecorder::StoppedState; - emit stateChanged(m_state); emit error(QMediaRecorder::ResourceError, QLatin1String("Unknown error.")); } diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.h b/src/plugins/android/src/mediacapture/qandroidcapturesession.h index f96e9766..c17f081d 100644 --- a/src/plugins/android/src/mediacapture/qandroidcapturesession.h +++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.h @@ -130,7 +130,7 @@ private: CaptureProfile getProfile(int id); - bool start(); + void start(); void stop(bool error = false); void setStatus(QMediaRecorder::Status status);