Based on AVFoundation framework Change-Id: If4cfd105a592f50b42606624548b9ffc870e3e47 Reviewed-by: Gunnar Sletta <gunnar.sletta@nokia.com>
343 lines
11 KiB
Plaintext
343 lines
11 KiB
Plaintext
/****************************************************************************
|
|
**
|
|
** 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"
|