Merge remote-tracking branch 'origin/5.4' into 5.5

Conflicts:
	.qmake.conf
	src/plugins/avfoundation/camera/avfcamerasession.h
	src/plugins/avfoundation/camera/avfcamerasession.mm

Change-Id: Ib2e25d907a7069591920d2f69e007f314cc0fd85
This commit is contained in:
Yoann Lopes
2015-05-05 11:48:21 +02:00
11 changed files with 191 additions and 65 deletions

64
dist/changes-5.4.2 vendored Normal file
View File

@@ -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().

View File

@@ -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
}
}

View File

@@ -3,6 +3,7 @@
<file>images/qt-logo.png</file> <file>images/qt-logo.png</file>
<file>qml/qmlvideofx/Button.qml</file> <file>qml/qmlvideofx/Button.qml</file>
<file>qml/qmlvideofx/Content.qml</file> <file>qml/qmlvideofx/Content.qml</file>
<file>qml/qmlvideofx/ContentCamera.qml</file>
<file>qml/qmlvideofx/ContentImage.qml</file> <file>qml/qmlvideofx/ContentImage.qml</file>
<file>qml/qmlvideofx/ContentVideo.qml</file> <file>qml/qmlvideofx/ContentVideo.qml</file>
<file>qml/qmlvideofx/Divider.qml</file> <file>qml/qmlvideofx/Divider.qml</file>

View File

@@ -151,23 +151,19 @@ void QAndroidCaptureSession::setState(QMediaRecorder::State state)
stop(); stop();
break; break;
case QMediaRecorder::RecordingState: case QMediaRecorder::RecordingState:
if (!start()) start();
return;
break; break;
case QMediaRecorder::PausedState: case QMediaRecorder::PausedState:
// Not supported by Android API // Not supported by Android API
qWarning("QMediaRecorder::PausedState is not supported on Android"); 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) if (m_state == QMediaRecorder::RecordingState || m_status != QMediaRecorder::LoadedStatus)
return false; return;
setStatus(QMediaRecorder::StartingStatus); setStatus(QMediaRecorder::StartingStatus);
@@ -225,13 +221,13 @@ bool QAndroidCaptureSession::start()
if (!m_mediaRecorder->prepare()) { if (!m_mediaRecorder->prepare()) {
emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder.")); emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder."));
restartViewfinder(); restartViewfinder();
return false; return;
} }
if (!m_mediaRecorder->start()) { if (!m_mediaRecorder->start()) {
emit error(QMediaRecorder::FormatError, QLatin1String("Unable to start the media recorder.")); emit error(QMediaRecorder::FormatError, QLatin1String("Unable to start the media recorder."));
restartViewfinder(); restartViewfinder();
return false; return;
} }
m_elapsedTime.start(); m_elapsedTime.start();
@@ -241,22 +237,21 @@ bool QAndroidCaptureSession::start()
if (m_cameraSession) if (m_cameraSession)
m_cameraSession->setReadyForCapture(false); m_cameraSession->setReadyForCapture(false);
return true; m_state = QMediaRecorder::RecordingState;
emit stateChanged(m_state);
} }
void QAndroidCaptureSession::stop(bool error) void QAndroidCaptureSession::stop(bool error)
{ {
if (m_state == QMediaRecorder::StoppedState) if (m_state == QMediaRecorder::StoppedState || m_mediaRecorder == 0)
return; return;
setStatus(QMediaRecorder::FinalizingStatus); setStatus(QMediaRecorder::FinalizingStatus);
m_mediaRecorder->stop(); m_mediaRecorder->stop();
m_notifyTimer.stop(); m_notifyTimer.stop();
updateDuration(); updateDuration();
m_elapsedTime.invalidate(); m_elapsedTime.invalidate();
m_mediaRecorder->release(); m_mediaRecorder->release();
delete m_mediaRecorder; delete m_mediaRecorder;
m_mediaRecorder = 0; m_mediaRecorder = 0;
@@ -279,6 +274,9 @@ void QAndroidCaptureSession::stop(bool error)
m_actualOutputLocation = m_usedOutputLocation; m_actualOutputLocation = m_usedOutputLocation;
emit actualLocationChanged(m_actualOutputLocation); emit actualLocationChanged(m_actualOutputLocation);
} }
m_state = QMediaRecorder::StoppedState;
emit stateChanged(m_state);
} }
void QAndroidCaptureSession::setStatus(QMediaRecorder::Status status) void QAndroidCaptureSession::setStatus(QMediaRecorder::Status status)
@@ -541,8 +539,6 @@ void QAndroidCaptureSession::onError(int what, int extra)
Q_UNUSED(what) Q_UNUSED(what)
Q_UNUSED(extra) Q_UNUSED(extra)
stop(true); stop(true);
m_state = QMediaRecorder::StoppedState;
emit stateChanged(m_state);
emit error(QMediaRecorder::ResourceError, QLatin1String("Unknown error.")); emit error(QMediaRecorder::ResourceError, QLatin1String("Unknown error."));
} }

