Initial implementation of Mac camera backend
Based on AVFoundation framework Change-Id: If4cfd105a592f50b42606624548b9ffc870e3e47 Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
This commit is contained in:
committed by
Qt by Nokia
parent
09a7fda971
commit
37b872da9e
342
src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
Normal file
342
src/plugins/avfoundation/camera/avfmediarecordercontrol.mm
Normal file
@@ -0,0 +1,342 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2012 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** Contact: http://www.qt-project.org/
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "avfcameradebug.h"
|
||||
#include "avfmediarecordercontrol.h"
|
||||
#include "avfcamerasession.h"
|
||||
#include "avfcameraservice.h"
|
||||
#include "avfcameracontrol.h"
|
||||
|
||||
#include <QtCore/qurl.h>
|
||||
#include <QtCore/qfileinfo.h>
|
||||
#include <QtMultimedia/qcameracontrol.h>
|
||||
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
@interface AVFMediaRecorderDelegate : NSObject <AVCaptureFileOutputRecordingDelegate>
|
||||
{
|
||||
@private
|
||||
AVFMediaRecorderControl *m_recorder;
|
||||
}
|
||||
|
||||
- (AVFMediaRecorderDelegate *) initWithRecorder:(AVFMediaRecorderControl*)recorder;
|
||||
|
||||
- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
|
||||
didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
|
||||
fromConnections:(NSArray *)connections;
|
||||
|
||||
- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
|
||||
didFinishRecordingToOutputFileAtURL:(NSURL *)fileURL
|
||||
fromConnections:(NSArray *)connections
|
||||
error:(NSError *)error;
|
||||
@end
|
||||
|
||||
@implementation AVFMediaRecorderDelegate
|
||||
|
||||
- (AVFMediaRecorderDelegate *) initWithRecorder:(AVFMediaRecorderControl*)recorder
|
||||
{
|
||||
if (!(self = [super init]))
|
||||
return nil;
|
||||
|
||||
self->m_recorder = recorder;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
|
||||
didStartRecordingToOutputFileAtURL:(NSURL *)fileURL
|
||||
fromConnections:(NSArray *)connections
|
||||
{
|
||||
Q_UNUSED(captureOutput);
|
||||
Q_UNUSED(fileURL);
|
||||
Q_UNUSED(connections)
|
||||
|
||||
QMetaObject::invokeMethod(m_recorder, "handleRecordingStarted", Qt::QueuedConnection);
|
||||
}
|
||||
|
||||
- (void) captureOutput:(AVCaptureFileOutput *)captureOutput
|
||||
didFinishRecordingToOutputFileAtURL:(NSURL *)fileURL
|
||||
fromConnections:(NSArray *)connections
|
||||
error:(NSError *)error
|
||||
{
|
||||
Q_UNUSED(captureOutput);
|
||||
Q_UNUSED(fileURL);
|
||||
Q_UNUSED(connections)
|
||||
|
||||
if (error) {
|
||||
QStringList messageParts;
|
||||
messageParts << QString::fromUtf8([[error localizedDescription] UTF8String]);
|
||||
messageParts << QString::fromUtf8([[error localizedFailureReason] UTF8String]);
|
||||
messageParts << QString::fromUtf8([[error localizedRecoverySuggestion] UTF8String]);
|
||||
|
||||
QString errorMessage = messageParts.join(" ");
|
||||
|
||||
QMetaObject::invokeMethod(m_recorder, "handleRecordingFailed", Qt::QueuedConnection,
|
||||
Q_ARG(QString, errorMessage));
|
||||
} else {
|
||||
QMetaObject::invokeMethod(m_recorder, "handleRecordingFinished", Qt::QueuedConnection);
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
AVFMediaRecorderControl::AVFMediaRecorderControl(AVFCameraService *service, QObject *parent)
|
||||
: QMediaRecorderControl(parent)
|
||||
, m_cameraControl(service->cameraControl())
|
||||
, m_session(service->session())
|
||||
, m_connected(false)
|
||||
, m_state(QMediaRecorder::StoppedState)
|
||||
, m_lastStatus(QMediaRecorder::UnloadedStatus)
|
||||
, m_recordingStarted(false)
|
||||
, m_recordingFinished(false)
|
||||
, m_muted(false)
|
||||
, m_volume(1.0)
|
||||
{
|
||||
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();
|
||||
}
|
||||
|
||||
AVFMediaRecorderControl::~AVFMediaRecorderControl()
|
||||
{
|
||||
if (m_movieOutput)
|
||||
[m_session->captureSession() removeOutput:m_movieOutput];
|
||||
|
||||
[m_recorderDelagate release];
|
||||
}
|
||||
|
||||
QUrl AVFMediaRecorderControl::outputLocation() const
|
||||
{
|
||||
return m_outputLocation;
|
||||
}
|
||||
|
||||
bool AVFMediaRecorderControl::setOutputLocation(const QUrl &location)
|
||||
{
|
||||
m_outputLocation = location;
|
||||
return location.scheme() == QLatin1String("file") || location.scheme().isEmpty();
|
||||
}
|
||||
|
||||
QMediaRecorder::State AVFMediaRecorderControl::state() const
|
||||
{
|
||||
return m_state;
|
||||
}
|
||||
|
||||
QMediaRecorder::Status AVFMediaRecorderControl::status() const
|
||||
{
|
||||
QMediaRecorder::Status status = m_lastStatus;
|
||||
//bool videoEnabled = m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo);
|
||||
|
||||
if (m_cameraControl->status() == QCamera::ActiveStatus && m_connected) {
|
||||
if (m_state == QMediaRecorder::StoppedState) {
|
||||
if (m_recordingStarted && !m_recordingFinished)
|
||||
status = QMediaRecorder::FinalizingStatus;
|
||||
else
|
||||
status = QMediaRecorder::LoadedStatus;
|
||||
} else {
|
||||
status = m_recordingStarted ? QMediaRecorder::RecordingStatus :
|
||||
QMediaRecorder::StartingStatus;
|
||||
}
|
||||
} else {
|
||||
//camera not started yet
|
||||
status = m_cameraControl->state() == QCamera::ActiveState && m_connected ?
|
||||
QMediaRecorder::LoadingStatus:
|
||||
QMediaRecorder::UnloadedStatus;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void AVFMediaRecorderControl::updateStatus()
|
||||
{
|
||||
QMediaRecorder::Status newStatus = status();
|
||||
|
||||
if (m_lastStatus != newStatus) {
|
||||
qDebugCamera() << "Camera recorder status changed: " << m_lastStatus << " -> " << newStatus;
|
||||
m_lastStatus = newStatus;
|
||||
Q_EMIT statusChanged(m_lastStatus);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
qint64 AVFMediaRecorderControl::duration() const
|
||||
{
|
||||
if (!m_movieOutput)
|
||||
return 0;
|
||||
|
||||
return qint64(CMTimeGetSeconds(m_movieOutput.recordedDuration) * 1000);
|
||||
}
|
||||
|
||||
bool AVFMediaRecorderControl::isMuted() const
|
||||
{
|
||||
return m_muted;
|
||||
}
|
||||
|
||||
qreal AVFMediaRecorderControl::volume() const
|
||||
{
|
||||
return m_volume;
|
||||
}
|
||||
|
||||
void AVFMediaRecorderControl::applySettings()
|
||||
{
|
||||
}
|
||||
|
||||
void AVFMediaRecorderControl::setState(QMediaRecorder::State state)
|
||||
{
|
||||
if (m_state == state)
|
||||
return;
|
||||
|
||||
qDebugCamera() << Q_FUNC_INFO << m_state << " -> " << state;
|
||||
|
||||
switch (state) {
|
||||
case QMediaRecorder::RecordingState:
|
||||
{
|
||||
if (m_connected) {
|
||||
m_state = QMediaRecorder::RecordingState;
|
||||
m_recordingStarted = false;
|
||||
m_recordingFinished = false;
|
||||
|
||||
QString outputLocationPath = m_outputLocation.scheme() == QLatin1String("file") ?
|
||||
m_outputLocation.path() : m_outputLocation.toString();
|
||||
|
||||
QUrl actualLocation = QUrl::fromLocalFile(
|
||||
m_storageLocation.generateFileName(outputLocationPath,
|
||||
QCamera::CaptureVideo,
|
||||
QLatin1String("clip_"),
|
||||
QLatin1String("mp4")));
|
||||
|
||||
qDebugCamera() << "Video capture location:" << actualLocation.toString();
|
||||
|
||||
NSString *urlString = [NSString stringWithUTF8String:actualLocation.toString().toUtf8().constData()];
|
||||
NSURL *fileURL = [NSURL URLWithString:urlString];
|
||||
|
||||
[m_movieOutput startRecordingToOutputFileURL:fileURL recordingDelegate:m_recorderDelagate];
|
||||
|
||||
Q_EMIT actualLocationChanged(actualLocation);
|
||||
} else {
|
||||
Q_EMIT error(QMediaRecorder::FormatError, tr("Recorder not configured"));
|
||||
}
|
||||
|
||||
} break;
|
||||
case QMediaRecorder::PausedState:
|
||||
{
|
||||
Q_EMIT error(QMediaRecorder::FormatError, tr("Recording pause not supported"));
|
||||
return;
|
||||
} break;
|
||||
case QMediaRecorder::StoppedState:
|
||||
{
|
||||
m_state = QMediaRecorder::StoppedState;
|
||||
[m_movieOutput stopRecording];
|
||||
}
|
||||
}
|
||||
|
||||
updateStatus();
|
||||
Q_EMIT stateChanged(m_state);
|
||||
}
|
||||
|
||||
void AVFMediaRecorderControl::setMuted(bool muted)
|
||||
{
|
||||
if (m_muted != muted) {
|
||||
m_muted = muted;
|
||||
Q_EMIT mutedChanged(muted);
|
||||
}
|
||||
}
|
||||
|
||||
void AVFMediaRecorderControl::setVolume(qreal volume)
|
||||
{
|
||||
if (m_volume != volume) {
|
||||
m_volume = volume;
|
||||
Q_EMIT volumeChanged(volume);
|
||||
}
|
||||
}
|
||||
|
||||
void AVFMediaRecorderControl::handleRecordingStarted()
|
||||
{
|
||||
m_recordingStarted = true;
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
void AVFMediaRecorderControl::handleRecordingFinished()
|
||||
{
|
||||
m_recordingFinished = true;
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
void AVFMediaRecorderControl::handleRecordingFailed(const QString &message)
|
||||
{
|
||||
m_recordingFinished = true;
|
||||
if (m_state != QMediaRecorder::StoppedState) {
|
||||
m_state = QMediaRecorder::StoppedState;
|
||||
Q_EMIT stateChanged(m_state);
|
||||
}
|
||||
updateStatus();
|
||||
|
||||
Q_EMIT error(QMediaRecorder::ResourceError, message);
|
||||
}
|
||||
|
||||
void AVFMediaRecorderControl::reconnectMovieOutput()
|
||||
{
|
||||
//adding movie output causes high CPU usage even when while recording is not active,
|
||||
//connect it only while video capture mode is enabled
|
||||
AVCaptureSession *captureSession = m_session->captureSession();
|
||||
|
||||
if (!m_connected && m_cameraControl->captureMode().testFlag(QCamera::CaptureVideo)) {
|
||||
if ([captureSession canAddOutput:m_movieOutput]) {
|
||||
[captureSession addOutput:m_movieOutput];
|
||||
m_connected = true;
|
||||
} else {
|
||||
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)) {
|
||||
[captureSession removeOutput:m_movieOutput];
|
||||
m_connected = false;
|
||||
}
|
||||
|
||||
updateStatus();
|
||||
}
|
||||
|
||||
#include "moc_avfmediarecordercontrol.cpp"
|
||||
Reference in New Issue
Block a user