View File

@@ -130,7 +130,7 @@ private:
CaptureProfile getProfile(int id); CaptureProfile getProfile(int id);
bool start(); void start();
void stop(bool error = false); void stop(bool error = false);
void setStatus(QMediaRecorder::Status status); void setStatus(QMediaRecorder::Status status);

View File

@@ -69,6 +69,7 @@ private:
QString m_activeInput; QString m_activeInput;
bool m_dirty; bool m_dirty;
QString m_defaultDevice;
QStringList m_devices; QStringList m_devices;
QMap<QString, QString> m_deviceDescriptions; QMap<QString, QString> m_deviceDescriptions;
}; };

View File

@@ -52,8 +52,11 @@ AVFAudioInputSelectorControl::AVFAudioInputSelectorControl(AVFCameraService *ser
QString::fromUtf8([[device localizedName] UTF8String])); QString::fromUtf8([[device localizedName] UTF8String]));
} }
if (m_devices.size() > 0) AVCaptureDevice *defaultDevice = [AVCaptureDevice defaultDeviceWithMediaType:AVMediaTypeAudio];
m_activeInput = m_devices.first(); if (defaultDevice) {
m_defaultDevice = QString::fromUtf8([defaultDevice.uniqueID UTF8String]);
m_activeInput = m_defaultDevice;
}
} }
AVFAudioInputSelectorControl::~AVFAudioInputSelectorControl() AVFAudioInputSelectorControl::~AVFAudioInputSelectorControl()
@@ -72,7 +75,7 @@ QString AVFAudioInputSelectorControl::inputDescription(const QString &name) cons
QString AVFAudioInputSelectorControl::defaultInput() const QString AVFAudioInputSelectorControl::defaultInput() const
{ {
return m_devices.size() > 0 ? m_devices.first() : QString(); return m_defaultDevice;
} }
QString AVFAudioInputSelectorControl::activeInput() const QString AVFAudioInputSelectorControl::activeInput() const

View File

@@ -99,7 +99,7 @@ Q_SIGNALS:
private: private:
static void updateCameraDevices(); static void updateCameraDevices();
void attachInputDevices(); void attachVideoInputDevice();
void applyImageEncoderSettings(); void applyImageEncoderSettings();
void applyViewfinderSettings(); void applyViewfinderSettings();
@@ -114,7 +114,6 @@ private:
AVCaptureSession *m_captureSession; AVCaptureSession *m_captureSession;
AVCaptureDeviceInput *m_videoInput; AVCaptureDeviceInput *m_videoInput;
AVCaptureDeviceInput *m_audioInput;
AVFCameraSessionObserver *m_observer; AVFCameraSessionObserver *m_observer;
QSet<AVFMediaVideoProbeControl *> m_videoProbes; QSet<AVFMediaVideoProbeControl *> m_videoProbes;

View File

@@ -143,7 +143,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_defaultCodec(0) , m_defaultCodec(0)
{ {
m_captureSession = [[AVCaptureSession alloc] init]; m_captureSession = [[AVCaptureSession alloc] init];
@@ -160,11 +159,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];
} }
@@ -283,10 +277,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();
@@ -332,7 +325,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()) {
@@ -360,29 +353,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];
}
}
} }
void AVFCameraSession::applyImageEncoderSettings() void AVFCameraSession::applyImageEncoderSettings()

View File

@@ -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;

View File

@@ -36,6 +36,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>
@@ -114,6 +115,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)
@@ -122,21 +124,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];
} }
@@ -307,13 +317,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;
@@ -321,8 +357,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;
} }