diff options
author | Michael Goddard <michael.goddard@nokia.com> | 2011-06-29 13:38:46 +1000 |
---|---|---|
committer | Michael Goddard <michael.goddard@nokia.com> | 2011-06-29 13:38:46 +1000 |
commit | 2a34e88c1e1ced28e75c487cd13402e1c9cf9fa3 (patch) | |
tree | e6c1b770c5c47212792a1f9344fa034ea3e54c44 /src/plugins/symbian/ecam |
Initial copy of QtMultimediaKit.
Comes from original repo, with SHA1:
2c82d5611655e5967f5c5095af50c0991c4378b2
Diffstat (limited to 'src/plugins/symbian/ecam')
46 files changed, 15178 insertions, 0 deletions
diff --git a/src/plugins/symbian/ecam/camera_s60.pri b/src/plugins/symbian/ecam/camera_s60.pri new file mode 100644 index 000000000..beb44db8d --- /dev/null +++ b/src/plugins/symbian/ecam/camera_s60.pri @@ -0,0 +1,157 @@ +INCLUDEPATH += $$PWD + +include (../videooutput/videooutput.pri) + +# Camera Service +DEFINES += QMEDIA_SYMBIAN_CAMERA + +# S60 3.1 platform +contains(S60_VERSION, 3.1) { + DEFINES += S60_31_PLATFORM + DEFINES *= S60_3X_PLATFORM +} + +# S60 3.2 platform +contains(S60_VERSION, 3.2) { + DEFINES += S60_32_PLATFORM + DEFINES *= S60_3X_PLATFORM +} + +# S60 5.0 platform +!contains(DEFINES, S60_31_PLATFORM) { + !contains(DEFINES, S60_32_PLATFORM) { + !contains(DEFINES, SYMBIAN_3_PLATFORM) { + DEFINES += S60_50_PLATFORM + } + } +} + +# Symbian 3 platform +contains(DEFINES, VIDEOOUTPUT_GRAPHICS_SURFACES) { + DEFINES += SYMBIAN_3_PLATFORM +} + +# AutoFocusing (CamAutoFocus) from ForumNokia example +contains(symbian_camera_camautofocus_enabled, yes) { + exists($${EPOCROOT}epoc32\\include\\CCamAutoFocus.h) { + message ("CameraBE: Using S60 3.1 autofocusing") + MMP_RULES += \ + "$${LITERAL_HASH}ifdef WINSCW" \ + "LIBRARY camautofocus.lib" \ + "$${LITERAL_HASH}else" \ + "STATICLIBRARY camautofocus_s.lib" \ + "$${LITERAL_HASH}endif // WINS" \ + "MACRO S60_CAM_AUTOFOCUS_SUPPORT" + } +} + +# ECam AdvancedSettings +contains(symbian_camera_ecamadvsettings_enabled, yes) { + exists($${EPOCROOT}epoc32\\include\\ecamadvancedsettings.h) { + MMP_RULES += \ + "$${LITERAL_HASH}ifndef WINSCW" \ + "LIBRARY ecamadvsettings.lib" \ + "MACRO USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER" \ + "$${LITERAL_HASH}endif" + message("CameraBE: Using from S60 3.2 CCameraAdvancedSettings header") + } + exists($${EPOCROOT}epoc32\\include\\ecamadvsettings.h) { + symbian:LIBS += -lecamadvsettings + DEFINES += USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER + message("CameraBE: Using CCameraAdvancedSettings header from S60 5.0 or later") + } +} + +# DevVideo API Check (Requires both, DevVideoPlay and DevVideoRecord plugins): +# DevVideoConstants has been problematic since not being included in SDK plugins +# For S60 5.0 this has changed with plugin extension 1.1 +# But for S60 3.2 this is still a problem +contains(symbian_camera_devvideorecord_enabled, yes) { + exists($${EPOCROOT}epoc32\\include\\mmf\\devvideo\\devvideorecord.h) { + exists($${EPOCROOT}epoc32\\include\\mmf\\devvideo\\devvideobase.h) { + exists($${EPOCROOT}epoc32\\include\\mmf\\devvideo\\devvideoconstants.h) { + symbian:LIBS += -ldevvideo + DEFINES += S60_DEVVIDEO_RECORDING_SUPPORTED + message("CameraBE: Devvideo API supported") + } + } + } +} + +# ECam Snapshot API: +contains(symbian_camera_snapshot_enabled, yes) { + exists($${EPOCROOT}epoc32\\include\\platform\\ecam\\camerasnapshot.h) { + DEFINES += ECAM_PREVIEW_API + message("CameraBE: Using CCameraSnapshot API") + symbian:LIBS += -lecamsnapshot + } else { + message("CameraBE: Using custom snapshot proving methods") + } +} else { + message("CameraBE: Using custom snapshot proving methods") +} + +# Libraries: +symbian:LIBS += -lfbscli \ + -lmediaclientvideo \ + -lecam \ + -lbafl \ + -lPlatformEnv \ + -lcharconv \ + -lconvnames \ + -lgb2312_shared \ + -ljisx0201 \ + -ljisx0208 \ + -lmmfcontrollerframework \ + -lfbscli \ + -lefsrv \ + -lcone \ + -lws32 \ + -limageconversion + +# Source: +HEADERS += $$PWD/s60cameraconstants.h \ + $$PWD/s60cameralockscontrol.h \ + $$PWD/s60camerafocuscontrol.h \ + $$PWD/s60cameraexposurecontrol.h \ + $$PWD/s60cameraflashcontrol.h \ + $$PWD/s60cameracontrol.h \ + $$PWD/s60mediarecordercontrol.h \ + $$PWD/s60videocapturesession.h \ + $$PWD/s60imagecapturesession.h \ + $$PWD/s60mediacontainercontrol.h \ + $$PWD/s60videoencodercontrol.h \ + $$PWD/s60audioencodercontrol.h \ + $$PWD/s60cameraservice.h \ + $$PWD/s60cameraimageprocessingcontrol.h \ + $$PWD/s60cameraimagecapturecontrol.h \ + $$PWD/s60videodevicecontrol.h \ + $$PWD/s60imageencodercontrol.h \ + $$PWD/s60camerasettings.h \ + $$PWD/s60cameraengine.h \ + $$PWD/s60cameraviewfinderengine.h \ + $$PWD/s60cameraengineobserver.h \ + $$PWD/s60videorenderercontrol.h + +SOURCES += $$PWD/s60cameralockscontrol.cpp \ + $$PWD/s60camerafocuscontrol.cpp \ + $$PWD/s60cameraexposurecontrol.cpp \ + $$PWD/s60cameraflashcontrol.cpp \ + $$PWD/s60cameracontrol.cpp \ + $$PWD/s60mediarecordercontrol.cpp \ + $$PWD/s60videocapturesession.cpp \ + $$PWD/s60imagecapturesession.cpp \ + $$PWD/s60mediacontainercontrol.cpp \ + $$PWD/s60videoencodercontrol.cpp \ + $$PWD/s60audioencodercontrol.cpp \ + $$PWD/s60cameraservice.cpp \ + $$PWD/s60cameraimageprocessingcontrol.cpp \ + $$PWD/s60cameraimagecapturecontrol.cpp \ + $$PWD/s60videodevicecontrol.cpp \ + $$PWD/s60imageencodercontrol.cpp \ + $$PWD/s60camerasettings.cpp \ + $$PWD/s60cameraengine.cpp \ + $$PWD/s60cameraviewfinderengine.cpp \ + $$PWD/s60videorenderercontrol.cpp + +# End of file diff --git a/src/plugins/symbian/ecam/ecam.pro b/src/plugins/symbian/ecam/ecam.pro new file mode 100644 index 000000000..31f30b61c --- /dev/null +++ b/src/plugins/symbian/ecam/ecam.pro @@ -0,0 +1,40 @@ +###################################################################### +# +# Mobility API project - Symbian Camera backend +# +###################################################################### + +TEMPLATE = lib +CONFIG += plugin + +TARGET = $$qtLibraryTarget(qtmultimediakit_ecamengine) +PLUGIN_TYPE = mediaservice +include (../../../../common.pri) + +CONFIG += mobility +MOBILITY += multimedia + +# Include here so that all defines are added here also +include(camera_s60.pri) + +DEPENDPATH += . + +INCLUDEPATH += . \ + $${SOURCE_DIR}/include \ + $${SOURCE_DIR}/src/multimedia \ + $${SOURCE_DIR}/src/multimedia/audio \ + $${SOURCE_DIR}/src/multimedia/video \ + $${SOURCE_DIR} + +HEADERS += s60cameraserviceplugin.h +SOURCES += s60cameraserviceplugin.cpp + +load(data_caging_paths) +TARGET.EPOCALLOWDLLDATA = 1 +TARGET.UID3 = 0x2002BFC2 +TARGET.CAPABILITY = ALL -TCB + +# Make a sis package from plugin + api + stub (plugin) +pluginDep.sources = $${TARGET}.dll +pluginDep.path = $${QT_PLUGINS_BASE_DIR}/$${PLUGIN_TYPE} +DEPLOYMENT += pluginDep diff --git a/src/plugins/symbian/ecam/s60audioencodercontrol.cpp b/src/plugins/symbian/ecam/s60audioencodercontrol.cpp new file mode 100644 index 000000000..bd8f0147d --- /dev/null +++ b/src/plugins/symbian/ecam/s60audioencodercontrol.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60audioencodercontrol.h" +#include "s60videocapturesession.h" + +S60AudioEncoderControl::S60AudioEncoderControl(QObject *parent) : + QAudioEncoderControl(parent) +{ +} + +S60AudioEncoderControl::S60AudioEncoderControl(S60VideoCaptureSession *session, QObject *parent) : + QAudioEncoderControl(parent) +{ + m_session = session; +} + +S60AudioEncoderControl::~S60AudioEncoderControl() +{ +} + +QStringList S60AudioEncoderControl::supportedAudioCodecs() const +{ + return m_session->supportedAudioCaptureCodecs(); +} + +QString S60AudioEncoderControl::codecDescription(const QString &codecName) const +{ + // According to ForumNokia MMF camcorder plugin supports AAC, AMR and QCELP + // QCELP is speech codec and can be discarded + if (qstrcmp(codecName.toLocal8Bit().constData(), "audio/aac") == 0) + return QLatin1String("Advanced Audio Coding"); + else if (qstrcmp(codecName.toLocal8Bit().constData(), "audio/amr") == 0) + return QLatin1String("Adaptive Multi-Rate Audio Codec"); + + return QString(); +} + +QStringList S60AudioEncoderControl::supportedEncodingOptions(const QString &codec) const +{ + // Possible settings: EncodingMode, Codec, BitRate, ChannelCount, SampleRate, Quality + // Possible (codec specific) Options: None + Q_UNUSED(codec); + return QStringList(); +} + +QVariant S60AudioEncoderControl::encodingOption(const QString &codec, const QString &name) const +{ + // Possible settings: EncodingMode, Codec, BitRate, ChannelCount, SampleRate, Quality + // Possible (codec specific) Options: None + Q_UNUSED(codec); + Q_UNUSED(name); + return QVariant(); +} + +void S60AudioEncoderControl::setEncodingOption( + const QString &codec, const QString &name, const QVariant &value) +{ + m_session->setError(KErrNotSupported, tr("Audio encoding option is not supported")); + + // The audio settings can currently be set only using setAudioSettings() function + Q_UNUSED(value) + Q_UNUSED(codec) + Q_UNUSED(name) +} + +QList<int> S60AudioEncoderControl::supportedSampleRates( + const QAudioEncoderSettings &settings, bool *continuous) const +{ + return m_session->supportedSampleRates(settings, continuous); +} + +QAudioEncoderSettings S60AudioEncoderControl::audioSettings() const +{ + QAudioEncoderSettings settings; + m_session->audioEncoderSettings(settings); + + return settings; +} + +void S60AudioEncoderControl::setAudioSettings(const QAudioEncoderSettings &settings) +{ + // Notify that settings have been implicitly set and there's no need to + // initialize them in case camera is changed + m_session->notifySettingsSet(); + + // Quality defines SampleRate/BitRate combination if either or both are missing + if (settings.codec().isEmpty()) { // Empty settings + m_session->setAudioCaptureQuality(settings.quality(), S60VideoCaptureSession::EOnlyAudioQuality); + + } else if (settings.bitRate() == -1 && settings.sampleRate() != -1) { // Only SampleRate set + m_session->setAudioCaptureCodec(settings.codec()); + m_session->setAudioChannelCount(settings.channelCount()); + m_session->setAudioSampleRate(settings.sampleRate()); + m_session->setAudioEncodingMode(settings.encodingMode()); + m_session->setAudioCaptureQuality(settings.quality(), S60VideoCaptureSession::EAudioQualityAndSampleRate); + + } else if (settings.bitRate() != -1 && settings.sampleRate() == -1) { // Only BitRate set + m_session->setAudioCaptureCodec(settings.codec()); + m_session->setAudioChannelCount(settings.channelCount()); + m_session->setAudioBitRate(settings.bitRate()); + m_session->setAudioEncodingMode(settings.encodingMode()); + m_session->setAudioCaptureQuality(settings.quality(), S60VideoCaptureSession::EAudioQualityAndBitRate); + + } else if (settings.bitRate() == -1 && settings.sampleRate() == -1) { // No BitRate or SampleRate set + m_session->setAudioCaptureCodec(settings.codec()); + m_session->setAudioChannelCount(settings.channelCount()); + m_session->setAudioEncodingMode(settings.encodingMode()); + m_session->setAudioCaptureQuality(settings.quality(), S60VideoCaptureSession::EOnlyAudioQuality); + + } else { // Both SampleRate and BitRate set + m_session->setAudioCaptureCodec(settings.codec()); + m_session->setAudioChannelCount(settings.channelCount()); + m_session->setAudioSampleRate(settings.sampleRate()); + m_session->setAudioBitRate(settings.bitRate()); + m_session->setAudioEncodingMode(settings.encodingMode()); + m_session->setAudioCaptureQuality(settings.quality(), S60VideoCaptureSession::ENoAudioQuality); + } +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60audioencodercontrol.h b/src/plugins/symbian/ecam/s60audioencodercontrol.h new file mode 100644 index 000000000..db6ada5d1 --- /dev/null +++ b/src/plugins/symbian/ecam/s60audioencodercontrol.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60AUDIOENCODERCONTROL_H +#define S60AUDIOENCODERCONTROL_H + +#include <QtCore/qstringlist.h> +#include <QtCore/qmap.h> + +#include <qaudioencodercontrol.h> + +QT_USE_NAMESPACE + +class S60VideoCaptureSession; + +/* + * Control for audio settings when recording video using QMediaRecorder. + */ +class S60AudioEncoderControl : public QAudioEncoderControl +{ + Q_OBJECT + +public: // Constructor & Destructor + + S60AudioEncoderControl(QObject *parent = 0); + S60AudioEncoderControl(S60VideoCaptureSession *session, QObject *parent = 0); + virtual ~S60AudioEncoderControl(); + +public: // QAudioEncoderControl + + // Audio Codec + QStringList supportedAudioCodecs() const; + QString codecDescription(const QString &codecName) const; + + // Sample Rate + QList<int> supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = 0) const; + + // Audio Settings + QAudioEncoderSettings audioSettings() const; + void setAudioSettings(const QAudioEncoderSettings &settings); + + // Encoding Option + QStringList supportedEncodingOptions(const QString &codec) const; + QVariant encodingOption(const QString &codec, const QString &name) const; + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + +private: // Data + + S60VideoCaptureSession* m_session; +}; + +#endif // S60AUDIOENCODERCONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraconstants.h b/src/plugins/symbian/ecam/s60cameraconstants.h new file mode 100644 index 000000000..4b415c3e8 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraconstants.h @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERACONSTANTS_H +#define S60CAMERACONSTANTS_H + +//============================================================================= + +// GENERAL SETTINGS + +#define KDefaultCameraDevice 0 +#define KECamCameraPriority 0 +#define KInactivityTimerTimeout 30000 // msec +#define KSymbianFineResolutionFactor 100.0 +#define KDefaultOpticalZoom 1.0 +#define KDefaultDigitalZoom 1.0 +#define KSmoothZoomStep 1 +#define KDefaultFocusMode QCameraFocus::AutoFocus + +#define KDefaultViewfinderSize QSize(320,240) +#define KDefaultSizePreview_Normal TSize(640,480) +#define KDefaultSizePreview_Wide TSize(640,360) +#define KDefaultSizePreview_CIF TSize(352,288) +#define KDefaultSizePreview_PAL TSize(640,512) +#define KDefaultSizePreview_NTSC TSize(640,426) +#define KDefaultFormatPreview CCamera::EFormatFbsBitmapColor16MU +#define KViewfinderFrameRate 30 +#define KMaxVFErrorsSignalled 3 + +//============================================================================= + +// IMAGE SETTINGS + +#define KDefaultImagePath QLatin1String("c:\\Data\\Images") +#define KDefaultImageFileName QLatin1String("image.jpg") +#define KDefaultImageCodec QLatin1String("image/jpeg") +#define KDefaultImageFormatPrimaryCam CCamera::EFormatExif +#ifdef SYMBIAN_3_PLATFORM +#define KDefaultImageFormatSecondaryCam CCamera::EFormatExif +#define KDefaultImageResolution QSize(3264, 2448) +#else // Pre-Symbian3 Platforms +#define KDefaultImageFormatSecondaryCam CCamera::EFormatFbsBitmapColor64K +#define KDefaultImageResolution QSize(2048, 1536) +#endif // SYMBIAN_3_PLATFORM +#define KSymbianImageQualityCoefficient 25 +// This must be divisible by 4 and creater or equal to 8 +#define KSnapshotDownScaleFactor 8 +#define KSnapshotMinWidth 640 +#define KSnapshotMinHeight 360 +#define KJpegQualityVeryLow 40 +#define KJpegQualityLow 50 +#define KJpegQualityNormal 75 +#define KJpegQualityHigh 85 +#define KJpegQualityVeryHigh 95 +#define KDefaultImageQuality KJpegQualityHigh + +//============================================================================= + +// VIDEO SETTINGS + +// ================ +// General settings +// ================ + +// Dummy file name to execute CVideoRecorderUtility::OpenFileL() without +// knowing the actual outputLocation. This is needed to be able to query/set +// supported video settings. +_LIT(KDummyVideoFile, "c:\\data\\temp"); + +// Default container MIME type +#define KMimeTypeDefaultContainer QLatin1String("video/mp4") +#define KDefaultVideoPath QLatin1String("c:\\Data\\Videos") +#define KDefaultVideoFileName QLatin1String("video.mp4") +#define KDurationChangedInterval 1000 // 1 second + +// ============== +// Audio Settings +// ============== + +// Default audio codec MIME type +#define KMimeTypeDefaultAudioCodec QLatin1String("audio/aac") + +// Default audio settings for video recording +#define KDefaultChannelCount -1 // Not Supported on Symbian +#define KDefaultBitRate 32000 // 32kbps +#define KDefaultSampleRate -1 // Not Supported on Symbian + +// ============== +// Video Settings +// ============== + +// Default video codec MIME type +#ifdef SYMBIAN_3_PLATFORM + // H.264: BaselineProfile Level 3.1, Max resolution: 1280x720 + #define KMimeTypeDefaultVideoCodec QLatin1String("video/H264; profile-level-id=42801F") +#else + // MPEG-4: Simple Profile, Level 4, Max resolution: 640x480 + #define KMimeTypeDefaultVideoCodec QLatin1String("video/mp4v-es; profile-level-id=4") +#endif + +// Maximum resolutions for encoder MIME Types +// H.263 +#define KResH263 QSize(176,144); +#define KResH263_Profile0 QSize(176,144); +#define KResH263_Profile0_Level10 QSize(176,144); +#define KResH263_Profile0_Level20 QSize(352,288); +#define KResH263_Profile0_Level30 QSize(352,288); +#define KResH263_Profile0_Level40 QSize(352,288); +#define KResH263_Profile0_Level45 QSize(176,144); +#define KResH263_Profile0_Level50 QSize(352,288); +#define KResH263_Profile3 QSize(176,144); +// MPEG-4 +#define KResMPEG4 QSize(176,144); +#define KResMPEG4_PLID_1 QSize(176,144); +#define KResMPEG4_PLID_2 QSize(352,288); +#define KResMPEG4_PLID_3 QSize(352,288); +#define KResMPEG4_PLID_4 QSize(640,480); +#define KResMPEG4_PLID_5 QSize(720,576); +#define KResMPEG4_PLID_6 QSize(1280,720); +#define KResMPEG4_PLID_8 QSize(176,144); +#define KResMPEG4_PLID_9 QSize(176,144); +// H.264 (Baseline Profile, same resolutions apply to Main and High Profile) +#define KResH264 QSize(176,144); +#define KResH264_PLID_42800A QSize(176,144); +#define KResH264_PLID_42900B QSize(176,144); +#define KResH264_PLID_42800B QSize(352,288); +#define KResH264_PLID_42800C QSize(352,288); +#define KResH264_PLID_42800D QSize(352,288); +#define KResH264_PLID_428014 QSize(352,288); +#define KResH264_PLID_428015 QSize(352,288); +#define KResH264_PLID_428016 QSize(640,480); +#define KResH264_PLID_42801E QSize(640,480); +#define KResH264_PLID_42801F QSize(1280,720); +#define KResH264_PLID_428020 QSize(1280,720); +#define KResH264_PLID_428028 QSize(1920,1080); + +// Maximum framerates for encoder MIME Types +// H.263 +#define KFrR_H263 qreal(15); +#define KFrR_H263_Profile0 qreal(15); +#define KFrR_H263_Profile0_Level10 qreal(15); +#define KFrR_H263_Profile0_Level20 qreal(15); +#define KFrR_H263_Profile0_Level30 qreal(30); +#define KFrR_H263_Profile0_Level40 qreal(30); +#define KFrR_H263_Profile0_Level45 qreal(15); +#define KFrR_H263_Profile0_Level50 qreal(15); +#define KFrR_H263_Profile3 qreal(15); +// MPEG-4 +#define KFrR_MPEG4 qreal(15); +#define KFrR_MPEG4_PLID_1 qreal(15); +#define KFrR_MPEG4_PLID_2 qreal(15); +#define KFrR_MPEG4_PLID_3 qreal(30); +// This is a workaround for a known platform bug +#if (defined(S60_31_PLATFORM) | defined(S60_32_PLATFORM)) +#define KFrR_MPEG4_PLID_4 qreal(15); +#else // All other platforms +#define KFrR_MPEG4_PLID_4 qreal(30); +#endif // S60 3.1 or 3.2 +#define KFrR_MPEG4_PLID_5 qreal(30); +#define KFrR_MPEG4_PLID_6 qreal(30); +#define KFrR_MPEG4_PLID_8 qreal(15); +#define KFrR_MPEG4_PLID_9 qreal(15); +// H.264 (Baseline Profile, same framerates apply to Main and High Profile) +#define KFrR_H264 qreal(15); +#define KFrR_H264_PLID_42800A qreal(15); +#define KFrR_H264_PLID_42900B qreal(15); +#define KFrR_H264_PLID_42800B qreal(7.5); +#define KFrR_H264_PLID_42800C qreal(15); +#define KFrR_H264_PLID_42800D qreal(30); +#define KFrR_H264_PLID_428014 qreal(30); +#define KFrR_H264_PLID_428015 qreal(50); +#define KFrR_H264_PLID_428016 qreal(16.9); +#define KFrR_H264_PLID_42801E qreal(33.8); +#define KFrR_H264_PLID_42801F qreal(30); +#define KFrR_H264_PLID_428020 qreal(60); +#define KFrR_H264_PLID_428028 qreal(30); + +// Maximum bitrates for encoder MIME Types +// H.263 +#define KBiR_H263 int(64000); +#define KBiR_H263_Profile0 int(64000); +#define KBiR_H263_Profile0_Level10 int(64000); +#define KBiR_H263_Profile0_Level20 int(128000); +#define KBiR_H263_Profile0_Level30 int(384000); +#define KBiR_H263_Profile0_Level40 int(2048000); +#define KBiR_H263_Profile0_Level45 int(128000); +#define KBiR_H263_Profile0_Level50 int(4096000); +#define KBiR_H263_Profile3 int(64000); +// MPEG-4 +#define KBiR_MPEG4 int(64000); +#define KBiR_MPEG4_PLID_1 int(64000); +#define KBiR_MPEG4_PLID_2 int(128000); +#define KBiR_MPEG4_PLID_3 int(384000); +// This is a workaround for a known platform bug +#if (defined(S60_31_PLATFORM) | defined(S60_32_PLATFORM)) +#define KBiR_MPEG4_PLID_4 int(2000000); +#else // All other platforms +#define KBiR_MPEG4_PLID_4 int(4000000); +#endif // S60 3.1 or 3.2 +#define KBiR_MPEG4_PLID_5 int(8000000); +#define KBiR_MPEG4_PLID_6 int(12000000); +#define KBiR_MPEG4_PLID_8 int(64000); +#define KBiR_MPEG4_PLID_9 int(128000); +// H.264 (Baseline Profile, same bitrates apply to Main and High Profile) +#define KBiR_H264 int(64000); +#define KBiR_H264_PLID_42800A int(64000); +#define KBiR_H264_PLID_42900B int(128000); +#define KBiR_H264_PLID_42800B int(192000); +#define KBiR_H264_PLID_42800C int(384000); +#define KBiR_H264_PLID_42800D int(768000); +#define KBiR_H264_PLID_428014 int(2000000); +#define KBiR_H264_PLID_428015 int(4000000); +#define KBiR_H264_PLID_428016 int(4000000); +#define KBiR_H264_PLID_42801E int(10000000); +#define KBiR_H264_PLID_42801F int(14000000); +#define KBiR_H264_PLID_428020 int(20000000); +#define KBiR_H264_PLID_428028 int(20000000); + +#endif // S60CAMERACONSTANTS_H diff --git a/src/plugins/symbian/ecam/s60cameracontrol.cpp b/src/plugins/symbian/ecam/s60cameracontrol.cpp new file mode 100644 index 000000000..64a3ff37e --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameracontrol.cpp @@ -0,0 +1,983 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <QTimer> + +#include "s60cameraservice.h" +#include "s60cameraengine.h" +#include "s60cameracontrol.h" +#include "s60imagecapturesession.h" +#include "s60videowidgetcontrol.h" +#include "s60cameraviewfinderengine.h" +#include "s60cameraconstants.h" + +S60CameraControl::S60CameraControl(QObject *parent) : + QCameraControl(parent) +{ +} + +S60CameraControl::S60CameraControl(S60VideoCaptureSession *videosession, + S60ImageCaptureSession *imagesession, + QObject *parent): + QCameraControl(parent), + m_cameraEngine(0), + m_viewfinderEngine(0), + m_imageSession(0), + m_videoSession(0), + m_advancedSettings(0), + m_videoOutput(0), + m_inactivityTimer(0), + m_captureMode(QCamera::CaptureStillImage), // Default CaptureMode + m_requestedCaptureMode(QCamera::CaptureStillImage), + m_settingCaptureModeInternally(false), + m_internalState(QCamera::UnloadedStatus), // Default Status + m_requestedState(QCamera::UnloadedState), // Default State + m_deviceIndex(KDefaultCameraDevice), + m_error(KErrNone), + m_changeCaptureModeWhenReady(false), + m_rotateCameraWhenReady(false), + m_videoCaptureState(S60VideoCaptureSession::ENotInitialized) +{ + m_videoSession = videosession; + m_imageSession = imagesession; + + m_inactivityTimer = new QTimer; + if (m_inactivityTimer) + m_inactivityTimer->setSingleShot(true); + + TRAPD(err, m_cameraEngine = CCameraEngine::NewL(m_deviceIndex, KECamCameraPriority, this)); + if (err) { + m_error = err; + if (err == KErrPermissionDenied) + qWarning("Failed to create camera. Possibly missing capabilities."); + else + qWarning("Failed to create camera."); + return; + } + + m_viewfinderEngine = new S60CameraViewfinderEngine(this, m_cameraEngine, this); + if (m_viewfinderEngine == 0) { + m_error = KErrNoMemory; + qWarning("Failed to create viewfinder engine."); + return; + } + + // Connect signals + connect(m_inactivityTimer, SIGNAL(timeout()), this, SLOT(toStandByStatus())); + connect(this, SIGNAL(statusChanged(QCamera::Status)), + m_imageSession, SLOT(cameraStatusChanged(QCamera::Status))); + connect(this, SIGNAL(statusChanged(QCamera::Status)), + m_videoSession, SLOT(cameraStatusChanged(QCamera::Status))); + connect(m_videoSession, SIGNAL(stateChanged(S60VideoCaptureSession::TVideoCaptureState)), + this, SLOT(videoStateChanged(S60VideoCaptureSession::TVideoCaptureState))); + connect(m_imageSession, SIGNAL(advancedSettingChanged()), this, SLOT(advancedSettingsCreated())); + connect(this, SIGNAL(cameraReadyChanged(bool)), m_imageSession, SIGNAL(readyForCaptureChanged(bool))); + connect(m_viewfinderEngine, SIGNAL(error(int, const QString&)), this, SIGNAL(error(int,const QString&))); + connect(m_imageSession, SIGNAL(cameraError(int, const QString&)), this, SIGNAL(error(int, const QString&))); + connect(m_imageSession, SIGNAL(captureSizeChanged(const QSize&)), + m_viewfinderEngine, SLOT(handleContentAspectRatioChange(const QSize&))); + connect(m_videoSession, SIGNAL(captureSizeChanged(const QSize&)), + m_viewfinderEngine, SLOT(handleContentAspectRatioChange(const QSize&))); + + setCameraHandles(); +} + +S60CameraControl::~S60CameraControl() +{ + unloadCamera(); + + if (m_viewfinderEngine) { + delete m_viewfinderEngine; + m_viewfinderEngine = 0; + } + + // Make sure AdvancedSettings are destructed + m_imageSession->deleteAdvancedSettings(); + + if (m_cameraEngine) { + delete m_cameraEngine; + m_cameraEngine = 0; + } + + if (m_inactivityTimer) { + delete m_inactivityTimer; + m_inactivityTimer = 0; + } +} + +void S60CameraControl::setState(QCamera::State state) +{ + if (m_error) { // Most probably failure in contructor + setError(m_error, tr("Unexpected camera error.")); + return; + } + + if (m_requestedState == state) + return; + + if (m_inactivityTimer->isActive()) + m_inactivityTimer->stop(); + + // Save the target state + m_requestedState = state; + emit stateChanged(m_requestedState); + + switch (state) { + case QCamera::UnloadedState: // To UnloadedState - Release resources + switch (m_internalState) { + case QCamera::UnloadedStatus: + // Do nothing + break; + case QCamera::LoadingStatus: + case QCamera::StartingStatus: + // Release resources when ready (setting state handles this) + return; + case QCamera::LoadedStatus: + case QCamera::StandbyStatus: + // Unload + unloadCamera(); + break; + case QCamera::ActiveStatus: + // Stop and Unload + stopCamera(); + unloadCamera(); + break; + + default: + // Unrecognized internal state (Status) + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + break; + + case QCamera::LoadedState: // To LoadedState - Reserve resources OR Stop ViewFinder and Cancel Capture + switch (m_internalState) { + case QCamera::UnloadedStatus: + case QCamera::StandbyStatus: + // Load + loadCamera(); + break; + case QCamera::LoadingStatus: + // Discard, already moving to LoadedStatus + return; + case QCamera::StartingStatus: + // Stop when ready (setting state handles this) + return; + case QCamera::LoadedStatus: + m_inactivityTimer->start(KInactivityTimerTimeout); + break; + case QCamera::ActiveStatus: + // Stop + stopCamera(); + break; + + default: + // Unregocnized internal state (Status) + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + break; + + case QCamera::ActiveState: // To ActiveState - (Reserve Resources and) Start ViewFinder + switch (m_internalState) { + case QCamera::UnloadedStatus: + case QCamera::StandbyStatus: + // Load and Start (setting state handles starting) + loadCamera(); + break; + case QCamera::LoadingStatus: + // Start when loaded (setting state handles this) + break; + case QCamera::StartingStatus: + // Discard, already moving to ActiveStatus + return; + case QCamera::LoadedStatus: + // Start + startCamera(); + break; + case QCamera::ActiveStatus: + // Do nothing + break; + + default: + // Unregocnized internal state (Status) + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + break; + + default: + setError(KErrNotSupported, tr("Requested state is not supported.")); + return; + } +} + +QCamera::State S60CameraControl::state() const +{ + return m_requestedState; +} + +QCamera::Status S60CameraControl::status() const +{ + return m_internalState; +} + +QCamera::CaptureMode S60CameraControl::captureMode() const +{ + return m_captureMode; +} + +void S60CameraControl::setCaptureMode(QCamera::CaptureMode mode) +{ + if (m_error) { // Most probably failure in contructor + setError(m_error, tr("Unexpected camera error.")); + return; + } + + if (m_captureMode == mode) + return; + + // Setting CaptureMode Internally or Externally (Client) + if (!m_settingCaptureModeInternally) { + // Save the requested mode + m_requestedCaptureMode = mode; + + // CaptureMode change pending (backend busy), wait + if (m_changeCaptureModeWhenReady) + return; + } else { + m_changeCaptureModeWhenReady = false; // Reset + } + m_settingCaptureModeInternally = false; // Reset + + if (!isCaptureModeSupported(mode)) { + setError(KErrNotSupported, tr("Requested capture mode is not supported.")); + return; + } + + if (m_inactivityTimer->isActive()) + m_inactivityTimer->stop(); + + switch (m_internalState) { + case QCamera::UnloadedStatus: + case QCamera::LoadedStatus: + case QCamera::StandbyStatus: + switch (mode) { + case QCamera::CaptureStillImage: + m_videoSession->releaseVideoRecording(); + m_captureMode = QCamera::CaptureStillImage; + if (m_internalState == QCamera::LoadedStatus) + m_inactivityTimer->start(KInactivityTimerTimeout); + else if (m_internalState == QCamera::StandbyStatus) + loadCamera(); + break; + case QCamera::CaptureVideo: + m_imageSession->releaseImageCapture(); + m_captureMode = QCamera::CaptureVideo; + if (m_internalState == QCamera::LoadedStatus) { + // Revet InternalState as we need to wait for the video + // side initialization to complete + m_internalState = QCamera::LoadingStatus; + emit statusChanged(m_internalState); + int prepareSuccess = m_videoSession->initializeVideoRecording(); + setError(prepareSuccess, tr("Loading video capture failed.")); + } else if (m_internalState == QCamera::StandbyStatus) + loadCamera(); + break; + } + break; + case QCamera::LoadingStatus: + case QCamera::StartingStatus: + m_changeCaptureModeWhenReady = true; + return; + case QCamera::ActiveStatus: + // Stop, Change Mode and Start again + stopCamera(); + switch (mode) { + case QCamera::CaptureStillImage: + m_videoSession->releaseVideoRecording(); + m_captureMode = QCamera::CaptureStillImage; + startCamera(); + break; + case QCamera::CaptureVideo: + m_imageSession->releaseImageCapture(); + m_captureMode = QCamera::CaptureVideo; + // Revet InternalState as we need to wait for the video + // side initialization to complete + m_internalState = QCamera::LoadingStatus; + emit statusChanged(m_internalState); + int prepareSuccess = m_videoSession->initializeVideoRecording(); + setError(prepareSuccess, tr("Loading video recorder failed.")); + break; + } + break; + + default: + // Unregocnized internal state (Status) + setError(KErrNotSupported, tr("Requested capture mode is not supported.")); + break; + } + + emit captureModeChanged(mode); +} + +bool S60CameraControl::isCaptureModeSupported(QCamera::CaptureMode mode) const +{ + switch (mode) { + case QCamera::CaptureStillImage: + return true; + case QCamera::CaptureVideo: + return true; + + default: + return false; + } +} + +bool S60CameraControl::canChangeProperty(QCameraControl::PropertyChangeType changeType, QCamera::Status status) const +{ + Q_UNUSED(status); + + bool returnValue = false; + + switch (changeType) { + case QCameraControl::CaptureMode: + case QCameraControl::VideoEncodingSettings: + case QCameraControl::ImageEncodingSettings: + returnValue = true; + break; + + case QCameraControl::Viewfinder: + returnValue = false; + break; + + default: + // Safer to revert state before the unknown operation + returnValue = false; + break; + } + + return returnValue; +} + +void S60CameraControl::setVideoOutput(QObject *output, + S60CameraViewfinderEngine::ViewfinderOutputType type) +{ + if (!m_viewfinderEngine) { + setError(KErrGeneral, tr("Failed to set viewfinder")); + return; + } + + switch (type) { + case S60CameraViewfinderEngine::OutputTypeVideoWidget: + m_viewfinderEngine->setVideoWidgetControl(output); + break; + case S60CameraViewfinderEngine::OutputTypeRenderer: + m_viewfinderEngine->setVideoRendererControl(output); + break; + case S60CameraViewfinderEngine::OutputTypeVideoWindow: + m_viewfinderEngine->setVideoWindowControl(output); + break; + + default: + break; + } +} + +void S60CameraControl::releaseVideoOutput(const S60CameraViewfinderEngine::ViewfinderOutputType type) +{ + m_viewfinderEngine->releaseControl(type); +} + +void S60CameraControl::loadCamera() +{ + if (m_internalState < QCamera::LoadingStatus) { + m_internalState = QCamera::LoadingStatus; + emit statusChanged(m_internalState); + } else if (m_internalState == QCamera::LoadedStatus + || m_internalState >= QCamera::StartingStatus) { + // Nothing to load (already loaded) + return; + } + // Status = Loading or Standby + + m_cameraEngine->ReserveAndPowerOn(); + + // Completion notified in MceoCameraReady() +} + +void S60CameraControl::unloadCamera() +{ + if (m_internalState > QCamera::LoadingStatus) { + m_internalState = QCamera::LoadingStatus; + emit statusChanged(m_internalState); + } else if (m_internalState < QCamera::LoadingStatus) { + // Nothing to unload + return; + } + // Status = Loading + + if (m_inactivityTimer->isActive()) + m_inactivityTimer->stop(); + + m_cameraEngine->ReleaseAndPowerOff(); + + m_internalState = QCamera::UnloadedStatus; + emit statusChanged(m_internalState); +} + +void S60CameraControl::startCamera() +{ + if (m_internalState < QCamera::StartingStatus) { + m_internalState = QCamera::StartingStatus; + emit statusChanged(m_internalState); + } else if (m_internalState > QCamera::StartingStatus) { + // Nothing to start (already started) + return; + } + // Status = Starting + + if (m_inactivityTimer->isActive()) + m_inactivityTimer->stop(); + + if (m_viewfinderEngine) + m_viewfinderEngine->startViewfinder(); + else + setError(KErrGeneral, tr("Failed to start viewfinder.")); + + m_internalState = QCamera::ActiveStatus; + emit statusChanged(m_internalState); + + emit cameraReadyChanged(true); + +#ifdef Q_CC_NOKIAX86 // Emulator + MceoCameraReady(); // Signal that we are ready +#endif +} + +void S60CameraControl::stopCamera() +{ + if (m_internalState > QCamera::StartingStatus) { + m_internalState = QCamera::StartingStatus; + emit statusChanged(m_internalState); + } else if (m_internalState < QCamera::StartingStatus) { + // Nothing to stop + return; + } + // Status = Starting + + // Cancel ongoing operations if any + m_imageSession->cancelCapture(); + m_videoSession->stopRecording(); + + emit cameraReadyChanged(false); + if (m_viewfinderEngine) + m_viewfinderEngine->stopViewfinder(); + else + setError(KErrGeneral, tr("Failed to stop viewfinder.")); + + m_internalState = QCamera::LoadedStatus; + emit statusChanged(m_internalState); + + m_inactivityTimer->start(KInactivityTimerTimeout); +} + +void S60CameraControl::videoStateChanged(const S60VideoCaptureSession::TVideoCaptureState state) +{ + // Save video state + m_videoCaptureState = state; + + if (m_rotateCameraWhenReady) { + if (m_videoCaptureState != S60VideoCaptureSession::ERecording && + m_videoCaptureState != S60VideoCaptureSession::EPaused) + resetCameraOrientation(); + } + + // If video recording was stopped, video state reverts back to + // Initializing. In that case revert also Camera status to notify that + // video initialization needs to be completed. + if (state == S60VideoCaptureSession::EInitializing) { + if (m_internalState > QCamera::LoadingStatus) { + m_internalState = QCamera::LoadingStatus; + emit statusChanged(m_internalState); + } + + // Handle video initialization completion + } else if (state == S60VideoCaptureSession::EInitialized) { + + // Make sure state is not downgraded + if (m_internalState == QCamera::LoadedStatus + || m_internalState == QCamera::ActiveStatus) { + // Do nothing (already in target state) + } else if (m_internalState == QCamera::StartingStatus) { + m_internalState = QCamera::ActiveStatus; + emit statusChanged(m_internalState); + } else { + m_internalState = QCamera::LoadedStatus; + emit statusChanged(m_internalState); + } + + switch (m_requestedState) { + case QCamera::UnloadedState: + stopCamera(); + unloadCamera(); + if (m_changeCaptureModeWhenReady) { + m_settingCaptureModeInternally = true; + setCaptureMode(m_requestedCaptureMode); + } + break; + case QCamera::LoadedState: + stopCamera(); + if (m_changeCaptureModeWhenReady) { + m_settingCaptureModeInternally = true; + setCaptureMode(m_requestedCaptureMode); + } + m_inactivityTimer->start(KInactivityTimerTimeout); + break; + case QCamera::ActiveState: + if (m_changeCaptureModeWhenReady) { + m_settingCaptureModeInternally = true; + setCaptureMode(m_requestedCaptureMode); + } + startCamera(); + break; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + } +} + +void S60CameraControl::imageCaptured(const int imageId, const QImage& preview) +{ + Q_UNUSED(imageId); + Q_UNUSED(preview); + + // Unsubscribe the readyForCaptureChanged notification + disconnect(m_imageSession, SIGNAL(imageCaptured(const int, const QImage&)), + this, SLOT(imageCaptured(const int, const QImage&))); + + if (m_rotateCameraWhenReady) + resetCameraOrientation(); +} + +void S60CameraControl::advancedSettingsCreated() +{ + m_advancedSettings = m_imageSession->advancedSettings(); + + if (m_advancedSettings) + connect(m_advancedSettings, SIGNAL(error(int, const QString&)), this, SIGNAL(error(int, const QString&))); +} + +void S60CameraControl::MceoCameraReady() +{ + // Rotate camera if requested + if (m_rotateCameraWhenReady) { + resetCameraOrientation(); + return; + } + + if (m_internalState != QCamera::LoadedStatus) { + + switch (m_requestedState) { + case QCamera::UnloadedState: + m_internalState = QCamera::LoadedStatus; + emit statusChanged(QCamera::LoadedStatus); + + stopCamera(); + unloadCamera(); + + if (m_changeCaptureModeWhenReady) { + m_settingCaptureModeInternally = true; + setCaptureMode(m_requestedCaptureMode); + } + break; + + case QCamera::LoadedState: + if (m_captureMode == QCamera::CaptureVideo) { + int prepareSuccess = m_videoSession->initializeVideoRecording(); + setError(prepareSuccess, tr("Loading video capture failed.")); + + // State change signalled when reservation is complete (in videoStateChanged()) + return; + } + m_internalState = QCamera::LoadedStatus; + emit statusChanged(QCamera::LoadedStatus); + + if (m_changeCaptureModeWhenReady) { + setCaptureMode(m_requestedCaptureMode); + m_changeCaptureModeWhenReady = false; // Reset + } + + if (m_requestedState == QCamera::LoadedStatus && + m_internalState == QCamera::LoadedStatus) + m_inactivityTimer->start(KInactivityTimerTimeout); + break; + + case QCamera::ActiveState: + if (m_captureMode == QCamera::CaptureVideo) { + int prepareSuccess = m_videoSession->initializeVideoRecording(); + setError(prepareSuccess, tr("Loading video capture failed.")); + + // State change signalled when reservation is complete (in videoStateChanged()) + return; + } + + m_internalState = QCamera::LoadedStatus; + emit statusChanged(QCamera::LoadedStatus); + + if (m_changeCaptureModeWhenReady) { + setCaptureMode(m_requestedCaptureMode); + m_changeCaptureModeWhenReady = false; // Reset + } + startCamera(); + break; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + } +} + +void S60CameraControl::MceoHandleError(TCameraEngineError aErrorType, TInt aError) +{ + Q_UNUSED(aErrorType); + + if (aError == KErrAccessDenied) { + setError(KErrGeneral, tr("Access to camera device was rejected.")); + } else if (aError == KErrHardwareNotAvailable) { + setError(aError, tr("Camera resources were lost.")); + toStandByStatus(); + } + else + setError(aError, tr("Unexpected camera error.")); +} + +void S60CameraControl::setError(const TInt error, const QString &description) +{ + if (error == KErrNone) + return; + + m_error = error; + QCamera::Error cameraError = fromSymbianErrorToQtMultimediaError(m_error); + + emit this->error(int(cameraError), description); + + // Reset everything, if other than not supported error or resource loss + if (error != KErrNotSupported && error != KErrHardwareNotAvailable) + resetCamera(true); // Try to recover from error + else + m_error = KErrNone; // Reset error +} + +QCamera::Error S60CameraControl::fromSymbianErrorToQtMultimediaError(int aError) +{ + switch(aError) { + case KErrNone: + return QCamera::NoError; // No errors have occurred + + case KErrNotSupported: + return QCamera::NotSupportedFeatureError; // The feature is not supported + case KErrNotFound: + case KErrBadHandle: + return QCamera::ServiceMissingError; // No camera service available + case KErrArgument: + case KErrNotReady: + return QCamera::InvalidRequestError; // Invalid parameter or state + + default: + return QCamera::CameraError; // An error has occurred (i.e. General Error) + } +} + +// For S60CameraVideoDeviceControl +int S60CameraControl::deviceCount() +{ +#ifdef Q_CC_NOKIAX86 // Emulator + return 1; +#endif + + return CCameraEngine::CamerasAvailable(); +} + +int S60CameraControl::defaultDevice() const +{ + return KDefaultCameraDevice; +} + +int S60CameraControl::selectedDevice() const +{ + return m_deviceIndex; +} + +void S60CameraControl::setSelectedDevice(const int index) +{ + if (m_deviceIndex != index) { + if (index >= 0 && index < deviceCount()) { + m_deviceIndex = index; + resetCamera(); + } else { + setError(KErrNotSupported, tr("Requested camera is not available.")); + } + } +} + +QString S60CameraControl::name(const int index) +{ + QString cameraName; + switch (index) { + case 0: + cameraName = tr("Primary camera"); + break; + case 1: + cameraName = tr("Secondary camera"); + break; + case 2: + cameraName = tr("Tertiary camera"); + break; + + default: + cameraName = tr("Unidentified Camera"); + break; + } + + return cameraName; +} + +QString S60CameraControl::description(const int index) +{ + QString cameraDesc; + switch (index) { + case 0: + cameraDesc = tr("Device primary camera"); + break; + case 1: + cameraDesc = tr("Device secondary camera"); + break; + case 2: + cameraDesc = tr("Device tertiary camera"); + break; + + default: + cameraDesc = tr("Unidentified Camera"); + break; + } + + return cameraDesc; +} + +void S60CameraControl::resetCamera(bool errorHandling) +{ + if (m_inactivityTimer->isActive()) + m_inactivityTimer->stop(); + + // Cancel ongoing activity + m_imageSession->cancelCapture(); + m_videoSession->stopRecording(false); // Don't re-initialize video + + // Advanced settings must be destructed before the camera + m_imageSession->deleteAdvancedSettings(); + + // Release resources + stopCamera(); + unloadCamera(); + + disconnect(m_viewfinderEngine, SIGNAL(error(int, const QString&)), this, SIGNAL(error(int,const QString&))); + if (m_viewfinderEngine) { + delete m_viewfinderEngine; + m_viewfinderEngine = 0; + } + + if (m_cameraEngine) { + delete m_cameraEngine; + m_cameraEngine = 0; + } + + TRAPD(err, m_cameraEngine = CCameraEngine::NewL(m_deviceIndex, 0, this)); + if (err) { + m_cameraEngine = 0; + if (errorHandling) { + qWarning("Failed to recover from error."); + if (err == KErrPermissionDenied) + emit error(int(QCamera::ServiceMissingError), tr("Recovering from error failed. Possibly missing capabilities.")); + else + emit error(int(QCamera::CameraError), tr("Recovering from error failed.")); + } else { + if (err == KErrPermissionDenied) + setError(err, tr("Camera device creation failed. Possibly missing capabilities.")); + else + setError(err, tr("Camera device creation failed.")); + } + return; + } + + // Notify list of available camera devices has been updated + emit devicesChanged(); + + m_viewfinderEngine = new S60CameraViewfinderEngine(this, m_cameraEngine, this); + if (m_viewfinderEngine == 0) + setError(KErrNoMemory, tr("Viewfinder device creation failed.")); + connect(m_viewfinderEngine, SIGNAL(error(int, const QString&)), this, SIGNAL(error(int,const QString&))); + + setCameraHandles(); + + // Reset state + //setState(QCamera::UnloadedState); + if (m_internalState != QCamera::UnloadedStatus) { + m_internalState = QCamera::UnloadedStatus; + emit statusChanged(m_internalState); + } + if (m_requestedState != QCamera::UnloadedState) { + m_requestedState = QCamera::UnloadedState; + emit stateChanged(m_requestedState); + } + + // Reset error + m_error = KErrNone; +} + +/* + * Reset everything else than viewfinder engine and errors. + */ +void S60CameraControl::resetCameraOrientation() +{ + // If camera has not been created, it will be created automatically to correct orientation + if (!m_cameraEngine) + return; + + // Check Image/VideoCapture allow rotation + if ((!m_cameraEngine->IsCameraReady() && m_internalState != QCamera::UnloadedStatus) || + m_videoCaptureState == S60VideoCaptureSession::ERecording || + m_videoCaptureState == S60VideoCaptureSession::EPaused) { + + // If image capture is ongoing, request notification about the + // completion (imageCaptured() is used because that comes asynchronously + // after the image is captured) + // Obs! If preview creation is changed to be synchnonously done during + // the image capture this implementation needs to be changed) + if (m_videoCaptureState != S60VideoCaptureSession::ERecording && + m_videoCaptureState != S60VideoCaptureSession::EPaused && + m_internalState == QCamera::ActiveStatus) + connect(m_imageSession, SIGNAL(imageCaptured(const int, const QImage&)), + this, SLOT(imageCaptured(const int, const QImage&))); + + m_rotateCameraWhenReady = true; + return; + } + + m_rotateCameraWhenReady = false; // Reset + + QCamera::State originalState = m_requestedState; + + // Cancel ongoing activity + m_imageSession->cancelCapture(); + m_videoSession->stopRecording(false); // Don't re-initialize video + + // Advanced settings must be destructed before the camera + m_imageSession->deleteAdvancedSettings(); + + // Release resources + stopCamera(); + unloadCamera(); + + // Unset CameraEngine to ViewfinderEngine + m_viewfinderEngine->setNewCameraEngine(0); + if (m_cameraEngine) { + delete m_cameraEngine; + m_cameraEngine = 0; + } + + TRAPD(err, m_cameraEngine = CCameraEngine::NewL(m_deviceIndex, 0, this)); + if (err) { + setError(err, tr("Camera device creation failed.")); + return; + } + // Reset CameraEngine to ViewfinderEngine + m_viewfinderEngine->setNewCameraEngine(m_cameraEngine); + + // Notify list of available camera devices has been updated + emit devicesChanged(); + + setCameraHandles(); + + // Reset state + if (m_internalState != QCamera::UnloadedStatus) { + m_internalState = QCamera::UnloadedStatus; + emit statusChanged(m_internalState); + } + if (m_requestedState != QCamera::UnloadedState) { + m_requestedState = QCamera::UnloadedState; + emit stateChanged(m_requestedState); + } + + setState(originalState); +} + +void S60CameraControl::setCameraHandles() +{ + m_imageSession->setCurrentDevice(m_deviceIndex); + m_imageSession->setCameraHandle(m_cameraEngine); + m_cameraEngine->SetImageCaptureObserver(m_imageSession); + m_videoSession->setCameraHandle(m_cameraEngine); +} + +void S60CameraControl::toStandByStatus() +{ + // Cancel ongoing operations if any + m_imageSession->cancelCapture(); + m_videoSession->stopRecording(false); // Don't re-initialize video + + emit cameraReadyChanged(false); + if (m_viewfinderEngine) + m_viewfinderEngine->stopViewfinder(); + else + setError(KErrGeneral, tr("Failed to stop viewfinder.")); + + m_cameraEngine->ReleaseAndPowerOff(); + + m_internalState = QCamera::StandbyStatus; + emit statusChanged(m_internalState); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameracontrol.h b/src/plugins/symbian/ecam/s60cameracontrol.h new file mode 100644 index 000000000..95c31fb34 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameracontrol.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERACONTROL_H +#define S60CAMERACONTROL_H + +#include <qcameracontrol.h> + +#include "s60cameraengineobserver.h" // MCameraEngineObserver +#include "s60videocapturesession.h" // TVideoCaptureState +#include "s60cameraviewfinderengine.h" // ViewfinderOutputType + +#include <e32base.h> +#include <fbs.h> + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; +class S60VideoCaptureSession; +class S60CameraSettings; +class CCameraEngine; +class S60CameraViewfinderEngine; +class QTimer; + +/* + * Control for controlling camera base operations (e.g. start/stop and capture + * mode). + */ +class S60CameraControl : public QCameraControl, public MCameraEngineObserver +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60CameraControl(QObject *parent = 0); + S60CameraControl(S60VideoCaptureSession *videosession, + S60ImageCaptureSession *imagesession, + QObject *parent = 0); + ~S60CameraControl(); + +public: // QCameraControl + + // State + QCamera::State state() const; + void setState(QCamera::State state); + + // Status + QCamera::Status status() const; + + // Capture Mode + QCamera::CaptureMode captureMode() const; + void setCaptureMode(QCamera::CaptureMode); + bool isCaptureModeSupported(QCamera::CaptureMode mode) const; + + // Property Setting + bool canChangeProperty(QCameraControl::PropertyChangeType changeType, QCamera::Status status) const; + +/* +Q_SIGNALS: + void stateChanged(QCamera::State); + void statusChanged(QCamera::Status); + void error(int error, const QString &errorString); + void captureModeChanged(QCamera::CaptureMode); +*/ + +public: // Internal + + void setError(const TInt error, const QString &description); + void resetCameraOrientation(); + + // To provide QVideoDeviceControl info + static int deviceCount(); + static QString name(const int index); + static QString description(const int index); + int defaultDevice() const; + int selectedDevice() const; + void setSelectedDevice(const int index); + + void setVideoOutput(QObject *output, + const S60CameraViewfinderEngine::ViewfinderOutputType type); + void releaseVideoOutput(const S60CameraViewfinderEngine::ViewfinderOutputType type); + +private slots: // Internal Slots + + void videoStateChanged(const S60VideoCaptureSession::TVideoCaptureState state); + // Needed to detect image capture completion when trying to rotate the camera + void imageCaptured(const int imageId, const QImage& preview); + /* + * This method moves the camera to the StandBy status: + * - If camera access was lost + * - If camera has been inactive in LoadedStatus for a long time + */ + void toStandByStatus(); + void advancedSettingsCreated(); + +protected: // MCameraEngineObserver + + void MceoCameraReady(); + void MceoHandleError(TCameraEngineError aErrorType, TInt aError); + +private: // Internal + + QCamera::Error fromSymbianErrorToQtMultimediaError(int aError); + + void loadCamera(); + void unloadCamera(); + void startCamera(); + void stopCamera(); + + void resetCamera(bool errorHandling = false); + void setCameraHandles(); + +signals: // Internal Signals + + void cameraReadyChanged(bool); + void devicesChanged(); + +private: // Data + + CCameraEngine *m_cameraEngine; + S60CameraViewfinderEngine *m_viewfinderEngine; + S60ImageCaptureSession *m_imageSession; + S60VideoCaptureSession *m_videoSession; + S60CameraSettings *m_advancedSettings; + QObject *m_videoOutput; + QTimer *m_inactivityTimer; + QCamera::CaptureMode m_captureMode; + QCamera::CaptureMode m_requestedCaptureMode; + bool m_settingCaptureModeInternally; + QCamera::Status m_internalState; + QCamera::State m_requestedState; + int m_deviceIndex; + mutable int m_error; + bool m_changeCaptureModeWhenReady; + bool m_rotateCameraWhenReady; + S60VideoCaptureSession::TVideoCaptureState m_videoCaptureState; +}; + +#endif // S60CAMERACONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraengine.cpp b/src/plugins/symbian/ecam/s60cameraengine.cpp new file mode 100644 index 000000000..dbd5fb33e --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraengine.cpp @@ -0,0 +1,824 @@ +/**************************************************************************** + ** + ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the Qt Mobility Components. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** No Commercial Usage + ** This file contains pre-release code and may not be distributed. + ** You may use this file in accordance with the terms and conditions + ** contained in the Technology Preview License Agreement accompanying + ** this package. + ** + ** 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 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. + ** + ** If you have questions regarding the use of this file, please contact + ** Nokia at qt-info@nokia.com. + ** + ** + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include "s60cameraengine.h" +#include "s60cameraengineobserver.h" +#include "s60cameraconstants.h" +#include <QtCore/qglobal.h> +#include <fbs.h> // CFbsBitmap +#ifdef ECAM_PREVIEW_API + #include <platform/ecam/camerasnapshot.h> +#endif // ECAM_PREVIEW_API + +CCameraEngine::CCameraEngine() +{ +} + +CCameraEngine::CCameraEngine(TInt aCameraHandle, + TInt aPriority, + MCameraEngineObserver* aObserver) : + // CBase initializes member variables to NULL + iObserver(aObserver), + iCameraIndex(aCameraHandle), + iPriority(aPriority), + iEngineState(EEngineNotReady), + iCaptureResolution(TSize(0,0)), + iNew2LImplementation(false), + iLatestImageBufferIndex(1) // Thus we start from index 0 +{ + // Observer is mandatory + ASSERT(aObserver != NULL); +} + +CCameraEngine::~CCameraEngine() +{ + StopViewFinder(); + ReleaseViewFinderBuffer(); // Releases iViewFinderBuffer + ReleaseImageBuffer(); // Releases iImageBuffer + iImageBitmap + + iAdvancedSettingsObserver = NULL; + iImageCaptureObserver = NULL; + iViewfinderObserver = NULL; + +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + delete iAutoFocus; +#endif // S60_CAM_AUTOFOCUS_SUPPORT + + if (iCamera) { + iCamera->Release(); + delete iCamera; + iCamera = NULL; + } +} + +TInt CCameraEngine::CamerasAvailable() +{ + return CCamera::CamerasAvailable(); +} + +CCameraEngine* CCameraEngine::NewL(TInt aCameraHandle, + TInt aPriority, + MCameraEngineObserver* aObserver) +{ + CCameraEngine* self = new (ELeave) CCameraEngine(aCameraHandle, aPriority, aObserver); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; +} + +void CCameraEngine::ConstructL() +{ + if (!CCamera::CamerasAvailable()) + User::Leave(KErrHardwareNotAvailable); + +#ifndef Q_CC_NOKIAX86 // Not Emulator + TInt err(KErrNone); +#else // Emulator + TInt err(KErrNotFound); +#endif // !(Q_CC_NOKIAX86) + +#ifdef S60_31_PLATFORM + // Construct CCamera object for S60 3.1 (NewL) + iNew2LImplementation = false; + TRAP(err, iCamera = CCamera::NewL(*this, iCameraIndex)); + if (err) + User::Leave(err); +#else // For S60 3.2 onwards - use this constructor (New2L) + iNew2LImplementation = true; + TRAP(err, iCamera = CCamera::New2L(*this, iCameraIndex, iPriority)); + if (err) + User::Leave(err); +#endif // S60_31_PLATFORM + +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + // Might not be supported for secondary camera, discard errors + TRAP(err, iAutoFocus = CCamAutoFocus::NewL(iCamera)); +#endif // S60_CAM_AUTOFOCUS_SUPPORT + + if (iCamera == NULL) + User::Leave(KErrNoMemory); + + iCamera->CameraInfo(iCameraInfo); +} + +void CCameraEngine::SetAdvancedObserver(MAdvancedSettingsObserver* aAdvancedSettingsObserver) +{ + iAdvancedSettingsObserver = aAdvancedSettingsObserver; +} + +void CCameraEngine::SetImageCaptureObserver(MCameraEngineImageCaptureObserver* aImageCaptureObserver) +{ + iImageCaptureObserver = aImageCaptureObserver; +} + +void CCameraEngine::SetViewfinderObserver(MCameraViewfinderObserver* aViewfinderObserver) +{ + iViewfinderObserver = aViewfinderObserver; +} + +void CCameraEngine::ReserveAndPowerOn() +{ + if (!iCamera || iEngineState > EEngineNotReady) { + iObserver->MceoHandleError(EErrReserve, KErrNotReady); + return; + } + + iCamera->Reserve(); +} + +void CCameraEngine::ReleaseAndPowerOff() +{ + if (iEngineState >= EEngineIdle) { + CancelCapture(); + StopViewFinder(); + FocusCancel(); + iCamera->PowerOff(); + iCamera->Release(); + } + iEngineState = EEngineNotReady; +} + +void CCameraEngine::StartViewFinderL(TSize& aSize) +{ + if (iEngineState < EEngineIdle) + User::Leave(KErrNotReady); + + if (0 == (iCameraInfo.iOptionsSupported & TCameraInfo::EViewFinderBitmapsSupported)) + User::Leave(KErrNotSupported); + + if (!iCamera->ViewFinderActive()) { + if (iCameraIndex != 0) + iCamera->SetViewFinderMirrorL(true); + iCamera->StartViewFinderBitmapsL(aSize); + } +} + +void CCameraEngine::StopViewFinder() +{ + if (iCamera && iCamera->ViewFinderActive()) + iCamera->StopViewFinder(); +} + +void CCameraEngine::StartDirectViewFinderL(RWsSession& aSession, + CWsScreenDevice& aScreenDevice, + RWindowBase& aWindow, + TRect& aScreenRect, + TRect& aClipRect) +{ + if (iEngineState < EEngineIdle) + User::Leave(KErrNotReady); + + if (0 == (iCameraInfo.iOptionsSupported & TCameraInfo::EViewFinderDirectSupported)) + User::Leave(KErrNotSupported); + + if (!iCamera->ViewFinderActive()) { + // Viewfinder extent needs to be clipped according to the clip rect. + // This is because the native camera framework does not support + // clipping and starting viewfinder with bigger than the display(S60 + // 5.0 and older)/window(Symbian^3 and later) would cause viewfinder + // starting to fail entirely. This causes shrinking effect in some + // cases, but is better than not having the viewfinder at all. + if (aScreenRect.Intersects(aClipRect)) + aScreenRect.Intersection(aClipRect); + + if (iCameraIndex != 0) + iCamera->SetViewFinderMirrorL(true); + if (aScreenRect.Width() > 0 && aScreenRect.Height() > 0) { + iCamera->StartViewFinderDirectL(aSession, aScreenDevice, aWindow, aScreenRect); + } else { + if (iObserver) + iObserver->MceoHandleError(EErrViewFinderReady, KErrArgument); + } + } +} + +void CCameraEngine::PrepareL(TSize& aCaptureSize, CCamera::TFormat aFormat) +{ + iImageCaptureFormat = aFormat; + + TInt closestVar = KMaxTInt, selected = 0; + TSize size; + + // Scan through supported capture sizes and select the closest match + for (TInt index = 0; index < iCameraInfo.iNumImageSizesSupported; index++) { + + iCamera->EnumerateCaptureSizes(size, index, aFormat); + if (size == aCaptureSize) { + selected = index; + break; + } + + TSize varSz = size - aCaptureSize; + TInt variation = varSz.iWidth * varSz.iHeight; + if (variation < closestVar) { + closestVar = variation; + selected = index; + } + } + + iCamera->EnumerateCaptureSizes(aCaptureSize, selected, aFormat); + iCaptureResolution = aCaptureSize; + iCamera->PrepareImageCaptureL(aFormat, selected); +} + +void CCameraEngine::CaptureL() +{ + if (iEngineState < EEngineIdle) + User::Leave(KErrNotReady); + + iCamera->CaptureImage(); + iEngineState = EEngineCapturing; +} + +void CCameraEngine::CancelCapture() +{ + if (iEngineState == EEngineCapturing) { + iCamera->CancelCaptureImage(); + iEngineState = EEngineIdle; + } +} + +void CCameraEngine::HandleEvent(const TECAMEvent &aEvent) +{ + if (aEvent.iEventType == KUidECamEventReserveComplete) { + ReserveComplete(aEvent.iErrorCode); + return; + } + + if (aEvent.iEventType == KUidECamEventPowerOnComplete) { + PowerOnComplete(aEvent.iErrorCode); + return; + } + + if (aEvent.iEventType == KUidECamEventCameraNoLongerReserved) { + // All camera related operations need to be stopped + iObserver->MceoHandleError(EErrReserve, KErrHardwareNotAvailable); + return; + } + +#ifdef ECAM_PREVIEW_API + if (aEvent.iEventType == KUidECamEventCameraSnapshot) { + HandlePreview(); + return; + } +#endif // ECAM_PREVIEW_API + +#if !defined(Q_CC_NOKIAX86) // Not Emulator + // Other events; Exposure, Zoom, etc. (See ecamadvancedsettings.h) + if (iAdvancedSettingsObserver) + iAdvancedSettingsObserver->HandleAdvancedEvent(aEvent); + + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleOtherEvent(aEvent); +#endif // !Q_CC_NOKIAX86 +} + +void CCameraEngine::ReserveComplete(TInt aError) +{ + if (aError == KErrNone) { + iCamera->PowerOn(); +#ifdef S60_31_PLATFORM + } else if (aError == KErrAlreadyExists) { // Known Issue on some S60 3.1 devices + User::After(500000); // Wait for 0,5 second and try again + iCamera->Reserve(); +#endif // S60_31_PLATFORM + } else { + iObserver->MceoHandleError(EErrReserve, aError); + } +} + +void CCameraEngine::PowerOnComplete(TInt aError) +{ + if (aError) { + iObserver->MceoHandleError(EErrPowerOn, aError); + iEngineState = EEngineNotReady; + return; + } + + // Init AutoFocus +#ifndef Q_CC_NOKIAX86 // Not Emulator +#ifdef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.1 + if( iAutoFocus ) { + TRAPD(afErr, iAutoFocus->InitL( *this )); + if (afErr) { + delete iAutoFocus; + iAutoFocus = 0; + } + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT +#endif // !Q_CC_NOKIAX86 + + iEngineState = EEngineIdle; + iObserver->MceoCameraReady(); +} + +#ifdef ECAM_PREVIEW_API +/** + * This method creates the CCameraPreview object and requests the previews to + * be provided during the image or video capture + */ +void CCameraEngine::EnablePreviewProvider(MCameraPreviewObserver *aPreviewObserver) +{ + // Delete old one if exists + if (iCameraSnapshot) + delete iCameraSnapshot; + + iPreviewObserver = aPreviewObserver; + + TInt error = KErrNone; + + if (iCamera) { + TRAP(error, iCameraSnapshot = CCamera::CCameraSnapshot::NewL(*iCamera)); + if (error) { + if (iObserver) + iObserver->MceoHandleError(EErrPreview, error); + return; + } + + TRAP(error, iCameraSnapshot->PrepareSnapshotL(KDefaultFormatPreview, SelectPreviewResolution(), EFalse)); + if (error) { + if (iObserver) + iObserver->MceoHandleError(EErrPreview, error); + return; + } + + iCameraSnapshot->StartSnapshot(); + } else { + if (iObserver) + iObserver->MceoHandleError(EErrPreview, KErrNotReady); + } +} + +/** + * This method disables and destroys the CCameraPreview object. Thus previews + * will not be provided during the image or video capture. + */ +void CCameraEngine::DisablePreviewProvider() +{ + if (!iCameraSnapshot) + return; + + iCameraSnapshot->StopSnapshot(); + + delete iCameraSnapshot; + iCameraSnapshot = 0; + + iPreviewObserver = 0; +} +#endif // ECAM_PREVIEW_API + +/* + * MCameraObserver2: + * New viewfinder frame available + */ +void CCameraEngine::ViewFinderReady(MCameraBuffer &aCameraBuffer, TInt aError) +{ + iViewFinderBuffer = &aCameraBuffer; + + if (aError == KErrNone) { + if (iViewfinderObserver) { + TRAPD(err, iViewfinderObserver->MceoViewFinderFrameReady(aCameraBuffer.BitmapL(0))); + if (err) + iObserver->MceoHandleError(EErrViewFinderReady, err); + } else { + iObserver->MceoHandleError(EErrViewFinderReady, KErrNotReady); + } + } + else { + iObserver->MceoHandleError(EErrViewFinderReady, aError); + } +} + +/* + * MCameraObserver: + * New viewfinder frame available + */ +void CCameraEngine::ViewFinderFrameReady(CFbsBitmap& aFrame) +{ + if (iViewfinderObserver) + iViewfinderObserver->MceoViewFinderFrameReady(aFrame); + else + iObserver->MceoHandleError(EErrViewFinderReady, KErrNotReady); +} + +void CCameraEngine::ReleaseViewFinderBuffer() +{ + if (iNew2LImplementation) { // NewL Implementation does not use MCameraBuffer + if (iViewFinderBuffer) { + iViewFinderBuffer->Release(); + iViewFinderBuffer = NULL; + } + } +} + +void CCameraEngine::ReleaseImageBuffer() +{ + // Reset Bitmap + if (iLatestImageBufferIndex == 1 || iImageBitmap2 == NULL) { + if (iImageBitmap1) { + if (!iNew2LImplementation) { // NewL - Ownership transferred + iImageBitmap1->Reset(); // Reset/Delete Bitmap + delete iImageBitmap1; + } + iImageBitmap1 = NULL; + } + } else { + if (iImageBitmap2) { + if (!iNew2LImplementation) { // NewL - Ownership transferred + iImageBitmap2->Reset(); // Reset/Delete Bitmap + delete iImageBitmap2; + } + iImageBitmap2 = NULL; + } + } + + // Reset Data pointers + if (iLatestImageBufferIndex == 1 || iImageData2 == NULL) { + if (!iNew2LImplementation) // NewL - Ownership transfers with buffer + delete iImageData1; + iImageData1 = NULL; + } else { + if (!iNew2LImplementation) // NewL - Ownership transfers with buffer + delete iImageData2; + iImageData2 = NULL; + } + + // Reset ImageBuffer - New2L Implementation only + if (iLatestImageBufferIndex == 1 || iImageBuffer2 == NULL) { + if (iImageBuffer1) { + iImageBuffer1->Release(); + iImageBuffer1 = NULL; + } + } else { + if (iImageBuffer2) { + iImageBuffer2->Release(); + iImageBuffer2 = NULL; + } + } +} + +/* + * MCameraObserver2 + * Captured image is ready (New2L version) + */ +void CCameraEngine::ImageBufferReady(MCameraBuffer &aCameraBuffer, TInt aError) +{ + // Use the buffer that is available + if (!iImageBuffer1) { + iLatestImageBufferIndex = 0; + iImageBuffer1 = &aCameraBuffer; + } else { + iLatestImageBufferIndex = 1; + iImageBuffer2 = &aCameraBuffer; + } + + bool isBitmap = true; + TInt err = KErrNone; + + switch (iImageCaptureFormat) { + case CCamera::EFormatFbsBitmapColor4K: + case CCamera::EFormatFbsBitmapColor64K: + case CCamera::EFormatFbsBitmapColor16M: + case CCamera::EFormatFbsBitmapColor16MU: + if (iLatestImageBufferIndex == 0) { + TRAP(err, iImageBitmap1 = &iImageBuffer1->BitmapL(0)); + if (err) { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, err); + } + } else { + TRAP(err, iImageBitmap2 = &iImageBuffer2->BitmapL(0)); + if (err) { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, err); + } + } + isBitmap = true; + break; + case CCamera::EFormatExif: + if (iLatestImageBufferIndex == 0) { + TRAP(err, iImageData1 = iImageBuffer1->DataL(0)); + if (err) { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, err); + } + } else { + TRAP(err, iImageData2 = iImageBuffer2->DataL(0)); + if (err) { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, err); + } + } + isBitmap = false; + break; + + default: + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, KErrNotSupported); + return; + } + + // Handle captured image + HandleImageReady(aError, isBitmap); +} + +/* + * MCameraObserver + * Captured image is ready (NewL version) + */ +void CCameraEngine::ImageReady(CFbsBitmap* aBitmap, HBufC8* aData, TInt aError) +{ + bool isBitmap = true; + + // Toggle between the 2 buffers + if (iLatestImageBufferIndex == 1) { + iLatestImageBufferIndex = 0; + } else { + iLatestImageBufferIndex = 1; + } + + switch (iImageCaptureFormat) { + case CCamera::EFormatFbsBitmapColor4K: + case CCamera::EFormatFbsBitmapColor64K: + case CCamera::EFormatFbsBitmapColor16M: + case CCamera::EFormatFbsBitmapColor16MU: + if (iLatestImageBufferIndex == 0) + iImageBitmap1 = aBitmap; + else + iImageBitmap2 = aBitmap; + isBitmap = true; + break; + case CCamera::EFormatExif: + if (iLatestImageBufferIndex == 0) + iImageData1 = aData; + else + iImageData2 = aData; + isBitmap = false; + break; + + default: + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, KErrNotSupported); + return; + } + + // Handle captured image + HandleImageReady(aError, isBitmap); +} + +void CCameraEngine::HandleImageReady(const TInt aError, const bool isBitmap) +{ + iEngineState = EEngineIdle; + + if (aError == KErrNone) { + if (isBitmap) + if (iImageCaptureObserver) { + if (iLatestImageBufferIndex == 0) + iImageCaptureObserver->MceoCapturedBitmapReady(iImageBitmap1); + else + iImageCaptureObserver->MceoCapturedBitmapReady(iImageBitmap2); + } + else + ReleaseImageBuffer(); + else { + if (iImageCaptureObserver) { + if (iLatestImageBufferIndex == 0) + iImageCaptureObserver->MceoCapturedDataReady(iImageData1); + else + iImageCaptureObserver->MceoCapturedDataReady(iImageData2); + } + else + ReleaseImageBuffer(); + } + } else { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, aError); + } +} + +#ifdef ECAM_PREVIEW_API +void CCameraEngine::HandlePreview() +{ + if (!iCameraSnapshot) { + if (iObserver) + iObserver->MceoHandleError(EErrPreview, KErrGeneral); + return; + } + + RArray<TInt> previewIndices; + CleanupClosePushL(previewIndices); + + MCameraBuffer &newPreview = iCameraSnapshot->SnapshotDataL(previewIndices); + + for (TInt i = 0; i < previewIndices.Count(); ++i) + iPreviewObserver->MceoPreviewReady(newPreview.BitmapL(0)); + + CleanupStack::PopAndDestroy(); // RArray<TInt> previewIndices +} + +TSize CCameraEngine::SelectPreviewResolution() +{ + TSize currentResolution(iCaptureResolution); + + TSize previewResolution(0, 0); + if (currentResolution == TSize(4000,2248) || + currentResolution == TSize(3264,1832) || + currentResolution == TSize(2592,1456) || + currentResolution == TSize(1920,1080) || + currentResolution == TSize(1280,720)) { + previewResolution = KDefaultSizePreview_Wide; + } else if (currentResolution == TSize(352,288) || + currentResolution == TSize(176,144)) { + previewResolution = KDefaultSizePreview_CIF; + } else if (currentResolution == TSize(720,576)) { + previewResolution = KDefaultSizePreview_PAL; + } else if (currentResolution == TSize(720,480)) { + previewResolution = KDefaultSizePreview_NTSC; + } else { + previewResolution = KDefaultSizePreview_Normal; + } + + return previewResolution; +} +#endif // ECAM_PREVIEW_API + +//============================================================================= +// S60 3.1 - AutoFocus support (Other platforms, see S60CameraSettings class) +//============================================================================= + +void CCameraEngine::InitComplete(TInt aError) +{ + if (aError) { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrAutoFocusInit, aError); + } +} + +void CCameraEngine::OptimisedFocusComplete(TInt aError) +{ + iEngineState = EEngineIdle; + + if (aError == KErrNone) + if (iImageCaptureObserver) + iImageCaptureObserver->MceoFocusComplete(); + else { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrOptimisedFocusComplete, aError); + } +} + +TBool CCameraEngine::IsCameraReady() const +{ + // If reserved and powered on, but not focusing or capturing + if (iEngineState == EEngineIdle) + return ETrue; + + return EFalse; +} + +TBool CCameraEngine::IsDirectViewFinderSupported() const +{ + if (iCameraInfo.iOptionsSupported & TCameraInfo::EViewFinderDirectSupported) + return true; + else + return false; +} + +TCameraInfo *CCameraEngine::CameraInfo() +{ + return &iCameraInfo; +} + +TBool CCameraEngine::IsAutoFocusSupported() const +{ +#ifndef Q_CC_NOKIAX86 // Not Emulator + +#ifdef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.1 + return (iAutoFocus) ? ETrue : EFalse; +#else // !S60_CAM_AUTOFOCUS_SUPPORT + return EFalse; +#endif // S60_CAM_AUTOFOCUS_SUPPORT + +#else // Q_CC_NOKIAX86 - Emulator + return EFalse; +#endif // !Q_CC_NOKIAX86 +} + +/* + * This function is used for focusing in S60 3.1 platform. Platforms from S60 + * 3.2 onwards should use the focusing provided by the S60CameraSettings class. + */ +void CCameraEngine::StartFocusL() +{ + if (iEngineState != EEngineIdle) + return; + +#ifndef Q_CC_NOKIAX86 // Not Emulator +#ifdef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.1 + if (iAutoFocus) { + if (!iAFRange) { + iAFRange = CCamAutoFocus::ERangeNormal; + iAutoFocus->SetFocusRangeL(iAFRange); + } + + iAutoFocus->AttemptOptimisedFocusL(); + iEngineState = EEngineFocusing; + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT +#endif // !Q_CC_NOKIAX86 +} + +/* + * This function is used for cancelling focusing in S60 3.1 platform. Platforms + * from S60 3.2 onwards should use the focusing provided by the + * S60CameraSettings class. + */ +void CCameraEngine::FocusCancel() +{ +#ifndef Q_CC_NOKIAX86 // Not Emulator +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + if (iAutoFocus) { + iAutoFocus->Cancel(); + iEngineState = EEngineIdle; + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT +#endif // !Q_CC_NOKIAX86 +} + +void CCameraEngine::SupportedFocusRanges(TInt& aSupportedRanges) const +{ + aSupportedRanges = 0; + +#ifndef Q_CC_NOKIAX86 // Not Emulator +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + if (iAutoFocus) { + // CCamAutoFocus doesn't provide a method for getting supported ranges! + // Assume everything is supported (rather optimistic) + aSupportedRanges = CCamAutoFocus::ERangeMacro | + CCamAutoFocus::ERangePortrait | + CCamAutoFocus::ERangeNormal | + CCamAutoFocus::ERangeInfinite; + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT +#endif // !Q_CC_NOKIAX86 +} + +void CCameraEngine::SetFocusRange(TInt aFocusRange) +{ +#if !defined(Q_CC_NOKIAX86) // Not Emulator + +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + if (iAutoFocus) { + TRAPD(focusErr, iAutoFocus->SetFocusRangeL((CCamAutoFocus::TAutoFocusRange)aFocusRange)); + if (focusErr) + iObserver->MceoHandleError(EErrAutoFocusRange, focusErr); + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT + +#else // Q_CC_NOKIAX86 // Emulator + Q_UNUSED(aFocusRange); + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrAutoFocusRange, KErrNotSupported); +#endif // !Q_CC_NOKIAX86 +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraengine.h b/src/plugins/symbian/ecam/s60cameraengine.h new file mode 100644 index 000000000..7a925c0ba --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraengine.h @@ -0,0 +1,407 @@ +/**************************************************************************** + ** + ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the Qt Mobility Components. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** No Commercial Usage + ** This file contains pre-release code and may not be distributed. + ** You may use this file in accordance with the terms and conditions + ** contained in the Technology Preview License Agreement accompanying + ** this package. + ** + ** 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 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. + ** + ** If you have questions regarding the use of this file, please contact + ** Nokia at qt-info@nokia.com. + ** + ** + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ +#ifndef S60CCAMERAENGINE_H +#define S60CCAMERAENGINE_H + +// INCLUDES +#include <e32base.h> +#include <ecam.h> // for MCameraObserver(2) +#ifdef S60_CAM_AUTOFOCUS_SUPPORT +#include <ccamautofocus.h> // for CCamAutoFocus, MCamAutoFocusObserver +#endif + +// FORWARD DECLARATIONS +class MCameraEngineObserver; +class MCameraEngineImageCaptureObserver; +class MAdvancedSettingsObserver; +class MCameraViewfinderObserver; +class MCameraPreviewObserver; + +/* + * CameraEngine handling ECam operations needed. + */ +NONSHARABLE_CLASS( CCameraEngine ) : public CBase, + public MCameraObserver, + public MCameraObserver2 +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + ,public MCamAutoFocusObserver +#endif + +{ +public: // Enums + + enum TCameraEngineState + { + EEngineNotReady = 0, // 0 - No resources reserved + EEngineInitializing, // 1 - Reserving and Powering On + EEngineIdle, // 2 - Reseved and Powered On + EEngineCapturing, // 3 - Capturing Still Image + EEngineFocusing // 4 - Focusing + }; + +public: // Constructor & Destructor + + static CCameraEngine* NewL( TInt aCameraHandle, + TInt aPriority, + MCameraEngineObserver* aObserver ); + ~CCameraEngine(); + +public: + + /** + * External Advanced Settings callback observer. + */ + void SetAdvancedObserver(MAdvancedSettingsObserver *aAdvancedSettingsObserver); + + /** + * External Image Capture callback observer. + */ + void SetImageCaptureObserver(MCameraEngineImageCaptureObserver *aImageCaptureObserver); + + /** + * External Viewfinder callback observer. + */ + void SetViewfinderObserver(MCameraViewfinderObserver *aViewfinderObserver); + + /** + * Static function that returns the number of cameras on the device. + */ + static TInt CamerasAvailable(); + + /** + * Returns the index of the currently active camera device + */ + TInt CurrentCameraIndex() const { return iCameraIndex; } + + /** + * Returns the current state (TCameraEngineState) + * of the camera engine. + */ + TCameraEngineState State() const { return iEngineState; } + + /** + * Returns true if the camera has been reserved and + * powered on, and not recording or capturing image + */ + TBool IsCameraReady() const; + + /** + * Returns whether DirectScreen ViewFinder is supported by the platform + */ + TBool IsDirectViewFinderSupported() const; + + /** + * Returns true if the camera supports AutoFocus. + */ + TBool IsAutoFocusSupported() const; + + /** + * Returns camera info + */ + TCameraInfo *CameraInfo(); + + /** + * Captures an image. When complete, observer will receive + * MceoCapturedDataReady() or MceoCapturedBitmapReady() callback, + * depending on which image format was used in PrepareL(). + * @leave May leave with KErrNotReady if camera is not + * reserved or prepared for capture. + */ + void CaptureL(); + + /** + * Cancels ongoing image capture + */ + void CancelCapture(); + + /** + * Reserves and powers on the camera. When complete, + * observer will receive MceoCameraReady() callback + * + */ + void ReserveAndPowerOn(); + + /** + * Releases and powers off the camera + * + */ + void ReleaseAndPowerOff(); + + /** + * Prepares for image capture. + * @param aCaptureSize requested capture size. On return, + * contains the selected size (closest match) + * @param aFormat Image format to use. Default is JPEG with + * EXIF information as provided by the camera module + * @leave KErrNotSupported, KErrNoMemory, KErrNotReady + */ + void PrepareL( TSize& aCaptureSize, + CCamera::TFormat aFormat = CCamera::EFormatExif ); + + /** + * Starts the viewfinder. Observer will receive + * MceoViewFinderFrameReady() callbacks periodically. + * @param aSize requested viewfinder size. On return, + * contains the selected size. + * + * @leave KErrNotSupported is viewfinding with bitmaps is not + * supported, KErrNotReady + */ + void StartViewFinderL( TSize& aSize ); + + /** + * Stops the viewfinder if active. + */ + void StopViewFinder(); + + void StartDirectViewFinderL(RWsSession& aSession, + CWsScreenDevice& aScreenDevice, + RWindowBase& aWindow, + TRect& aScreenRect, + TRect& aClipRect); + + /** + * Releases memory for the last received viewfinder frame. + * Client must call this in response to MceoViewFinderFrameReady() + * callback, after drawing the viewfinder frame is complete. + */ + void ReleaseViewFinderBuffer(); + + /** + * Releases memory for the last captured image. + * Client must call this in response to MceoCapturedDataReady() + * or MceoCapturedBitmapReady()callback, after processing the + * data/bitmap is complete. + */ + void ReleaseImageBuffer(); + + /** + * Starts focusing. Does nothing if AutoFocus is not supported. + * When complete, observer will receive MceoFocusComplete() + * callback. + * @leave KErrInUse, KErrNotReady + */ + void StartFocusL(); + + /** + * Cancels the ongoing focusing operation. + */ + void FocusCancel(); + + /** + * Gets a bitfield of supported focus ranges. + * @param aSupportedRanges a bitfield of either TAutoFocusRange + * (S60 3.0/3.1 devices) or TFocusRange (S60 3.2 and onwards) values + */ + void SupportedFocusRanges( TInt& aSupportedRanges ) const; + + /** + * Sets the focus range + * @param aFocusRange one of the values returned by + * SupportedFocusRanges(). + */ + void SetFocusRange( TInt aFocusRange ); + + /** + * Returns a pointer to CCamera object used by the engine. + * Allows getting access to additional functionality + * from CCamera - do not use for functionality already provided + * by CCameraEngine methods. + */ + CCamera* Camera() { return iCamera; } + +#ifdef ECAM_PREVIEW_API + /** + * This enables the preview creation during the capture (image or video). + */ + void EnablePreviewProvider(MCameraPreviewObserver *aPreviewObserver); + + /** + * This disabled the preview creation during the capture (image or video) + */ + void DisablePreviewProvider(); +#endif // ECAM_PREVIEW_API + +protected: // Protected constructors + + CCameraEngine(); + CCameraEngine( TInt aCameraHandle, + TInt aPriority, + MCameraEngineObserver* aObserver ); + void ConstructL(); + +protected: // MCameraObserver + + /** + * From MCameraObserver + * Gets called when CCamera::Reserve() is completed. + * (V2: Called internally from HandleEvent) + */ + virtual void ReserveComplete(TInt aError); + + /** + * From MCameraObserver. + * Gets called when CCamera::PowerOn() is completed. + * (V2: Called internally from HandleEvent) + */ + virtual void PowerOnComplete(TInt aError); + + /** + * From MCameraObserver. + * Gets called when CCamera::StartViewFinderBitmapsL() is completed. + * (V2: Called internally from ViewFinderReady) + */ + virtual void ViewFinderFrameReady( CFbsBitmap& aFrame ); + + /** + * From MCameraObserver. + * Gets called when CCamera::CaptureImage() is completed. + */ + virtual void ImageReady( CFbsBitmap* aBitmap, HBufC8* aData, TInt aError ); + + /** + * From MCameraObserver. + * Video capture not implemented. + */ + virtual void FrameBufferReady( MFrameBuffer* /*aFrameBuffer*/, TInt /*aError*/ ) {} + +protected: // MCameraObserver2 + + /** + * From MCameraObserver2 + * Camera event handler + */ + virtual void HandleEvent(const TECAMEvent &aEvent); + + /** + * From MCameraObserver2 + * Notifies the client of new viewfinder data + */ + virtual void ViewFinderReady(MCameraBuffer &aCameraBuffer, TInt aError); + + /** + * From MCameraObserver2 + * Notifies the client of a new captured image + */ + virtual void ImageBufferReady(MCameraBuffer &aCameraBuffer, TInt aError); + + /** + * From MCameraObserver2 + * Video capture not implemented. + */ + virtual void VideoBufferReady(MCameraBuffer& /*aCameraBuffer*/, TInt /*aError*/) {} + +protected: // MCamAutoFocusObserver + + /** + * From MCamAutoFocusObserver. + * Delivers notification of completion of auto focus initialisation to + * an interested party. + * @param aError Reason for completion of focus request. + */ + virtual void InitComplete( TInt aError ); + + /** + * From MCamAutoFocusObserver. + * Gets called when CCamAutoFocus::AttemptOptimisedFocusL() is + * completed. + * (V2: Called internally from HandleEvent) + */ + virtual void OptimisedFocusComplete( TInt aError ); + +private: // Internal functions + + /** + * Internal function to handle ImageReady callbacks from + * both observer (V1 & V2) interfaces + */ + void HandleImageReady(const TInt aError, const bool isBitmap); + +#ifdef ECAM_PREVIEW_API + /** + * Handle preview. Retrieve preview data and notify observer about the + * preview availability. + */ + void HandlePreview(); + + /** + * Calculate proper resolution for the SnapShot (Preview) image. + */ + TSize SelectPreviewResolution(); +#endif // ECAM_PREVIEW_API + +private: // Data + + CCamera *iCamera; + MCameraEngineObserver *iObserver; + MCameraEngineImageCaptureObserver *iImageCaptureObserver; + MAdvancedSettingsObserver *iAdvancedSettingsObserver; + MCameraViewfinderObserver *iViewfinderObserver; + MCameraPreviewObserver *iPreviewObserver; + MCameraBuffer *iViewFinderBuffer; + /* + * Following pointers are for the image buffers: + * * Makes buffering of 2 concurrent image buffers possible + */ + MCameraBuffer *iImageBuffer1; + MCameraBuffer *iImageBuffer2; + TDesC8 *iImageData1; + TDesC8 *iImageData2; + CFbsBitmap *iImageBitmap1; + CFbsBitmap *iImageBitmap2; + TInt iCameraIndex; + TInt iPriority; + TCameraEngineState iEngineState; + TCameraInfo iCameraInfo; + CCamera::TFormat iImageCaptureFormat; + TSize iCaptureResolution; + bool iNew2LImplementation; + int iLatestImageBufferIndex; // 0 = Buffer1, 1 = Buffer2 +#ifdef ECAM_PREVIEW_API + CCamera::CCameraSnapshot *iCameraSnapshot; +#endif // ECAM_PREVIEW_API +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + CCamAutoFocus* iAutoFocus; + CCamAutoFocus::TAutoFocusRange iAFRange; +#endif // S60_CAM_AUTOFOCUS_SUPPORT +}; + +#endif // S60CCAMERAENGINE_H diff --git a/src/plugins/symbian/ecam/s60cameraengineobserver.h b/src/plugins/symbian/ecam/s60cameraengineobserver.h new file mode 100644 index 000000000..b1e669d70 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraengineobserver.h @@ -0,0 +1,178 @@ +/**************************************************************************** + ** + ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the Qt Mobility Components. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** No Commercial Usage + ** This file contains pre-release code and may not be distributed. + ** You may use this file in accordance with the terms and conditions + ** contained in the Technology Preview License Agreement accompanying + ** this package. + ** + ** 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 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. + ** + ** If you have questions regarding the use of this file, please contact + ** Nokia at qt-info@nokia.com. + ** + ** + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ +#ifndef S60CCAMERAENGINEOBSERVER_H +#define S60CCAMERAENGINEOBSERVER_H + +// FORWARD DECLARATIONS +class CFbsBitmap; +class TECAMEvent; + +enum TCameraEngineError +{ + EErrReserve, + EErrPowerOn, + EErrViewFinderReady, + EErrImageReady, + EErrPreview, + EErrAutoFocusInit, + EErrAutoFocusMode, + EErrAutoFocusArea, + EErrAutoFocusRange, + EErrAutoFocusType, + EErrOptimisedFocusComplete, +}; + +/* + * CameraEngine Observer class towards Camera AdvancedSettings + */ +class MAdvancedSettingsObserver +{ +public: + + virtual void HandleAdvancedEvent( const TECAMEvent &aEvent ) = 0; + +}; + +//============================================================================= + +/* + * CameraEngine Observer class towards Camera Control + */ +class MCameraEngineObserver +{ +public: + + /** + * Camera is ready to use for capturing images. + */ + virtual void MceoCameraReady() = 0; + + /** + * Notifies clients about errors in camera engine + * @param aErrorType type of error (see TCameraEngineError) + * @param aError Symbian system-wide error code + */ + virtual void MceoHandleError( TCameraEngineError aErrorType, TInt aError ) = 0; + +}; + +//============================================================================= + +/* + * CameraEngine Observer class towards Camera ImageCaptureSession + */ +class MCameraEngineImageCaptureObserver +{ +public: + + /** + * Camera AF lens has attained optimal focus + */ + virtual void MceoFocusComplete() = 0; + + /** + * Captured data is ready - call CCameraEngine::ReleaseImageBuffer() + * after processing/saving the data (typically, JPG-encoded image) + * @param aData Pointer to a descriptor containing a frame of camera data. + */ + virtual void MceoCapturedDataReady( TDesC8* aData ) = 0; + + /** + * Captured bitmap is ready. + * after processing/saving the image, call + * CCameraEngine::ReleaseImageBuffer() to free the bitmap. + * @param aBitmap Pointer to an FBS bitmap containing a captured image. + */ + virtual void MceoCapturedBitmapReady( CFbsBitmap* aBitmap ) = 0; + + /** + * Notifies clients about errors in camera engine + * @param aErrorType type of error (see TCameraEngineError) + * @param aError Symbian system-wide error code + */ + virtual void MceoHandleError( TCameraEngineError aErrorType, TInt aError ) = 0; + + /** + * Notifies client about other events not recognized by camera engine. + * The default implementation is empty. + * @param aEvent camera event (see MCameraObserver2::HandleEvent()) + */ + virtual void MceoHandleOtherEvent( const TECAMEvent& /*aEvent*/ ) {} +}; + +//============================================================================= + +/* + * CameraEngine Observer class towards Camera ViewFinderEngine + */ +class MCameraViewfinderObserver +{ +public: + /** + * A new viewfinder frame is ready. + * after displaying the frame, call + * CCameraEngine::ReleaseViewFinderBuffer() + * to free the bitmap. + * @param aFrame Pointer to an FBS bitmap containing a viewfinder frame. + */ + virtual void MceoViewFinderFrameReady( CFbsBitmap& aFrame ) = 0; +}; + +//============================================================================= + +#ifdef ECAM_PREVIEW_API +/* + * CameraEngine Observer class towards Camera ViewFinderEngine + */ +class MCameraPreviewObserver +{ +public: + /** + * A new preview is available. + * @param aPreview Pointer to an FBS bitmap containing a preview. + */ + virtual void MceoPreviewReady( CFbsBitmap& aPreview ) = 0; +}; +#endif // ECAM_PREVIEW_API + +#endif // CCAMERAENGINEOBSERVER_H + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraexposurecontrol.cpp b/src/plugins/symbian/ecam/s60cameraexposurecontrol.cpp new file mode 100644 index 000000000..9ebdd2c6e --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraexposurecontrol.cpp @@ -0,0 +1,584 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> + +#include "s60cameraexposurecontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" + +S60CameraExposureControl::S60CameraExposureControl(QObject *parent) : + QCameraExposureControl(parent) +{ +} + +S60CameraExposureControl::S60CameraExposureControl(S60ImageCaptureSession *session, QObject *parent) : + QCameraExposureControl(parent), + m_session(0), + m_service(0), + m_advancedSettings(0), + m_exposureMode(QCameraExposure::ExposureAuto), + m_meteringMode(QCameraExposure::MeteringMatrix) +{ + m_session = session; + + connect(m_session, SIGNAL(advancedSettingChanged()), this, SLOT(resetAdvancedSetting())); + m_advancedSettings = m_session->advancedSettings(); + + if (m_advancedSettings) { + connect(m_advancedSettings, SIGNAL(apertureChanged()), this, SLOT(apertureChanged())); + connect(m_advancedSettings, SIGNAL(apertureRangeChanged()), this, SLOT(apertureRangeChanged())); + connect(m_advancedSettings, SIGNAL(shutterSpeedChanged()), this, SLOT(shutterSpeedChanged())); + connect(m_advancedSettings, SIGNAL(isoSensitivityChanged()), this, SLOT(isoSensitivityChanged())); + connect(m_advancedSettings, SIGNAL(evChanged()), this, SLOT(evChanged())); + } +} + +S60CameraExposureControl::~S60CameraExposureControl() +{ + m_advancedSettings = 0; +} + +void S60CameraExposureControl::resetAdvancedSetting() +{ + m_advancedSettings = m_session->advancedSettings(); + if (m_advancedSettings) { + connect(m_advancedSettings, SIGNAL(apertureChanged()), this, SLOT(apertureChanged())); + connect(m_advancedSettings, SIGNAL(apertureRangeChanged()), this, SLOT(apertureRangeChanged())); + connect(m_advancedSettings, SIGNAL(shutterSpeedChanged()), this, SLOT(shutterSpeedChanged())); + connect(m_advancedSettings, SIGNAL(isoSensitivityChanged()), this, SLOT(isoSensitivityChanged())); + connect(m_advancedSettings, SIGNAL(evChanged()), this, SLOT(evChanged())); + } +} + +void S60CameraExposureControl::apertureChanged() +{ + emit exposureParameterChanged(QCameraExposureControl::Aperture); +} + +void S60CameraExposureControl::apertureRangeChanged() +{ + emit exposureParameterRangeChanged(QCameraExposureControl::Aperture); +} + +void S60CameraExposureControl::shutterSpeedChanged() +{ + emit exposureParameterChanged(QCameraExposureControl::ShutterSpeed); +} + +void S60CameraExposureControl::isoSensitivityChanged() +{ + emit exposureParameterChanged(QCameraExposureControl::ISO); +} + +void S60CameraExposureControl::evChanged() +{ + emit exposureParameterChanged(QCameraExposureControl::ExposureCompensation); +} + +QCameraExposure::ExposureMode S60CameraExposureControl::exposureMode() const +{ + return m_session->exposureMode(); +} + +void S60CameraExposureControl::setExposureMode(QCameraExposure::ExposureMode mode) +{ + if (isExposureModeSupported(mode)) { + m_exposureMode = mode; + m_session->setExposureMode(m_exposureMode); + return; + } + + m_session->setError(KErrNotSupported, tr("Requested exposure mode is not supported.")); +} + +bool S60CameraExposureControl::isExposureModeSupported(QCameraExposure::ExposureMode mode) const +{ + if (m_session->isExposureModeSupported(mode)) + return true; + + return false; +} + +QCameraExposure::MeteringMode S60CameraExposureControl::meteringMode() const +{ + if (m_advancedSettings) + return m_advancedSettings->meteringMode(); + + return QCameraExposure::MeteringMode(); +} + +void S60CameraExposureControl::setMeteringMode(QCameraExposure::MeteringMode mode) +{ + if (m_advancedSettings) { + if (isMeteringModeSupported(mode)) { + m_meteringMode = mode; + m_advancedSettings->setMeteringMode(mode); + return; + } + } + + m_session->setError(KErrNotSupported, tr("Requested metering mode is not supported.")); +} + +bool S60CameraExposureControl::isMeteringModeSupported(QCameraExposure::MeteringMode mode) const +{ + if (m_advancedSettings) + return m_advancedSettings->isMeteringModeSupported(mode); + + return false; +} + +bool S60CameraExposureControl::isParameterSupported(ExposureParameter parameter) const +{ + // Settings supported only if advanced settings available + if (m_advancedSettings) { + switch (parameter) { + case QCameraExposureControl::ISO: + if (m_advancedSettings->supportedIsoSensitivities().count() > 0) + return true; + else + return false; + case QCameraExposureControl::Aperture: + if (m_advancedSettings->supportedApertures().count() > 0) + return true; + else + return false; + case QCameraExposureControl::ShutterSpeed: + if (m_advancedSettings->supportedShutterSpeeds().count() > 0) + return true; + else + return false; + case QCameraExposureControl::ExposureCompensation: + if (m_advancedSettings->supportedExposureCompensationValues().count() > 0) + return true; + else + return false; + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + return false; + + default: + return false; + } + } + + return false; +} + +QVariant S60CameraExposureControl::exposureParameter(ExposureParameter parameter) const +{ + switch (parameter) { + case QCameraExposureControl::ISO: + return QVariant(isoSensitivity()); + case QCameraExposureControl::Aperture: + return QVariant(aperture()); + case QCameraExposureControl::ShutterSpeed: + return QVariant(shutterSpeed()); + case QCameraExposureControl::ExposureCompensation: + return QVariant(exposureCompensation()); + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + // Not supported in Symbian + return QVariant(); + + default: + // Not supported in Symbian + return QVariant(); + } +} + +QCameraExposureControl::ParameterFlags S60CameraExposureControl::exposureParameterFlags(ExposureParameter parameter) const +{ + QCameraExposureControl::ParameterFlags flags; + + /* + * ISO, ExposureCompensation: + * - Automatic/Manual + * - Read/Write + * - Discrete range + * + * Aperture, ShutterSpeed, FlashPower, FlashCompensation: + * - Not supported + */ + switch (parameter) { + case QCameraExposureControl::ISO: + case QCameraExposureControl::ExposureCompensation: + flags |= QCameraExposureControl::AutomaticValue; + break; + case QCameraExposureControl::Aperture: + case QCameraExposureControl::ShutterSpeed: + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + // Do nothing - no flags + break; + + default: + // Do nothing - no flags + break; + } + + return flags; +} + +QVariantList S60CameraExposureControl::supportedParameterRange(ExposureParameter parameter) const +{ + QVariantList valueList; + + if (m_advancedSettings) { + switch (parameter) { + case QCameraExposureControl::ISO: { + foreach (int iso, m_advancedSettings->supportedIsoSensitivities()) + valueList << QVariant(iso); + break; + } + case QCameraExposureControl::Aperture: { + foreach (qreal aperture, m_advancedSettings->supportedApertures()) + valueList << QVariant(aperture); + break; + } + case QCameraExposureControl::ShutterSpeed: { + foreach (qreal shutterSpeed, m_advancedSettings->supportedShutterSpeeds()) + valueList << QVariant(shutterSpeed); + break; + } + case QCameraExposureControl::ExposureCompensation: { + foreach (qreal ev, m_advancedSettings->supportedExposureCompensationValues()) + valueList << QVariant(ev); + break; + } + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + // Not supported in Symbian + break; + + default: + // Not supported in Symbian + break; + } + } + + return valueList; +} + +bool S60CameraExposureControl::setExposureParameter(ExposureParameter parameter, const QVariant& value) +{ + bool useDefaultValue = false; + + if (value.isNull()) + useDefaultValue = true; + + switch (parameter) { + case QCameraExposureControl::ISO: + if (useDefaultValue) { + setAutoIsoSensitivity(); + return false; + } + else + return setManualIsoSensitivity(value.toInt()); + + case QCameraExposureControl::Aperture: + if (useDefaultValue) { + setAutoAperture(); + return false; + } + else + return setManualAperture(value.toFloat()); + + case QCameraExposureControl::ShutterSpeed: + if (useDefaultValue) { + setAutoShutterSpeed(); + return false; + } + else + return setManualShutterSpeed(value.toFloat()); + + case QCameraExposureControl::ExposureCompensation: + if (useDefaultValue) { + setAutoExposureCompensation(); + return false; + } + else + return setManualExposureCompensation(value.toFloat()); + + case QCameraExposureControl::FlashPower: + return false; + case QCameraExposureControl::FlashCompensation: + return false; + + default: + // Not supported in Symbian + return false; + } +} + +QString S60CameraExposureControl::extendedParameterName(ExposureParameter parameter) +{ + switch (parameter) { + case QCameraExposureControl::ISO: + return QLatin1String("ISO Sensitivity"); + case QCameraExposureControl::Aperture: + return QLatin1String("Aperture"); + case QCameraExposureControl::ShutterSpeed: + return QLatin1String("Shutter Speed"); + case QCameraExposureControl::ExposureCompensation: + return QLatin1String("Exposure Compensation"); + case QCameraExposureControl::FlashPower: + return QLatin1String("Flash Power"); + case QCameraExposureControl::FlashCompensation: + return QLatin1String("Flash Compensation"); + + default: + return QString(); + } +} + +int S60CameraExposureControl::isoSensitivity() const +{ + if (m_advancedSettings) + return m_advancedSettings->isoSensitivity(); + return 0; +} + +bool S60CameraExposureControl::isIsoSensitivitySupported(const int iso) const +{ + if (m_advancedSettings && + m_advancedSettings->supportedIsoSensitivities().contains(iso)) + return true; + else + return false; +} + +bool S60CameraExposureControl::setManualIsoSensitivity(int iso) +{ + if (m_advancedSettings) { + if (isIsoSensitivitySupported(iso)) { + m_advancedSettings->setManualIsoSensitivity(iso); + return true; + } + } + + return false; +} + +void S60CameraExposureControl::setAutoIsoSensitivity() +{ + if (m_advancedSettings) + m_advancedSettings->setAutoIsoSensitivity(); +} + +qreal S60CameraExposureControl::aperture() const +{ + if (m_advancedSettings) + return m_advancedSettings->aperture(); + return 0.0; +} + +bool S60CameraExposureControl::isApertureSupported(const qreal aperture) const +{ + if (m_advancedSettings) { + QList<qreal> supportedValues = m_advancedSettings->supportedApertures(); + if(supportedValues.indexOf(aperture) != -1) + return true; + } + + return false; +} + +bool S60CameraExposureControl::setManualAperture(qreal aperture) +{ + if (m_advancedSettings) { + if (isApertureSupported(aperture)) { + m_advancedSettings->setManualAperture(aperture); + return true; + } else { + QList<qreal> supportedApertureValues = m_advancedSettings->supportedApertures(); + int minAperture = supportedApertureValues.first(); + int maxAperture = supportedApertureValues.last(); + + if (aperture < minAperture) { // Smaller than minimum + aperture = minAperture; + } else if (aperture > maxAperture) { // Bigger than maximum + aperture = maxAperture; + } else { // Find closest + int indexOfClosest = 0; + int smallestDiff = 100000000; // Sensible max diff + for(int i = 0; i < supportedApertureValues.count(); ++i) { + if((abs((aperture*100) - (supportedApertureValues[i]*100))) < smallestDiff) { + smallestDiff = abs((aperture*100) - (supportedApertureValues[i]*100)); + indexOfClosest = i; + } + } + aperture = supportedApertureValues[indexOfClosest]; + } + m_advancedSettings->setManualAperture(aperture); + } + } + + return false; +} + +void S60CameraExposureControl::setAutoAperture() +{ + // Not supported in Symbian +} + +qreal S60CameraExposureControl::shutterSpeed() const +{ + if (m_advancedSettings) + return m_advancedSettings->shutterSpeed(); + return 0.0; +} + +bool S60CameraExposureControl::isShutterSpeedSupported(const qreal seconds) const +{ + if (m_advancedSettings) { + QList<qreal> supportedValues = m_advancedSettings->supportedShutterSpeeds(); + if(supportedValues.indexOf(seconds) != -1) + return true; + } + + return false; +} + +bool S60CameraExposureControl::setManualShutterSpeed(qreal seconds) +{ + if (m_advancedSettings) { + if (isShutterSpeedSupported(seconds)) { + m_advancedSettings->setManualShutterSpeed(seconds); + return true; + } else { + QList<qreal> supportedShutterSpeeds = m_advancedSettings->supportedShutterSpeeds(); + + if (supportedShutterSpeeds.count() == 0) + return false; + + int minShutterSpeed = supportedShutterSpeeds.first(); + int maxShutterSpeed = supportedShutterSpeeds.last(); + + if (seconds < minShutterSpeed) { // Smaller than minimum + seconds = minShutterSpeed; + } else if (seconds > maxShutterSpeed) { // Bigger than maximum + seconds = maxShutterSpeed; + } else { // Find closest + int indexOfClosest = 0; + int smallestDiff = 100000000; // Sensible max diff + for(int i = 0; i < supportedShutterSpeeds.count(); ++i) { + if((abs((seconds*100) - (supportedShutterSpeeds[i]*100))) < smallestDiff) { + smallestDiff = abs((seconds*100) - (supportedShutterSpeeds[i]*100)); + indexOfClosest = i; + } + } + seconds = supportedShutterSpeeds[indexOfClosest]; + } + m_advancedSettings->setManualShutterSpeed(seconds); + } + } + + return false; +} + +void S60CameraExposureControl::setAutoShutterSpeed() +{ + // Not supported in Symbian +} + +qreal S60CameraExposureControl::exposureCompensation() const +{ + if (m_advancedSettings) + return m_advancedSettings->exposureCompensation(); + return 0.0; +} + +bool S60CameraExposureControl::isExposureCompensationSupported(const qreal ev) const +{ + if (m_advancedSettings) { + QList<qreal> supportedValues = m_advancedSettings->supportedExposureCompensationValues(); + if(supportedValues.indexOf(ev) != -1) + return true; + } + + return false; +} + +bool S60CameraExposureControl::setManualExposureCompensation(qreal ev) +{ + if (m_advancedSettings) { + if (isExposureCompensationSupported(ev)) { + m_advancedSettings->setExposureCompensation(ev); + return true; + } else { + QList<qreal> supportedEVs = m_advancedSettings->supportedExposureCompensationValues(); + + if (supportedEVs.count() == 0) + return false; + + int minEV = supportedEVs.first(); + int maxEV = supportedEVs.last(); + + if (ev < minEV) { // Smaller than minimum + ev = minEV; + } else if (ev > maxEV) { // Bigger than maximum + ev = maxEV; + } else { // Find closest + int indexOfClosest = 0; + int smallestDiff = 100000000; // Sensible max diff + for(int i = 0; i < supportedEVs.count(); ++i) { + if((abs((ev*100) - (supportedEVs[i]*100))) < smallestDiff) { + smallestDiff = abs((ev*100) - (supportedEVs[i]*100)); + indexOfClosest = i; + } + } + ev = supportedEVs[indexOfClosest]; + } + m_advancedSettings->setExposureCompensation(ev); + } + } + + return false; +} + +void S60CameraExposureControl::setAutoExposureCompensation() +{ + // Not supported in Symbian +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraexposurecontrol.h b/src/plugins/symbian/ecam/s60cameraexposurecontrol.h new file mode 100644 index 000000000..1c623c774 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraexposurecontrol.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERAEXPOSURECONTROL_H +#define S60CAMERAEXPOSURECONTROL_H + +#include <qcameraexposurecontrol.h> + +#include "s60camerasettings.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; + +/* + * Control for exposure related camera operation. + */ +class S60CameraExposureControl : public QCameraExposureControl +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60CameraExposureControl(QObject *parent = 0); + S60CameraExposureControl(S60ImageCaptureSession *session, QObject *parent = 0); + ~S60CameraExposureControl(); + +public: // QCameraExposureControl + + // Exposure Mode + QCameraExposure::ExposureMode exposureMode() const; + void setExposureMode(QCameraExposure::ExposureMode mode); + bool isExposureModeSupported(QCameraExposure::ExposureMode mode) const; + + // Metering Mode + QCameraExposure::MeteringMode meteringMode() const; + void setMeteringMode(QCameraExposure::MeteringMode mode); + bool isMeteringModeSupported(QCameraExposure::MeteringMode mode) const; + + // Exposure Parameter + bool isParameterSupported(ExposureParameter parameter) const; + QVariant exposureParameter(ExposureParameter parameter) const; + QCameraExposureControl::ParameterFlags exposureParameterFlags(ExposureParameter parameter) const; + QVariantList supportedParameterRange(ExposureParameter parameter) const; + bool setExposureParameter(ExposureParameter parameter, const QVariant& value); + + QString extendedParameterName(ExposureParameter parameter); + +/* +Q_SIGNALS: // QCameraExposureControl + void exposureParameterChanged(int parameter); + void exposureParameterRangeChanged(int parameter); +*/ + +private slots: // Internal Slots + + void resetAdvancedSetting(); + void apertureChanged(); + void apertureRangeChanged(); + void shutterSpeedChanged(); + void isoSensitivityChanged(); + void evChanged(); + +private: // Internal - Implementing ExposureParameter + + // ISO Sensitivity + int isoSensitivity() const; + bool setManualIsoSensitivity(int iso); + void setAutoIsoSensitivity(); + bool isIsoSensitivitySupported(const int iso) const; + + // Aperture + qreal aperture() const; + bool setManualAperture(qreal aperture); + void setAutoAperture(); + bool isApertureSupported(const qreal aperture) const; + + // Shutter Speed + qreal shutterSpeed() const; + bool setManualShutterSpeed(qreal seconds); + void setAutoShutterSpeed(); + bool isShutterSpeedSupported(const qreal seconds) const; + + // Exposure Compensation + qreal exposureCompensation() const; + bool setManualExposureCompensation(qreal ev); + void setAutoExposureCompensation(); + bool isExposureCompensationSupported(const qreal ev) const; + +private: // Data + + S60ImageCaptureSession *m_session; + S60CameraService *m_service; + S60CameraSettings *m_advancedSettings; + QCameraExposure::ExposureMode m_exposureMode; + QCameraExposure::MeteringMode m_meteringMode; +}; + +#endif // S60CAMERAEXPOSURECONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraflashcontrol.cpp b/src/plugins/symbian/ecam/s60cameraflashcontrol.cpp new file mode 100644 index 000000000..a18c57a03 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraflashcontrol.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> + +#include "s60cameraflashcontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" + +S60CameraFlashControl::S60CameraFlashControl(QObject *parent) : + QCameraFlashControl(parent) +{ +} + +S60CameraFlashControl::S60CameraFlashControl(S60ImageCaptureSession *session, QObject *parent) : + QCameraFlashControl(parent), + m_session(0), + m_service(0), + m_advancedSettings(0), + m_flashMode(QCameraExposure::FlashOff) +{ + m_session = session; + + connect(m_session, SIGNAL(advancedSettingChanged()), this, SLOT(resetAdvancedSetting())); + m_advancedSettings = m_session->advancedSettings(); + + if (m_advancedSettings) + connect(m_advancedSettings, SIGNAL(flashReady(bool)), this, SIGNAL(flashReady(bool))); +} + +S60CameraFlashControl::~S60CameraFlashControl() +{ + m_advancedSettings = 0; +} + +void S60CameraFlashControl::resetAdvancedSetting() +{ + m_advancedSettings = m_session->advancedSettings(); + if (m_advancedSettings) + connect(m_advancedSettings, SIGNAL(flashReady(bool)), this, SIGNAL(flashReady(bool))); +} + +QCameraExposure::FlashModes S60CameraFlashControl::flashMode() const +{ + return m_session->flashMode(); +} + +void S60CameraFlashControl::setFlashMode(QCameraExposure::FlashModes mode) +{ + if (isFlashModeSupported(mode)) { + m_flashMode = mode; + m_session->setFlashMode(m_flashMode); + } + else + m_session->setError(KErrNotSupported, tr("Requested flash mode is not supported.")); +} + +bool S60CameraFlashControl::isFlashModeSupported(QCameraExposure::FlashModes mode) const +{ + return m_session->supportedFlashModes() & mode; +} + +bool S60CameraFlashControl::isFlashReady() const +{ + if (m_advancedSettings) + return m_advancedSettings->isFlashReady(); + + return false; +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraflashcontrol.h b/src/plugins/symbian/ecam/s60cameraflashcontrol.h new file mode 100644 index 000000000..50dbc41dc --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraflashcontrol.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERAFLASHCONTROL_H +#define S60CAMERAFLASHCONTROL_H + +#include <qcameraflashcontrol.h> + +#include "s60camerasettings.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; + +/* + * Control to setup Flash related camera settings. + */ +class S60CameraFlashControl : public QCameraFlashControl +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60CameraFlashControl(QObject *parent = 0); + S60CameraFlashControl(S60ImageCaptureSession *session, QObject *parent = 0); + ~S60CameraFlashControl(); + +public: // QCameraExposureControl + + // Flash Mode + QCameraExposure::FlashModes flashMode() const; + void setFlashMode(QCameraExposure::FlashModes mode); + bool isFlashModeSupported(QCameraExposure::FlashModes mode) const; + + bool isFlashReady() const; + +/* +Q_SIGNALS: // QCameraExposureControl + void flashReady(bool); +*/ + +private slots: // Internal Slots + + void resetAdvancedSetting(); + +private: // Data + + S60ImageCaptureSession *m_session; + S60CameraService *m_service; + S60CameraSettings *m_advancedSettings; + QCameraExposure::FlashModes m_flashMode; +}; + +#endif // S60CAMERAFLASHCONTROL_H diff --git a/src/plugins/symbian/ecam/s60camerafocuscontrol.cpp b/src/plugins/symbian/ecam/s60camerafocuscontrol.cpp new file mode 100644 index 000000000..a7941ce20 --- /dev/null +++ b/src/plugins/symbian/ecam/s60camerafocuscontrol.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> + +#include "s60camerafocuscontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" +#include "s60cameraconstants.h" + +S60CameraFocusControl::S60CameraFocusControl(QObject *parent) : + QCameraFocusControl(parent) +{ +} + +S60CameraFocusControl::S60CameraFocusControl(S60ImageCaptureSession *session, QObject *parent) : + QCameraFocusControl(parent), + m_session(0), + m_service(0), + m_advancedSettings(0), + m_isFocusLocked(false), + m_opticalZoomValue(KDefaultOpticalZoom), + m_digitalZoomValue(KDefaultDigitalZoom), + m_focusMode(KDefaultFocusMode) +{ + m_session = session; + + connect(m_session, SIGNAL(advancedSettingChanged()), this, SLOT(resetAdvancedSetting())); + m_advancedSettings = m_session->advancedSettings(); + + TRAPD(err, m_session->doSetZoomFactorL(m_opticalZoomValue, m_digitalZoomValue)); + if (err) + m_session->setError(KErrNotSupported, tr("Setting default zoom factors failed.")); +} + +S60CameraFocusControl::~S60CameraFocusControl() +{ +} + +QCameraFocus::FocusMode S60CameraFocusControl::focusMode() const +{ + return m_focusMode; +} + +void S60CameraFocusControl::setFocusMode(QCameraFocus::FocusMode mode) +{ + if (isFocusModeSupported(mode)) { + // FocusMode and FocusRange are set. Focusing is triggered by setting + // the corresponding FocusType active by calling searchAndLock in LocksControl. + m_focusMode = mode; + if (m_advancedSettings) + m_advancedSettings->setFocusMode(m_focusMode); + else + m_session->setError(KErrGeneral, tr("Unable to set focus mode before camera is started.")); + } else { + m_session->setError(KErrNotSupported, tr("Requested focus mode is not supported.")); + } +} + +bool S60CameraFocusControl::isFocusModeSupported(QCameraFocus::FocusMode mode) const +{ + if (m_advancedSettings) { + return m_advancedSettings->supportedFocusModes() & mode; + } else { + if (mode == QCameraFocus::AutoFocus) + return m_session->isFocusSupported(); + } + + return false; +} + +qreal S60CameraFocusControl::maximumOpticalZoom() const +{ + return m_session->maximumZoom(); +} + +qreal S60CameraFocusControl::maximumDigitalZoom() const +{ + return m_session->maxDigitalZoom(); +} + +qreal S60CameraFocusControl::opticalZoom() const +{ + return m_session->opticalZoomFactor(); +} + +qreal S60CameraFocusControl::digitalZoom() const +{ + return m_session->digitalZoomFactor(); +} + +void S60CameraFocusControl::zoomTo(qreal optical, qreal digital) +{ + TRAPD(err, m_session->doSetZoomFactorL(optical, digital)); + if (err) + m_session->setError(KErrNotSupported, tr("Requested zoom factor is not supported.")); + + // Query new values + if (m_opticalZoomValue != m_session->opticalZoomFactor()) { + m_opticalZoomValue = m_session->opticalZoomFactor(); + emit opticalZoomChanged(m_opticalZoomValue); + } + if (m_digitalZoomValue != m_session->digitalZoomFactor()) { + m_digitalZoomValue = m_session->digitalZoomFactor(); + emit digitalZoomChanged(m_digitalZoomValue); + } +} + +void S60CameraFocusControl::resetAdvancedSetting() +{ + m_advancedSettings = m_session->advancedSettings(); +} + +QCameraFocus::FocusPointMode S60CameraFocusControl::focusPointMode() const +{ + // Not supported in Symbian + return QCameraFocus::FocusPointAuto; +} + +void S60CameraFocusControl::setFocusPointMode(QCameraFocus::FocusPointMode mode) +{ + if (mode != QCameraFocus::FocusPointAuto) + m_session->setError(KErrNotSupported, tr("Requested focus point mode is not supported.")); +} + +bool S60CameraFocusControl::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const +{ + // Not supported in Symbian + if (mode == QCameraFocus::FocusPointAuto) + return true; + else + return false; +} + +QPointF S60CameraFocusControl::customFocusPoint() const +{ + // Not supported in Symbian, return image center + return QPointF(0.5, 0.5); +} + +void S60CameraFocusControl::setCustomFocusPoint(const QPointF &point) +{ + // Not supported in Symbian + Q_UNUSED(point); + m_session->setError(KErrNotSupported, tr("Setting custom focus point is not supported.")); +} + +QCameraFocusZoneList S60CameraFocusControl::focusZones() const +{ + // Not supported in Symbian + return QCameraFocusZoneList(); // Return empty list +} + +// End of file + diff --git a/src/plugins/symbian/ecam/s60camerafocuscontrol.h b/src/plugins/symbian/ecam/s60camerafocuscontrol.h new file mode 100644 index 000000000..28c83ed70 --- /dev/null +++ b/src/plugins/symbian/ecam/s60camerafocuscontrol.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERAFOCUSCONTROL_H +#define S60CAMERAFOCUSCONTROL_H + +#include <qcamerafocuscontrol.h> + +#include "s60camerasettings.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; + +/* + * Control for focusing related operations (inc. zooming) + */ +class S60CameraFocusControl : public QCameraFocusControl +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60CameraFocusControl(QObject *parent = 0); + S60CameraFocusControl(S60ImageCaptureSession *session, QObject *parent = 0); + ~S60CameraFocusControl(); + +public: // QCameraFocusControl + + // Focus Mode + QCameraFocus::FocusMode focusMode() const; + void setFocusMode(QCameraFocus::FocusMode mode); + bool isFocusModeSupported(QCameraFocus::FocusMode) const; + + // Zoom + qreal maximumOpticalZoom() const; + qreal maximumDigitalZoom() const; + qreal opticalZoom() const; + qreal digitalZoom() const; + + void zoomTo(qreal optical, qreal digital); + + // Focus Point + QCameraFocus::FocusPointMode focusPointMode() const; + void setFocusPointMode(QCameraFocus::FocusPointMode mode); + bool isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const; + QPointF customFocusPoint() const; + void setCustomFocusPoint(const QPointF &point); + + QCameraFocusZoneList focusZones() const; + +/* +Q_SIGNALS: // QCameraFocusControl + void opticalZoomChanged(qreal opticalZoom); + void digitalZoomChanged(qreal digitalZoom); + void focusZonesChanged(); +*/ + +private slots: // Internal Slots + + void resetAdvancedSetting(); + +private: // Data + S60ImageCaptureSession *m_session; + S60CameraService *m_service; + S60CameraSettings *m_advancedSettings; + bool m_isFocusLocked; + qreal m_opticalZoomValue; + qreal m_digitalZoomValue; + QCameraFocus::FocusMode m_focusMode; +}; + +#endif // S60CAMERAFOCUSCONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.cpp b/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.cpp new file mode 100644 index 000000000..427a3bd97 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> + +#include "s60cameraimagecapturecontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" +#include "s60cameracontrol.h" + +S60CameraImageCaptureControl::S60CameraImageCaptureControl(QObject *parent) : + QCameraImageCaptureControl(parent) +{ +} + +S60CameraImageCaptureControl::S60CameraImageCaptureControl(S60CameraService *service, + S60ImageCaptureSession *session, + QObject *parent) : + QCameraImageCaptureControl(parent), + m_driveMode(QCameraImageCapture::SingleImageCapture) // Default DriveMode +{ + m_session = session; + m_service = service; + m_cameraControl = qobject_cast<S60CameraControl *>(m_service->requestControl(QCameraControl_iid)); + + if (!m_cameraControl) + m_session->setError(KErrGeneral, tr("Unexpected camera error.")); + + // Chain these signals from session class + connect(m_session, SIGNAL(imageCaptured(const int, QImage)), + this, SIGNAL(imageCaptured(const int, QImage))); + connect(m_session, SIGNAL(readyForCaptureChanged(bool)), + this, SIGNAL(readyForCaptureChanged(bool)), Qt::QueuedConnection); + connect(m_session, SIGNAL(imageSaved(const int, const QString&)), + this, SIGNAL(imageSaved(const int, const QString&))); + connect(m_session, SIGNAL(imageExposed(int)), + this, SIGNAL(imageExposed(int))); + connect(m_session, SIGNAL(captureError(int, int, const QString&)), + this, SIGNAL(error(int, int, const QString&))); +} + +S60CameraImageCaptureControl::~S60CameraImageCaptureControl() +{ +} + +bool S60CameraImageCaptureControl::isReadyForCapture() const +{ + if (m_cameraControl && m_cameraControl->captureMode() != QCamera::CaptureStillImage) + return false; + + return m_session->isDeviceReady(); +} + +QCameraImageCapture::DriveMode S60CameraImageCaptureControl::driveMode() const +{ + return m_driveMode; +} + +void S60CameraImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode) +{ + if (mode != QCameraImageCapture::SingleImageCapture) { + emit error((m_session->currentImageId() + 1), QCamera::NotSupportedFeatureError, tr("DriveMode not supported.")); + return; + } + + m_driveMode = mode; +} + +int S60CameraImageCaptureControl::capture(const QString &fileName) +{ + if (m_cameraControl && m_cameraControl->captureMode() != QCamera::CaptureStillImage) { + emit error((m_session->currentImageId() + 1), QCameraImageCapture::NotReadyError, tr("Incorrect CaptureMode.")); + return 0; + } + + int imageId = m_session->capture(fileName); + + return imageId; +} + +void S60CameraImageCaptureControl::cancelCapture() +{ + m_session->cancelCapture(); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.h b/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.h new file mode 100644 index 000000000..4c369e807 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERAIMAGECAPTURECONTROL_H +#define S60CAMERAIMAGECAPTURECONTROL_H + +#include "qcameraimagecapturecontrol.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; +class S60CameraControl; + +/* + * Control for image capture operations. + */ +class S60CameraImageCaptureControl : public QCameraImageCaptureControl +{ + Q_OBJECT + +public: // Contructors & Destrcutor + + S60CameraImageCaptureControl(QObject *parent = 0); + S60CameraImageCaptureControl(S60CameraService *service, + S60ImageCaptureSession *session, + QObject *parent = 0); + ~S60CameraImageCaptureControl(); + +public: // QCameraImageCaptureControl + + bool isReadyForCapture() const; + + // Drive Mode + QCameraImageCapture::DriveMode driveMode() const; + void setDriveMode(QCameraImageCapture::DriveMode mode); + + // Capture + int capture(const QString &fileName); + void cancelCapture(); + +/* +Q_SIGNALS: // QCameraImageCaptureControl + void readyForCaptureChanged(bool); + + void imageExposed(int id); + void imageCaptured(int id, const QImage &preview); + void imageSaved(int id, const QString &fileName); + + void error(int id, int error, const QString &errorString); +*/ + +private: // Data + + S60ImageCaptureSession *m_session; + S60CameraService *m_service; + S60CameraControl *m_cameraControl; + QCameraImageCapture::DriveMode m_driveMode; +}; + +#endif // S60CAMERAIMAGECAPTURECONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.cpp b/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.cpp new file mode 100644 index 000000000..ae2c4535a --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.cpp @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> + +#include "s60cameraimageprocessingcontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" + +S60CameraImageProcessingControl::S60CameraImageProcessingControl(QObject *parent) : + QCameraImageProcessingControl(parent) +{ +} + +S60CameraImageProcessingControl::S60CameraImageProcessingControl(S60ImageCaptureSession *session, QObject *parent) : + QCameraImageProcessingControl(parent), + m_session(0), + m_advancedSettings(0) +{ + m_session = session; + m_advancedSettings = m_session->advancedSettings(); +} + +S60CameraImageProcessingControl::~S60CameraImageProcessingControl() +{ + m_advancedSettings = 0; +} + +void S60CameraImageProcessingControl::resetAdvancedSetting() +{ + m_advancedSettings = m_session->advancedSettings(); +} + +QCameraImageProcessing::WhiteBalanceMode S60CameraImageProcessingControl::whiteBalanceMode() const +{ + return m_session->whiteBalanceMode(); +} + +void S60CameraImageProcessingControl::setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode) +{ + if (isWhiteBalanceModeSupported(mode)) + m_session->setWhiteBalanceMode(mode); + else + m_session->setError(KErrNotSupported, tr("Requested white balance mode is not supported.")); +} + +bool S60CameraImageProcessingControl::isWhiteBalanceModeSupported( + QCameraImageProcessing::WhiteBalanceMode mode) const +{ + return m_session->isWhiteBalanceModeSupported(mode); +} + +int S60CameraImageProcessingControl::manualWhiteBalance() const +{ + return 0; +} + +void S60CameraImageProcessingControl::setManualWhiteBalance(int colorTemperature) +{ + m_session->setError(KErrNotSupported, tr("Setting manual white balance is not supported.")); + Q_UNUSED(colorTemperature) +} + +bool S60CameraImageProcessingControl::isProcessingParameterSupported(ProcessingParameter parameter) const +{ + // First check settings requiring Adv. Settings + if (m_advancedSettings) { + switch (parameter) { + case QCameraImageProcessingControl::Saturation: + return true; + case QCameraImageProcessingControl::Sharpening: + return isSharpeningSupported(); + case QCameraImageProcessingControl::Denoising: + return isDenoisingSupported(); + case QCameraImageProcessingControl::ColorTemperature: + return false; + } + } + + // Then the rest + switch (parameter) { + case QCameraImageProcessingControl::Contrast: + case QCameraImageProcessingControl::Brightness: + return true; + + default: + return false; + } +} + +QVariant S60CameraImageProcessingControl::processingParameter( + QCameraImageProcessingControl::ProcessingParameter parameter) const +{ + switch (parameter) { + case QCameraImageProcessingControl::Contrast: + return QVariant(contrast()); + case QCameraImageProcessingControl::Saturation: + return QVariant(saturation()); + case QCameraImageProcessingControl::Brightness: + return QVariant(brightness()); + case QCameraImageProcessingControl::Sharpening: + return QVariant(sharpeningLevel()); + case QCameraImageProcessingControl::Denoising: + return QVariant(denoisingLevel()); + case QCameraImageProcessingControl::ColorTemperature: + return QVariant(manualWhiteBalance()); + + default: + return QVariant(); + } +} + +void S60CameraImageProcessingControl::setProcessingParameter( + QCameraImageProcessingControl::ProcessingParameter parameter, QVariant value) +{ + switch (parameter) { + case QCameraImageProcessingControl::Contrast: + setContrast(value.toInt()); + break; + case QCameraImageProcessingControl::Saturation: + setSaturation(value.toInt()); + break; + case QCameraImageProcessingControl::Brightness: + setBrightness(value.toInt()); + break; + case QCameraImageProcessingControl::Sharpening: + if (isSharpeningSupported()) + setSharpeningLevel(value.toInt()); + break; + case QCameraImageProcessingControl::Denoising: + if (isDenoisingSupported()) + setDenoisingLevel(value.toInt()); + break; + case QCameraImageProcessingControl::ColorTemperature: + setManualWhiteBalance(value.toInt()); + break; + + default: + break; + } +} + +void S60CameraImageProcessingControl::setContrast(int value) +{ + m_session->setContrast(value); +} + +int S60CameraImageProcessingControl::contrast() const +{ + return m_session->contrast(); +} + +void S60CameraImageProcessingControl::setBrightness(int value) +{ + m_session->setBrightness(value); +} + +int S60CameraImageProcessingControl::brightness() const +{ + return m_session->brightness(); +} + +void S60CameraImageProcessingControl::setSaturation(int value) +{ + if (m_advancedSettings) + m_advancedSettings->setSaturation(value); + else + m_session->setError(KErrNotSupported, tr("Setting saturation is not supported.")); +} + +int S60CameraImageProcessingControl::saturation() const +{ + if (m_advancedSettings) + return m_advancedSettings->saturation(); + return 0; +} + +void S60CameraImageProcessingControl::setDenoisingLevel(int value) +{ + m_session->setError(KErrNotSupported, tr("Setting denoising level is not supported.")); + Q_UNUSED(value); // Not supported for Symbian +} + +bool S60CameraImageProcessingControl::isDenoisingSupported() const +{ + return false; // Not supported for Symbian +} + +int S60CameraImageProcessingControl::denoisingLevel() const +{ + return 0; // Not supported for Symbian +} + +void S60CameraImageProcessingControl::setSharpeningLevel(int value) +{ + if (m_advancedSettings) + m_advancedSettings->setSharpeningLevel(value); + else + m_session->setError(KErrNotSupported, tr("Setting sharpening level is not supported.")); +} + +bool S60CameraImageProcessingControl::isSharpeningSupported() const +{ + if (m_advancedSettings) + return m_advancedSettings->isSharpeningSupported(); + return false; +} + +int S60CameraImageProcessingControl::sharpeningLevel() const +{ + if (m_advancedSettings) + return m_advancedSettings->sharpeningLevel(); + return 0; +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.h b/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.h new file mode 100644 index 000000000..7fc6b8900 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERAIMAGEPROCESSINGCONTROL_H +#define S60CAMERAIMAGEPROCESSINGCONTROL_H + +#include <qcameraimageprocessing.h> +#include <qcameraimageprocessingcontrol.h> + +#include "s60camerasettings.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; + +/* + * Control for image processing related camera operations (inc. white balance). + */ +class S60CameraImageProcessingControl : public QCameraImageProcessingControl +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60CameraImageProcessingControl(QObject *parent = 0); + S60CameraImageProcessingControl(S60ImageCaptureSession *session, QObject *parent = 0); + ~S60CameraImageProcessingControl(); + +public: // QCameraImageProcessingControl + + // White Balance + QCameraImageProcessing::WhiteBalanceMode whiteBalanceMode() const; + void setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode); + bool isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const; + + // Processing Parameter + bool isProcessingParameterSupported(ProcessingParameter parameter) const; + QVariant processingParameter(QCameraImageProcessingControl::ProcessingParameter parameter) const; + void setProcessingParameter(QCameraImageProcessingControl::ProcessingParameter parameter, QVariant value); + +private slots: // Internal Slots + + void resetAdvancedSetting(); + +private: // Internal operations - Implementing ProcessingParameter + + // Manual White Balance (Color Temperature) + int manualWhiteBalance() const; + void setManualWhiteBalance(int colorTemperature); + + // Contrast + int contrast() const; + void setContrast(int value); + + // Brightness + int brightness() const; + void setBrightness(int value); + + // Saturation + int saturation() const; + void setSaturation(int value); + + // Sharpening + bool isSharpeningSupported() const; + int sharpeningLevel() const; + void setSharpeningLevel(int value); + + // Denoising + bool isDenoisingSupported() const; + int denoisingLevel() const; + void setDenoisingLevel(int value); + +private: // Data + + S60ImageCaptureSession *m_session; + S60CameraSettings *m_advancedSettings; +}; + +#endif // S60CAMERAIMAGEPROCESSINGCONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameralockscontrol.cpp b/src/plugins/symbian/ecam/s60cameralockscontrol.cpp new file mode 100644 index 000000000..cce030f22 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameralockscontrol.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <qcamerafocus.h> // FocusMode + +#include "s60cameralockscontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" +#include "s60camerasettings.h" +#include "s60camerafocuscontrol.h" + +S60CameraLocksControl::S60CameraLocksControl(QObject *parent) : + QCameraLocksControl(parent) +{ +} + +S60CameraLocksControl::S60CameraLocksControl(S60CameraService *service, + S60ImageCaptureSession *session, + QObject *parent) : + QCameraLocksControl(parent), + m_session(0), + m_service(0), + m_advancedSettings(0), + m_focusControl(0), + m_focusStatus(QCamera::Unlocked), + m_exposureStatus(QCamera::Unlocked), + m_whiteBalanceStatus(QCamera::Unlocked) +{ + m_session = session; + m_service = service; + m_focusControl = qobject_cast<S60CameraFocusControl *>(m_service->requestControl(QCameraFocusControl_iid)); + + connect(m_session, SIGNAL(advancedSettingChanged()), this, SLOT(resetAdvancedSetting())); + m_advancedSettings = m_session->advancedSettings(); + + // Exposure Lock Signals + if (m_advancedSettings) + connect(m_advancedSettings, SIGNAL(exposureStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)), + this, SLOT(exposureStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason))); + + // Focus Lock Signal + // * S60 3.2 and later (through Adv. Settings) + if (m_advancedSettings) + connect(m_advancedSettings, SIGNAL(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)), + this, SLOT(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason))); + // * S60 3.1 (through ImageSession) + connect(m_session, SIGNAL(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)), + this, SLOT(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason))); +} + +S60CameraLocksControl::~S60CameraLocksControl() +{ + m_advancedSettings = 0; +} + +QCamera::LockTypes S60CameraLocksControl::supportedLocks() const +{ + QCamera::LockTypes supportedLocks = 0; + +#ifdef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.1 + if (m_session) + if (m_session->isFocusSupported()) + supportedLocks |= QCamera::LockFocus; +#else // S60 3.2 and later + if (m_advancedSettings) { + QCameraFocus::FocusModes supportedFocusModes = m_advancedSettings->supportedFocusModes(); + if (supportedFocusModes & QCameraFocus::AutoFocus) + supportedLocks |= QCamera::LockFocus; + + // Exposure/WhiteBalance Locking not implemented in Symbian + // supportedLocks |= QCamera::LockExposure; + // supportedLocks |= QCamera::LockWhiteBalance; + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT + + return supportedLocks; +} + +QCamera::LockStatus S60CameraLocksControl::lockStatus(QCamera::LockType lock) const +{ + switch (lock) { + case QCamera::LockExposure: + return m_exposureStatus; + case QCamera::LockWhiteBalance: + return m_whiteBalanceStatus; + case QCamera::LockFocus: + return m_focusStatus; + + default: + // Unsupported lock + return QCamera::Unlocked; + } +} + +void S60CameraLocksControl::searchAndLock(QCamera::LockTypes locks) +{ + if (locks & QCamera::LockExposure) { + // Not implemented in Symbian + //startExposureLocking(); + } + if (locks & QCamera::LockWhiteBalance) { + // Not implemented in Symbian + } + if (locks & QCamera::LockFocus) + startFocusing(); +} + +void S60CameraLocksControl::unlock(QCamera::LockTypes locks) +{ + if (locks & QCamera::LockExposure) { + // Not implemented in Symbian + //cancelExposureLocking(); + } + + if (locks & QCamera::LockFocus) + cancelFocusing(); +} + +void S60CameraLocksControl::resetAdvancedSetting() +{ + m_advancedSettings = m_session->advancedSettings(); + + // Reconnect Lock Signals + if (m_advancedSettings) { + connect(m_advancedSettings, SIGNAL(exposureStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)), + this, SLOT(exposureStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason))); + connect(m_advancedSettings, SIGNAL(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)), + this, SLOT(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason))); + } +} + +void S60CameraLocksControl::exposureStatusChanged(QCamera::LockStatus status, + QCamera::LockChangeReason reason) +{ + if(status != m_exposureStatus) { + m_exposureStatus = status; + emit lockStatusChanged(QCamera::LockExposure, status, reason); + } +} + +void S60CameraLocksControl::focusStatusChanged(QCamera::LockStatus status, + QCamera::LockChangeReason reason) +{ + if(status != m_focusStatus) { + m_focusStatus = status; + emit lockStatusChanged(QCamera::LockFocus, status, reason); + } +} + +void S60CameraLocksControl::startFocusing() +{ +#ifndef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.2 or later + // Focusing is triggered on Symbian by setting the FocusType corresponding + // to the FocusMode set to FocusControl + if (m_focusControl) { + if (m_advancedSettings) { + m_advancedSettings->startFocusing(); + m_focusStatus = QCamera::Searching; + emit lockStatusChanged(QCamera::LockFocus, QCamera::Searching, QCamera::UserRequest); + } + else + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::LockFailed); + } + else + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::LockFailed); + +#else // S60 3.1 + if (m_focusControl && m_focusControl->focusMode() == QCameraFocus::AutoFocus) { + m_session->startFocus(); + m_focusStatus = QCamera::Searching; + emit lockStatusChanged(QCamera::LockFocus, QCamera::Searching, QCamera::UserRequest); + } + else + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::LockFailed); +#endif // S60_CAM_AUTOFOCUS_SUPPORT +} + +void S60CameraLocksControl::cancelFocusing() +{ + if (m_focusStatus == QCamera::Unlocked) + return; + +#ifndef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.2 or later + if (m_advancedSettings) { + m_advancedSettings->cancelFocusing(); + m_focusStatus = QCamera::Unlocked; + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::UserRequest); + } + else + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::LockFailed); + +#else // S60 3.1 + m_session->cancelFocus(); + m_focusStatus = QCamera::Unlocked; + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::UserRequest); +#endif // S60_CAM_AUTOFOCUS_SUPPORT +} + +void S60CameraLocksControl::startExposureLocking() +{ + if (m_advancedSettings) { + m_advancedSettings->lockExposure(true); + m_exposureStatus = QCamera::Searching; + emit lockStatusChanged(QCamera::LockExposure, QCamera::Searching, QCamera::UserRequest); + } + else + emit lockStatusChanged(QCamera::LockExposure, QCamera::Unlocked, QCamera::LockFailed); +} + +void S60CameraLocksControl::cancelExposureLocking() +{ + if (m_exposureStatus == QCamera::Unlocked) + return; + + if (m_advancedSettings) { + m_advancedSettings->lockExposure(false); + m_exposureStatus = QCamera::Unlocked; + emit lockStatusChanged(QCamera::LockExposure, QCamera::Unlocked, QCamera::UserRequest); + } + else + emit lockStatusChanged(QCamera::LockExposure, QCamera::Unlocked, QCamera::LockFailed); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameralockscontrol.h b/src/plugins/symbian/ecam/s60cameralockscontrol.h new file mode 100644 index 000000000..3b49cbaba --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameralockscontrol.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERALOCKSCONTROL_H +#define S60CAMERALOCKSCONTROL_H + +#include <QtCore/qobject.h> +#include "qcameralockscontrol.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; +class S60CameraSettings; +class S60CameraFocusControl; + +/* + * Control for searching and locking 3A algorithms (AutoFocus, AutoExposure + * and AutoWhitebalance). + */ +class S60CameraLocksControl : public QCameraLocksControl +{ + Q_OBJECT + +public: // Contructors & Destrcutor + + S60CameraLocksControl(QObject *parent = 0); + S60CameraLocksControl(S60CameraService *service, + S60ImageCaptureSession *session, + QObject *parent = 0); + ~S60CameraLocksControl(); + +public: // QCameraLocksControl + + QCamera::LockTypes supportedLocks() const; + + QCamera::LockStatus lockStatus(QCamera::LockType lock) const; + + void searchAndLock(QCamera::LockTypes locks); + void unlock(QCamera::LockTypes locks); + +/* +Q_SIGNALS: // QCameraLocksControl + + void lockStatusChanged(QCamera::LockType type, + QCamera::LockStatus status, + QCamera::LockChangeReason reason); +*/ + +private slots: // Internal Slots + + void exposureStatusChanged(QCamera::LockStatus status, QCamera::LockChangeReason reason); + void focusStatusChanged(QCamera::LockStatus status, QCamera::LockChangeReason reason); + void resetAdvancedSetting(); + +private: // Internal + + // Focus + void startFocusing(); + void cancelFocusing(); + + // Exposure + void startExposureLocking(); + void cancelExposureLocking(); + +private: // Data + + S60ImageCaptureSession *m_session; + S60CameraService *m_service; + S60CameraSettings *m_advancedSettings; + S60CameraFocusControl *m_focusControl; + QCamera::LockStatus m_focusStatus; + QCamera::LockStatus m_exposureStatus; + QCamera::LockStatus m_whiteBalanceStatus; +}; + +#endif // S60CAMERALOCKSCONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraservice.cpp b/src/plugins/symbian/ecam/s60cameraservice.cpp new file mode 100644 index 000000000..5cc4485c1 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraservice.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qvariant.h> +#include <QtGui/qwidget.h> +#include <QtCore/qlist.h> + +#include "s60cameraservice.h" +#include "s60cameracontrol.h" +#include "s60videodevicecontrol.h" +#include "s60camerafocuscontrol.h" +#include "s60cameraexposurecontrol.h" +#include "s60cameraflashcontrol.h" +#include "s60cameraimageprocessingcontrol.h" +#include "s60cameraimagecapturecontrol.h" +#include "s60mediarecordercontrol.h" +#include "s60videocapturesession.h" +#include "s60imagecapturesession.h" +#include "s60videowidgetcontrol.h" +#include "s60mediacontainercontrol.h" +#include "s60videoencodercontrol.h" +#include "s60audioencodercontrol.h" +#include "s60imageencodercontrol.h" +#include "s60cameralockscontrol.h" +#include "s60videorenderercontrol.h" +#include "s60videowindowcontrol.h" + +#include "s60cameraviewfinderengine.h" // ViewfinderOutputType + +S60CameraService::S60CameraService(QObject *parent) : + QMediaService(parent) +{ + // Session classes for video and image capturing + m_imagesession = new S60ImageCaptureSession(this); + m_videosession = new S60VideoCaptureSession(this); + + if (m_imagesession && m_videosession) { + // Different control classes implementing the Camera API + m_control = new S60CameraControl(m_videosession, m_imagesession, this); + m_videoDeviceControl = new S60VideoDeviceControl(m_control, this); + m_focusControl = new S60CameraFocusControl(m_imagesession, this); + m_exposureControl = new S60CameraExposureControl(m_imagesession, this); + m_flashControl = new S60CameraFlashControl(m_imagesession, this); + m_imageProcessingControl = new S60CameraImageProcessingControl(m_imagesession, this); + m_imageCaptureControl = new S60CameraImageCaptureControl(this, m_imagesession, this); + m_media = new S60MediaRecorderControl(this, m_videosession, this); + m_mediaFormat = new S60MediaContainerControl(m_videosession, this); + m_videoEncoder = new S60VideoEncoderControl(m_videosession, this); + m_audioEncoder = new S60AudioEncoderControl(m_videosession, this); + m_viewFinderWidget = new S60VideoWidgetControl(this); + m_imageEncoderControl = new S60ImageEncoderControl(m_imagesession, this); + m_locksControl = new S60CameraLocksControl(this, m_imagesession, this); + m_rendererControl = new S60VideoRendererControl(this); + m_windowControl = new S60VideoWindowControl(this); + } +} + +S60CameraService::~S60CameraService() +{ + // Delete controls + if (m_videoDeviceControl) + delete m_videoDeviceControl; + if (m_focusControl) + delete m_focusControl; + if (m_exposureControl) + delete m_exposureControl; + if (m_flashControl) + delete m_flashControl; + if (m_imageProcessingControl) + delete m_imageProcessingControl; + if (m_imageCaptureControl) + delete m_imageCaptureControl; + if (m_media) + delete m_media; + if (m_mediaFormat) + delete m_mediaFormat; + if (m_videoEncoder) + delete m_videoEncoder; + if (m_audioEncoder) + delete m_audioEncoder; + if (m_imageEncoderControl) + delete m_imageEncoderControl; + if (m_locksControl) + delete m_locksControl; + + // CameraControl destroys: + // * ViewfinderEngine + // * CameraEngine + if (m_control) + delete m_control; + + // Delete viewfinder controls after CameraControl to be sure that + // ViewFinder gets stopped before widget (and window) is destroyed + if (m_viewFinderWidget) + delete m_viewFinderWidget; + if (m_rendererControl) + delete m_rendererControl; + if (m_windowControl) + delete m_windowControl; + + // Delete sessions + if (m_videosession) + delete m_videosession; + if (m_imagesession) + delete m_imagesession; +} + +QMediaControl *S60CameraService::requestControl(const char *name) +{ + if (qstrcmp(name, QMediaRecorderControl_iid) == 0) + return m_media; + + if (qstrcmp(name, QCameraControl_iid) == 0) + return m_control; + + if (qstrcmp(name, QVideoEncoderControl_iid) == 0) + return m_videoEncoder; + + if (qstrcmp(name, QAudioEncoderControl_iid) == 0) + return m_audioEncoder; + + if (qstrcmp(name, QMediaContainerControl_iid) == 0) + return m_mediaFormat; + + if (qstrcmp(name, QCameraExposureControl_iid) == 0) + return m_exposureControl; + + if (qstrcmp(name, QCameraFlashControl_iid) == 0) + return m_flashControl; + + if (qstrcmp(name, QVideoWidgetControl_iid) == 0) { + if (m_viewFinderWidget) { + m_control->setVideoOutput(m_viewFinderWidget, + S60CameraViewfinderEngine::OutputTypeVideoWidget); + return m_viewFinderWidget; + } + else + return 0; + } + + if (qstrcmp(name, QVideoRendererControl_iid) == 0) { + if (m_rendererControl) { + m_control->setVideoOutput(m_rendererControl, + S60CameraViewfinderEngine::OutputTypeRenderer); + return m_rendererControl; + } + else + return 0; + } + + if (qstrcmp(name, QVideoWindowControl_iid) == 0) { + if (m_windowControl) { + m_control->setVideoOutput(m_windowControl, + S60CameraViewfinderEngine::OutputTypeVideoWindow); + return m_windowControl; + } + else + return 0; + } + + + if (qstrcmp(name, QCameraFocusControl_iid) == 0) + return m_focusControl; + + if (qstrcmp(name, QCameraImageProcessingControl_iid) == 0) + return m_imageProcessingControl; + + if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) + return m_imageCaptureControl; + + if (qstrcmp(name, QVideoDeviceControl_iid) == 0) + return m_videoDeviceControl; + + if (qstrcmp(name, QImageEncoderControl_iid) == 0) + return m_imageEncoderControl; + + if (qstrcmp(name, QCameraLocksControl_iid) == 0) + return m_locksControl; + + return 0; +} + +void S60CameraService::releaseControl(QMediaControl *control) +{ + if (control == 0) + return; + + // Release viewfinder output + if (control == m_viewFinderWidget) { + if (m_viewFinderWidget) + m_control->releaseVideoOutput(S60CameraViewfinderEngine::OutputTypeVideoWidget); + } + + if (control == m_rendererControl) { + if (m_rendererControl) + m_control->releaseVideoOutput(S60CameraViewfinderEngine::OutputTypeRenderer); + } + + if (control == m_windowControl) { + if (m_windowControl) + m_control->releaseVideoOutput(S60CameraViewfinderEngine::OutputTypeVideoWindow); + } +} + +int S60CameraService::deviceCount() +{ + return S60CameraControl::deviceCount(); +} + +QString S60CameraService::deviceDescription(const int index) +{ + return S60CameraControl::description(index); +} + +QString S60CameraService::deviceName(const int index) +{ + return S60CameraControl::name(index); +} + +// End of file + diff --git a/src/plugins/symbian/ecam/s60cameraservice.h b/src/plugins/symbian/ecam/s60cameraservice.h new file mode 100644 index 000000000..a2744c1fa --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraservice.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERASERVICE_H +#define S60CAMERASERVICE_H + +#include <QtCore/qobject.h> +#include <qmediaservice.h> + +QT_USE_NAMESPACE + +class S60MediaContainerControl; +class S60VideoEncoderControl; +class S60AudioEncoderControl; +class S60CameraControl; +class S60VideoDeviceControl; +class S60MediaRecorderControl; +class S60ImageCaptureSession; +class S60VideoCaptureSession; +class S60CameraFocusControl; +class S60CameraExposureControl; +class S60CameraFlashControl; +class S60CameraImageProcessingControl; +class S60CameraImageCaptureControl; +class S60VideoWidgetControl; +class S60ImageEncoderControl; +class S60CameraLocksControl; +class S60VideoRendererControl; +class S60VideoWindowControl; + +class S60CameraService : public QMediaService +{ + Q_OBJECT + +public: // Contructor & Destructor + + S60CameraService(QObject *parent = 0); + ~S60CameraService(); + +public: // QMediaService + + QMediaControl *requestControl(const char *name); + void releaseControl(QMediaControl *control); + +public: // Static Device Info + + static int deviceCount(); + static QString deviceName(const int index); + static QString deviceDescription(const int index); + +private: // Data + + S60ImageCaptureSession *m_imagesession; + S60VideoCaptureSession *m_videosession; + S60MediaContainerControl *m_mediaFormat; + S60VideoEncoderControl *m_videoEncoder; + S60AudioEncoderControl *m_audioEncoder; + S60CameraControl *m_control; + S60VideoDeviceControl *m_videoDeviceControl; + S60CameraFocusControl *m_focusControl; + S60CameraExposureControl *m_exposureControl; + S60CameraFlashControl *m_flashControl; + S60CameraImageProcessingControl *m_imageProcessingControl; + S60CameraImageCaptureControl *m_imageCaptureControl; + S60MediaRecorderControl *m_media; + S60VideoWidgetControl *m_viewFinderWidget; + S60ImageEncoderControl *m_imageEncoderControl; + S60CameraLocksControl *m_locksControl; + S60VideoRendererControl *m_rendererControl; + S60VideoWindowControl *m_windowControl; +}; + +#endif // S60CAMERASERVICE_H diff --git a/src/plugins/symbian/ecam/s60cameraserviceplugin.cpp b/src/plugins/symbian/ecam/s60cameraserviceplugin.cpp new file mode 100644 index 000000000..8f22fd205 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraserviceplugin.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> + +#include "s60cameraserviceplugin.h" +#ifdef QMEDIA_SYMBIAN_CAMERA +#include "s60cameraservice.h" +#endif + +QStringList S60CameraServicePlugin::keys() const +{ + QStringList list; +#ifdef QMEDIA_SYMBIAN_CAMERA + list << QLatin1String(Q_MEDIASERVICE_CAMERA); +#endif + return list; +} + +QMediaService* S60CameraServicePlugin::create(QString const& key) +{ +#ifdef QMEDIA_SYMBIAN_CAMERA + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) + return new S60CameraService; +#endif + return 0; +} + +void S60CameraServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QList<QByteArray> S60CameraServicePlugin::devices(const QByteArray &service) const +{ +#ifdef QMEDIA_SYMBIAN_CAMERA + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + return m_cameraDevices; + } +#endif + return QList<QByteArray>(); +} + +QString S60CameraServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) +{ +#ifdef QMEDIA_SYMBIAN_CAMERA + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + for (int i=0; i<m_cameraDevices.count(); i++) + if (m_cameraDevices[i] == device) + return m_cameraDescriptions[i]; + } +#endif + return QString(); +} + +void S60CameraServicePlugin::updateDevices() const +{ +#ifdef QMEDIA_SYMBIAN_CAMERA + m_cameraDevices.clear(); + m_cameraDescriptions.clear(); + for (int i=0; i < S60CameraService::deviceCount(); i ++) { + m_cameraDevices.append(S60CameraService::deviceName(i).toUtf8()); + m_cameraDescriptions.append(S60CameraService::deviceDescription(i)); + } +#endif +} + +Q_EXPORT_PLUGIN2(qtmultimedia_ecamngine, S60CameraServicePlugin); + +// End of file + diff --git a/src/plugins/symbian/ecam/s60cameraserviceplugin.h b/src/plugins/symbian/ecam/s60cameraserviceplugin.h new file mode 100644 index 000000000..c56b054c6 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraserviceplugin.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef S60CAMERASERVICEPLUGIN_H +#define S60CAMERASERVICEPLUGIN_H + +#include <qmediaservice.h> +#include <qmediaserviceproviderplugin.h> + +QT_USE_NAMESPACE + +/* + * Plugin implementation for the Camera Service + */ +class S60CameraServicePlugin : public QMediaServiceProviderPlugin, + public QMediaServiceSupportedDevicesInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedDevicesInterface) + +public: // QMediaServiceProviderPlugin + + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); + +public: // QMediaServiceSupportedDevicesInterface + + QList<QByteArray> devices(const QByteArray &service) const; + QString deviceDescription(const QByteArray &service, const QByteArray &device); + +private: // Internal + + void updateDevices() const; + +private: // Data + + mutable QList<QByteArray> m_cameraDevices; + mutable QStringList m_cameraDescriptions; +}; + +#endif // S60CAMERASERVICEPLUGIN_H diff --git a/src/plugins/symbian/ecam/s60camerasettings.cpp b/src/plugins/symbian/ecam/s60camerasettings.cpp new file mode 100644 index 000000000..a5918078f --- /dev/null +++ b/src/plugins/symbian/ecam/s60camerasettings.cpp @@ -0,0 +1,986 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60camerasettings.h" +#include "s60cameraconstants.h" + +// S60 3.2 Platform +#ifdef USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER +#define POST_31_PLATFORM +#include <ecamadvancedsettings.h> // CCameraAdvancedSettings (inc. TValueInfo) +#endif // S60 3.2 + +// S60 5.0 or later +#ifdef USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER +#define POST_31_PLATFORM +#include <ecamadvsettings.h> // CCameraAdvancedSettings +#include <ecam/ecamconstants.h> // TValueInfo +#endif // S60 5.0 or later + +S60CameraSettings::S60CameraSettings(QObject *parent, CCameraEngine *engine) : + QObject(parent), +#ifndef S60_31_PLATFORM // Post S60 3.1 Platforms + m_advancedSettings(0), + m_imageProcessingSettings(0), +#endif // S60_31_PLATFORM + m_cameraEngine(engine), + m_continuousFocusing(false) +{ +} + +S60CameraSettings::~S60CameraSettings() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + delete m_advancedSettings; + m_advancedSettings = 0; + } + + if (m_imageProcessingSettings) { + delete m_imageProcessingSettings; + m_imageProcessingSettings = 0; + } +#endif // POST_31_PLATFORM +} + +/* + * This is Symbian NewL kind of consructor, but unlike Symbian version this + * constructor will not leave, but instead it will return possible errors in + * the error variable. This is to be able to write the class without deriving + * it form CBase. Also CleanupStack is cleaned here if the ConstructL leaves. + */ +S60CameraSettings* S60CameraSettings::New(int &error, QObject *parent, CCameraEngine *engine) +{ + S60CameraSettings* self = new S60CameraSettings(parent, engine); + if (!self) { + error = KErrNoMemory; + return 0; + } + + TRAPD(err, self->ConstructL()); + if (err) { + // Clean created object + delete self; + self = 0; + error = err; + return 0; + } + + error = KErrNone; + return self; +} + +void S60CameraSettings::ConstructL() +{ +#ifdef POST_31_PLATFORM + if (!m_cameraEngine) + User::Leave(KErrGeneral); + // From now on it is safe to assume engine exists + + // If no AdvancedSettings is available, there's no benefit of S60CameraSettings + // Leave if creation fails + m_advancedSettings = CCamera::CCameraAdvancedSettings::NewL(*m_cameraEngine->Camera()); + CleanupStack::PushL(m_advancedSettings); + + // ImageProcessing module may not be supported, don't Leave + TRAPD(err, m_imageProcessingSettings = CCamera::CCameraImageProcessing::NewL(*m_cameraEngine->Camera())); + if (err == KErrNone && m_imageProcessingSettings) { + CleanupStack::PushL(m_imageProcessingSettings); + } else { + if (err == KErrNotSupported) + m_imageProcessingSettings = 0; + else { + // Leave with error + if (!m_imageProcessingSettings) + User::Leave(KErrNoMemory); + else + User::Leave(err); + } + } + + if (m_advancedSettings) { + RArray<TInt> digitalZoomFactors; + CleanupClosePushL(digitalZoomFactors); + + TValueInfo info = ENotActive; + m_advancedSettings->GetDigitalZoomStepsL(digitalZoomFactors, info); + + for (int i = 0; i < digitalZoomFactors.Count(); ++i) + m_supportedSymbianDigitalZoomFactors << digitalZoomFactors[i]; + + CleanupStack::PopAndDestroy(); // RArray<TInt> digitalZoomFactors + } + + // Pop objects from CleanupStack + if (m_imageProcessingSettings) + CleanupStack::Pop(m_imageProcessingSettings); + CleanupStack::Pop(m_advancedSettings); + +#else // S60 3.1 + // AdvancedSettings are not suppoted on S60 3.1 (There's no use for S60CameraSettings) + User::Leave(KErrNotSupported); +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setFocusMode(QCameraFocus::FocusMode mode) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + switch (mode) { + case QCameraFocus::ManualFocus: // Manual focus mode + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeManual); + m_continuousFocusing = false; + break; + case QCameraFocus::AutoFocus: // Single-shot AutoFocus mode + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeAuto); + m_advancedSettings->SetFocusRange(CCamera::CCameraAdvancedSettings::EFocusRangeAuto); + m_continuousFocusing = false; + break; + case QCameraFocus::HyperfocalFocus: + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeAuto); + m_advancedSettings->SetFocusRange(CCamera::CCameraAdvancedSettings::EFocusRangeHyperfocal); + m_continuousFocusing = false; + break; + case QCameraFocus::InfinityFocus: + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeAuto); + m_advancedSettings->SetFocusRange(CCamera::CCameraAdvancedSettings::EFocusRangeInfinite); + m_continuousFocusing = false; + break; + case QCameraFocus::ContinuousFocus: + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeAuto); + m_advancedSettings->SetFocusRange(CCamera::CCameraAdvancedSettings::EFocusRangeAuto); + m_continuousFocusing = true; + break; + case QCameraFocus::MacroFocus: + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeAuto); + m_advancedSettings->SetFocusRange(CCamera::CCameraAdvancedSettings::EFocusRangeMacro); + m_continuousFocusing = false; + break; + + default: + emit error(QCamera::NotSupportedFeatureError, tr("Requested focus mode is not supported.")); + break; + } + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#else // S60 3.1 + Q_UNUSED(mode); + emit error(QCamera::NotSupportedFeatureError, tr("Settings focus mode is not supported.")); +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::startFocusing() +{ +#ifdef POST_31_PLATFORM + // Setting AutoFocusType triggers the focusing on Symbian + if (m_advancedSettings) { + if (m_continuousFocusing) + m_advancedSettings->SetAutoFocusType(CCamera::CCameraAdvancedSettings::EAutoFocusTypeContinuous); + else + m_advancedSettings->SetAutoFocusType(CCamera::CCameraAdvancedSettings::EAutoFocusTypeSingle); + } else { + emit error(QCamera::CameraError, tr("Unable to focus.")); + } +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::cancelFocusing() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) + m_advancedSettings->SetAutoFocusType(CCamera::CCameraAdvancedSettings::EAutoFocusTypeOff); + else + emit error(QCamera::CameraError, tr("Unable to cancel focusing.")); +#endif // POST_31_PLATFORM +} + +QCameraFocus::FocusMode S60CameraSettings::focusMode() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + // First request needed info + CCamera::CCameraAdvancedSettings::TFocusMode mode = m_advancedSettings->FocusMode(); + CCamera::CCameraAdvancedSettings::TFocusRange range = m_advancedSettings->FocusRange(); + CCamera::CCameraAdvancedSettings::TAutoFocusType autoType = m_advancedSettings->AutoFocusType(); + + switch (mode) { + case CCamera::CCameraAdvancedSettings::EFocusModeManual: + case CCamera::CCameraAdvancedSettings::EFocusModeFixed: + return QCameraFocus::ManualFocus; + + case CCamera::CCameraAdvancedSettings::EFocusModeAuto: + if (autoType == CCamera::CCameraAdvancedSettings::EAutoFocusTypeContinuous) { + return QCameraFocus::ContinuousFocus; + } else { + // Single-shot focusing + switch (range) { + case CCamera::CCameraAdvancedSettings::EFocusRangeMacro: + case CCamera::CCameraAdvancedSettings::EFocusRangeSuperMacro: + return QCameraFocus::MacroFocus; + case CCamera::CCameraAdvancedSettings::EFocusRangeHyperfocal: + return QCameraFocus::HyperfocalFocus; + case CCamera::CCameraAdvancedSettings::EFocusRangeInfinite: + return QCameraFocus::InfinityFocus; + case CCamera::CCameraAdvancedSettings::EFocusRangeAuto: + case CCamera::CCameraAdvancedSettings::EFocusRangeNormal: + return QCameraFocus::AutoFocus; + + default: + return QCameraFocus::AutoFocus; + } + } + default: + return QCameraFocus::AutoFocus; // Return automatic focusing + } + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#endif // POST_31_PLATFORM + return QCameraFocus::AutoFocus; // Return automatic focusing +} + +QCameraFocus::FocusModes S60CameraSettings::supportedFocusModes() +{ + QCameraFocus::FocusModes modes = 0; + +#ifdef POST_31_PLATFORM + TInt supportedModes = 0; + TInt autoFocusTypes = 0; + TInt supportedRanges = 0; + + if (m_advancedSettings) { + supportedModes = m_advancedSettings->SupportedFocusModes(); + autoFocusTypes = m_advancedSettings->SupportedAutoFocusTypes(); + supportedRanges = m_advancedSettings->SupportedFocusRanges(); + + if (supportedModes == 0 || autoFocusTypes == 0 || supportedRanges == 0) + return modes; + + // EFocusModeAuto is the only supported on Symbian + if (supportedModes & CCamera::CCameraAdvancedSettings::EFocusModeAuto) { + // Check supported types (Single-shot Auto vs. Continuous) + if (autoFocusTypes & CCamera::CCameraAdvancedSettings::EAutoFocusTypeSingle) + modes |= QCameraFocus::AutoFocus; + if (autoFocusTypes & CCamera::CCameraAdvancedSettings::EAutoFocusTypeContinuous) + modes |= QCameraFocus::ContinuousFocus; + + // Check supported ranges (Note! Some are actually fixed focuses + // even though the mode is Auto on Symbian) + if (supportedRanges & CCamera::CCameraAdvancedSettings::EFocusRangeMacro) + modes |= QCameraFocus::MacroFocus; + if (supportedRanges & CCamera::CCameraAdvancedSettings::EFocusRangeHyperfocal) + modes |= QCameraFocus::HyperfocalFocus; + if (supportedRanges & CCamera::CCameraAdvancedSettings::EFocusRangeInfinite) + modes |= QCameraFocus::InfinityFocus; + } + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#endif // POST_31_PLATFORM + + return modes; +} + +qreal S60CameraSettings::opticalZoomFactorL() const +{ + // Not supported on Symbian + return 1.0; +} + +void S60CameraSettings::setOpticalZoomFactorL(const qreal zoomFactor) +{ + // Not supported on Symbian + Q_UNUSED(zoomFactor); +} + +QList<qreal> S60CameraSettings::supportedDigitalZoomFactors() const +{ + QList<qreal> zoomFactors; + foreach (int factor, m_supportedSymbianDigitalZoomFactors) + zoomFactors << qreal(factor) / KSymbianFineResolutionFactor; + + return zoomFactors; +} + +qreal S60CameraSettings::digitalZoomFactorL() const +{ + qreal factor = 1.0; + +#ifdef POST_31_PLATFORM + int symbianFactor = 0; + if (m_advancedSettings) + symbianFactor = m_advancedSettings->DigitalZoom(); + else + User::Leave(KErrNotSupported); + + if (symbianFactor != 0) + factor = qreal(symbianFactor) / KSymbianFineResolutionFactor; +#endif // POST_31_PLATFORM + + return factor; +} + +void S60CameraSettings::setDigitalZoomFactorL(const qreal zoomFactor) +{ +#ifdef POST_31_PLATFORM + int symbianFactor = zoomFactor * KSymbianFineResolutionFactor; + + // Find closest supported Symbian ZoomFactor if needed + if (!m_supportedSymbianDigitalZoomFactors.contains(symbianFactor)) { + int closestIndex = -1; + int closestDiff = 1000000; // Sensible maximum + for (int i = 0; i < m_supportedSymbianDigitalZoomFactors.count(); ++i) { + int diff = abs(m_supportedSymbianDigitalZoomFactors.at(i) - symbianFactor); + if (diff < closestDiff) { + closestDiff = diff; + closestIndex = i; + } + } + if (closestIndex != -1) + symbianFactor = m_supportedSymbianDigitalZoomFactors.at(closestIndex); + else + User::Leave(KErrGeneral); + } + if (m_advancedSettings) + m_advancedSettings->SetDigitalZoom(symbianFactor); + else + User::Leave(KErrNotSupported); +#else // S60 3.1 Platform + Q_UNUSED(zoomFactor); + emit error(QCamera::NotSupportedFeatureError, tr("Settings digital zoom factor is not supported.")); +#endif // POST_31_PLATFORM +} + +// MCameraObserver2 +void S60CameraSettings::HandleAdvancedEvent(const TECAMEvent& aEvent) +{ +#ifdef POST_31_PLATFORM + + if (aEvent.iErrorCode != KErrNone) { + switch (aEvent.iErrorCode) { + case KErrECamCameraDisabled: + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return; + case KErrECamSettingDisabled: + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return; + case KErrECamParameterNotInRange: + emit error(QCamera::NotSupportedFeatureError, tr("Requested value is not in supported range.")); + return; + case KErrECamSettingNotSupported: + emit error(QCamera::NotSupportedFeatureError, tr("Requested setting is not supported.")); + return; + case KErrECamNotOptimalFocus: + if (m_continuousFocusing) + emit focusStatusChanged(QCamera::Searching, QCamera::LockTemporaryLost); + else + emit focusStatusChanged(QCamera::Unlocked, QCamera::LockFailed); + return; + } + + if (aEvent.iEventType == KUidECamEventCameraSettingFocusRange || + aEvent.iEventType == KUidECamEventCameraSettingAutoFocusType2) { + emit focusStatusChanged(QCamera::Unlocked, QCamera::LockFailed); + return; + } else if (aEvent.iEventType == KUidECamEventCameraSettingIsoRate) { + if (aEvent.iErrorCode == KErrNotSupported) + emit error(QCamera::NotSupportedFeatureError, tr("Requested ISO value is not supported.")); + else + emit error(QCamera::CameraError, tr("Setting ISO value failed.")); + return; + } else if (aEvent.iEventType == KUidECamEventCameraSettingAperture) { + if (aEvent.iErrorCode == KErrNotSupported) + emit error(QCamera::NotSupportedFeatureError, tr("Requested aperture value is not supported.")); + else + emit error(QCamera::CameraError, tr("Setting aperture value failed.")); + return; + } else if (aEvent.iEventType == KUidECamEventCameraSettingExposureCompensation) { + if (aEvent.iErrorCode == KErrNotSupported) + emit error(QCamera::NotSupportedFeatureError, tr("Requested exposure compensation is not supported.")); + else + emit error(QCamera::CameraError, tr("Setting exposure compensation failed.")); + return; + } else if (aEvent.iEventType == KUidECamEventCameraSettingOpticalZoom || + aEvent.iEventType == KUidECamEventCameraSettingDigitalZoom) { + if (aEvent.iErrorCode == KErrNotSupported) + return; // Discard + else { + emit error(QCamera::CameraError, tr("Setting zoom factor failed.")); + return; + } + } else if (aEvent.iEventType == KUidECamEventCameraSettingFocusMode) { + if (aEvent.iErrorCode == KErrNotSupported) + if (m_cameraEngine && m_cameraEngine->CurrentCameraIndex() != 0) + emit error(QCamera::NotSupportedFeatureError, tr("Focusing is not supported with this camera.")); + else + emit error(QCamera::NotSupportedFeatureError, tr("Requested focus mode is not supported.")); + else + emit error(QCamera::CameraError, tr("Setting focus mode failed.")); + return; + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return; + } + } + + if (aEvent.iEventType == KUidECamEventCameraSettingExposureLock) { + if (m_advancedSettings) { + if (m_advancedSettings->ExposureLockOn()) + emit exposureStatusChanged(QCamera::Locked, QCamera::LockAcquired); + else + emit exposureStatusChanged(QCamera::Unlocked, QCamera::LockLost); + } + else + emit exposureStatusChanged(QCamera::Unlocked, QCamera::LockLost); + } + else if (aEvent.iEventType == KUidECamEventCameraSettingAperture) + emit apertureChanged(); + + else if (aEvent.iEventType == KUidECamEventCameraSettingApertureRange) + emit apertureRangeChanged(); + + else if (aEvent.iEventType == KUidECamEventCameraSettingIsoRateType) + emit isoSensitivityChanged(); + + else if (aEvent.iEventType == KUidECamEventCameraSettingShutterSpeed) + emit shutterSpeedChanged(); + + else if (aEvent.iEventType == KUidECamEventCameraSettingExposureCompensationStep) + emit evChanged(); + + else if (aEvent.iEventType == KUidECamEventFlashReady) + emit flashReady(true); + + else if (aEvent.iEventType == KUidECamEventFlashNotReady) + emit flashReady(false); + + else if (aEvent.iEventType.iUid == KUidECamEventCameraSettingsOptimalFocusUidValue) + emit focusStatusChanged(QCamera::Locked, QCamera::LockAcquired); + +#else // S60 3.1 Platform + Q_UNUSED(aEvent); +#endif // POST_31_PLATFORM +} + +bool S60CameraSettings::isFlashReady() +{ + TBool isReady = false; +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + int flashErr = m_advancedSettings->IsFlashReady(isReady); + if(flashErr != KErrNone) { + if (flashErr != KErrNotSupported) + emit error(QCamera::CameraError, tr("Unexpected error with flash.")); + return false; + } + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#endif + return isReady; +} + +QCameraExposure::MeteringMode S60CameraSettings::meteringMode() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + CCamera::CCameraAdvancedSettings::TMeteringMode mode = m_advancedSettings->MeteringMode(); + switch (mode) { + case CCamera::CCameraAdvancedSettings::EMeteringModeCenterWeighted: + return QCameraExposure::MeteringAverage; + case CCamera::CCameraAdvancedSettings::EMeteringModeEvaluative: + return QCameraExposure::MeteringMatrix; + case CCamera::CCameraAdvancedSettings::EMeteringModeSpot: + return QCameraExposure::MeteringSpot; + + default: + return QCameraExposure::MeteringAverage; + } + }else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return QCameraExposure::MeteringAverage; + } +#else // S60 3.1 Platform + return QCameraExposure::MeteringAverage; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setMeteringMode(QCameraExposure::MeteringMode mode) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + switch(mode) { + case QCameraExposure::MeteringAverage: + m_advancedSettings->SetMeteringMode(CCamera::CCameraAdvancedSettings::EMeteringModeCenterWeighted); + break; + case QCameraExposure::MeteringMatrix: + m_advancedSettings->SetMeteringMode(CCamera::CCameraAdvancedSettings::EMeteringModeEvaluative); + break; + case QCameraExposure::MeteringSpot: + m_advancedSettings->SetMeteringMode(CCamera::CCameraAdvancedSettings::EMeteringModeSpot); + break; + default: + break; + } + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#else // S60 3.1 + Q_UNUSED(mode); + emit error(QCamera::NotSupportedFeatureError, tr("Setting metering mode is not supported.")); +#endif // POST_31_PLATFORM +} + +bool S60CameraSettings::isMeteringModeSupported(QCameraExposure::MeteringMode mode) +{ +#ifdef POST_31_PLATFORM + TInt supportedModes = 0; + + if (m_advancedSettings) { + supportedModes = m_advancedSettings->SupportedMeteringModes(); + if (supportedModes == 0) + return false; + + switch (mode) { + case QCameraExposure::MeteringMatrix: + if (supportedModes & CCamera::CCameraAdvancedSettings::EMeteringModeEvaluative) + return true; + else + return false; + case QCameraExposure::MeteringAverage: + if (supportedModes & CCamera::CCameraAdvancedSettings::EMeteringModeCenterWeighted) + return true; + else + return false; + case QCameraExposure::MeteringSpot: + if (supportedModes & CCamera::CCameraAdvancedSettings::EMeteringModeSpot) + return true; + else + return false; + + default: + return false; + } + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#else // S60 3.1 + Q_UNUSED(mode); +#endif // POST_31_PLATFORM + + return false; +} + +int S60CameraSettings::isoSensitivity() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + CCamera::CCameraAdvancedSettings::TISORateType isoRateType; + TInt param = 0; + TInt isoRate = 0; + TRAPD(err, m_advancedSettings->GetISORateL(isoRateType, param, isoRate)); + if (err) + return 0; + if (isoRate != KErrNotFound) + return isoRate; + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#endif // POST_31_PLATFORM + return 0; +} + +QList<int> S60CameraSettings::supportedIsoSensitivities() +{ + QList<int> isoSentitivities; +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + RArray<TInt> supportedIsoRates; + CleanupClosePushL(supportedIsoRates); + + TRAPD(err, m_advancedSettings->GetSupportedIsoRatesL(supportedIsoRates)); + if (err != KErrNone) { + if (err != KErrNotSupported) // Don's emit error if ISO is not supported + emit error(QCamera::CameraError, tr("Failure while querying supported iso sensitivities.")); + } else { + for (int i = 0; i < supportedIsoRates.Count(); ++i) + isoSentitivities << supportedIsoRates[i]; + } + CleanupStack::PopAndDestroy(); // RArray<TInt> supportedIsoRates + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } + + return isoSentitivities; +#else // S60 3.1 Platform + return isoSentitivities; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setManualIsoSensitivity(int iso) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + TRAPD(err, m_advancedSettings->SetISORateL(CCamera::CCameraAdvancedSettings::EISOManual, iso)); + if (err) + emit error(QCamera::CameraError, tr("Setting manual iso sensitivity failed.")); + return; + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#else // S60 3.1 Platform + Q_UNUSED(iso); + emit error(QCamera::NotSupportedFeatureError, tr("Setting manual iso sensitivity is not supported.")); +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setAutoIsoSensitivity() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + TRAPD(err, m_advancedSettings->SetISORateL(CCamera::CCameraAdvancedSettings::EISOAutoUnPrioritised, 0)); + if (err) + emit error(QCamera::CameraError, tr("Setting auto iso sensitivity failed.")); + return; + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#else // S60 3.1 Platform + emit error(QCamera::NotSupportedFeatureError, tr("Setting auto iso sensitivity is not supported.")); +#endif // POST_31_PLATFORM +} + +qreal S60CameraSettings::aperture() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) + return qreal(m_advancedSettings->Aperture()) / KSymbianFineResolutionFactor; + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return 0; +#else // S60 3.1 Platform + return 0; +#endif // POST_31_PLATFORM +} + +QList<qreal> S60CameraSettings::supportedApertures() +{ + QList<qreal> apertures; + +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + RArray<TInt> supportedApertures; + TValueInfo info = ENotActive; + + TRAPD(err, m_advancedSettings->GetAperturesL(supportedApertures, info)); + if (err != KErrNone) + if (err != KErrNotSupported) + emit error(QCamera::CameraError, tr("Failure while querying supported apertures.")); + else { + for (int i = 0; i < supportedApertures.Count(); i++) { + qreal q = qreal(supportedApertures[i]) / KSymbianFineResolutionFactor; + apertures.append(q); + } + } + supportedApertures.Close(); + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return apertures; +#else // S60 3.1 Platform + return apertures; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setManualAperture(qreal aperture) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + int symbianAperture = (aperture * KSymbianFineResolutionFactor); // KSymbianFineResolutionFactor = 100 + m_advancedSettings->SetAperture(symbianAperture); + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#else // S60 3.1 + Q_UNUSED(aperture); + emit error(QCamera::NotSupportedFeatureError, tr("Setting manual aperture is not supported.")); +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::lockExposure(bool lock) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + m_advancedSettings->SetExposureLockOn(lock); + return; + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#else // S60 3.1 + Q_UNUSED(lock); + emit error(QCamera::NotSupportedFeatureError, tr("Locking exposure is not supported.")); +#endif // POST_31_PLATFORM +} + +bool S60CameraSettings::isExposureLocked() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) + return m_advancedSettings->ExposureLockOn(); + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#endif // POST_31_PLATFORM + return false; +} + +qreal S60CameraSettings::shutterSpeed() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + qreal shutterSpeed = qreal(m_advancedSettings->ShutterSpeed()) / 1000000.0; + return shutterSpeed; // In seconds + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } + return 0; +#else // S60 3.1 Platform + return 0; +#endif // POST_31_PLATFORM +} + +QList<qreal> S60CameraSettings::supportedShutterSpeeds() +{ + QList<qreal> speeds; + +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + RArray<TInt> supportedSpeeds; + TValueInfo info = ENotActive; + + TRAPD(err, m_advancedSettings->GetShutterSpeedsL(supportedSpeeds, info)); + if (err != KErrNone) + if (err != KErrNotSupported) + emit error(QCamera::CameraError, tr("Failure while querying supported shutter speeds.")); + else { + for (int i = 0; i < supportedSpeeds.Count(); i++) { + qreal q = qreal(supportedSpeeds[i]) / 1000000.0; + speeds.append(q); // In seconds + } + } + supportedSpeeds.Close(); + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return speeds; +#else // S60 3.1 Platform + return speeds; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setManualShutterSpeed(qreal speed) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + TInt shutterSpeed = speed * 1000000; // From seconds to microseconds + m_advancedSettings->SetShutterSpeed(shutterSpeed); + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#else // S60 3.1 + emit error(QCamera::NotSupportedFeatureError, tr("Setting manual shutter speed is not supported.")); + Q_UNUSED(speed); +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setExposureCompensation(qreal ev) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + TInt evStep = ev * KSymbianFineResolutionFactor; + m_advancedSettings->SetExposureCompensationStep(evStep); + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#else // S60 3.1 Platform + Q_UNUSED(ev); + emit error(QCamera::NotSupportedFeatureError, tr("Setting exposure compensation is not supported.")); +#endif // POST_31_PLATFORM +} + +qreal S60CameraSettings::exposureCompensation() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + TInt evStepSymbian = 0; + m_advancedSettings->GetExposureCompensationStep(evStepSymbian); + qreal evStep = evStepSymbian; + evStep /= KSymbianFineResolutionFactor; + return evStep; + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } + return 0; +#else // S60 3.1 Platform + return 0; +#endif // POST_31_PLATFORM +} + +QList<qreal> S60CameraSettings::supportedExposureCompensationValues() +{ + QList<qreal> valueList; + +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + RArray<TInt> evSteps; + TValueInfo info; + TRAPD(err, m_advancedSettings->GetExposureCompensationStepsL(evSteps, info)); + if (err) { + if (err != KErrNotSupported) + emit error(QCamera::CameraError, tr("Failure while querying supported exposure compensation values.")); + return valueList; + } + + if (info == ENotActive || evSteps.Count() == 0) { + // EV not supported, return empty list + return valueList; + } + + for (int i = 0; i < evSteps.Count(); ++i) { + qreal appendValue = evSteps[i]; + appendValue /= KSymbianFineResolutionFactor; + valueList.append(appendValue); + } + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return valueList; +#else // S60 3.1 Platform + return valueList; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setSharpeningLevel(int value) +{ +#ifdef POST_31_PLATFORM + if (m_imageProcessingSettings && isSharpeningSupported()) + m_imageProcessingSettings->SetTransformationValue(KUidECamEventImageProcessingAdjustSharpness, value); + else + emit error(QCamera::NotSupportedFeatureError, tr("Setting sharpening level is not supported.")); +#else // S60 3.1 + Q_UNUSED(value); + emit error(QCamera::NotSupportedFeatureError, tr("Setting sharpening level is not supported.")); +#endif // POST_31_PLATFORM +} + +bool S60CameraSettings::isSharpeningSupported() const +{ +#ifdef POST_31_PLATFORM + if (m_imageProcessingSettings) { + RArray<TUid> suppTransforms; + TRAPD(err, m_imageProcessingSettings->GetSupportedTransformationsL(suppTransforms)); + if (err) + return false; + + if (suppTransforms.Find(KUidECamEventImageProcessingAdjustSharpness)) + return true; + } + return false; +#else // S60 3.1 Platform + return false; +#endif // POST_31_PLATFORM +} + +int S60CameraSettings::sharpeningLevel() const +{ +#ifdef POST_31_PLATFORM + if (m_imageProcessingSettings && isSharpeningSupported()) + return m_imageProcessingSettings->TransformationValue(KUidECamEventImageProcessingAdjustSharpness); + else + return 0; +#else // S60 3.1 Platform + return 0; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setSaturation(int value) +{ +#ifdef POST_31_PLATFORM + if (m_imageProcessingSettings) { + RArray<TUid> suppTransforms; + TRAPD(err, m_imageProcessingSettings->GetSupportedTransformationsL(suppTransforms)); + if (err) + if (err != KErrNotSupported) + emit error(QCamera::CameraError, tr("Failure while querying supported transformations.")); + + if (suppTransforms.Find(KUidECamEventtImageProcessingAdjustSaturation)) + m_imageProcessingSettings->SetTransformationValue(KUidECamEventtImageProcessingAdjustSaturation, value == -1 ? 0 : value*2-100); + else + emit error(QCamera::NotSupportedFeatureError, tr("Setting saturation is not supported.")); + } + else + emit error(QCamera::NotSupportedFeatureError, tr("Setting saturation is not supported.")); +#else // S60 3.1 + Q_UNUSED(value); + emit error(QCamera::NotSupportedFeatureError, tr("Setting saturation is not supported.")); +#endif // POST_31_PLATFORM +} + +int S60CameraSettings::saturation() +{ +#ifdef POST_31_PLATFORM + if (m_imageProcessingSettings) { + RArray<TUid> suppTransforms; + TRAPD(err, m_imageProcessingSettings->GetSupportedTransformationsL(suppTransforms)); + if (err) + if (err != KErrNotSupported) + emit error(QCamera::CameraError, tr("Failure while querying supported transformations.")); + + if (suppTransforms.Find(KUidECamEventtImageProcessingAdjustSaturation)) + return m_imageProcessingSettings->TransformationValue(KUidECamEventtImageProcessingAdjustSaturation); + } + return 0; +#else // S60 3.1 Platform + return 0; +#endif // POST_31_PLATFORM +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60camerasettings.h b/src/plugins/symbian/ecam/s60camerasettings.h new file mode 100644 index 000000000..4ecb131b2 --- /dev/null +++ b/src/plugins/symbian/ecam/s60camerasettings.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERASETTINGS_H +#define S60CAMERASETTINGS_H + +#include "qcamera.h" + +#include "s60cameraengine.h" +#include "s60cameraengineobserver.h" + +#include <e32base.h> + +QT_USE_NAMESPACE + +/* + * Class handling CCamera AdvancedSettings and ImageProcessing operations. + */ +class S60CameraSettings : public QObject, + public MAdvancedSettingsObserver +{ + Q_OBJECT + +public: // Static Contructor & Destructor + + static S60CameraSettings* New(int &error, QObject *parent = 0, CCameraEngine *engine = 0); + ~S60CameraSettings(); + +public: // Methods + + // Focus + QCameraFocus::FocusMode focusMode(); + void setFocusMode(QCameraFocus::FocusMode mode); + QCameraFocus::FocusModes supportedFocusModes(); + void startFocusing(); + void cancelFocusing(); + + // Zoom + qreal opticalZoomFactorL() const; + void setOpticalZoomFactorL(const qreal zoomFactor); + QList<qreal> supportedDigitalZoomFactors() const; + qreal digitalZoomFactorL() const; + void setDigitalZoomFactorL(const qreal zoomFactor); + + // Flash + bool isFlashReady(); + + // Exposure + void setExposureMode(QCameraExposure::ExposureMode mode); + void lockExposure(bool lock); + bool isExposureLocked(); + + // Metering Mode + QCameraExposure::MeteringMode meteringMode(); + void setMeteringMode(QCameraExposure::MeteringMode mode); + bool isMeteringModeSupported(QCameraExposure::MeteringMode mode); + + // ISO Sensitivity + int isoSensitivity(); + void setManualIsoSensitivity(int iso); + void setAutoIsoSensitivity(); + QList<int> supportedIsoSensitivities(); + + // Aperture + qreal aperture(); + void setManualAperture(qreal aperture); + QList<qreal> supportedApertures(); + + // Shutter Speed + qreal shutterSpeed(); + void setManualShutterSpeed(qreal speed); + QList<qreal> supportedShutterSpeeds(); + + // ExposureCompensation + qreal exposureCompensation(); + void setExposureCompensation(qreal ev); + QList<qreal> supportedExposureCompensationValues(); + + // Sharpening Level + int sharpeningLevel() const; + void setSharpeningLevel(int value); + bool isSharpeningSupported() const; + + // Saturation + int saturation(); + void setSaturation(int value); + +signals: // Notifications + + // For QCameraExposureControl + void flashReady(bool ready); + void apertureChanged(); + void apertureRangeChanged(); + void shutterSpeedChanged(); + void isoSensitivityChanged(); + void evChanged(); + + // For QCameraLocksControl + void exposureStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason); + void focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason); + + // Errors + void error(int, const QString&); + +protected: // Protected constructors + + S60CameraSettings(QObject *parent, CCameraEngine *engine); + void ConstructL(); + +protected: // MAdvancedSettingsObserver + + void HandleAdvancedEvent(const TECAMEvent& aEvent); + +private: // Internal + + bool queryAdvancedSettingsInfo(); + +private: // Enums + + enum EcamErrors { + KErrECamCameraDisabled = -12100, // The camera has been disabled, hence calls do not succeed + KErrECamSettingDisabled = -12101, // This parameter or operation is supported, but presently is disabled. + KErrECamParameterNotInRange = -12102, // This value is out of range. + KErrECamSettingNotSupported = -12103, // This parameter or operation is not supported. + KErrECamNotOptimalFocus = -12104 // The optimum focus is lost + }; + +private: // Data + +#ifndef S60_31_PLATFORM // Post S60 3.1 Platforms + CCamera::CCameraAdvancedSettings *m_advancedSettings; + CCamera::CCameraImageProcessing *m_imageProcessingSettings; +#endif // S60_31_PLATFORM + CCameraEngine *m_cameraEngine; + QList<int> m_supportedSymbianDigitalZoomFactors; + bool m_continuousFocusing; +}; + +#endif // S60CAMERASETTINGS_H diff --git a/src/plugins/symbian/ecam/s60cameraviewfinderengine.cpp b/src/plugins/symbian/ecam/s60cameraviewfinderengine.cpp new file mode 100644 index 000000000..55d7cbc67 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraviewfinderengine.cpp @@ -0,0 +1,789 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QApplication> +#include <QDesktopWidget> +#include <qcamera.h> +#include <qabstractvideosurface.h> +#include <qvideoframe.h> + +#include "s60cameraviewfinderengine.h" +#include "s60cameraengine.h" +#include "s60cameracontrol.h" +#include "s60videowidgetcontrol.h" +#include "s60videowidgetdisplay.h" +#include "s60videorenderercontrol.h" +#include "s60videowindowcontrol.h" +#include "s60videowindowdisplay.h" +#include "s60cameraconstants.h" + +#include <coemain.h> // CCoeEnv +#include <coecntrl.h> // CCoeControl +#include <w32std.h> + +// Helper function +TRect qRect2TRect(const QRect &qr) +{ + return TRect(TPoint(qr.left(), qr.top()), TSize(qr.width(), qr.height())); +} + + +S60CameraViewfinderEngine::S60CameraViewfinderEngine(S60CameraControl *control, + CCameraEngine *engine, + QObject *parent): + QObject(parent), + m_cameraEngine(engine), + m_cameraControl(0), + m_viewfinderOutput(0), + m_viewfinderDisplay(0), + m_viewfinderSurface(0), + m_wsSession(CCoeEnv::Static()->WsSession()), + m_screenDevice(*CCoeEnv::Static()->ScreenDevice()), + m_window(0), + m_desktopWidget(0), + m_vfState(EVFNotConnectedNotStarted), + m_viewfinderSize(KDefaultViewfinderSize), + m_actualViewFinderSize(KDefaultViewfinderSize), + m_viewfinderAspectRatio(0.0), + m_viewfinderType(OutputTypeNotSet), + m_viewfinderNativeType(EBitmapViewFinder), // Default type + m_isViewFinderVisible(true), // True by default (only QVideoWidgetControl supports being hidden) + m_uiLandscape(true), + m_vfErrorsSignalled(0) +{ + m_cameraControl = control; + + // Check whether platform supports DirectScreen ViewFinder + if (m_cameraEngine) { + if (m_cameraEngine->IsDirectViewFinderSupported()) + m_viewfinderNativeType = EDirectScreenViewFinder; + else + m_viewfinderNativeType = EBitmapViewFinder; + + MCameraViewfinderObserver *vfObserver = this; + m_cameraEngine->SetViewfinderObserver(vfObserver); + } + else + m_cameraControl->setError(KErrGeneral, tr("Unexpected camera error.")); + // From now on it is safe to assume engine exists + + // Check the UI orientation + QDesktopWidget* desktopWidget = QApplication::desktop(); + QRect screenRect = desktopWidget->screenGeometry(); + if (screenRect.width() > screenRect.height()) + m_uiLandscape = true; + else + m_uiLandscape = false; + + // Detect UI Rotations + m_desktopWidget = QApplication::desktop(); + if (m_desktopWidget) + connect(m_desktopWidget, SIGNAL(resized(int)), this, SLOT(handleDesktopResize(int))); +} + +S60CameraViewfinderEngine::~S60CameraViewfinderEngine() +{ + // No need to stop viewfinder: + // Engine has stopped it already + // Surface will be stopped by VideoRendererControl + + m_viewfinderOutput = 0; + m_viewfinderSurface = 0; +} + +void S60CameraViewfinderEngine::setNewCameraEngine(CCameraEngine *engine) +{ + m_cameraEngine = engine; + + if (m_cameraEngine) { + // And set observer to the new CameraEngine + MCameraViewfinderObserver *vfObserver = this; + m_cameraEngine->SetViewfinderObserver(vfObserver); + } +} + +void S60CameraViewfinderEngine::handleDesktopResize(int screen) +{ + Q_UNUSED(screen); + // UI Rotation is handled by the QVideoWidgetControl, thus this is needed + // only for the QVideoRendererControl + if (m_viewfinderType == OutputTypeRenderer) { + QSize newResolution(-1,-1); + if (m_viewfinderSurface) + newResolution = m_viewfinderSurface->nativeResolution(); + + if (newResolution.width() == -1 || newResolution.height() == -1) { + QDesktopWidget* desktopWidget = QApplication::desktop(); + QRect screenRect = desktopWidget->screenGeometry(); + newResolution = QSize(screenRect.width(), screenRect.height()); + } + + resetViewfinderSize(newResolution); + } + + // Rotate Camera if UI has rotated + checkAndRotateCamera(); +} + +void S60CameraViewfinderEngine::setVideoWidgetControl(QObject *viewfinderOutput) +{ + // Release old control if it has not already been done + if (m_viewfinderOutput) + releaseControl(m_viewfinderType); + + // Rotate Camera if UI has rotated + checkAndRotateCamera(); + + S60VideoWidgetControl* viewFinderWidgetControl = + qobject_cast<S60VideoWidgetControl*>(viewfinderOutput); + + if (viewFinderWidgetControl) { + // Check whether platform supports DirectScreen ViewFinder + if (m_cameraEngine) { + if (m_cameraEngine->IsDirectViewFinderSupported()) + m_viewfinderNativeType = EDirectScreenViewFinder; + else + m_viewfinderNativeType = EBitmapViewFinder; + } + else + return; + + m_viewfinderDisplay = viewFinderWidgetControl->display(); + + if (m_viewfinderNativeType == EDirectScreenViewFinder) { + m_viewfinderDisplay->setPaintingEnabled(false); // No Qt Painter painting - Direct rendering + connect(m_viewfinderDisplay, SIGNAL(windowHandleChanged(RWindow *)), this, SLOT(resetViewfinderDisplay())); + } else { + m_viewfinderDisplay->setPaintingEnabled(true); // Qt Painter painting - Bitmap rendering + connect(this, SIGNAL(viewFinderFrameReady(const CFbsBitmap &)), m_viewfinderDisplay, SLOT(setFrame(const CFbsBitmap &))); + } + + connect(m_viewfinderDisplay, SIGNAL(visibilityChanged(bool)), this, SLOT(handleVisibilityChange(bool))); + connect(m_viewfinderDisplay, SIGNAL(displayRectChanged(QRect, QRect)), this, SLOT(resetVideoWindowSize())); + connect(m_viewfinderDisplay, SIGNAL(windowHandleChanged(RWindow*)), this, SLOT(handleWindowChange(RWindow*))); + + m_viewfinderSize = m_viewfinderDisplay->extentRect().size(); + m_viewfinderOutput = viewfinderOutput; + m_viewfinderType = OutputTypeVideoWidget; + m_isViewFinderVisible = m_viewfinderDisplay->isVisible(); + + switch (m_vfState) { + case EVFNotConnectedNotStarted: + m_vfState = EVFIsConnectedNotStarted; + break; + case EVFNotConnectedIsStarted: + if (m_isViewFinderVisible) + m_vfState = EVFIsConnectedIsStartedIsVisible; + else + m_vfState = EVFIsConnectedIsStartedNotVisible; + break; + case EVFIsConnectedNotStarted: + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + // Already connected, state does not change + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + + if (m_vfState == EVFIsConnectedIsStartedIsVisible) + startViewfinder(true); // Internal start (i.e. start if started externally) + } +} + +void S60CameraViewfinderEngine::setVideoRendererControl(QObject *viewfinderOutput) +{ + // Release old control if it has not already been done + if (m_viewfinderOutput) + releaseControl(m_viewfinderType); + + // Rotate Camera if UI has rotated + checkAndRotateCamera(); + + S60VideoRendererControl* viewFinderRenderControl = + qobject_cast<S60VideoRendererControl*>(viewfinderOutput); + + if (viewFinderRenderControl) { + m_viewfinderNativeType = EBitmapViewFinder; // Always Bitmap + + connect(viewFinderRenderControl, SIGNAL(viewFinderSurfaceSet()), + this, SLOT(rendererSurfaceSet())); + + Q_ASSERT(!viewFinderRenderControl->surface()); + m_viewfinderOutput = viewfinderOutput; + m_viewfinderType = OutputTypeRenderer; + // RendererControl viewfinder is "visible" when surface is set + m_isViewFinderVisible = false; + if (EVFIsConnectedIsStartedIsVisible) + m_vfState = EVFIsConnectedIsStartedNotVisible; + + // Use display resolution as default viewfinder resolution + m_viewfinderSize = QApplication::desktop()->screenGeometry().size(); + + switch (m_vfState) { + case EVFNotConnectedNotStarted: + m_vfState = EVFIsConnectedNotStarted; + break; + case EVFNotConnectedIsStarted: + m_vfState = EVFIsConnectedIsStartedIsVisible; // GraphicsItem "always visible" (FrameWork decides to draw/not draw) + break; + case EVFIsConnectedNotStarted: + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + // Already connected, state does not change + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + + if (m_vfState == EVFIsConnectedIsStartedIsVisible) + startViewfinder(true); + } +} + +void S60CameraViewfinderEngine::setVideoWindowControl(QObject *viewfinderOutput) +{ + // Release old control if it has not already been done + if (m_viewfinderOutput) + releaseControl(m_viewfinderType); + + // Rotate Camera if UI has rotated + checkAndRotateCamera(); + + S60VideoWindowControl* viewFinderWindowControl = + qobject_cast<S60VideoWindowControl*>(viewfinderOutput); + + if (viewFinderWindowControl) { + // Check whether platform supports DirectScreen ViewFinder + if (m_cameraEngine) { + if (m_cameraEngine->IsDirectViewFinderSupported()) + m_viewfinderNativeType = EDirectScreenViewFinder; + else + m_viewfinderNativeType = EBitmapViewFinder; + } else { + return; + } + + m_viewfinderDisplay = viewFinderWindowControl->display(); + + if (m_viewfinderNativeType == EDirectScreenViewFinder) { + m_viewfinderDisplay->setPaintingEnabled(false); // No Qt Painter painting - Direct rendering + connect(m_viewfinderDisplay, SIGNAL(windowHandleChanged(RWindow *)), this, SLOT(resetViewfinderDisplay())); + } else { + m_viewfinderDisplay->setPaintingEnabled(true); // Qt Painter painting - Bitmap rendering + connect(this, SIGNAL(viewFinderFrameReady(const CFbsBitmap &)), m_viewfinderDisplay, SLOT(setFrame(const CFbsBitmap &))); + } + + connect(m_viewfinderDisplay, SIGNAL(displayRectChanged(QRect, QRect)), this, SLOT(resetVideoWindowSize())); + connect(m_viewfinderDisplay, SIGNAL(visibilityChanged(bool)), this, SLOT(handleVisibilityChange(bool))); + connect(m_viewfinderDisplay, SIGNAL(windowHandleChanged(RWindow*)), this, SLOT(handleWindowChange(RWindow*))); + + m_viewfinderSize = m_viewfinderDisplay->extentRect().size(); + m_viewfinderOutput = viewfinderOutput; + m_viewfinderType = OutputTypeVideoWindow; + m_isViewFinderVisible = m_viewfinderDisplay->isVisible(); + + switch (m_vfState) { + case EVFNotConnectedNotStarted: + m_vfState = EVFIsConnectedNotStarted; + break; + case EVFNotConnectedIsStarted: + if (m_isViewFinderVisible) + m_vfState = EVFIsConnectedIsStartedIsVisible; + else + m_vfState = EVFIsConnectedIsStartedNotVisible; + break; + case EVFIsConnectedNotStarted: + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + // Already connected, state does not change + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + + if (m_vfState == EVFIsConnectedIsStartedIsVisible) + startViewfinder(true); // Internal start (i.e. start if started externally) + } +} + +void S60CameraViewfinderEngine::releaseControl(ViewfinderOutputType type) +{ + if (m_vfState == EVFIsConnectedIsStartedIsVisible) + stopViewfinder(true); + + if (m_viewfinderOutput) { + switch (type) { + case OutputTypeNotSet: + return; + case OutputTypeVideoWidget: + if (m_viewfinderType != OutputTypeVideoWidget) + return; + disconnect(m_viewfinderOutput); + m_viewfinderOutput->disconnect(this); + Q_ASSERT(m_viewfinderDisplay); + disconnect(m_viewfinderDisplay); + m_viewfinderDisplay->disconnect(this); + m_viewfinderDisplay = 0; + // Invalidate the extent rect + qobject_cast<S60VideoWidgetControl*>(m_viewfinderOutput)->setExtentRect(QRect()); + break; + case OutputTypeVideoWindow: + if (m_viewfinderType != OutputTypeVideoWindow) + return; + disconnect(m_viewfinderOutput); + m_viewfinderOutput->disconnect(this); + Q_ASSERT(m_viewfinderDisplay); + disconnect(m_viewfinderDisplay); + m_viewfinderDisplay->disconnect(this); + m_viewfinderDisplay = 0; + break; + case OutputTypeRenderer: + if (m_viewfinderType != OutputTypeRenderer) + return; + disconnect(m_viewfinderOutput); + m_viewfinderOutput->disconnect(this); + if (m_viewfinderSurface) + m_viewfinderSurface->disconnect(this); + disconnect(this, SIGNAL(viewFinderFrameReady(const CFbsBitmap &)), + this, SLOT(viewFinderBitmapReady(const CFbsBitmap &))); + break; + default: + emit error(QCamera::CameraError, tr("Unexpected viewfinder error.")); + return; + } + } + + Q_ASSERT(!m_viewfinderDisplay); + m_viewfinderOutput = 0; + m_viewfinderType = OutputTypeNotSet; + + // Update state + switch (m_vfState) { + case EVFNotConnectedNotStarted: + case EVFNotConnectedIsStarted: + // Do nothing + break; + case EVFIsConnectedNotStarted: + m_vfState = EVFNotConnectedNotStarted; + break; + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + m_vfState = EVFNotConnectedIsStarted; + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } +} + +void S60CameraViewfinderEngine::startViewfinder(const bool internalStart) +{ + if (!internalStart) { + switch (m_vfState) { + case EVFNotConnectedNotStarted: + m_vfState = EVFNotConnectedIsStarted; + break; + case EVFIsConnectedNotStarted: + if (m_isViewFinderVisible) + m_vfState = EVFIsConnectedIsStartedIsVisible; + else + m_vfState = EVFIsConnectedIsStartedNotVisible; + break; + case EVFNotConnectedIsStarted: + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + // Already started, state does not change + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + } + + // Start viewfinder + if (m_vfState == EVFIsConnectedIsStartedIsVisible) { + + if (!m_cameraEngine) + return; + + if (m_viewfinderNativeType == EDirectScreenViewFinder) { + + if (RWindow *window = m_viewfinderDisplay ? m_viewfinderDisplay->windowHandle() : 0) { + m_window = window; + } else { + emit error(QCamera::CameraError, tr("Requesting window for viewfinder failed.")); + return; + } + + const QRect extentRect = m_viewfinderDisplay ? m_viewfinderDisplay->extentRect() : QRect(); + const QRect clipRect = m_viewfinderDisplay ? m_viewfinderDisplay->clipRect() : QRect(); + + TRect extentRectSymbian = qRect2TRect(extentRect); + TRect clipRectSymbian = qRect2TRect(clipRect); + TRAPD(err, m_cameraEngine->StartDirectViewFinderL(m_wsSession, m_screenDevice, *m_window, extentRectSymbian, clipRectSymbian)); + if (err) { + if (err == KErrNotSupported) { + emit error(QCamera::NotSupportedFeatureError, tr("Requested viewfinder size is not supported.")); + } else { + emit error(QCamera::CameraError, tr("Starting viewfinder failed.")); + } + return; + } + + m_actualViewFinderSize = QSize(extentRectSymbian.Size().iWidth, extentRectSymbian.Size().iHeight); + m_viewfinderAspectRatio = qreal(m_actualViewFinderSize.width()) / qreal(m_actualViewFinderSize.height()); + + } else { // Bitmap ViewFinder + TSize size = TSize(m_viewfinderSize.width(), m_viewfinderSize.height()); + + if( m_viewfinderType == OutputTypeRenderer && m_viewfinderSurface) { + if (!m_surfaceFormat.isValid()) { + emit error(QCamera::NotSupportedFeatureError, tr("Invalid surface format.")); + return; + } + + // Start rendering to surface with correct size and format + if (!m_viewfinderSurface->isFormatSupported(m_surfaceFormat) || + !m_viewfinderSurface->start(m_surfaceFormat)) { + emit error(QCamera::NotSupportedFeatureError, tr("Failed to start surface.")); + return; + } + + if (!m_viewfinderSurface->isActive()) + return; + } + + TRAPD(vfErr, m_cameraEngine->StartViewFinderL(size)); + if (vfErr) { + if (vfErr == KErrNotSupported) { + emit error(QCamera::NotSupportedFeatureError, tr("Requested viewfinder size is not supported.")); + } else { + emit error(QCamera::CameraError, tr("Starting viewfinder failed.")); + } + return; + } + + m_actualViewFinderSize = QSize(size.iWidth, size.iHeight); + m_viewfinderAspectRatio = qreal(m_actualViewFinderSize.width()) / qreal(m_actualViewFinderSize.height()); + + // Notify control about the frame size (triggers frame position calculation) + if (m_viewfinderDisplay) { + m_viewfinderDisplay->setNativeSize(m_actualViewFinderSize); + } else { + if (m_viewfinderType == OutputTypeRenderer && m_viewfinderSurface) { + m_viewfinderSurface->stop(); + QVideoSurfaceFormat format = m_viewfinderSurface->surfaceFormat(); + format.setFrameSize(QSize(m_actualViewFinderSize)); + format.setViewport(QRect(0, 0, m_actualViewFinderSize.width(), m_actualViewFinderSize.height())); + m_viewfinderSurface->start(format); + } + } + } + } +} + +void S60CameraViewfinderEngine::stopViewfinder(const bool internalStop) +{ + // Stop if viewfinder is started + if (m_vfState == EVFIsConnectedIsStartedIsVisible) { + if (m_viewfinderOutput && m_viewfinderType == OutputTypeRenderer && m_viewfinderSurface) { + // Stop surface if one still exists + m_viewfinderSurface->stop(); + } + + if (m_cameraEngine) + m_cameraEngine->StopViewFinder(); + } + + // Update state + if (!internalStop) { + switch (m_vfState) { + case EVFNotConnectedNotStarted: + case EVFIsConnectedNotStarted: + // Discard + break; + case EVFNotConnectedIsStarted: + m_vfState = EVFNotConnectedNotStarted; + break; + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + m_vfState = EVFIsConnectedNotStarted; + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + } +} + +void S60CameraViewfinderEngine::MceoViewFinderFrameReady(CFbsBitmap& aFrame) +{ + emit viewFinderFrameReady(aFrame); + if (m_cameraEngine) + m_cameraEngine->ReleaseViewFinderBuffer(); +} + +void S60CameraViewfinderEngine::resetViewfinderSize(const QSize size) +{ + m_viewfinderSize = size; + + if(m_vfState != EVFIsConnectedIsStartedIsVisible) { + // Set native size to Window/Renderer Control + if (m_viewfinderDisplay) + m_viewfinderDisplay->setNativeSize(m_actualViewFinderSize); + return; + } + + stopViewfinder(true); + + startViewfinder(true); +} + +void S60CameraViewfinderEngine::resetVideoWindowSize() +{ + if (m_viewfinderDisplay) + resetViewfinderSize(m_viewfinderDisplay->extentRect().size()); +} + +void S60CameraViewfinderEngine::resetViewfinderDisplay() +{ + if (m_viewfinderNativeType == EDirectScreenViewFinder) { + + switch (m_viewfinderType) { + case OutputTypeVideoWidget: { + if (!m_viewfinderOutput) + return; + + // First stop viewfinder + stopViewfinder(true); + + RWindow *window = m_viewfinderDisplay->windowHandle(); + if (!window) { + return; + } + + // Then start it with the new WindowID + startViewfinder(true); + break; + } + case OutputTypeRenderer: + case OutputTypeVideoWindow: + // Do nothing + break; + + default: + // Not ViewFinder Output has been set, Discard + break; + } + } +} + +void S60CameraViewfinderEngine::rendererSurfaceSet() +{ + S60VideoRendererControl* viewFinderRenderControl = + qobject_cast<S60VideoRendererControl*>(m_viewfinderOutput); + + // Reset old surface if needed + if (m_viewfinderSurface) { + handleVisibilityChange(false); + disconnect(m_viewfinderSurface); + if (viewFinderRenderControl->surface()) + stopViewfinder(true); // Temporary stop + else + stopViewfinder(); // Stop for good + m_viewfinderSize = QApplication::desktop()->screenGeometry().size(); + m_viewfinderSurface = 0; + } + + // Set new surface + m_viewfinderSurface = viewFinderRenderControl->surface(); + if (!m_viewfinderSurface) + return; + if (!m_viewfinderSurface->nativeResolution().isEmpty()) { + if (m_viewfinderSurface->nativeResolution() != m_viewfinderSize) + resetViewfinderSize(m_viewfinderSurface->nativeResolution()); + } + + connect(m_viewfinderSurface, SIGNAL(nativeResolutionChanged(const QSize&)), + this, SLOT(resetViewfinderSize(QSize))); + + // Set Surface Properties + if (m_viewfinderSurface->supportedPixelFormats().contains(QVideoFrame::Format_RGB32)) + m_surfaceFormat = QVideoSurfaceFormat(m_actualViewFinderSize, QVideoFrame::Format_RGB32); + else if (m_viewfinderSurface->supportedPixelFormats().contains(QVideoFrame::Format_ARGB32)) + m_surfaceFormat = QVideoSurfaceFormat(m_actualViewFinderSize, QVideoFrame::Format_ARGB32); + else { + return; + } + m_surfaceFormat.setFrameRate(KViewfinderFrameRate); + m_surfaceFormat.setYCbCrColorSpace(QVideoSurfaceFormat::YCbCr_Undefined); // EColor16MU (compatible with EColor16MA) + m_surfaceFormat.setPixelAspectRatio(1,1); // PAR 1:1 + + + connect(this, SIGNAL(viewFinderFrameReady(const CFbsBitmap &)), + this, SLOT(viewFinderBitmapReady(const CFbsBitmap &))); + + // Surface set, viewfinder is "visible" + handleVisibilityChange(true); +} + +void S60CameraViewfinderEngine::viewFinderBitmapReady(const CFbsBitmap &bitmap) +{ + CFbsBitmap *bitmapPtr = const_cast<CFbsBitmap*>(&bitmap); + QPixmap pixmap = QPixmap::fromSymbianCFbsBitmap(bitmapPtr); + + QImage newImage = pixmap.toImage(); + if (newImage.format() != QImage::Format_ARGB32 && + newImage.format() != QImage::Format_RGB32) { + newImage = newImage.convertToFormat(QImage::Format_RGB32); + } + + if (!newImage.isNull()) { + QVideoFrame newFrame(newImage); + if (newFrame.isValid()) { + if (!m_viewfinderSurface->present(newFrame)) { + // Presenting may fail even if there are no errors (e.g. busy) + if (m_viewfinderSurface->error()) { + if (m_vfErrorsSignalled < KMaxVFErrorsSignalled) { + emit error(QCamera::CameraError, tr("Presenting viewfinder frame failed.")); + ++m_vfErrorsSignalled; + } + } + } + } else { + if (m_vfErrorsSignalled < KMaxVFErrorsSignalled) { + emit error(QCamera::CameraError, tr("Invalid viewfinder frame was received.")); + ++m_vfErrorsSignalled; + } + } + + } else { + if (m_vfErrorsSignalled < KMaxVFErrorsSignalled) { + emit error(QCamera::CameraError, tr("Failed to convert viewfinder frame to presentable image.")); + ++m_vfErrorsSignalled; + } + } +} + +void S60CameraViewfinderEngine::handleVisibilityChange(const bool isVisible) +{ + if (m_isViewFinderVisible == isVisible) + return; + + m_isViewFinderVisible = isVisible; + + if (m_isViewFinderVisible) { + switch (m_vfState) { + case EVFNotConnectedNotStarted: + case EVFIsConnectedNotStarted: + case EVFNotConnectedIsStarted: + case EVFIsConnectedIsStartedIsVisible: + // Discard + break; + case EVFIsConnectedIsStartedNotVisible: + m_vfState = EVFIsConnectedIsStartedIsVisible; + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + startViewfinder(true); + } else { + // Stopping takes care of the state change + stopViewfinder(true); + } +} + +void S60CameraViewfinderEngine::handleWindowChange(RWindow *handle) +{ + stopViewfinder(true); + + if (handle) // New handle available, start viewfinder + startViewfinder(true); +} + +void S60CameraViewfinderEngine::checkAndRotateCamera() +{ + bool isUiNowLandscape = false; + QDesktopWidget* desktopWidget = QApplication::desktop(); + QRect screenRect = desktopWidget->screenGeometry(); + + if (screenRect.width() > screenRect.height()) + isUiNowLandscape = true; + else + isUiNowLandscape = false; + + // Rotate camera if possible + if (isUiNowLandscape != m_uiLandscape) { + stopViewfinder(true); + + // Request orientation reset + m_cameraControl->resetCameraOrientation(); + } + m_uiLandscape = isUiNowLandscape; +} + +void S60CameraViewfinderEngine::handleContentAspectRatioChange(const QSize& newSize) +{ + qreal newAspectRatio = qreal(newSize.width()) / qreal(newSize.height()); + // Check if aspect ratio changed + if (qFuzzyCompare(newAspectRatio, m_viewfinderAspectRatio)) + return; + + // Resize viewfinder by reducing either width or height to comply with the new aspect ratio + QSize newNativeResolution; + if (newAspectRatio > m_viewfinderAspectRatio) { // New AspectRatio is wider => Reduce height + newNativeResolution = QSize(m_actualViewFinderSize.width(), (m_actualViewFinderSize.width() / newAspectRatio)); + } else { // New AspectRatio is higher => Reduce width + newNativeResolution = QSize((m_actualViewFinderSize.height() * newAspectRatio), m_actualViewFinderSize.height()); + } + + // Notify aspect ratio change (use actual content size to notify that) + // This triggers item size/position re-calculation + if (m_viewfinderDisplay) + m_viewfinderDisplay->setNativeSize(newNativeResolution); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraviewfinderengine.h b/src/plugins/symbian/ecam/s60cameraviewfinderengine.h new file mode 100644 index 000000000..c5df760d9 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraviewfinderengine.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef S60CAMERAVIEWFINDERENGINE_H +#define S60CAMERAVIEWFINDERENGINE_H + +#include <QtCore/qsize.h> +#include <QtGui/qpixmap.h> + +#include <qvideosurfaceformat.h> + +#include "s60cameraengineobserver.h" + +class CCameraEngine; +class S60CameraControl; +class QAbstractVideoSurface; + +// For DirectScreen ViewFinder +class RWsSession; +class CWsScreenDevice; +class RWindowBase; +class RWindow; +class QDesktopWidget; + +class S60VideoDisplay; + +/* + * Class implementing video output selection for the viewfinder and the handler of + * all common viewfinder operations. + */ +class S60CameraViewfinderEngine : public QObject, public MCameraViewfinderObserver +{ + Q_OBJECT + +public: // Enums + + /* + * Defines whether viewfinder output backend control is of type + * QVideoWidgetControl, QVideoRendererControl or QVideoWindowControl + */ + enum ViewfinderOutputType { + OutputTypeNotSet = 0, // No viewfinder output connected + OutputTypeVideoWidget, // Using QVideoWidget + OutputTypeRenderer, // (Using QGraphicsVideoItem with) QVideoRendererControl + OutputTypeVideoWindow // Using QGraphicsVideoItem with QVideoWindow + }; + +public: // Constructor & Destructor + + S60CameraViewfinderEngine(S60CameraControl *control, + CCameraEngine *engine, + QObject *parent = 0); + ~S60CameraViewfinderEngine(); + +public: // Methods + + // Setting Viewfinder Output + void setVideoWidgetControl(QObject *viewfinderOutput); + void setVideoRendererControl(QObject *viewfinderOutput); + void setVideoWindowControl(QObject *viewfinderOutput); + void releaseControl(ViewfinderOutputType type); + + // Controls + void startViewfinder(const bool internalStart = false); + void stopViewfinder(const bool internalStop = false); + + // Start using new CameraEngine + void setNewCameraEngine(CCameraEngine *engine); + +protected: // MCameraViewfinderObserver + + void MceoViewFinderFrameReady(CFbsBitmap& aFrame); + +private: // Internal operation + + void checkAndRotateCamera(); + +signals: + + void error(int error, const QString &errorString); + void viewFinderFrameReady(const CFbsBitmap &bitmap); + +private slots: + + void resetViewfinderSize(const QSize size); + void resetVideoWindowSize(); + void resetViewfinderDisplay(); + void viewFinderBitmapReady(const CFbsBitmap &bitmap); + void handleVisibilityChange(const bool isVisible); + void handleWindowChange(RWindow *handle); + void handleDesktopResize(int screen); + void handleContentAspectRatioChange(const QSize& newSize); + void rendererSurfaceSet(); + +private: // Enums + + /* + * Defines the internal state of the viewfinder. ViewFinder will only be + * started if output is connected to Camera and Camera is started (and + * ViewFinder widget is visible in case of QVideoWidget). + */ + enum ViewFinderState { + EVFNotConnectedNotStarted = 0, // 0 - No output connected, viewfinder is not started + EVFNotConnectedIsStarted, // 1 - No output connected, viewfinder is started + EVFIsConnectedNotStarted, // 2 - Output is connected, viewfinder is not started + EVFIsConnectedIsStartedNotVisible, // 3 - Output is connected, viewfinder is started but is not visible + EVFIsConnectedIsStartedIsVisible // 4 - Output is connected, viewfinder is started and is visible + }; + + /* + * The native type of ViewFinder. DirectScreen ViewFinder is used with + * QVideoWidget if support for it is available in the platform. For + * QGraphicsVideoItem Bitmap ViewFinder is always used. + */ + enum NativeViewFinderType { + EBitmapViewFinder = 0, + EDirectScreenViewFinder + }; + +private: // Data + + CCameraEngine *m_cameraEngine; + S60CameraControl *m_cameraControl; + QObject *m_viewfinderOutput; + S60VideoDisplay *m_viewfinderDisplay; + QAbstractVideoSurface *m_viewfinderSurface; // Used only by QVideoRendererControl + RWsSession &m_wsSession; + CWsScreenDevice &m_screenDevice; + RWindowBase *m_window; + QDesktopWidget *m_desktopWidget; + ViewFinderState m_vfState; + QSize m_viewfinderSize; + // Actual viewfinder size, which may differ from requested + // (m_viewfinderSize), if the size/aspect ratio was not supported. + QSize m_actualViewFinderSize; + qreal m_viewfinderAspectRatio; + ViewfinderOutputType m_viewfinderType; + NativeViewFinderType m_viewfinderNativeType; + QVideoSurfaceFormat m_surfaceFormat; // Used only by QVideoRendererControl + bool m_isViewFinderVisible; + bool m_uiLandscape; // For detecting UI rotation + int m_vfErrorsSignalled; +}; + +#endif // S60CAMERAVIEWFINDERENGINE_H diff --git a/src/plugins/symbian/ecam/s60imagecapturesession.cpp b/src/plugins/symbian/ecam/s60imagecapturesession.cpp new file mode 100644 index 000000000..16d240c7b --- /dev/null +++ b/src/plugins/symbian/ecam/s60imagecapturesession.cpp @@ -0,0 +1,1884 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <QtCore/qdir.h> + +#include "s60imagecapturesession.h" +#include "s60videowidgetcontrol.h" +#include "s60cameraservice.h" +#include "s60cameraconstants.h" + +#include <fbs.h> // CFbsBitmap +#include <pathinfo.h> +#include <imageconversion.h> // ICL Decoder (for SnapShot) & Encoder (for Bitmap Images) + +S60ImageCaptureDecoder::S60ImageCaptureDecoder(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC8 *data, + const TDesC16 *fileName) : + CActive(CActive::EPriorityStandard), + m_imageSession(imageSession), + m_fs(fileSystemAccess), + m_jpegImageData(data), + m_jpegImageFile(fileName), + m_fileInput(false) +{ + CActiveScheduler::Add(this); +} + +S60ImageCaptureDecoder::~S60ImageCaptureDecoder() +{ + if (m_imageDecoder) { + delete m_imageDecoder; + m_imageDecoder = 0; + } +} + +S60ImageCaptureDecoder *S60ImageCaptureDecoder::FileNewL(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC16 *fileName) +{ + S60ImageCaptureDecoder* self = new (ELeave) S60ImageCaptureDecoder(imageSession, + fileSystemAccess, + 0, + fileName); + CleanupStack::PushL(self); + self->ConstructL(true); + CleanupStack::Pop(self); + return self; +} + +S60ImageCaptureDecoder *S60ImageCaptureDecoder::DataNewL(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC8 *data) +{ + S60ImageCaptureDecoder* self = new (ELeave) S60ImageCaptureDecoder(imageSession, + fileSystemAccess, + data, + 0); + CleanupStack::PushL(self); + self->ConstructL(false); + CleanupStack::Pop(self); + return self; +} + +void S60ImageCaptureDecoder::ConstructL(const bool fileInput) +{ + if (fileInput) { + if (!m_imageSession || !m_fs || !m_jpegImageFile) + User::Leave(KErrGeneral); + m_imageDecoder = CImageDecoder::FileNewL(*m_fs, *m_jpegImageFile); + } else { + if (!m_imageSession || !m_fs || !m_jpegImageData) + User::Leave(KErrGeneral); + m_imageDecoder = CImageDecoder::DataNewL(*m_fs, *m_jpegImageData); + } +} + +void S60ImageCaptureDecoder::decode(CFbsBitmap *destBitmap) +{ + if (m_imageDecoder) { + m_imageDecoder->Convert(&iStatus, *destBitmap, 0); + SetActive(); + } + else + m_imageSession->setError(KErrGeneral, QLatin1String("Preview image creation failed.")); +} + +TFrameInfo *S60ImageCaptureDecoder::frameInfo() +{ + if (m_imageDecoder) { + m_frameInfo = m_imageDecoder->FrameInfo(); + return &m_frameInfo; + } + else + return 0; +} + +void S60ImageCaptureDecoder::RunL() +{ + m_imageSession->handleImageDecoded(iStatus.Int()); +} + +void S60ImageCaptureDecoder::DoCancel() +{ + if (m_imageDecoder) + m_imageDecoder->Cancel(); +} + +TInt S60ImageCaptureDecoder::RunError(TInt aError) +{ + m_imageSession->setError(aError, QLatin1String("Preview image creation failed.")); + return KErrNone; +} + +//============================================================================= + +S60ImageCaptureEncoder::S60ImageCaptureEncoder(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC16 *fileName, + TInt jpegQuality) : + CActive(CActive::EPriorityStandard), + m_imageSession(imageSession), + m_fileSystemAccess(fileSystemAccess), + m_fileName(fileName), + m_jpegQuality(jpegQuality) +{ + CActiveScheduler::Add(this); +} + +S60ImageCaptureEncoder::~S60ImageCaptureEncoder() +{ + if (m_frameImageData) { + delete m_frameImageData; + m_frameImageData = 0; + } + if (m_imageEncoder) { + delete m_imageEncoder; + m_imageEncoder = 0; + } +} + +S60ImageCaptureEncoder *S60ImageCaptureEncoder::NewL(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC16 *fileName, + TInt jpegQuality) +{ + S60ImageCaptureEncoder* self = new (ELeave) S60ImageCaptureEncoder(imageSession, + fileSystemAccess, + fileName, + jpegQuality); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; +} + +void S60ImageCaptureEncoder::ConstructL() +{ + if (!m_imageSession || !m_fileSystemAccess || !m_fileName) + User::Leave(KErrGeneral); + + m_imageEncoder = CImageEncoder::FileNewL(*m_fileSystemAccess, + *m_fileName, + CImageEncoder::EOptionNone, + KImageTypeJPGUid); + CleanupStack::PushL(m_imageEncoder); + + // Set Jpeg Quality + m_frameImageData = CFrameImageData::NewL(); + CleanupStack::PushL(m_frameImageData); + + TJpegImageData* jpegFormat = new( ELeave ) TJpegImageData; + CleanupStack::PushL(jpegFormat); + + jpegFormat->iQualityFactor = m_jpegQuality; + + // jpegFormat (TJpegImageData) ownership transferred to m_frameImageData (CFrameImageData) + User::LeaveIfError( m_frameImageData->AppendImageData(jpegFormat)); + + CleanupStack::Pop(jpegFormat); + CleanupStack::Pop(m_frameImageData); + CleanupStack::Pop(m_imageEncoder); +} + +void S60ImageCaptureEncoder::encode(CFbsBitmap *sourceBitmap) +{ + if (m_imageEncoder) { + m_imageEncoder->Convert(&iStatus, *sourceBitmap, m_frameImageData); + SetActive(); + } + else + m_imageSession->setError(KErrGeneral, QLatin1String("Saving image to file failed.")); +} + +void S60ImageCaptureEncoder::RunL() +{ + m_imageSession->handleImageEncoded(iStatus.Int()); +} + +void S60ImageCaptureEncoder::DoCancel() +{ + if (m_imageEncoder) + m_imageEncoder->Cancel(); +} + +TInt S60ImageCaptureEncoder::RunError(TInt aError) +{ + m_imageSession->setError(aError, QLatin1String("Saving image to file failed.")); + return KErrNone; +} + +//============================================================================= + +S60ImageCaptureSession::S60ImageCaptureSession(QObject *parent) : + QObject(parent), + m_cameraEngine(0), + m_advancedSettings(0), + m_cameraInfo(0), + m_previewBitmap(0), + m_activeScheduler(0), + m_fileSystemAccess(0), + m_imageDecoder(0), + m_imageEncoder(0), + m_error(KErrNone), + m_activeDeviceIndex(KDefaultCameraDevice), + m_cameraStarted(false), + m_icState(EImageCaptureNotPrepared), + m_currentCodec(QString()), + m_captureSize(QSize()), + m_symbianImageQuality(QtMultimediaKit::HighQuality * KSymbianImageQualityCoefficient), + m_captureSettingsSet(false), + m_stillCaptureFileName(QString()), + m_requestedStillCaptureFileName(QString()), + m_currentImageId(0), + m_captureWhenReady(false), + m_previewDecodingOngoing(false), + m_previewInWaitLoop(false) +{ + // Define supported image codecs + m_supportedImageCodecs << "image/jpeg"; + + initializeImageCaptureSettings(); + + // Install ActiveScheduler if needed + if (!CActiveScheduler::Current()) { + m_activeScheduler = new CActiveScheduler; + CActiveScheduler::Install(m_activeScheduler); + } +} + +S60ImageCaptureSession::~S60ImageCaptureSession() +{ + // Delete AdvancedSettings (Should already be destroyed by CameraControl) + deleteAdvancedSettings(); + + m_formats.clear(); + m_supportedImageCodecs.clear(); + + if (m_imageDecoder) { + m_imageDecoder->Cancel(); + delete m_imageDecoder; + m_imageDecoder = 0; + } + if (m_imageEncoder) { + m_imageEncoder->Cancel(); + delete m_imageEncoder; + m_imageEncoder = 0; + } + + if (m_previewBitmap) { + delete m_previewBitmap; + m_previewBitmap = 0; + } + + // Uninstall ActiveScheduler if needed + if (m_activeScheduler) { + CActiveScheduler::Install(0); + delete m_activeScheduler; + m_activeScheduler = 0; + } +} + +CCamera::TFormat S60ImageCaptureSession::defaultImageFormat() +{ + // Primary Camera + if (m_activeDeviceIndex == 0) + return KDefaultImageFormatPrimaryCam; + + // Secondary Camera or other + else + return KDefaultImageFormatSecondaryCam; +} + +bool S60ImageCaptureSession::isDeviceReady() +{ +#ifdef Q_CC_NOKIAX86 // Emulator + return true; +#endif + + if (m_cameraEngine) + return m_cameraEngine->IsCameraReady(); + + return false; +} + +void S60ImageCaptureSession::deleteAdvancedSettings() +{ + if (m_advancedSettings) { + delete m_advancedSettings; + m_advancedSettings = 0; + emit advancedSettingChanged(); + } +} + +void S60ImageCaptureSession::setCameraHandle(CCameraEngine* camerahandle) +{ + if (camerahandle) { + m_cameraEngine = camerahandle; + resetSession(); + + // Set default settings + initializeImageCaptureSettings(); + } +} + +void S60ImageCaptureSession::setCurrentDevice(TInt deviceindex) +{ + m_activeDeviceIndex = deviceindex; +} + +void S60ImageCaptureSession::notifySettingsSet() +{ + m_captureSettingsSet = true; +} + +void S60ImageCaptureSession::resetSession(bool errorHandling) +{ + // Delete old AdvancedSettings + deleteAdvancedSettings(); + + m_captureWhenReady = false; + m_previewDecodingOngoing = false; + m_previewInWaitLoop = false; + m_stillCaptureFileName = QString(); + m_requestedStillCaptureFileName = QString(); + m_icState = EImageCaptureNotPrepared; + + m_error = KErrNone; + m_currentFormat = defaultImageFormat(); + + int err = KErrNone; + m_advancedSettings = S60CameraSettings::New(err, this, m_cameraEngine); + if (err == KErrNotSupported) { + m_advancedSettings = 0; +#ifndef S60_31_PLATFORM // Post S60 3.1 Platform + // Adv. settings may not be supported for other than the Primary Camera + if (m_cameraEngine->CurrentCameraIndex() == 0) + setError(err, tr("Unexpected camera error.")); +#endif // !S60_31_PLATFORM + } else if (err != KErrNone) { // Other errors + m_advancedSettings = 0; + qWarning("Failed to create camera settings handler."); + if (errorHandling) + emit cameraError(QCamera::ServiceMissingError, tr("Failed to recover from error.")); + else + setError(err, tr("Unexpected camera error.")); + return; + } + + if (m_advancedSettings) { + if (m_cameraEngine) + m_cameraEngine->SetAdvancedObserver(m_advancedSettings); + else + setError(KErrNotReady, tr("Unexpected camera error.")); + } + + updateImageCaptureFormats(); + + emit advancedSettingChanged(); +} + +S60CameraSettings* S60ImageCaptureSession::advancedSettings() +{ + return m_advancedSettings; +} + +/* + * This function can be used both internally and from Control classes using + * this session. The error notification will go to the client application + * either through QCameraImageCapture (if captureError is true) or QCamera (if + * captureError is false, default) error signal. + */ +void S60ImageCaptureSession::setError(const TInt error, + const QString &description, + const bool captureError) +{ + if (error == KErrNone) + return; + + m_error = error; + QCameraImageCapture::Error cameraError = fromSymbianErrorToQtMultimediaError(error); + + if (captureError) { + emit this->captureError(m_currentImageId, cameraError, description); + if (cameraError != QCameraImageCapture::NotSupportedFeatureError) + resetSession(true); + } else { + emit this->cameraError(cameraError, description); + if (cameraError != QCamera::NotSupportedFeatureError) + resetSession(true); + } +} + +QCameraImageCapture::Error S60ImageCaptureSession::fromSymbianErrorToQtMultimediaError(int aError) +{ + switch(aError) { + case KErrNone: + return QCameraImageCapture::NoError; // No errors have occurred + case KErrNotReady: + return QCameraImageCapture::NotReadyError; // Not ready for operation + case KErrNotSupported: + return QCameraImageCapture::NotSupportedFeatureError; // The feature is not supported + case KErrNoMemory: + return QCameraImageCapture::OutOfSpaceError; // Out of disk space + case KErrNotFound: + case KErrBadHandle: + return QCameraImageCapture::ResourceError; // No resources available + + default: + return QCameraImageCapture::ResourceError; // Other error has occurred + } +} + +int S60ImageCaptureSession::currentImageId() const +{ + return m_currentImageId; +} + +void S60ImageCaptureSession::initializeImageCaptureSettings() +{ + if (m_captureSettingsSet) + return; + + m_currentCodec = KDefaultImageCodec; + m_captureSize = QSize(-1, -1); + m_currentFormat = defaultImageFormat(); + + // Resolution + if (m_cameraEngine) { + QList<QSize> resolutions = supportedCaptureSizesForCodec(imageCaptureCodec()); + foreach (QSize reso, resolutions) { + if ((reso.width() * reso.height()) > (m_captureSize.width() * m_captureSize.height())) + m_captureSize = reso; + } + } else { + m_captureSize = KDefaultImageResolution; + } + + m_symbianImageQuality = KDefaultImageQuality; +} + +/* + * This function selects proper format to be used for the captured image based + * on the requested image codec. + */ +CCamera::TFormat S60ImageCaptureSession::selectFormatForCodec(const QString &codec) +{ + CCamera::TFormat format = CCamera::EFormatMonochrome; + + if (codec == "image/jpg" || codec == "image/jpeg") { + // Primary Camera + if (m_activeDeviceIndex == 0) + format = KDefaultImageFormatPrimaryCam; + + // Secondary Camera or other + else + format = KDefaultImageFormatSecondaryCam; + + return format; + } + + setError(KErrNotSupported, tr("Failed to select color format to be used with image codec.")); + return format; +} + +int S60ImageCaptureSession::prepareImageCapture() +{ + if (m_cameraEngine) { + if (!m_cameraEngine->IsCameraReady()) { + // Reset state to make sure camera is prepared before capturing image + m_icState = EImageCaptureNotPrepared; + return KErrNotReady; + } + + // First set the quality + CCamera *camera = m_cameraEngine->Camera(); + if (camera) + camera->SetJpegQuality(m_symbianImageQuality); + else + setError(KErrNotReady, tr("Setting image quality failed."), true); + + // Then prepare with correct resolution and format + TSize captureSize = TSize(m_captureSize.width(), m_captureSize.height()); + TRAPD(symbianError, m_cameraEngine->PrepareL(captureSize, m_currentFormat)); + if (!symbianError) + m_icState = EImageCapturePrepared; + + // Check if CaptureSize was modified + if (captureSize.iWidth != m_captureSize.width() || captureSize.iHeight != m_captureSize.height()) + m_captureSize = QSize(captureSize.iWidth, captureSize.iHeight); + emit captureSizeChanged(m_captureSize); + +#ifdef ECAM_PREVIEW_API + // Subscribe previews + MCameraPreviewObserver *observer = this; + m_cameraEngine->EnablePreviewProvider(observer); +#endif // ECAM_PREVIEW_API + + return symbianError; + } + + return KErrGeneral; +} + +void S60ImageCaptureSession::releaseImageCapture() +{ + // Make sure ImageCapture is prepared the next time it is being activated + m_icState = EImageCaptureNotPrepared; + +#ifdef ECAM_PREVIEW_API + // Cancel preview subscription + m_cameraEngine->DisablePreviewProvider(); +#endif // ECAM_PREVIEW_API +} + +int S60ImageCaptureSession::capture(const QString &fileName) +{ + if (!m_cameraStarted) { + m_captureWhenReady = true; + m_requestedStillCaptureFileName = fileName; // Save name, it will be processed during actual capture + return 0; + } + + if (m_icState < EImageCapturePrepared) { + int prepareSuccess = prepareImageCapture(); + if (prepareSuccess) { + setError(prepareSuccess, tr("Failure during image capture preparation."), true); + return 0; + } + } else if (m_icState > EImageCapturePrepared) { + setError(KErrNotReady, tr("Previous operation is still ongoing."), true); + return 0; + } + + m_icState = EImageCaptureCapturing; + + // Give new ID for the new image + m_currentImageId += 1; + + emit readyForCaptureChanged(false); + + processFileName(fileName); + + if (m_cameraEngine) { + TRAPD(err, m_cameraEngine->CaptureL()); + setError(err, tr("Image capture failed."), true); + } else { + setError(KErrNotReady, tr("Unexpected camera error."), true); + } + +#ifdef Q_CC_NOKIAX86 // Emulator + QImage *snapImage = new QImage(QLatin1String("C:/Data/testimage.jpg")); + emit imageExposed(m_currentImageId); + emit imageCaptured(m_currentImageId, *snapImage); + emit imageSaved(m_currentImageId, m_stillCaptureFileName); +#endif // Q_CC_NOKIAX86 + + return m_currentImageId; +} + +void S60ImageCaptureSession::cancelCapture() +{ + if (m_icState != EImageCaptureCapturing) + return; + + if (m_cameraEngine) + m_cameraEngine->CancelCapture(); + + m_icState = EImageCapturePrepared; +} + +void S60ImageCaptureSession::processFileName(const QString &fileName) +{ + // Empty FileName - Use default file name and path (C:\Data\Images\image.jpg) + if (fileName.isEmpty()) { + // Make sure default directory exists + QDir videoDir(QDir::rootPath()); + if (!videoDir.exists(KDefaultImagePath)) + videoDir.mkpath(KDefaultImagePath); + QString defaultFile = KDefaultImagePath; + defaultFile.append("\\"); + defaultFile.append(KDefaultImageFileName); + m_stillCaptureFileName = defaultFile; + + } else { // Not empty + + QString fullFileName; + + // Relative FileName + if (!fileName.contains(":")) { + // Extract file name and path from the URL + fullFileName = KDefaultImagePath; + if (fileName.at(0) != '\\') + fullFileName.append("\\"); + fullFileName.append(QDir::toNativeSeparators(QDir::cleanPath(fileName))); + + // Absolute FileName + } else { + // Extract file name and path from the given location + fullFileName = QDir::toNativeSeparators(QDir::cleanPath(fileName)); + } + + QString fileNameOnly = fullFileName.right(fullFileName.length() - fullFileName.lastIndexOf("\\") - 1); + QString directory = fullFileName.left(fullFileName.lastIndexOf("\\")); + if (directory.lastIndexOf("\\") == (directory.length() - 1)) + directory = directory.left(directory.length() - 1); + + // URL is Absolute path, not including file name + if (!fileNameOnly.contains(".")) { + if (fileNameOnly != "") { + directory.append("\\"); + directory.append(fileNameOnly); + } + fileNameOnly = KDefaultImageFileName; + } + + // Make sure absolute directory exists + QDir imageDir(QDir::rootPath()); + if (!imageDir.exists(directory)) + imageDir.mkpath(directory); + + QString resolvedFileName = directory; + resolvedFileName.append("\\"); + resolvedFileName.append(fileNameOnly); + m_stillCaptureFileName = resolvedFileName; + } +} + +void S60ImageCaptureSession::MceoFocusComplete() +{ + emit focusStatusChanged(QCamera::Locked, QCamera::LockAcquired); +} + +void S60ImageCaptureSession::MceoCapturedDataReady(TDesC8* aData) +{ + emit imageExposed(m_currentImageId); + + m_icState = EImageCaptureWritingImage; + + TFileName path = convertImagePath(); + + // Try to save image and inform if it was succcesful + TRAPD(err, saveImageL(aData, path)); + if (err) { + if (m_previewDecodingOngoing) + m_previewDecodingOngoing = false; // Reset + + setError(err, tr("Writing captured image to a file failed."), true); + m_icState = EImageCapturePrepared; + return; + } + + m_icState = EImageCapturePrepared; + +} + +void S60ImageCaptureSession::MceoCapturedBitmapReady(CFbsBitmap* aBitmap) +{ + emit imageExposed(m_currentImageId); + + m_icState = EImageCaptureWritingImage; + + if(aBitmap) + { +#ifndef ECAM_PREVIEW_API + if (m_previewDecodingOngoing) { + m_previewInWaitLoop = true; + CActiveScheduler::Start(); // Wait for the completion of the previous Preview generation + } + + // Delete old instances if needed + if (m_imageDecoder) { + delete m_imageDecoder; + m_imageDecoder = 0; + } + if (m_previewBitmap) { + delete m_previewBitmap; + m_previewBitmap = 0; + } +#endif // ECAM_CAMERA_API + if (m_imageEncoder) { + delete m_imageEncoder; + m_imageEncoder = 0; + } + if (m_fileSystemAccess) { + m_fileSystemAccess->Close(); + delete m_fileSystemAccess; + m_fileSystemAccess = 0; + } + + TInt saveError = KErrNone; + TFileName path = convertImagePath(); + + // Create FileSystem access + m_fileSystemAccess = new RFs; + if (!m_fileSystemAccess) { + setError(KErrNoMemory, tr("Failed to write captured image to a file.")); + return; + } + saveError = m_fileSystemAccess->Connect(); + if (saveError) { + setError(saveError, tr("Failed to write captured image to a file.")); + return; + } + + TRAP(saveError, m_imageEncoder = S60ImageCaptureEncoder::NewL(this, + m_fileSystemAccess, + &path, + m_symbianImageQuality)); + if (saveError) + setError(saveError, tr("Saving captured image failed."), true); + m_previewDecodingOngoing = true; + m_imageEncoder->encode(aBitmap); + + } else { + setError(KErrBadHandle, tr("Unexpected camera error."), true); + } + + m_icState = EImageCapturePrepared; +} + +void S60ImageCaptureSession::MceoHandleError(TCameraEngineError aErrorType, TInt aError) +{ + Q_UNUSED(aErrorType); + setError(aError, tr("General camera error.")); +} + +TFileName S60ImageCaptureSession::convertImagePath() +{ + TFileName path = KNullDesC(); + + // Convert to Symbian path + TPtrC16 attachmentPath(KNullDesC); + + // Path is already included in filename + attachmentPath.Set(reinterpret_cast<const TUint16*>(QDir::toNativeSeparators(m_stillCaptureFileName).utf16())); + path.Append(attachmentPath); + + return path; +} + +/* + * Creates (asynchronously) Preview Image from Jpeg ImageBuffer and also + * writes Jpeg (synchronously) to a file. + */ +void S60ImageCaptureSession::saveImageL(TDesC8 *aData, TFileName &aPath) +{ + if (aData == 0) + setError(KErrGeneral, tr("Captured image data is not available."), true); + + if (aPath.Size() > 0) { +#ifndef ECAM_PREVIEW_API + if (m_previewDecodingOngoing) { + m_previewInWaitLoop = true; + CActiveScheduler::Start(); // Wait for the completion of the previous Preview generation + } + + // Delete old instances if needed + if (m_imageDecoder) { + delete m_imageDecoder; + m_imageDecoder = 0; + } + if (m_previewBitmap) { + delete m_previewBitmap; + m_previewBitmap = 0; + } +#endif // ECAM_PREVIEW_API + if (m_fileSystemAccess) { + m_fileSystemAccess->Close(); + delete m_fileSystemAccess; + m_fileSystemAccess = 0; + } + + RFs *fileSystemAccess = new (ELeave) RFs; + User::LeaveIfError(fileSystemAccess->Connect()); + CleanupClosePushL(*fileSystemAccess); + +#ifndef ECAM_PREVIEW_API + // Generate Thumbnail to be used as Preview + S60ImageCaptureDecoder *imageDecoder = S60ImageCaptureDecoder::DataNewL(this, fileSystemAccess, aData); + CleanupStack::PushL(imageDecoder); + + // Set proper Preview Size + TSize scaledSize((m_captureSize.width() / KSnapshotDownScaleFactor), (m_captureSize.height() / KSnapshotDownScaleFactor)); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize((m_captureSize.width() / (KSnapshotDownScaleFactor/2)), (m_captureSize.height() / (KSnapshotDownScaleFactor/2))); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize((m_captureSize.width() / (KSnapshotDownScaleFactor/4)), (m_captureSize.height() / (KSnapshotDownScaleFactor/4))); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize(m_captureSize.width(), m_captureSize.height()); + + TFrameInfo *info = imageDecoder->frameInfo(); + if (!info) { + setError(KErrGeneral, tr("Preview image creation failed.")); + return; + } + + CFbsBitmap *previewBitmap = new (ELeave) CFbsBitmap; + CleanupStack::PushL(previewBitmap); + TInt bitmapCreationErr = previewBitmap->Create(scaledSize, info->iFrameDisplayMode); + if (bitmapCreationErr) { + setError(bitmapCreationErr, tr("Preview creation failed.")); + return; + } + + // Jpeg conversion completes in RunL + m_previewDecodingOngoing = true; + imageDecoder->decode(previewBitmap); +#endif // ECAM_PREVIEW_API + + RFile file; + TInt fileWriteErr = KErrNone; + fileWriteErr = file.Replace(*fileSystemAccess, aPath, EFileWrite); + if (fileWriteErr) + User::Leave(fileWriteErr); + CleanupClosePushL(file); // Close if Leaves + + fileWriteErr = file.Write(*aData); + if (fileWriteErr) + User::Leave(fileWriteErr); + + CleanupStack::PopAndDestroy(&file); +#ifdef ECAM_PREVIEW_API + CleanupStack::PopAndDestroy(fileSystemAccess); +#else // !ECAM_PREVIEW_API + // Delete when Image is decoded + CleanupStack::Pop(previewBitmap); + CleanupStack::Pop(imageDecoder); + CleanupStack::Pop(fileSystemAccess); + + // Set member variables (Cannot leave any more) + m_previewBitmap = previewBitmap; + m_imageDecoder = imageDecoder; + m_fileSystemAccess = fileSystemAccess; +#endif // ECAM_PREVIEW_API + + emit imageSaved(m_currentImageId, m_stillCaptureFileName); + + // Inform that we can continue taking more pictures + emit readyForCaptureChanged(true); + + // For custom preview generation, image buffer gets released in RunL() +#ifdef ECAM_PREVIEW_API + releaseImageBuffer(); +#endif // ECAM_PREVIEW_API + + } else { + setError(KErrPathNotFound, tr("Invalid path given."), true); + } +} + +void S60ImageCaptureSession::releaseImageBuffer() +{ + if (m_cameraEngine) + m_cameraEngine->ReleaseImageBuffer(); + else + setError(KErrNotReady, tr("Unexpected camera error."), true); +} + +/* + * Queries camera properties + * Results are returned to member variable m_info + * + * @return boolean indicating if querying the info was a success + */ +bool S60ImageCaptureSession::queryCurrentCameraInfo() +{ + if (m_cameraEngine) { + m_cameraInfo = m_cameraEngine->CameraInfo(); + return true; + } + + return false; +} + +/* + * This function handles different camera status changes + */ +void S60ImageCaptureSession::cameraStatusChanged(QCamera::Status status) +{ + if (status == QCamera::ActiveStatus) { + m_cameraStarted = true; + if (m_captureWhenReady) + capture(m_requestedStillCaptureFileName); + }else if (status == QCamera::UnloadedStatus) { + m_cameraStarted = false; + m_icState = EImageCaptureNotPrepared; + } + else + m_cameraStarted = false; +} + +QSize S60ImageCaptureSession::captureSize() const +{ + return m_captureSize; +} + +QSize S60ImageCaptureSession::minimumCaptureSize() +{ + return supportedCaptureSizesForCodec(formatMap().key(m_currentFormat)).first(); +} +QSize S60ImageCaptureSession::maximumCaptureSize() +{ + return supportedCaptureSizesForCodec(formatMap().key(m_currentFormat)).last(); +} + +void S60ImageCaptureSession::setCaptureSize(const QSize &size) +{ + if (size.isNull() || + size.isEmpty() || + size == QSize(-1,-1)) { + // An empty QSize indicates the encoder should make an optimal choice based on what is + // available from the image source and the limitations of the codec. + m_captureSize = supportedCaptureSizesForCodec(formatMap().key(m_currentFormat)).last(); + } + else + m_captureSize = size; +} + +QList<QSize> S60ImageCaptureSession::supportedCaptureSizesForCodec(const QString &codecName) +{ + QList<QSize> list; + + // If we have CameraEngine loaded and we can update CameraInfo + if (m_cameraEngine && queryCurrentCameraInfo()) { + CCamera::TFormat format; + if (codecName == "") + format = defaultImageFormat(); + else + format = selectFormatForCodec(codecName); + + CCamera *camera = m_cameraEngine->Camera(); + TSize imageSize; + if (camera) { + for (int i = 0; i < m_cameraInfo->iNumImageSizesSupported; i++) { + camera->EnumerateCaptureSizes(imageSize, i, format); + list << QSize(imageSize.iWidth, imageSize.iHeight); // Add resolution to the list + } + } + } + +#ifdef Q_CC_NOKIAX86 // Emulator + // Add some for testing purposes + list << QSize(50, 50); + list << QSize(100, 100); + list << QSize(800,600); +#endif + + return list; +} + +QMap<QString, int> S60ImageCaptureSession::formatMap() +{ + QMap<QString, int> formats; + + // Format list copied from CCamera::TFormat (in ecam.h) + formats.insert("Monochrome", 0x0001); + formats.insert("16bitRGB444", 0x0002); + formats.insert("16BitRGB565", 0x0004); + formats.insert("32BitRGB888", 0x0008); + formats.insert("Jpeg", 0x0010); + formats.insert("Exif", 0x0020); + formats.insert("FbsBitmapColor4K", 0x0040); + formats.insert("FbsBitmapColor64K", 0x0080); + formats.insert("FbsBitmapColor16M", 0x0100); + formats.insert("UserDefined", 0x0200); + formats.insert("YUV420Interleaved", 0x0400); + formats.insert("YUV420Planar", 0x0800); + formats.insert("YUV422", 0x1000); + formats.insert("YUV422Reversed", 0x2000); + formats.insert("YUV444", 0x4000); + formats.insert("YUV420SemiPlanar", 0x8000); + formats.insert("FbsBitmapColor16MU", 0x00010000); + formats.insert("MJPEG", 0x00020000); + formats.insert("EncodedH264", 0x00040000); + + return formats; +} + +QMap<QString, QString> S60ImageCaptureSession::codecDescriptionMap() +{ + QMap<QString, QString> formats; + + formats.insert("image/jpg", "JPEG image codec"); + + return formats; +} + +QStringList S60ImageCaptureSession::supportedImageCaptureCodecs() +{ +#ifdef Q_CC_NOKIAX86 // Emulator + return formatMap().keys(); +#endif + + return m_supportedImageCodecs; +} + +void S60ImageCaptureSession::updateImageCaptureFormats() +{ + m_formats.clear(); + if (m_cameraEngine && queryCurrentCameraInfo()) { + TUint32 supportedFormats = m_cameraInfo->iImageFormatsSupported; + +#ifdef S60_3X_PLATFORM // S60 3.1 & 3.2 + int maskEnd = CCamera::EFormatFbsBitmapColor16MU; +#else // S60 5.0 or later + int maskEnd = CCamera::EFormatEncodedH264; +#endif // S60_3X_PLATFORM + + for (int mask = CCamera::EFormatMonochrome; mask <= maskEnd; mask <<= 1) { + if (supportedFormats & mask) + m_formats << mask; // Store mask of supported format + } + } +} + +QString S60ImageCaptureSession::imageCaptureCodec() +{ + return m_currentCodec; +} +void S60ImageCaptureSession::setImageCaptureCodec(const QString &codecName) +{ + if (!codecName.isEmpty()) { + if (supportedImageCaptureCodecs().contains(codecName, Qt::CaseInsensitive) || + codecName == "image/jpg") { + m_currentCodec = codecName; + m_currentFormat = selectFormatForCodec(m_currentCodec); + } else { + setError(KErrNotSupported, tr("Requested image codec is not supported")); + } + } else { + m_currentCodec = KDefaultImageCodec; + m_currentFormat = selectFormatForCodec(m_currentCodec); + } +} + +QString S60ImageCaptureSession::imageCaptureCodecDescription(const QString &codecName) +{ + QString description = codecDescriptionMap().value(codecName); + return description; +} + +QtMultimediaKit::EncodingQuality S60ImageCaptureSession::captureQuality() const +{ + switch (m_symbianImageQuality) { + case KJpegQualityVeryLow: + return QtMultimediaKit::VeryLowQuality; + case KJpegQualityLow: + return QtMultimediaKit::LowQuality; + case KJpegQualityNormal: + return QtMultimediaKit::NormalQuality; + case KJpegQualityHigh: + return QtMultimediaKit::HighQuality; + case KJpegQualityVeryHigh: + return QtMultimediaKit::VeryHighQuality; + + default: + // Return normal as default + return QtMultimediaKit::NormalQuality; + } +} + +void S60ImageCaptureSession::setCaptureQuality(const QtMultimediaKit::EncodingQuality &quality) +{ + // Use sensible presets + switch (quality) { + case QtMultimediaKit::VeryLowQuality: + m_symbianImageQuality = KJpegQualityVeryLow; + break; + case QtMultimediaKit::LowQuality: + m_symbianImageQuality = KJpegQualityLow; + break; + case QtMultimediaKit::NormalQuality: + m_symbianImageQuality = KJpegQualityNormal; + break; + case QtMultimediaKit::HighQuality: + m_symbianImageQuality = KJpegQualityHigh; + break; + case QtMultimediaKit::VeryHighQuality: + m_symbianImageQuality = KJpegQualityVeryHigh; + break; + + default: + m_symbianImageQuality = quality * KSymbianImageQualityCoefficient; + break; + } +} + +qreal S60ImageCaptureSession::maximumZoom() +{ + qreal maxZoomFactor = 1.0; + + if (queryCurrentCameraInfo()) { + maxZoomFactor = m_cameraInfo->iMaxZoomFactor; + + if (maxZoomFactor == 0.0 || maxZoomFactor == 1.0) { + return 1.0; // Not supported + } else { + return maxZoomFactor; + } + } else { + return 1.0; + } +} + +qreal S60ImageCaptureSession::minZoom() +{ + qreal minZoomValue = 1.0; + + if (queryCurrentCameraInfo()) { + minZoomValue = m_cameraInfo->iMinZoomFactor; + if (minZoomValue == 0.0 || minZoomValue == 1.0) + return 1.0; // Macro Zoom is not supported + else { + return minZoomValue; + } + + } else { + return 1.0; + } +} + +qreal S60ImageCaptureSession::maxDigitalZoom() +{ + qreal maxDigitalZoomFactor = 1.0; + + if (queryCurrentCameraInfo()) { + maxDigitalZoomFactor = m_cameraInfo->iMaxDigitalZoomFactor; + return maxDigitalZoomFactor; + } else { + return 1.0; + } +} + +void S60ImageCaptureSession::doSetZoomFactorL(qreal optical, qreal digital) +{ +#if !defined(USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER) & !defined(USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER) + // Convert Zoom Factor to Zoom Value if AdvSettings are not available + int digitalSymbian = (digital * m_cameraInfo->iMaxDigitalZoom) / maxDigitalZoom(); + if (m_cameraInfo->iMaxDigitalZoom != 0 && digital == 1.0) + digitalSymbian = 1; // Make sure zooming out to initial value if requested +#endif // !USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER & !USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER + + if (m_cameraEngine && !m_cameraEngine->IsCameraReady()) + return; + + if (m_cameraEngine && queryCurrentCameraInfo()) { + CCamera *camera = m_cameraEngine->Camera(); + if (camera) { + + // Optical Zoom + if (!qFuzzyCompare(optical, qreal(1.0)) && !qFuzzyCompare(optical, qreal(0.0))) { + setError(KErrNotSupported, tr("Requested optical zoom factor is not supported.")); + return; + } + + // Digital Zoom (Smooth Zoom - Zoom value set in steps) + if (digital != digitalZoomFactor()) { + if ((digital > 1.0 || qFuzzyCompare(digital, qreal(1.0))) && + digital <= m_cameraInfo->iMaxDigitalZoomFactor) { +#if defined(USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER) | defined(USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER) + if (m_advancedSettings) { + qreal currentZoomFactor = m_advancedSettings->digitalZoomFactorL(); + + QList<qreal> smoothZoomSetValues; + QList<qreal> factors = m_advancedSettings->supportedDigitalZoomFactors(); + if (currentZoomFactor < digital) { + for (int i = 0; i < factors.count(); ++i) { + if (factors.at(i) > currentZoomFactor && factors.at(i) < digital) + smoothZoomSetValues << factors.at(i); + } + + for (int i = 0; i < smoothZoomSetValues.count(); i = i + KSmoothZoomStep) { + m_advancedSettings->setDigitalZoomFactorL(smoothZoomSetValues[i]); // Using Zoom Factor + } + + } else { + for (int i = 0; i < factors.count(); ++i) { + if (factors.at(i) < currentZoomFactor && factors.at(i) > digital) + smoothZoomSetValues << factors.at(i); + } + + for (int i = (smoothZoomSetValues.count() - 1); i >= 0; i = i - KSmoothZoomStep) { + m_advancedSettings->setDigitalZoomFactorL(smoothZoomSetValues[i]); // Using Zoom Factor + } + } + + // Set final value + m_advancedSettings->setDigitalZoomFactorL(digital); + } + else + setError(KErrNotReady, tr("Zooming failed.")); +#else // No advanced settigns + // Define zoom steps + int currentZoomFactor = camera->DigitalZoomFactor(); + int difference = abs(currentZoomFactor - digitalSymbian); + int midZoomValue = currentZoomFactor; + + if (currentZoomFactor < digitalSymbian) { + while (midZoomValue < (digitalSymbian - KSmoothZoomStep)) { + midZoomValue = midZoomValue + KSmoothZoomStep; + camera->SetDigitalZoomFactorL(midZoomValue); + } + } else { + while (midZoomValue > (digitalSymbian + KSmoothZoomStep)) { + midZoomValue = midZoomValue - KSmoothZoomStep; + camera->SetDigitalZoomFactorL(midZoomValue); + } + } + + // Set final and emit signal + camera->SetDigitalZoomFactorL(digitalSymbian); +#endif // USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER | USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER + } else { + setError(KErrNotSupported, tr("Requested digital zoom factor is not supported.")); + return; + } + } + } + } else { + setError(KErrGeneral, tr("Unexpected camera error.")); + } +} + +qreal S60ImageCaptureSession::opticalZoomFactor() +{ + qreal factor = 1.0; + +#if defined(USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER) | defined(USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER) + if (m_advancedSettings) { + TRAPD(err, factor = m_advancedSettings->opticalZoomFactorL()); + if (err) + return 1.0; + } +#else // No advanced settigns + if (m_cameraEngine && m_cameraInfo) { + if (m_cameraEngine->Camera()) { + if (m_cameraInfo->iMaxZoom != 0) + factor = (m_cameraEngine->Camera()->ZoomFactor()* maximumZoom()) / m_cameraInfo->iMaxZoom; + else + factor = 1.0; + } + } +#endif // USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER | USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER + + if (factor == 0.0) // If not supported + factor = 1.0; + + return factor; +} + +qreal S60ImageCaptureSession::digitalZoomFactor() +{ + qreal factor = 1.0; + +#if defined(USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER) | defined(USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER) + if (m_advancedSettings) { + TRAPD(err, factor = m_advancedSettings->digitalZoomFactorL()); + if (err) + return 1.0; + } +#else // No advanced settigns + if (m_cameraEngine && m_cameraInfo) { + if (m_cameraEngine->Camera()) { + if (m_cameraInfo->iMaxDigitalZoom != 0) + factor = (m_cameraEngine->Camera()->DigitalZoomFactor()* maxDigitalZoom()) / m_cameraInfo->iMaxDigitalZoom; + else + factor = 1.0; + } + } +#endif // USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER | USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER + + if (factor == 0.0) + factor = 1.0; + + return factor; +} + +void S60ImageCaptureSession::setFlashMode(QCameraExposure::FlashModes mode) +{ + TRAPD(err, doSetFlashModeL(mode)); + setError(err, tr("Failed to set flash mode.")); +} + +void S60ImageCaptureSession::doSetFlashModeL(QCameraExposure::FlashModes mode) +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera *camera = m_cameraEngine->Camera(); + switch(mode) { + case QCameraExposure::FlashOff: + camera->SetFlashL(CCamera::EFlashNone); + break; + case QCameraExposure::FlashAuto: + camera->SetFlashL(CCamera::EFlashAuto); + break; + case QCameraExposure::FlashOn: + camera->SetFlashL(CCamera::EFlashForced); + break; + case QCameraExposure::FlashRedEyeReduction: + camera->SetFlashL(CCamera::EFlashRedEyeReduce); + break; + case QCameraExposure::FlashFill: + camera->SetFlashL(CCamera::EFlashFillIn); + break; + + default: + setError(KErrNotSupported, tr("Requested flash mode is not suported")); + break; + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +QCameraExposure::FlashMode S60ImageCaptureSession::flashMode() +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera *camera = m_cameraEngine->Camera(); + switch(camera->Flash()) { + case CCamera::EFlashAuto: + return QCameraExposure::FlashAuto; + case CCamera::EFlashForced: + return QCameraExposure::FlashOn; + case CCamera::EFlashRedEyeReduce: + return QCameraExposure::FlashRedEyeReduction; + case CCamera::EFlashFillIn: + return QCameraExposure::FlashFill; + case CCamera::EFlashNone: + return QCameraExposure::FlashOff; + + default: + return QCameraExposure::FlashAuto; // Most probable default + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } + + return QCameraExposure::FlashOff; +} + +QCameraExposure::FlashModes S60ImageCaptureSession::supportedFlashModes() +{ + QCameraExposure::FlashModes modes = QCameraExposure::FlashOff; + + if (queryCurrentCameraInfo()) { + TInt supportedModes = m_cameraInfo->iFlashModesSupported; + + if (supportedModes == 0) + return modes; + + if (supportedModes & CCamera::EFlashManual) + modes |= QCameraExposure::FlashOff; + if (supportedModes & CCamera::EFlashForced) + modes |= QCameraExposure::FlashOn; + if (supportedModes & CCamera::EFlashAuto) + modes |= QCameraExposure::FlashAuto; + if (supportedModes & CCamera::EFlashFillIn) + modes |= QCameraExposure::FlashFill; + if (supportedModes & CCamera::EFlashRedEyeReduce) + modes |= QCameraExposure::FlashRedEyeReduction; + } + + return modes; +} + +QCameraExposure::ExposureMode S60ImageCaptureSession::exposureMode() +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera* camera = m_cameraEngine->Camera(); + switch(camera->Exposure()) { + case CCamera::EExposureManual: + return QCameraExposure::ExposureManual; + case CCamera::EExposureAuto: + return QCameraExposure::ExposureAuto; + case CCamera::EExposureNight: + return QCameraExposure::ExposureNight; + case CCamera::EExposureBacklight: + return QCameraExposure::ExposureBacklight; + case CCamera::EExposureSport: + return QCameraExposure::ExposureSports; + case CCamera::EExposureSnow: + return QCameraExposure::ExposureSnow; + case CCamera::EExposureBeach: + return QCameraExposure::ExposureBeach; + + default: + return QCameraExposure::ExposureAuto; + } + } + + return QCameraExposure::ExposureAuto; +} + +bool S60ImageCaptureSession::isExposureModeSupported(QCameraExposure::ExposureMode mode) const +{ + TInt supportedModes = m_cameraInfo->iExposureModesSupported; + + if (supportedModes == 0) + return false; + + switch (mode) { + case QCameraExposure::ExposureManual: + if(supportedModes & CCamera::EExposureManual) + return true; + else + return false; + case QCameraExposure::ExposureAuto: + return true; // Always supported + case QCameraExposure::ExposureNight: + if(supportedModes & CCamera::EExposureNight) + return true; + else + return false; + case QCameraExposure::ExposureBacklight: + if(supportedModes & CCamera::EExposureBacklight) + return true; + else + return false; + case QCameraExposure::ExposureSports: + if(supportedModes & CCamera::EExposureSport) + return true; + else + return false; + case QCameraExposure::ExposureSnow: + if(supportedModes & CCamera::EExposureSnow) + return true; + else + return false; + case QCameraExposure::ExposureBeach: + if(supportedModes & CCamera::EExposureBeach) + return true; + else + return false; + + default: + return false; + } +} + +void S60ImageCaptureSession::setExposureMode(QCameraExposure::ExposureMode mode) +{ + TRAPD(err, doSetExposureModeL(mode)); + setError(err, tr("Failed to set exposure mode.")); +} + +void S60ImageCaptureSession::doSetExposureModeL( QCameraExposure::ExposureMode mode) +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera *camera = m_cameraEngine->Camera(); + switch(mode) { + case QCameraExposure::ExposureManual: + camera->SetExposureL(CCamera::EExposureManual); + break; + case QCameraExposure::ExposureAuto: + camera->SetExposureL(CCamera::EExposureAuto); + break; + case QCameraExposure::ExposureNight: + camera->SetExposureL(CCamera::EExposureNight); + break; + case QCameraExposure::ExposureBacklight: + camera->SetExposureL(CCamera::EExposureBacklight); + break; + case QCameraExposure::ExposureSports: + camera->SetExposureL(CCamera::EExposureSport); + break; + case QCameraExposure::ExposureSnow: + camera->SetExposureL(CCamera::EExposureSnow); + break; + case QCameraExposure::ExposureBeach: + camera->SetExposureL(CCamera::EExposureBeach); + break; + case QCameraExposure::ExposureLargeAperture: + case QCameraExposure::ExposureSmallAperture: + break; + case QCameraExposure::ExposurePortrait: + case QCameraExposure::ExposureSpotlight: + default: + setError(KErrNotSupported, tr("Requested exposure mode is not suported")); + break; + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +int S60ImageCaptureSession::contrast() const +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + return m_cameraEngine->Camera()->Contrast(); + } else { + return 0; + } +} + +void S60ImageCaptureSession::setContrast(int value) +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + TRAPD(err, m_cameraEngine->Camera()->SetContrastL(value)); + setError(err, tr("Failed to set contrast.")); + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +int S60ImageCaptureSession::brightness() const +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + return m_cameraEngine->Camera()->Brightness(); + } else { + return 0; + } +} + +void S60ImageCaptureSession::setBrightness(int value) +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + TRAPD(err, m_cameraEngine->Camera()->SetBrightnessL(value)); + setError(err, tr("Failed to set brightness.")); + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + + QCameraImageProcessing::WhiteBalanceMode S60ImageCaptureSession::whiteBalanceMode() +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera::TWhiteBalance mode = m_cameraEngine->Camera()->WhiteBalance(); + switch(mode) { + case CCamera::EWBAuto: + return QCameraImageProcessing::WhiteBalanceAuto; + case CCamera::EWBDaylight: + return QCameraImageProcessing::WhiteBalanceSunlight; + case CCamera::EWBCloudy: + return QCameraImageProcessing::WhiteBalanceCloudy; + case CCamera::EWBTungsten: + return QCameraImageProcessing::WhiteBalanceTungsten; + case CCamera::EWBFluorescent: + return QCameraImageProcessing::WhiteBalanceFluorescent; + case CCamera::EWBFlash: + return QCameraImageProcessing::WhiteBalanceFlash; + case CCamera::EWBBeach: + return QCameraImageProcessing::WhiteBalanceSunset; + case CCamera::EWBManual: + return QCameraImageProcessing::WhiteBalanceManual; + case CCamera::EWBShade: + return QCameraImageProcessing::WhiteBalanceShade; + + default: + return QCameraImageProcessing::WhiteBalanceAuto; + } + } + + return QCameraImageProcessing::WhiteBalanceAuto; +} + +void S60ImageCaptureSession::setWhiteBalanceMode( QCameraImageProcessing::WhiteBalanceMode mode) +{ + TRAPD(err, doSetWhiteBalanceModeL(mode)); + setError(err, tr("Failed to set white balance mode.")); +} + +void S60ImageCaptureSession::doSetWhiteBalanceModeL( QCameraImageProcessing::WhiteBalanceMode mode) +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera* camera = m_cameraEngine->Camera(); + switch(mode) { + case QCameraImageProcessing::WhiteBalanceAuto: + camera->SetWhiteBalanceL(CCamera::EWBAuto); + break; + case QCameraImageProcessing::WhiteBalanceSunlight: + camera->SetWhiteBalanceL(CCamera::EWBDaylight); + break; + case QCameraImageProcessing::WhiteBalanceCloudy: + camera->SetWhiteBalanceL(CCamera::EWBCloudy); + break; + case QCameraImageProcessing::WhiteBalanceTungsten: + camera->SetWhiteBalanceL(CCamera::EWBTungsten); + break; + case QCameraImageProcessing::WhiteBalanceFluorescent: + camera->SetWhiteBalanceL(CCamera::EWBFluorescent); + break; + case QCameraImageProcessing::WhiteBalanceFlash: + camera->SetWhiteBalanceL(CCamera::EWBFlash); + break; + case QCameraImageProcessing::WhiteBalanceSunset: + camera->SetWhiteBalanceL(CCamera::EWBBeach); + break; + case QCameraImageProcessing::WhiteBalanceManual: + camera->SetWhiteBalanceL(CCamera::EWBManual); + break; + case QCameraImageProcessing::WhiteBalanceShade: + camera->SetWhiteBalanceL(CCamera::EWBShade); + break; + + default: + setError(KErrNotSupported, tr("Requested white balance mode is not suported")); + break; + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +bool S60ImageCaptureSession::isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const +{ + if (m_cameraEngine) { + TInt supportedModes = m_cameraInfo->iWhiteBalanceModesSupported; + switch (mode) { + case QCameraImageProcessing::WhiteBalanceManual: + if (supportedModes & CCamera::EWBManual) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceAuto: + if (supportedModes & CCamera::EWBAuto) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceSunlight: + if (supportedModes & CCamera::EWBDaylight) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceCloudy: + if (supportedModes & CCamera::EWBCloudy) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceShade: + if (supportedModes & CCamera::EWBShade) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceTungsten: + if (supportedModes & CCamera::EWBTungsten) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceFluorescent: + if (supportedModes & CCamera::EWBFluorescent) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceIncandescent: // Not available in Symbian + return false; + case QCameraImageProcessing::WhiteBalanceFlash: + if (supportedModes & CCamera::EWBFlash) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceSunset: + if (supportedModes & CCamera::EWBBeach) + return true; + else + return false; + + default: + return false; + } + } + + return false; +} + +/* + * ==================== + * S60 3.1 AutoFocosing + * ==================== + */ +bool S60ImageCaptureSession::isFocusSupported() const +{ + return m_cameraEngine->IsAutoFocusSupported(); +} + +void S60ImageCaptureSession::startFocus() +{ + if (m_cameraEngine) { + TRAPD(err, m_cameraEngine->StartFocusL()); + setError(err, tr("Failed to start focusing.")); + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); +} + +void S60ImageCaptureSession::cancelFocus() +{ + if (m_cameraEngine) { + TRAPD(err, m_cameraEngine->FocusCancel()); + setError(err, tr("Failed to cancel focusing.")); + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); +} + +void S60ImageCaptureSession::handleImageDecoded(int error) +{ + // Delete unneeded objects + if (m_imageDecoder) { + delete m_imageDecoder; + m_imageDecoder = 0; + } + if (m_fileSystemAccess) { + m_fileSystemAccess->Close(); + delete m_fileSystemAccess; + m_fileSystemAccess = 0; + } + + // Check status of decoding + if (error != KErrNone) { + if (m_previewBitmap) { + m_previewBitmap->Reset(); + delete m_previewBitmap; + m_previewBitmap = 0; + } + releaseImageBuffer(); + if (m_previewInWaitLoop) { + CActiveScheduler::Stop(); // Notify to continue execution of next Preview Image generation + m_previewInWaitLoop = false; // Reset + } + setError(error, tr("Preview creation failed.")); + return; + } + + m_previewDecodingOngoing = false; + + QPixmap prevPixmap = QPixmap::fromSymbianCFbsBitmap(m_previewBitmap); + QImage preview = prevPixmap.toImage(); + + if (m_previewBitmap) { + m_previewBitmap->Reset(); + delete m_previewBitmap; + m_previewBitmap = 0; + } + + QT_TRYCATCH_LEAVING( emit imageCaptured(m_currentImageId, preview) ); + + // Release image resources (if not already done) + releaseImageBuffer(); + + if (m_previewInWaitLoop) { + CActiveScheduler::Stop(); // Notify to continue execution of next Preview Image generation + m_previewInWaitLoop = false; // Reset + } +} + +void S60ImageCaptureSession::handleImageEncoded(int error) +{ + // Check status of encoding + if (error != KErrNone) { + releaseImageBuffer(); + if (m_previewInWaitLoop) { + CActiveScheduler::Stop(); // Notify to continue execution of next Preview Image generation + m_previewInWaitLoop = false; // Reset + } + setError(error, tr("Saving captured image to file failed.")); + return; + } else { + QT_TRYCATCH_LEAVING( emit imageSaved(m_currentImageId, m_stillCaptureFileName) ); + } + + if (m_imageEncoder) { + delete m_imageEncoder; + m_imageEncoder = 0; + } + +#ifndef ECAM_PREVIEW_API + // Start preview generation + TInt previewError = KErrNone; + TFileName fileName = convertImagePath(); + TRAP(previewError, m_imageDecoder = S60ImageCaptureDecoder::FileNewL(this, m_fileSystemAccess, &fileName)); + if (previewError) { + setError(previewError, tr("Failed to create preview image.")); + return; + } + + // Set proper Preview Size + TSize scaledSize((m_captureSize.width() / KSnapshotDownScaleFactor), (m_captureSize.height() / KSnapshotDownScaleFactor)); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize((m_captureSize.width() / (KSnapshotDownScaleFactor/2)), (m_captureSize.height() / (KSnapshotDownScaleFactor/2))); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize((m_captureSize.width() / (KSnapshotDownScaleFactor/4)), (m_captureSize.height() / (KSnapshotDownScaleFactor/4))); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize(m_captureSize.width(), m_captureSize.height()); + + TFrameInfo *info = m_imageDecoder->frameInfo(); + if (!info) { + setError(KErrGeneral, tr("Preview image creation failed.")); + return; + } + + m_previewBitmap = new CFbsBitmap; + if (!m_previewBitmap) { + setError(KErrNoMemory, tr("Failed to create preview image.")); + return; + } + previewError = m_previewBitmap->Create(scaledSize, info->iFrameDisplayMode); + if (previewError) { + setError(previewError, tr("Preview creation failed.")); + return; + } + + // Jpeg decoding completes in handleImageDecoded() + m_imageDecoder->decode(m_previewBitmap); +#endif // ECAM_PREVIEW_API + + // Buffer can be released since Preview is created from file + releaseImageBuffer(); + + // Inform that we can continue taking more pictures + QT_TRYCATCH_LEAVING( emit readyForCaptureChanged(true) ); +} + +#ifdef ECAM_PREVIEW_API +void S60ImageCaptureSession::MceoPreviewReady(CFbsBitmap& aPreview) +{ + QPixmap previewPixmap = QPixmap::fromSymbianCFbsBitmap(&aPreview); + QImage preview = previewPixmap.toImage(); + + // Notify preview availability + emit imageCaptured(m_currentImageId, preview); +} +#endif // ECAM_PREVIEW_API + +// End of file + diff --git a/src/plugins/symbian/ecam/s60imagecapturesession.h b/src/plugins/symbian/ecam/s60imagecapturesession.h new file mode 100644 index 000000000..9cae05fc9 --- /dev/null +++ b/src/plugins/symbian/ecam/s60imagecapturesession.h @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60IMAGECAPTURESESSION_H +#define S60IMAGECAPTURESESSION_H + +#include <QtCore/qurl.h> +#include <QtCore/qlist.h> +#include <QtCore/qmap.h> +#include <QtGui/qicon.h> + +#include <qcamera.h> +#include <qcamerafocus.h> +#include <qcameraimagecapture.h> +#include <qvideoframe.h> + +#include "s60camerasettings.h" +#include "s60cameraengine.h" +#include "s60cameraengineobserver.h" +#include "s60cameraconstants.h" // Default Jpeg Quality + +#include <icl/imagedata.h> // TFrameInfo + +QT_USE_NAMESPACE + +class S60CameraService; +class CImageDecoder; +class CImageEncoder; +class CFrameImageData; +class RFs; +class S60ImageCaptureSession; + +/* + * This class implements asynchronous image decoding service for the + * S60ImageCaptureSession. + */ +class S60ImageCaptureDecoder : public CActive +{ +public: // Static Contructor & Destructor + + static S60ImageCaptureDecoder* FileNewL(S60ImageCaptureSession *imageSession = 0, + RFs *fileSystemAccess = 0, + const TDesC16 *fileName = 0); + static S60ImageCaptureDecoder* DataNewL(S60ImageCaptureSession *imageSession = 0, + RFs *fileSystemAccess = 0, + const TDesC8 *data = 0); + ~S60ImageCaptureDecoder(); + +public: // Operations + + void decode(CFbsBitmap *destBitmap); + TFrameInfo *frameInfo(); + +protected: // CActive + + void RunL(); + void DoCancel(); + TInt RunError(TInt aError); + +protected: // Protected constructors + + S60ImageCaptureDecoder(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC8 *data, + const TDesC16 *fileName); + void ConstructL(const bool fileInput = false); + +private: // Data + + S60ImageCaptureSession *m_imageSession; + CImageDecoder *m_imageDecoder; + RFs *m_fs; + const TDesC8 *m_jpegImageData; + const TDesC16 *m_jpegImageFile; + bool m_fileInput; + TFrameInfo m_frameInfo; + +}; + +//============================================================================= + +/* + * This class implements asynchronous image encoding service for the + * S60ImageCaptureSession. + */ +class S60ImageCaptureEncoder : public CActive +{ +public: // Static Contructor & Destructor + + static S60ImageCaptureEncoder* NewL(S60ImageCaptureSession *imageSession = 0, + RFs *fileSystemAccess = 0, + const TDesC16 *fileName = 0, + TInt jpegQuality = KDefaultImageQuality); + ~S60ImageCaptureEncoder(); + +public: // Operations + + void encode(CFbsBitmap *sourceBitmap); + +protected: // CActive + + void RunL(); + void DoCancel(); + TInt RunError(TInt aError); + +protected: // Protected constructors + + S60ImageCaptureEncoder(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC16 *fileName, + TInt jpegQuality); + void ConstructL(); + +private: // Data + + S60ImageCaptureSession *m_imageSession; + CImageEncoder *m_imageEncoder; + RFs *m_fileSystemAccess; + const TDesC16 *m_fileName; + CFrameImageData *m_frameImageData; + TInt m_jpegQuality; + +}; + +//============================================================================= + +/* + * Session handling all image capture activities. + */ +class S60ImageCaptureSession : public QObject, +#ifdef ECAM_PREVIEW_API + public MCameraPreviewObserver, +#endif // ECAM_PREVIEW_API + public MCameraEngineImageCaptureObserver +{ + Q_OBJECT + +public: // Enums + + enum ImageCaptureState { + EImageCaptureNotPrepared = 0, // 0 - ImageCapture has not been prepared + EImageCapturePrepared, // 1 - ImageCapture has been prepared + EImageCaptureCapturing, // 2 - Image capture ongoing + EImageCaptureWritingImage // 3 - Image captured and image writing to file ongoing + }; + +public: // Constructor & Destructor + + S60ImageCaptureSession(QObject *parent = 0); + ~S60ImageCaptureSession(); + +public: // Methods + + void setError(const TInt error, const QString &description, const bool captureError = false); + int currentImageId() const; + + bool isDeviceReady(); + void setCameraHandle(CCameraEngine* camerahandle); + void setCurrentDevice(TInt deviceindex); + void notifySettingsSet(); + + // Ecam Advanced Settings + S60CameraSettings* advancedSettings(); + void deleteAdvancedSettings(); + + // Controls + int prepareImageCapture(); + void releaseImageCapture(); + int capture(const QString &fileName); + void cancelCapture(); + void releaseImageBuffer(); + + // Image Resolution + QSize captureSize() const; + QSize minimumCaptureSize(); + QSize maximumCaptureSize(); + QList<QSize> supportedCaptureSizesForCodec(const QString &codecName); + void setCaptureSize(const QSize &size); + + // Image Codec + QStringList supportedImageCaptureCodecs(); + QString imageCaptureCodec(); + void setImageCaptureCodec(const QString &codecName); + QString imageCaptureCodecDescription(const QString &codecName); + + // Image Quality + QtMultimediaKit::EncodingQuality captureQuality() const; + void setCaptureQuality(const QtMultimediaKit::EncodingQuality &quality); + + // S60 3.1 Focus Control (S60 3.2 and later via S60CameraSettings class) + bool isFocusSupported() const; + void startFocus(); + void cancelFocus(); + + // Zoom Control + qreal maximumZoom(); + qreal minZoom(); + qreal maxDigitalZoom(); + void doSetZoomFactorL(qreal optical, qreal digital); + qreal opticalZoomFactor(); + qreal digitalZoomFactor(); + + // Exposure Mode Control + QCameraExposure::ExposureMode exposureMode(); + void setExposureMode(QCameraExposure::ExposureMode mode); + bool isExposureModeSupported(QCameraExposure::ExposureMode mode) const; + + // Flash Mode Control + QCameraExposure::FlashMode flashMode(); + void setFlashMode(QCameraExposure::FlashModes mode); + QCameraExposure::FlashModes supportedFlashModes(); + + // Contrast Control + int contrast() const; + void setContrast(int value); + + // Brightness Control + int brightness() const; + void setBrightness(int value); + + // White Balance Mode Control + QCameraImageProcessing::WhiteBalanceMode whiteBalanceMode(); + void setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode); + bool isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const; + +public: // Image Decoding & Encoding Notifications + + void handleImageDecoded(int error); + void handleImageEncoded(int error); + +protected: // MCameraEngineObserver + + void MceoFocusComplete(); + void MceoCapturedDataReady(TDesC8* aData); + void MceoCapturedBitmapReady(CFbsBitmap* aBitmap); + void MceoHandleError(TCameraEngineError aErrorType, TInt aError); + +#ifdef ECAM_PREVIEW_API +protected: // MCameraPreviewObserver + + void MceoPreviewReady(CFbsBitmap& aPreview); +#endif // ECAM_PREVIEW_API + +private: // Internal + + QCameraImageCapture::Error fromSymbianErrorToQtMultimediaError(int aError); + + void initializeImageCaptureSettings(); + void resetSession(bool errorHandling = false); + + CCamera::TFormat selectFormatForCodec(const QString &codec); + CCamera::TFormat defaultImageFormat(); + bool queryCurrentCameraInfo(); + QMap<QString, int> formatMap(); + QMap<QString, QString> codecDescriptionMap(); + void updateImageCaptureFormats(); + + void doSetWhiteBalanceModeL(QCameraImageProcessing::WhiteBalanceMode mode); + + void doSetFlashModeL(QCameraExposure::FlashModes mode); + void doSetExposureModeL(QCameraExposure::ExposureMode mode); + + void saveImageL(TDesC8 *aData, TFileName &aPath); + void processFileName(const QString &fileName); + TFileName convertImagePath(); + +signals: // Notifications + + void stateChanged(QCamera::State); + void advancedSettingChanged(); + void captureSizeChanged(const QSize&); + + // Error signals + void cameraError(int, const QString&); // For QCamera::error + void captureError(int, int, const QString&); // For QCameraImageCapture::error + + // Capture notifications + void readyForCaptureChanged(bool); + void imageExposed(int); + void imageCaptured(const int, const QImage&); + void imageSaved(const int, const QString&); + + // Focus notifications + void focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason); + +private slots: // Internal Slots + + void cameraStatusChanged(QCamera::Status); + +private: // Data + + CCameraEngine *m_cameraEngine; + S60CameraSettings *m_advancedSettings; + mutable TCameraInfo *m_cameraInfo; + CFbsBitmap *m_previewBitmap; + CActiveScheduler *m_activeScheduler; + RFs *m_fileSystemAccess; + S60ImageCaptureDecoder *m_imageDecoder; + S60ImageCaptureEncoder *m_imageEncoder; + mutable int m_error; // Symbian ErrorCode + TInt m_activeDeviceIndex; + bool m_cameraStarted; + ImageCaptureState m_icState; + QStringList m_supportedImageCodecs; + QString m_currentCodec; + CCamera::TFormat m_currentFormat; + QSize m_captureSize; + int m_symbianImageQuality; + bool m_captureSettingsSet; + QString m_stillCaptureFileName; + QString m_requestedStillCaptureFileName; + mutable int m_currentImageId; + QList<uint> m_formats; + // This indicates that image capture should be triggered right after + // camera and image setting initialization has completed + bool m_captureWhenReady; + bool m_previewDecodingOngoing; + bool m_previewInWaitLoop; +}; + +#endif // S60IMAGECAPTURESESSION_H diff --git a/src/plugins/symbian/ecam/s60imageencodercontrol.cpp b/src/plugins/symbian/ecam/s60imageencodercontrol.cpp new file mode 100644 index 000000000..bfbf8d36b --- /dev/null +++ b/src/plugins/symbian/ecam/s60imageencodercontrol.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> + +#include "s60imageencodercontrol.h" +#include "s60imagecapturesession.h" + +S60ImageEncoderControl::S60ImageEncoderControl(QObject *parent) : + QImageEncoderControl(parent) +{ +} + +S60ImageEncoderControl::S60ImageEncoderControl(S60ImageCaptureSession *session, QObject *parent) : + QImageEncoderControl(parent) +{ + m_session = session; +} + +S60ImageEncoderControl::~S60ImageEncoderControl() +{ +} + +QList<QSize> S60ImageEncoderControl::supportedResolutions( + const QImageEncoderSettings &settings, bool *continuous) const +{ + QList<QSize> resolutions = m_session->supportedCaptureSizesForCodec(settings.codec()); + + // Discrete resolutions are returned + if (continuous) + *continuous = false; + + return resolutions; +} +QStringList S60ImageEncoderControl::supportedImageCodecs() const +{ + return m_session->supportedImageCaptureCodecs(); +} + +QString S60ImageEncoderControl::imageCodecDescription(const QString &codec) const +{ + return m_session->imageCaptureCodecDescription(codec); +} + +QImageEncoderSettings S60ImageEncoderControl::imageSettings() const +{ + // Update setting values from session + QImageEncoderSettings settings; + settings.setCodec(m_session->imageCaptureCodec()); + settings.setResolution(m_session->captureSize()); + settings.setQuality(m_session->captureQuality()); + + return settings; +} +void S60ImageEncoderControl::setImageSettings(const QImageEncoderSettings &settings) +{ + // Notify that settings have been implicitly set and there's no need to + // initialize them in case camera is changed + m_session->notifySettingsSet(); + + if (!settings.isNull()) { + if (!settings.codec().isEmpty()) { + if (settings.resolution() != QSize(-1,-1)) { // Codec, Resolution & Quality + m_session->setImageCaptureCodec(settings.codec()); + m_session->setCaptureSize(settings.resolution()); + m_session->setCaptureQuality(settings.quality()); + } else { // Codec and Quality + m_session->setImageCaptureCodec(settings.codec()); + m_session->setCaptureQuality(settings.quality()); + } + } else { + if (settings.resolution() != QSize(-1,-1)) { // Resolution & Quality + m_session->setCaptureSize(settings.resolution()); + m_session->setCaptureQuality(settings.quality()); + } + else // Only Quality + m_session->setCaptureQuality(settings.quality()); + } + + // Prepare ImageCapture with the settings and set error if needed + int prepareSuccess = m_session->prepareImageCapture(); + + // Preparation fails with KErrNotReady if camera has not been started. + // That can be ignored since settings are set internally in that case. + if (prepareSuccess != KErrNotReady && prepareSuccess != KErrNone) + m_session->setError(prepareSuccess, tr("Failure in preparation of image capture.")); + } +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60imageencodercontrol.h b/src/plugins/symbian/ecam/s60imageencodercontrol.h new file mode 100644 index 000000000..99a308286 --- /dev/null +++ b/src/plugins/symbian/ecam/s60imageencodercontrol.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60IMAGEENCODERCONTROL_H +#define S60IMAGEENCODERCONTROL_H + +#include <QtCore/qobject.h> +#include "qimageencodercontrol.h" + +QT_USE_NAMESPACE + +class S60ImageCaptureSession; + +/* + * Control for setting encoding settings for the captured image. + */ +class S60ImageEncoderControl : public QImageEncoderControl +{ + Q_OBJECT + +public: // Contructors & Destructor + + S60ImageEncoderControl(QObject *parent = 0); + S60ImageEncoderControl(S60ImageCaptureSession *session, QObject *parent = 0); + ~S60ImageEncoderControl(); + +public: // QImageEncoderControl + + // Codec + QStringList supportedImageCodecs() const; + QString imageCodecDescription(const QString &codec) const; + + // Resolution + QList<QSize> supportedResolutions(const QImageEncoderSettings &settings, + bool *continuous = 0) const; + + // Settings + QImageEncoderSettings imageSettings() const; + void setImageSettings(const QImageEncoderSettings &settings); + +private: // Data + + S60ImageCaptureSession *m_session; +}; + +#endif // S60IMAGEENCODERCONTROL_H diff --git a/src/plugins/symbian/ecam/s60mediacontainercontrol.cpp b/src/plugins/symbian/ecam/s60mediacontainercontrol.cpp new file mode 100644 index 000000000..f0d20f4e3 --- /dev/null +++ b/src/plugins/symbian/ecam/s60mediacontainercontrol.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60mediacontainercontrol.h" +#include "s60videocapturesession.h" +#include "s60cameraconstants.h" + +S60MediaContainerControl::S60MediaContainerControl(QObject *parent): + QMediaContainerControl(parent) +{ +} + +S60MediaContainerControl::S60MediaContainerControl(S60VideoCaptureSession *session, QObject *parent): + QMediaContainerControl(parent) +{ + m_session = session; + + // Set default video container + m_supportedContainers = m_session->supportedVideoContainers(); + + if (!m_supportedContainers.isEmpty()) { + // Check if default container is supported + if (m_supportedContainers.indexOf(KMimeTypeDefaultContainer) != -1) + setContainerMimeType(KMimeTypeDefaultContainer); + // Otherwise use first in the list + else + setContainerMimeType(m_supportedContainers[0]); // First as default + } else { + m_session->setError(KErrGeneral, tr("No supported video containers found.")); + } +} + +S60MediaContainerControl::~S60MediaContainerControl() +{ + m_supportedContainers.clear(); + m_containerDescriptions.clear(); +} + +QStringList S60MediaContainerControl::supportedContainers() const +{ + return m_session->supportedVideoContainers(); +} + +QString S60MediaContainerControl::containerMimeType() const +{ + return m_session->videoContainer(); +} + +void S60MediaContainerControl::setContainerMimeType(const QString &containerMimeType) +{ + m_session->setVideoContainer(containerMimeType); +} + +QString S60MediaContainerControl::containerDescription(const QString &containerMimeType) const +{ + return m_session->videoContainerDescription(containerMimeType); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60mediacontainercontrol.h b/src/plugins/symbian/ecam/s60mediacontainercontrol.h new file mode 100644 index 000000000..057b4bc43 --- /dev/null +++ b/src/plugins/symbian/ecam/s60mediacontainercontrol.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60MEDIACONTAINERCONTROL_H +#define S60MEDIACONTAINERCONTROL_H + +#include <QtCore/qstringlist.h> +#include <QtCore/qmap.h> +#include <qmediacontainercontrol.h> + +QT_USE_NAMESPACE + +class S60VideoCaptureSession; + +/* + * Control for setting container (file format) for video recorded using + * QMediaRecorder. + */ +class S60MediaContainerControl : public QMediaContainerControl +{ + Q_OBJECT + +public: // Contructors & Destructor + + S60MediaContainerControl(QObject *parent = 0); + S60MediaContainerControl(S60VideoCaptureSession *session, QObject *parent = 0); + virtual ~S60MediaContainerControl(); + +public: // QMediaContainerControl + + QStringList supportedContainers() const; + QString containerMimeType() const; + void setContainerMimeType(const QString &containerMimeType); + + QString containerDescription(const QString &containerMimeType) const; + +private: // Data + + S60VideoCaptureSession *m_session; + QStringList m_supportedContainers; + QMap<QString, QString> m_containerDescriptions; +}; + +#endif // S60MEDIACONTAINERCONTROL_H diff --git a/src/plugins/symbian/ecam/s60mediarecordercontrol.cpp b/src/plugins/symbian/ecam/s60mediarecordercontrol.cpp new file mode 100644 index 000000000..2a1394fa5 --- /dev/null +++ b/src/plugins/symbian/ecam/s60mediarecordercontrol.cpp @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60cameraservice.h" +#include "s60mediarecordercontrol.h" +#include "s60cameracontrol.h" +#include "s60videocapturesession.h" + +S60MediaRecorderControl::S60MediaRecorderControl(QObject *parent) : + QMediaRecorderControl(parent) +{ +} + +S60MediaRecorderControl::S60MediaRecorderControl(S60CameraService *service, + S60VideoCaptureSession *session, + QObject *parent): + QMediaRecorderControl(parent), + m_state(QMediaRecorder::StoppedState) // Default RecorderState +{ + m_session = session; + m_service = service; + m_cameraControl = qobject_cast<S60CameraControl *>(m_service->requestControl(QCameraControl_iid)); + + // Connect signals + connect(m_session, SIGNAL(stateChanged(S60VideoCaptureSession::TVideoCaptureState)), + this, SLOT(updateState(S60VideoCaptureSession::TVideoCaptureState))); + connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(durationChanged(qint64))); + connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool))); + connect(m_session, SIGNAL(error(int,const QString &)), this, SIGNAL(error(int,const QString &))); +} + +S60MediaRecorderControl::~S60MediaRecorderControl() +{ + // Release requested control + if (m_cameraControl) + m_service->releaseControl(m_cameraControl); +} + +QUrl S60MediaRecorderControl::outputLocation() const +{ + return m_session->outputLocation(); +} + +bool S60MediaRecorderControl::setOutputLocation(const QUrl& sink) +{ + // Output location can only be set in StoppedState + if (m_state == QMediaRecorder::StoppedState) + return m_session->setOutputLocation(sink); + + // Do not signal error, but notify that setting was not effective + return false; +} + +QMediaRecorder::State S60MediaRecorderControl::convertInternalStateToQtState(S60VideoCaptureSession::TVideoCaptureState aState) const +{ + QMediaRecorder::State state; + + switch (aState) { + case S60VideoCaptureSession::ERecording: + state = QMediaRecorder::RecordingState; + break; + case S60VideoCaptureSession::EPaused: + state = QMediaRecorder::PausedState; + break; + + default: + // All others + state = QMediaRecorder::StoppedState; + break; + } + + return state; +} + +void S60MediaRecorderControl::updateState(S60VideoCaptureSession::TVideoCaptureState state) +{ + QMediaRecorder::State newState = convertInternalStateToQtState(state); + + if (m_state != newState) { + m_state = newState; + emit stateChanged(m_state); + } +} + +QMediaRecorder::State S60MediaRecorderControl::state() const +{ + return m_state; +} + +qint64 S60MediaRecorderControl::duration() const +{ + return m_session->position(); +} + +/* +This method is called after encoder configuration is done. +Encoder can load necessary resources at this point, +to reduce delay before recording is started. Calling this method reduces the +latency when calling record() to start video recording. +*/ +void S60MediaRecorderControl::applySettings() +{ + m_session->applyAllSettings(); +} + +void S60MediaRecorderControl::record() +{ + if (m_state == QMediaRecorder::RecordingState) + return; + + if (m_cameraControl && m_cameraControl->captureMode() != QCamera::CaptureVideo) { + emit error(QCamera::CameraError, tr("Video capture mode is not selected.")); + return; + } + + m_session->startRecording(); +} + +void S60MediaRecorderControl::pause() +{ + if (m_state != QMediaRecorder::RecordingState) { + // Discard + return; + } + + m_session->pauseRecording(); +} + +void S60MediaRecorderControl::stop() +{ + if (m_state == QMediaRecorder::StoppedState) { + // Ignore + return; + } + + m_session->stopRecording(); +} + +bool S60MediaRecorderControl::isMuted() const +{ + return m_session->isMuted(); +} + +void S60MediaRecorderControl::setMuted(bool muted) +{ + m_session->setMuted(muted); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60mediarecordercontrol.h b/src/plugins/symbian/ecam/s60mediarecordercontrol.h new file mode 100644 index 000000000..5db7477bd --- /dev/null +++ b/src/plugins/symbian/ecam/s60mediarecordercontrol.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60MEDIARECORDERCONTROL_H +#define S60MEDIARECORDERCONTROL_H + +#include <QtCore/qurl.h> +#include <qmediarecorder.h> +#include <qmediarecordercontrol.h> + +#include "s60videocapturesession.h" + +QT_USE_NAMESPACE + +class S60VideoCaptureSession; +class S60CameraService; +class S60CameraControl; + +/* + * Control for video recording operations. + */ +class S60MediaRecorderControl : public QMediaRecorderControl +{ + Q_OBJECT + +public: // Contructors & Destructor + + S60MediaRecorderControl(QObject *parent = 0); + S60MediaRecorderControl(S60CameraService *service, + S60VideoCaptureSession *session, + QObject *parent = 0); + ~S60MediaRecorderControl(); + +public: // QMediaRecorderControl + + QUrl outputLocation() const; + bool setOutputLocation(const QUrl &sink); + + QMediaRecorder::State state() const; + + qint64 duration() const; + + bool isMuted() const; + + void applySettings(); + +/* +Q_SIGNALS: // QMediaRecorderControl + void stateChanged(QMediaRecorder::State state); + void durationChanged(qint64 position); + void mutedChanged(bool muted); + void error(int error, const QString &errorString); +*/ + +public slots: // QMediaRecorderControl + + void record(); + void pause(); + void stop(); + void setMuted(bool); + +private: + + QMediaRecorder::State convertInternalStateToQtState( + S60VideoCaptureSession::TVideoCaptureState aState) const; + +private slots: + + void updateState(S60VideoCaptureSession::TVideoCaptureState state); + +private: // Data + + S60VideoCaptureSession *m_session; + S60CameraService *m_service; + S60CameraControl *m_cameraControl; + QMediaRecorder::State m_state; + +}; + +#endif // S60MEDIARECORDERCONTROL_H diff --git a/src/plugins/symbian/ecam/s60videocapturesession.cpp b/src/plugins/symbian/ecam/s60videocapturesession.cpp new file mode 100644 index 000000000..7158f8696 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videocapturesession.cpp @@ -0,0 +1,2995 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <QtCore/qdir.h> +#include <QtCore/qtimer.h> + +#include "s60videocapturesession.h" +#include "s60cameraconstants.h" + +#include <utf.h> +#include <bautils.h> + +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED +#include <mmf/devvideo/devvideorecord.h> +#endif + +S60VideoCaptureSession::S60VideoCaptureSession(QObject *parent) : + QObject(parent), + m_cameraEngine(0), + m_videoRecorder(0), + m_position(0), + m_error(KErrNone), + m_cameraStarted(false), + m_captureState(ENotInitialized), // Default state + m_sink(QUrl()), + m_requestedSink(QUrl()), + m_captureSettingsSet(false), + m_container(QString()), + m_requestedContainer(QString()), + m_muted(false), + m_maxClipSize(-1), + m_videoControllerMap(QHash<int, QHash<int,VideoFormatData> >()), + m_videoParametersForEncoder(QList<MaxResolutionRatesAndTypes>()), + m_openWhenReady(false), + m_prepareAfterOpenComplete(false), + m_startAfterPrepareComplete(false), + m_uncommittedSettings(false), + m_commitSettingsWhenReady(false) +{ +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED + // Populate info of supported codecs, and their resolution, etc. + TRAPD(err, doPopulateVideoCodecsDataL()); + setError(err, tr("Failed to gather video codec information.")); +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + + initializeVideoCaptureSettings(); + + m_durationTimer = new QTimer; + m_durationTimer->setInterval(KDurationChangedInterval); + connect(m_durationTimer, SIGNAL(timeout()), this, SLOT(durationTimerTriggered())); +} + +S60VideoCaptureSession::~S60VideoCaptureSession() +{ + if (m_captureState >= ERecording) + m_videoRecorder->Stop(); + + if (m_captureState >= EInitialized) + m_videoRecorder->Close(); + + if (m_videoRecorder) { + delete m_videoRecorder; + m_videoRecorder = 0; + } + + if (m_durationTimer) { + delete m_durationTimer; + m_durationTimer = 0; + } + + // Clear all data structures + foreach (MaxResolutionRatesAndTypes structure, m_videoParametersForEncoder) { + structure.frameRatePictureSizePair.clear(); + structure.mimeTypes.clear(); + } + m_videoParametersForEncoder.clear(); + + m_videoCodecList.clear(); + m_audioCodecList.clear(); + + QList<TInt> controllers = m_videoControllerMap.keys(); + for (int i = 0; i < controllers.size(); ++i) { + foreach(VideoFormatData data, m_videoControllerMap[controllers[i]]){ + data.supportedMimeTypes.clear(); + } + m_videoControllerMap[controllers[i]].clear(); + } + m_videoControllerMap.clear(); +} + +/* + * This function can be used both internally and from Control classes using this session. + * The error notification will go to client application through QMediaRecorder error signal. + */ +void S60VideoCaptureSession::setError(const TInt error, const QString &description) +{ + if (error == KErrNone) + return; + + m_error = error; + QMediaRecorder::Error recError = fromSymbianErrorToQtMultimediaError(m_error); + + // Stop/Close/Reset only of other than "not supported" error + if (m_error != KErrNotSupported) { + if (m_captureState >= ERecording) + m_videoRecorder->Stop(); + + if (m_captureState >= EInitialized) + m_videoRecorder->Close(); + + // Reset state + if (m_captureState != ENotInitialized) { + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + m_captureState = ENotInitialized; + emit stateChanged(m_captureState); + } + } + + emit this->error(recError, description); + + // Reset only of other than "not supported" error + if (m_error != KErrNotSupported) + resetSession(true); + else + m_error = KErrNone; // Reset error +} + +QMediaRecorder::Error S60VideoCaptureSession::fromSymbianErrorToQtMultimediaError(int aError) +{ + switch(aError) { + case KErrNone: + return QMediaRecorder::NoError; // No errors have occurred + case KErrArgument: + case KErrNotSupported: + return QMediaRecorder::FormatError; // The feature/format is not supported + case KErrNoMemory: + case KErrNotFound: + case KErrBadHandle: + return QMediaRecorder::ResourceError; // Not able to use camera/recorder resources + + default: + return QMediaRecorder::ResourceError; // Other error has occurred + } +} + +/* + * This function applies all recording settings to make latency during the + * start of the recording as short as possible. After this it is not possible to + * set settings (inc. output location) before stopping the recording. + */ +void S60VideoCaptureSession::applyAllSettings() +{ + switch (m_captureState) { + case ENotInitialized: + case EInitializing: + m_commitSettingsWhenReady = true; + return; + case EInitialized: + setOutputLocation(QUrl()); + m_prepareAfterOpenComplete = true; + return; + case EOpening: + m_prepareAfterOpenComplete = true; + return; + case EOpenComplete: + // Do nothing, ready to commit + break; + case EPreparing: + m_commitSettingsWhenReady = true; + return; + case EPrepared: + // Revert state internally, since logically applying settings means going + // from OpenComplete ==> Preparing ==> Prepared. + m_captureState = EOpenComplete; + break; + case ERecording: + case EPaused: + setError(KErrNotReady, tr("Cannot apply settings while recording.")); + return; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + + // Commit settings - State is now OpenComplete (possibly reverted from Prepared) + commitVideoEncoderSettings(); + + // If capture state has been changed to: + // * Opening: A different media container has been requested + // * Other: Failure during the setting committing + // ==> Return + if (m_captureState != EOpenComplete) + return; + + // Start preparing + m_captureState = EPreparing; + emit stateChanged(m_captureState); + + if (m_cameraEngine->IsCameraReady()) + m_videoRecorder->Prepare(); +} + +void S60VideoCaptureSession::setCameraHandle(CCameraEngine* cameraHandle) +{ + m_cameraEngine = cameraHandle; + + // Initialize settings for the new camera + initializeVideoCaptureSettings(); + + resetSession(); +} + +void S60VideoCaptureSession::notifySettingsSet() +{ + m_captureSettingsSet = true; +} + +void S60VideoCaptureSession::doInitializeVideoRecorderL() +{ + if (m_captureState > ENotInitialized) + resetSession(); + + m_captureState = EInitializing; + emit stateChanged(m_captureState); + + // Open Dummy file to be able to query supported settings + int cameraHandle = m_cameraEngine->Camera() ? m_cameraEngine->Camera()->Handle() : 0; + + TUid controllerUid; + TUid formatUid; + selectController(m_requestedContainer, controllerUid, formatUid); + + if (m_videoRecorder) { + // File open completes in MvruoOpenComplete + TRAPD(err, m_videoRecorder->OpenFileL(KDummyVideoFile, cameraHandle, controllerUid, formatUid)); + setError(err, tr("Failed to initialize video recorder.")); + m_container = m_requestedContainer; + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +void S60VideoCaptureSession::resetSession(bool errorHandling) +{ + if (m_videoRecorder) { + delete m_videoRecorder; + m_videoRecorder = 0; + } + + if (m_captureState != ENotInitialized) { + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + m_captureState = ENotInitialized; + emit stateChanged(m_captureState); + } + + // Reset error to be able to recover + m_error = KErrNone; + + // Reset flags + m_openWhenReady = false; + m_prepareAfterOpenComplete = false; + m_startAfterPrepareComplete = false; + m_uncommittedSettings = false; + m_commitSettingsWhenReady = false; + + TRAPD(err, m_videoRecorder = CVideoRecorderUtility::NewL(*this)); + if (err) { + qWarning("Failed to create video recorder."); + if (errorHandling) + emit error(QMediaRecorder::ResourceError, tr("Failed to recover from video error.")); + else + setError(err, tr("Failure in creation of video recorder device.")); + return; + } + + updateVideoCaptureContainers(); +} + +QList<QSize> S60VideoCaptureSession::supportedVideoResolutions(bool *continuous) +{ + QList<QSize> resolutions; + + // Secondary Camera + if (m_cameraEngine->CurrentCameraIndex() != 0) { + TCameraInfo *info = m_cameraEngine->CameraInfo(); + if (info) { + TInt videoResolutionCount = info->iNumVideoFrameSizesSupported; + CCamera *camera = m_cameraEngine->Camera(); + if (camera) { + for (TInt i = 0; i < videoResolutionCount; ++i) { + TSize checkedResolution; + camera->EnumerateVideoFrameSizes(checkedResolution, i, CCamera::EFormatYUV420Planar); + QSize qtResolution(checkedResolution.iWidth, checkedResolution.iHeight); + if (!resolutions.contains(qtResolution)) + resolutions.append(qtResolution); + } + } else { + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + } + } else { + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + } + + // Primary Camera + } else { + + if (m_videoParametersForEncoder.count() > 0) { + + // Also arbitrary resolutions are supported + if (continuous) + *continuous = true; + + // Append all supported resolutions to the list + foreach (MaxResolutionRatesAndTypes parameters, m_videoParametersForEncoder) + for (int i = 0; i < parameters.frameRatePictureSizePair.count(); ++i) + if (!resolutions.contains(parameters.frameRatePictureSizePair[i].frameSize)) + resolutions.append(parameters.frameRatePictureSizePair[i].frameSize); + } + } + +#ifdef Q_CC_NOKIAX86 // Emulator + resolutions << QSize(160, 120); + resolutions << QSize(352, 288); + resolutions << QSize(640,480); +#endif // Q_CC_NOKIAX86 + + return resolutions; +} + +QList<QSize> S60VideoCaptureSession::supportedVideoResolutions(const QVideoEncoderSettings &settings, bool *continuous) +{ + QList<QSize> supportedFrameSizes; + + // Secondary Camera + if (m_cameraEngine->CurrentCameraIndex() != 0) { + TCameraInfo *info = m_cameraEngine->CameraInfo(); + if (info) { + TInt videoResolutionCount = info->iNumVideoFrameSizesSupported; + CCamera *camera = m_cameraEngine->Camera(); + if (camera) { + for (TInt i = 0; i < videoResolutionCount; ++i) { + TSize checkedResolution; + camera->EnumerateVideoFrameSizes(checkedResolution, i, CCamera::EFormatYUV420Planar); + QSize qtResolution(checkedResolution.iWidth, checkedResolution.iHeight); + if (!supportedFrameSizes.contains(qtResolution)) + supportedFrameSizes.append(qtResolution); + } + } else { + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + } + } else { + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + } + + // Primary Camera + } else { + + if (settings.codec().isEmpty()) + return supportedFrameSizes; + + if (!m_videoCodecList.contains(settings.codec(), Qt::CaseInsensitive)) + return supportedFrameSizes; + + // Also arbitrary resolutions are supported + if (continuous) + *continuous = true; + + // Find maximum resolution (using defined framerate if set) + for (int i = 0; i < m_videoParametersForEncoder.count(); ++i) { + // Check if encoder supports the requested codec + if (!m_videoParametersForEncoder[i].mimeTypes.contains(settings.codec(), Qt::CaseInsensitive)) + continue; + + foreach (SupportedFrameRatePictureSize pair, m_videoParametersForEncoder[i].frameRatePictureSizePair) { + if (!supportedFrameSizes.contains(pair.frameSize)) { + QSize maxForMime = maximumResolutionForMimeType(settings.codec()); + if (settings.frameRate() != 0) { + if (settings.frameRate() <= pair.frameRate) { + if ((pair.frameSize.width() * pair.frameSize.height()) <= (maxForMime.width() * maxForMime.height())) + supportedFrameSizes.append(pair.frameSize); + } + } else { + if ((pair.frameSize.width() * pair.frameSize.height()) <= (maxForMime.width() * maxForMime.height())) + supportedFrameSizes.append(pair.frameSize); + } + } + } + } + } + +#ifdef Q_CC_NOKIAX86 // Emulator + supportedFrameSizes << QSize(160, 120); + supportedFrameSizes << QSize(352, 288); + supportedFrameSizes << QSize(640,480); +#endif + + return supportedFrameSizes; +} + +QList<qreal> S60VideoCaptureSession::supportedVideoFrameRates(bool *continuous) +{ + QList<qreal> supportedRatesList; + + if (m_videoParametersForEncoder.count() > 0) { + // Insert min and max to the list + supportedRatesList.append(1.0); // Use 1fps as sensible minimum + qreal foundMaxFrameRate(0.0); + + // Also arbitrary framerates are supported + if (continuous) + *continuous = true; + + // Find max framerate + foreach (MaxResolutionRatesAndTypes parameters, m_videoParametersForEncoder) { + for (int i = 0; i < parameters.frameRatePictureSizePair.count(); ++i) { + qreal maxFrameRate = parameters.frameRatePictureSizePair[i].frameRate; + if (maxFrameRate > foundMaxFrameRate) + foundMaxFrameRate = maxFrameRate; + } + } + + supportedRatesList.append(foundMaxFrameRate); + } + + // Add also other standard framerates to the list + if (!supportedRatesList.isEmpty()) { + if (supportedRatesList.last() > 30.0) { + if (!supportedRatesList.contains(30.0)) + supportedRatesList.insert(1, 30.0); + } + if (supportedRatesList.last() > 25.0) { + if (!supportedRatesList.contains(25.0)) + supportedRatesList.insert(1, 25.0); + } + if (supportedRatesList.last() > 15.0) { + if (!supportedRatesList.contains(15.0)) + supportedRatesList.insert(1, 15.0); + } + if (supportedRatesList.last() > 10.0) { + if (!supportedRatesList.contains(10)) + supportedRatesList.insert(1, 10.0); + } + } + +#ifdef Q_CC_NOKIAX86 // Emulator + supportedRatesList << 30.0 << 25.0 << 15.0 << 10.0 << 5.0; +#endif + + return supportedRatesList; +} + +QList<qreal> S60VideoCaptureSession::supportedVideoFrameRates(const QVideoEncoderSettings &settings, bool *continuous) +{ + QList<qreal> supportedFrameRates; + + if (settings.codec().isEmpty()) + return supportedFrameRates; + if (!m_videoCodecList.contains(settings.codec(), Qt::CaseInsensitive)) + return supportedFrameRates; + + // Also arbitrary framerates are supported + if (continuous) + *continuous = true; + + // Find maximum framerate (using defined resolution if set) + for (int i = 0; i < m_videoParametersForEncoder.count(); ++i) { + // Check if encoder supports the requested codec + if (!m_videoParametersForEncoder[i].mimeTypes.contains(settings.codec(), Qt::CaseInsensitive)) + continue; + + foreach (SupportedFrameRatePictureSize pair, m_videoParametersForEncoder[i].frameRatePictureSizePair) { + if (!supportedFrameRates.contains(pair.frameRate)) { + qreal maxRateForMime = maximumFrameRateForMimeType(settings.codec()); + if (settings.resolution().width() != 0 && settings.resolution().height() != 0) { + if((settings.resolution().width() * settings.resolution().height()) <= (pair.frameSize.width() * pair.frameSize.height())) { + if (pair.frameRate <= maxRateForMime) + supportedFrameRates.append(pair.frameRate); + } + } else { + if (pair.frameRate <= maxRateForMime) + supportedFrameRates.append(pair.frameRate); + } + } + } + } + + // Add also other standard framerates to the list + if (!supportedFrameRates.isEmpty()) { + if (supportedFrameRates.last() > 30.0) { + if (!supportedFrameRates.contains(30.0)) + supportedFrameRates.insert(1, 30.0); + } + if (supportedFrameRates.last() > 25.0) { + if (!supportedFrameRates.contains(25.0)) + supportedFrameRates.insert(1, 25.0); + } + if (supportedFrameRates.last() > 15.0) { + if (!supportedFrameRates.contains(15.0)) + supportedFrameRates.insert(1, 15.0); + } + if (supportedFrameRates.last() > 10.0) { + if (!supportedFrameRates.contains(10)) + supportedFrameRates.insert(1, 10.0); + } + } + +#ifdef Q_CC_NOKIAX86 // Emulator + supportedFrameRates << 30.0 << 25.0 << 15.0 << 10.0 << 5.0; +#endif + + return supportedFrameRates; +} + +bool S60VideoCaptureSession::setOutputLocation(const QUrl &sink) +{ + m_requestedSink = sink; + + if (m_error) + return false; + + switch (m_captureState) { + case ENotInitialized: + case EInitializing: + case EOpening: + case EPreparing: + m_openWhenReady = true; + return true; + + case EInitialized: + case EOpenComplete: + case EPrepared: + // Continue + break; + + case ERecording: + case EPaused: + setError(KErrNotReady, tr("Cannot set file name while recording.")); + return false; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return false; + } + + // Empty URL - Use default file name and path (C:\Data\Videos\video.mp4) + if (sink.isEmpty()) { + + // Make sure default directory exists + QDir videoDir(QDir::rootPath()); + if (!videoDir.exists(KDefaultVideoPath)) + videoDir.mkpath(KDefaultVideoPath); + QString defaultFile = KDefaultVideoPath; + defaultFile.append("\\"); + defaultFile.append(KDefaultVideoFileName); + m_sink.setUrl(defaultFile); + + } else { // Non-empty URL + + QString fullUrl = sink.scheme(); + + // Relative URL + if (sink.isRelative()) { + + // Extract file name and path from the URL + fullUrl = KDefaultVideoPath; + fullUrl.append("\\"); + fullUrl.append(QDir::toNativeSeparators(sink.path())); + + // Absolute URL + } else { + + // Extract file name and path from the URL + if (fullUrl == "file") { + fullUrl = QDir::toNativeSeparators(sink.path().right(sink.path().length() - 1)); + } else { + fullUrl.append(":"); + fullUrl.append(QDir::toNativeSeparators(sink.path())); + } + } + + QString fileName = fullUrl.right(fullUrl.length() - fullUrl.lastIndexOf("\\") - 1); + QString directory = fullUrl.left(fullUrl.lastIndexOf("\\")); + if (directory.lastIndexOf("\\") == (directory.length() - 1)) + directory = directory.left(directory.length() - 1); + + // URL is Absolute path, not including file name + if (!fileName.contains(".")) { + if (fileName != "") { + directory.append("\\"); + directory.append(fileName); + } + fileName = KDefaultVideoFileName; + } + + // Make sure absolute directory exists + QDir videoDir(QDir::rootPath()); + if (!videoDir.exists(directory)) + videoDir.mkpath(directory); + + QString resolvedURL = directory; + resolvedURL.append("\\"); + resolvedURL.append(fileName); + m_sink = QUrl(resolvedURL); + } + + // State is either Initialized, OpenComplete or Prepared, Close previously opened file + if (m_videoRecorder) + m_videoRecorder->Close(); + else + setError(KErrNotReady, tr("Unexpected camera error.")); + + // Open file + + QString fileName = QDir::toNativeSeparators(m_sink.toString()); + TPtrC16 fileSink(reinterpret_cast<const TUint16*>(fileName.utf16())); + + int cameraHandle = m_cameraEngine->Camera() ? m_cameraEngine->Camera()->Handle() : 0; + + TUid controllerUid; + TUid formatUid; + selectController(m_requestedContainer, controllerUid, formatUid); + + if (m_videoRecorder) { + // File open completes in MvruoOpenComplete + TRAPD(err, m_videoRecorder->OpenFileL(fileSink, cameraHandle, controllerUid, formatUid)); + setError(err, tr("Failed to initialize video recorder.")); + m_container = m_requestedContainer; + m_captureState = EOpening; + emit stateChanged(m_captureState); + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); + + m_uncommittedSettings = true; + return true; +} + +QUrl S60VideoCaptureSession::outputLocation() const +{ + return m_sink; +} + +qint64 S60VideoCaptureSession::position() +{ + // Update position only if recording is ongoing + if ((m_captureState == ERecording) && m_videoRecorder) { + // Signal will be automatically emitted of position changes + TRAPD(err, m_position = m_videoRecorder->DurationL().Int64() / 1000); + setError(err, tr("Cannot retrieve video position.")); + } + + return m_position; +} + +S60VideoCaptureSession::TVideoCaptureState S60VideoCaptureSession::state() const +{ + return m_captureState; +} + +bool S60VideoCaptureSession::isMuted() const +{ + return m_muted; +} + +void S60VideoCaptureSession::setMuted(const bool muted) +{ + // CVideoRecorderUtility can mute/unmute only if not recording + if (m_captureState > EPrepared) { + if (muted) + setError(KErrNotSupported, tr("Muting audio is not supported during recording.")); + else + setError(KErrNotSupported, tr("Unmuting audio is not supported during recording.")); + return; + } + + // Check if request is already active + if (muted == isMuted()) + return; + + m_muted = muted; + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::commitVideoEncoderSettings() +{ + if (m_captureState == EOpenComplete) { + + if (m_container != m_requestedContainer) { + setOutputLocation(m_requestedSink); + return; + } + + TRAPD(err, doSetCodecsL()); + if (err) { + setError(err, tr("Failed to set audio or video codec.")); + m_audioSettings.setCodec(KMimeTypeDefaultAudioCodec); + m_videoSettings.setCodec(KMimeTypeDefaultVideoCodec); + } + + doSetVideoResolution(m_videoSettings.resolution()); + doSetFrameRate(m_videoSettings.frameRate()); + doSetBitrate(m_videoSettings.bitRate()); + + // Audio/Video EncodingMode are not supported in Symbian + +#ifndef S60_31_PLATFORM + if (m_audioSettings.sampleRate() != -1 && m_audioSettings.sampleRate() != 0) { + TRAP(err, m_videoRecorder->SetAudioSampleRateL((TInt)m_audioSettings.sampleRate())); + if (err != KErrNotSupported) { + setError(err, tr("Setting audio sample rate failed.")); + } else { + setError(err, tr("Setting audio sample rate is not supported.")); + m_audioSettings.setSampleRate(KDefaultSampleRate); // Reset + } + } +#endif // S60_31_PLATFORM + + TRAP(err, m_videoRecorder->SetAudioBitRateL((TInt)m_audioSettings.bitRate())); + if (err != KErrNotSupported) { + if (err == KErrArgument) { + setError(KErrNotSupported, tr("Requested audio bitrate is not supported or previously set codec is not supported with requested bitrate.")); + int fallback = 16000; + TRAP(err, m_videoRecorder->SetAudioBitRateL(TInt(fallback))); + if (err == KErrNone) + m_audioSettings.setBitRate(fallback); + } else { + setError(err, tr("Setting audio bitrate failed.")); + } + } + +#ifndef S60_31_PLATFORM + if (m_audioSettings.channelCount() != -1) { + TRAP(err, m_videoRecorder->SetAudioChannelsL(TUint(m_audioSettings.channelCount()))); + if (err != KErrNotSupported) { + setError(err, tr("Setting audio channel count failed.")); + } else { + setError(err, tr("Setting audio channel count is not supported.")); + m_audioSettings.setChannelCount(KDefaultChannelCount); // Reset + } + } +#endif // S60_31_PLATFORM + + TBool isAudioMuted = EFalse; + TRAP(err, isAudioMuted = !m_videoRecorder->AudioEnabledL()); + if (err != KErrNotSupported && err != KErrNone) + setError(err, tr("Failure when checking if audio is enabled.")); + + if (m_muted != (bool)isAudioMuted) { + TRAP(err, m_videoRecorder->SetAudioEnabledL(TBool(!m_muted))); + if (err) { + if (err != KErrNotSupported) { + setError(err, tr("Failed to mute/unmute audio.")); + } else { + setError(err, tr("Muting/unmuting audio is not supported.")); + } + } + else + emit mutedChanged(m_muted); + } + + m_uncommittedSettings = false; // Reset + } +} + +void S60VideoCaptureSession::queryAudioEncoderSettings() +{ + if (!m_videoRecorder) + return; + + switch (m_captureState) { + case ENotInitialized: + case EInitializing: + case EOpening: + case EPreparing: + return; + + // Possible to query settings from CVideoRecorderUtility + case EInitialized: + case EOpenComplete: + case EPrepared: + case ERecording: + case EPaused: + break; + + default: + return; + } + + TInt err = KErrNone; + + // Codec + TFourCC audioCodec; + TRAP(err, audioCodec = m_videoRecorder->AudioTypeL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying audio codec failed.")); + } + QString codec = ""; + foreach (TFourCC aCodec, m_audioCodecList) { + if (audioCodec == aCodec) + codec = m_audioCodecList.key(aCodec); + } + m_audioSettings.setCodec(codec); + +#ifndef S60_31_PLATFORM + // Samplerate + TInt sampleRate = -1; + TRAP(err, sampleRate = m_videoRecorder->AudioSampleRateL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying audio sample rate failed.")); + } + m_audioSettings.setSampleRate(int(sampleRate)); +#endif // S60_31_PLATFORM + + // BitRate + TInt bitRate = -1; + TRAP(err, bitRate = m_videoRecorder->AudioBitRateL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying audio bitrate failed.")); + } + m_audioSettings.setBitRate(int(bitRate)); + +#ifndef S60_31_PLATFORM + // ChannelCount + TUint channelCount = 0; + TRAP(err, channelCount = m_videoRecorder->AudioChannelsL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying audio channel count failed.")); + } + if (channelCount != 0) + m_audioSettings.setChannelCount(int(channelCount)); + else + m_audioSettings.setChannelCount(-1); +#endif // S60_31_PLATFORM + + // EncodingMode + m_audioSettings.setEncodingMode(QtMultimediaKit::ConstantQualityEncoding); + + // IsMuted + TBool isEnabled = ETrue; + TRAP(err, isEnabled = m_videoRecorder->AudioEnabledL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying whether audio is muted failed.")); + } + m_muted = bool(!isEnabled); +} + +void S60VideoCaptureSession::queryVideoEncoderSettings() +{ + if (!m_videoRecorder) + return; + + switch (m_captureState) { + case ENotInitialized: + case EInitializing: + case EOpening: + case EPreparing: + return; + + // Possible to query settings from CVideoRecorderUtility + case EInitialized: + case EOpenComplete: + case EPrepared: + case ERecording: + case EPaused: + break; + + default: + return; + } + + TInt err = KErrNone; + + // Codec + const TDesC8 &videoMimeType = m_videoRecorder->VideoFormatMimeType(); + QString videoMimeTypeString = ""; + if (videoMimeType.Length() > 0) { + // First convert the 8-bit descriptor to Unicode + HBufC16* videoCodec; + videoCodec = CnvUtfConverter::ConvertToUnicodeFromUtf8L(videoMimeType); + CleanupStack::PushL(videoCodec); + + // Then deep copy QString from that + videoMimeTypeString = QString::fromUtf16(videoCodec->Ptr(), videoCodec->Length()); + m_videoSettings.setCodec(videoMimeTypeString); + + CleanupStack::PopAndDestroy(videoCodec); + } + + // Resolution + TSize symbianResolution; + TRAP(err, m_videoRecorder->GetVideoFrameSizeL(symbianResolution)); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying video resolution failed.")); + } + QSize resolution = QSize(symbianResolution.iWidth, symbianResolution.iHeight); + m_videoSettings.setResolution(resolution); + + // FrameRate + TReal32 frameRate = 0; + TRAP(err, frameRate = m_videoRecorder->VideoFrameRateL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying video framerate failed.")); + } + m_videoSettings.setFrameRate(qreal(frameRate)); + + // BitRate + TInt bitRate = -1; + TRAP(err, bitRate = m_videoRecorder->VideoBitRateL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying video bitrate failed.")); + } + m_videoSettings.setBitRate(int(bitRate)); + + // EncodingMode + m_audioSettings.setEncodingMode(QtMultimediaKit::ConstantQualityEncoding); +} + +void S60VideoCaptureSession::videoEncoderSettings(QVideoEncoderSettings &videoSettings) +{ + switch (m_captureState) { + // CVideoRecorderUtility, return requested settings + case ENotInitialized: + case EInitializing: + case EInitialized: + case EOpening: + case EOpenComplete: + case EPreparing: + break; + + // Possible to query settings from CVideoRecorderUtility + case EPrepared: + case ERecording: + case EPaused: + queryVideoEncoderSettings(); + break; + + default: + videoSettings = QVideoEncoderSettings(); + setError(KErrGeneral, tr("Unexpected video error.")); + return; + } + + videoSettings = m_videoSettings; +} + +void S60VideoCaptureSession::audioEncoderSettings(QAudioEncoderSettings &audioSettings) +{ + switch (m_captureState) { + // CVideoRecorderUtility, return requested settings + case ENotInitialized: + case EInitializing: + case EInitialized: + case EOpening: + case EOpenComplete: + case EPreparing: + break; + + // Possible to query settings from CVideoRecorderUtility + case EPrepared: + case ERecording: + case EPaused: + queryAudioEncoderSettings(); + break; + + default: + audioSettings = QAudioEncoderSettings(); + setError(KErrGeneral, tr("Unexpected video error.")); + return; + } + + audioSettings = m_audioSettings; +} + +void S60VideoCaptureSession::validateRequestedCodecs() +{ + if (!m_audioCodecList.contains(m_audioSettings.codec())) { + m_audioSettings.setCodec(KMimeTypeDefaultAudioCodec); + setError(KErrNotSupported, tr("Currently selected audio codec is not supported by the platform.")); + } + if (!m_videoCodecList.contains(m_videoSettings.codec())) { + m_videoSettings.setCodec(KMimeTypeDefaultVideoCodec); + setError(KErrNotSupported, tr("Currently selected video codec is not supported by the platform.")); + } +} + +void S60VideoCaptureSession::setVideoCaptureQuality(const QtMultimediaKit::EncodingQuality quality, + const VideoQualityDefinition mode) +{ + // Sensible presets + switch (mode) { + case ENoVideoQuality: + // Do nothing + break; + case EOnlyVideoQuality: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setResolution(QSize(128,96)); + m_videoSettings.setFrameRate(10); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(128000); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setResolution(QSize(352,288)); + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(384000); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + if (m_cameraEngine && m_cameraEngine->CurrentCameraIndex() == 0) + m_videoSettings.setResolution(QSize(640,480)); // Primary camera + else + m_videoSettings.setResolution(QSize(352,288)); // Other cameras + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(2000000); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndResolution: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setFrameRate(10); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(128000); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(384000); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(2000000); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndFrameRate: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setResolution(QSize(128,96)); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setBitRate(128000); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setResolution(QSize(352,288)); + m_videoSettings.setBitRate(384000); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + if (m_cameraEngine && m_cameraEngine->CurrentCameraIndex() == 0) + m_videoSettings.setResolution(QSize(640,480)); // Primary camera + else + m_videoSettings.setResolution(QSize(352,288)); // Other cameras + m_videoSettings.setBitRate(2000000); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndBitRate: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setResolution(QSize(128,96)); + m_videoSettings.setFrameRate(10); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setResolution(QSize(352,288)); + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + if (m_cameraEngine && m_cameraEngine->CurrentCameraIndex() == 0) + m_videoSettings.setResolution(QSize(640,480)); // Primary camera + else + m_videoSettings.setResolution(QSize(352,288)); // Other cameras + m_videoSettings.setFrameRate(15); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndResolutionAndBitRate: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setFrameRate(10); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + m_videoSettings.setFrameRate(15); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndResolutionAndFrameRate: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setBitRate(128000); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setBitRate(384000); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + m_videoSettings.setBitRate(2000000); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndFrameRateAndBitRate: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setResolution(QSize(128,96)); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setResolution(QSize(176,144)); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setResolution(QSize(176,144)); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setResolution(QSize(352,288)); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + if (m_cameraEngine && m_cameraEngine->CurrentCameraIndex() == 0) + m_videoSettings.setResolution(QSize(640,480)); // Primary camera + else + m_videoSettings.setResolution(QSize(352,288)); // Other cameras + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + } + + m_videoSettings.setQuality(quality); + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setAudioCaptureQuality(const QtMultimediaKit::EncodingQuality quality, + const AudioQualityDefinition mode) +{ + // Based on audio quality definition mode, select proper SampleRate and BitRate + switch (mode) { + case EOnlyAudioQuality: + switch (quality) { + case QtMultimediaKit::VeryLowQuality: + m_audioSettings.setBitRate(16000); + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::LowQuality: + m_audioSettings.setBitRate(16000); + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::NormalQuality: + m_audioSettings.setBitRate(32000); + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::HighQuality: + m_audioSettings.setBitRate(64000); + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::VeryHighQuality: + m_audioSettings.setBitRate(64000); + m_audioSettings.setSampleRate(-1); + break; + default: + m_audioSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported audio quality.")); + return; + } + break; + case EAudioQualityAndBitRate: + switch (quality) { + case QtMultimediaKit::VeryLowQuality: + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::LowQuality: + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::NormalQuality: + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::HighQuality: + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::VeryHighQuality: + m_audioSettings.setSampleRate(-1); + break; + default: + m_audioSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported audio quality.")); + return; + } + break; + case EAudioQualityAndSampleRate: + switch (quality) { + case QtMultimediaKit::VeryLowQuality: + m_audioSettings.setBitRate(16000); + break; + case QtMultimediaKit::LowQuality: + m_audioSettings.setBitRate(16000); + break; + case QtMultimediaKit::NormalQuality: + m_audioSettings.setBitRate(32000); + break; + case QtMultimediaKit::HighQuality: + m_audioSettings.setBitRate(64000); + break; + case QtMultimediaKit::VeryHighQuality: + m_audioSettings.setBitRate(64000); + break; + default: + m_audioSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported audio quality.")); + return; + } + break; + case ENoAudioQuality: + // No actions required, just set quality parameter + break; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + + m_audioSettings.setQuality(quality); + m_uncommittedSettings = true; +} + +int S60VideoCaptureSession::initializeVideoRecording() +{ + if (m_error) + return m_error; + + TRAPD(symbianError, doInitializeVideoRecorderL()); + setError(symbianError, tr("Failed to initialize video recorder.")); + + return symbianError; +} + +void S60VideoCaptureSession::releaseVideoRecording() +{ + if (m_captureState >= ERecording) { + m_videoRecorder->Stop(); + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + } + + if (m_captureState >= EInitialized) + m_videoRecorder->Close(); + + // Reset state + m_captureState = ENotInitialized; + + // Reset error to be able to recover from error + m_error = KErrNone; + + // Reset flags + m_openWhenReady = false; + m_prepareAfterOpenComplete = false; + m_startAfterPrepareComplete = false; + m_uncommittedSettings = false; + m_commitSettingsWhenReady = false; +} + +void S60VideoCaptureSession::startRecording() +{ + if (m_error) { + setError(m_error, tr("Unexpected recording error.")); + return; + } + + switch (m_captureState) { + case ENotInitialized: + case EInitializing: + case EInitialized: + if (m_captureState == EInitialized) + setOutputLocation(m_requestedSink); + m_startAfterPrepareComplete = true; + return; + + case EOpening: + case EPreparing: + // Execute FileOpenL() and Prepare() asap and then start recording + m_startAfterPrepareComplete = true; + return; + case EOpenComplete: + case EPrepared: + if (m_captureState == EPrepared && !m_uncommittedSettings) + break; + + // Revert state internally, since logically applying settings means going + // from OpenComplete ==> Preparing ==> Prepared. + m_captureState = EOpenComplete; + m_startAfterPrepareComplete = true; + + // Commit settings and prepare with them + applyAllSettings(); + return; + case ERecording: + // Discard + return; + case EPaused: + // Continue + break; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + + // State should now be either Prepared with no Uncommitted Settings or Paused + + if (!m_cameraStarted) { + m_startAfterPrepareComplete = true; + return; + } + + if (m_cameraEngine && !m_cameraEngine->IsCameraReady()) { + setError(KErrNotReady, tr("Camera not ready to start video recording.")); + return; + } + + if (m_videoRecorder) { + m_videoRecorder->Record(); + m_captureState = ERecording; + emit stateChanged(m_captureState); + m_durationTimer->start(); + + // Reset all flags + m_openWhenReady = false; + m_prepareAfterOpenComplete = false; + m_startAfterPrepareComplete = false; + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +void S60VideoCaptureSession::pauseRecording() +{ + if (m_captureState == ERecording) { + if (m_videoRecorder) { + TRAPD(err, m_videoRecorder->PauseL()); + setError(err, tr("Pausing video recording failed.")); + m_captureState = EPaused; + emit stateChanged(m_captureState); + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + + // Notify last duration + TRAP(err, m_position = m_videoRecorder->DurationL().Int64() / 1000); + setError(err, tr("Cannot retrieve video position.")); + emit positionChanged(m_position); + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +void S60VideoCaptureSession::stopRecording(const bool reInitialize) +{ + if (m_captureState != ERecording && m_captureState != EPaused) + return; // Ignore + + if (m_videoRecorder) { + m_videoRecorder->Stop(); + m_videoRecorder->Close(); + + // Notify muting is disabled if needed + if (m_muted) + emit mutedChanged(false); + + m_captureState = ENotInitialized; + emit stateChanged(m_captureState); + + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + + // VideoRecording will be re-initialized unless explicitly requested not to do so + if (reInitialize) { + if (m_cameraEngine->IsCameraReady()) + initializeVideoRecording(); + } + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); +} + +void S60VideoCaptureSession::updateVideoCaptureContainers() +{ + TRAPD(err, doUpdateVideoCaptureContainersL()); + setError(err, tr("Failed to gather video container information.")); +} + +void S60VideoCaptureSession::doUpdateVideoCaptureContainersL() +{ + // Clear container data structure + QList<TInt> mapControllers = m_videoControllerMap.keys(); + for (int i = 0; i < mapControllers.size(); ++i) { + foreach(VideoFormatData data, m_videoControllerMap[mapControllers[i]]){ + data.supportedMimeTypes.clear(); + } + m_videoControllerMap[mapControllers[i]].clear(); + } + m_videoControllerMap.clear(); + + // Resolve the supported video format and retrieve a list of controllers + CMMFControllerPluginSelectionParameters* pluginParameters = + CMMFControllerPluginSelectionParameters::NewLC(); + CMMFFormatSelectionParameters* format = + CMMFFormatSelectionParameters::NewLC(); + + // Set the play and record format selection parameters to be blank. + // Format support is only retrieved if requested. + pluginParameters->SetRequiredPlayFormatSupportL(*format); + pluginParameters->SetRequiredRecordFormatSupportL(*format); + + // Set the media IDs + RArray<TUid> mediaIds; + CleanupClosePushL(mediaIds); + + User::LeaveIfError(mediaIds.Append(KUidMediaTypeVideo)); + + // Get plugins that support at least video + pluginParameters->SetMediaIdsL(mediaIds, + CMMFPluginSelectionParameters::EAllowOtherMediaIds); + pluginParameters->SetPreferredSupplierL(KNullDesC, + CMMFPluginSelectionParameters::EPreferredSupplierPluginsFirstInList); + + // Array to hold all the controllers support the match data + RMMFControllerImplInfoArray controllers; + CleanupResetAndDestroyPushL(controllers); + pluginParameters->ListImplementationsL(controllers); + + // Find the first controller with at least one record format available + for (TInt index = 0; index < controllers.Count(); ++index) { + + m_videoControllerMap.insert(controllers[index]->Uid().iUid, QHash<TInt,VideoFormatData>()); + + const RMMFFormatImplInfoArray& recordFormats = controllers[index]->RecordFormats(); + for (TInt j = 0; j < recordFormats.Count(); ++j) { + VideoFormatData formatData; + formatData.description = QString::fromUtf16( + recordFormats[j]->DisplayName().Ptr(), + recordFormats[j]->DisplayName().Length()); + + const CDesC8Array& mimeTypes = recordFormats[j]->SupportedMimeTypes(); + for (int k = 0; k < mimeTypes.Count(); ++k) { + TPtrC8 mimeType = mimeTypes[k]; + QString type = QString::fromUtf8((char *)mimeType.Ptr(), + mimeType.Length()); + formatData.supportedMimeTypes.append(type); + } + + m_videoControllerMap[controllers[index]->Uid().iUid].insert(recordFormats[j]->Uid().iUid, formatData); + } + } + + CleanupStack::PopAndDestroy(&controllers); + CleanupStack::PopAndDestroy(&mediaIds); + CleanupStack::PopAndDestroy(format); + CleanupStack::PopAndDestroy(pluginParameters); +} + +/* + * This goes through the available controllers and selects proper one based + * on the format. Function sets proper UIDs to be used for controller and format. + */ +void S60VideoCaptureSession::selectController(const QString &format, + TUid &controllerUid, + TUid &formatUid) +{ + QList<TInt> controllers = m_videoControllerMap.keys(); + QList<TInt> formats; + + for (int i = 0; i < controllers.count(); ++i) { + formats = m_videoControllerMap[controllers[i]].keys(); + for (int j = 0; j < formats.count(); ++j) { + VideoFormatData formatData = m_videoControllerMap[controllers[i]][formats[j]]; + if (formatData.supportedMimeTypes.contains(format, Qt::CaseInsensitive)) { + controllerUid = TUid::Uid(controllers[i]); + formatUid = TUid::Uid(formats[j]); + } + } + } +} + +QStringList S60VideoCaptureSession::supportedVideoCaptureCodecs() +{ + return m_videoCodecList; +} + +QStringList S60VideoCaptureSession::supportedAudioCaptureCodecs() +{ + QStringList keys = m_audioCodecList.keys(); + keys.sort(); + return keys; +} + +QList<int> S60VideoCaptureSession::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) +{ + QList<int> rates; + + TRAPD(err, rates = doGetSupportedSampleRatesL(settings, continuous)); + if (err != KErrNotSupported) + setError(err, tr("Failed to query information of supported sample rates.")); + + return rates; +} + +QList<int> S60VideoCaptureSession::doGetSupportedSampleRatesL(const QAudioEncoderSettings &settings, bool *continuous) +{ + QList<int> sampleRates; + + if (m_captureState < EOpenComplete) + return sampleRates; + +#ifndef S60_31_PLATFORM + RArray<TUint> supportedSampleRates; + CleanupClosePushL(supportedSampleRates); + + if (!settings.codec().isEmpty()) { + + TFourCC currentAudioCodec; + currentAudioCodec = m_videoRecorder->AudioTypeL(); + + TFourCC requestedAudioCodec; + if (qstrcmp(settings.codec().toLocal8Bit().constData(), "audio/aac") == 0) + requestedAudioCodec.Set(KMMFFourCCCodeAAC); + else if (qstrcmp(settings.codec().toLocal8Bit().constData(), "audio/amr") == 0) + requestedAudioCodec.Set(KMMFFourCCCodeAMR); + m_videoRecorder->SetAudioTypeL(requestedAudioCodec); + + m_videoRecorder->GetSupportedAudioSampleRatesL(supportedSampleRates); + + m_videoRecorder->SetAudioTypeL(currentAudioCodec); + } + else + m_videoRecorder->GetSupportedAudioSampleRatesL(supportedSampleRates); + + for (int i = 0; i < supportedSampleRates.Count(); ++i) + sampleRates.append(int(supportedSampleRates[i])); + + CleanupStack::PopAndDestroy(); // RArray<TUint> supportedSampleRates +#else // S60 3.1 Platform + Q_UNUSED(settings); +#endif // S60_31_PLATFORM + + if (continuous) + *continuous = false; + + return sampleRates; +} + +void S60VideoCaptureSession::setAudioSampleRate(const int sampleRate) +{ + if (sampleRate != -1) + m_audioSettings.setSampleRate(sampleRate); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setAudioBitRate(const int bitRate) +{ + if (bitRate != -1) + m_audioSettings.setBitRate(bitRate); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setAudioChannelCount(const int channelCount) +{ + if (channelCount != -1) + m_audioSettings.setChannelCount(channelCount); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setVideoCaptureCodec(const QString &codecName) +{ + if (codecName == m_videoSettings.codec()) + return; + + if (codecName.isEmpty()) + m_videoSettings.setCodec(KMimeTypeDefaultVideoCodec); // Use default + else + m_videoSettings.setCodec(codecName); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setAudioCaptureCodec(const QString &codecName) +{ + if (codecName == m_audioSettings.codec()) + return; + + if (codecName.isEmpty()) { + m_audioSettings.setCodec(KMimeTypeDefaultAudioCodec); // Use default + } else { + // If information of supported codecs is already available check that + // given codec is supported + if (m_captureState >= EOpenComplete) { + if (m_audioCodecList.contains(codecName)) { + m_audioSettings.setCodec(codecName); + m_uncommittedSettings = true; + } else { + setError(KErrNotSupported, tr("Requested audio codec is not supported")); + } + } else { + m_audioSettings.setCodec(codecName); + m_uncommittedSettings = true; + } + } +} + +QString S60VideoCaptureSession::videoCaptureCodecDescription(const QString &codecName) +{ + QString codecDescription; + if (codecName.contains("video/H263-2000", Qt::CaseInsensitive)) + codecDescription.append("H.263 Video Codec"); + else if (codecName.contains("video/mp4v-es", Qt::CaseInsensitive)) + codecDescription.append("MPEG-4 Part 2 Video Codec"); + else if (codecName.contains("video/H264", Qt::CaseInsensitive)) + codecDescription.append("H.264 AVC (MPEG-4 Part 10) Video Codec"); + else + codecDescription.append("Video Codec"); + + return codecDescription; +} + +void S60VideoCaptureSession::doSetCodecsL() +{ + // Determine Profile and Level for the video codec if needed + // (MimeType/Profile-level-id contains "profile" if profile/level info is available) + if (!m_videoSettings.codec().contains(QString("profile"), Qt::CaseInsensitive)) + m_videoSettings.setCodec(determineProfileAndLevel()); + + if (m_videoRecorder) { + TPtrC16 str(reinterpret_cast<const TUint16*>(m_videoSettings.codec().utf16())); + HBufC8* videoCodec(0); + videoCodec = CnvUtfConverter::ConvertFromUnicodeToUtf8L(str); + CleanupStack::PushL(videoCodec); + + TFourCC audioCodec = m_audioCodecList[m_audioSettings.codec()]; + + TInt vErr = KErrNone; + TInt aErr = KErrNone; + TRAP(vErr, m_videoRecorder->SetVideoTypeL(*videoCodec)); + TRAP(aErr, m_videoRecorder->SetAudioTypeL(audioCodec)); + + User::LeaveIfError(vErr); + User::LeaveIfError(aErr); + + CleanupStack::PopAndDestroy(videoCodec); + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); +} + +QString S60VideoCaptureSession::determineProfileAndLevel() +{ + QString determinedMimeType = m_videoSettings.codec(); + + // H.263 + if (determinedMimeType.contains(QString("video/H263-2000"), Qt::CaseInsensitive)) { + if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (176*144)) { + if (m_videoSettings.frameRate() > 15.0) + determinedMimeType.append("; profile=0; level=20"); + else + determinedMimeType.append("; profile=0; level=40"); + } else { + if (m_videoSettings.bitRate() > 64000) + determinedMimeType.append("; profile=0; level=45"); + else + determinedMimeType.append("; profile=0; level=10"); + } + + // MPEG-4 + } else if (determinedMimeType.contains(QString("video/mp4v-es"), Qt::CaseInsensitive)) { + if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (720*480)) { + determinedMimeType.append("; profile-level-id=6"); + } else if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (640*480)) { + determinedMimeType.append("; profile-level-id=5"); + } else if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (352*288)) { + determinedMimeType.append("; profile-level-id=4"); + } else if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (176*144)) { + if (m_videoSettings.frameRate() > 15.0) + determinedMimeType.append("; profile-level-id=3"); + else + determinedMimeType.append("; profile-level-id=2"); + } else { + if (m_videoSettings.bitRate() > 64000) + determinedMimeType.append("; profile-level-id=9"); + else + determinedMimeType.append("; profile-level-id=1"); + } + + // H.264 + } else if (determinedMimeType.contains(QString("video/H264"), Qt::CaseInsensitive)) { + if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (640*480)) { + determinedMimeType.append("; profile-level-id=42801F"); + } else if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (352*288)) { + determinedMimeType.append("; profile-level-id=42801E"); + } else if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (176*144)) { + if (m_videoSettings.frameRate() > 15.0) + determinedMimeType.append("; profile-level-id=428015"); + else + determinedMimeType.append("; profile-level-id=42800C"); + } else { + determinedMimeType.append("; profile-level-id=42900B"); + } + } + + return determinedMimeType; +} + +void S60VideoCaptureSession::setBitrate(const int bitrate) +{ + m_videoSettings.setBitRate(bitrate); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::doSetBitrate(const int &bitrate) +{ + if (bitrate != -1) { + if (m_videoRecorder) { + TRAPD(err, m_videoRecorder->SetVideoBitRateL(bitrate)); + if (err) { + if (err == KErrNotSupported || err == KErrArgument) { + setError(KErrNotSupported, tr("Requested video bitrate is not supported.")); + m_videoSettings.setBitRate(64000); // Reset + } else { + setError(err, tr("Failed to set video bitrate.")); + } + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } + } +} + +void S60VideoCaptureSession::setVideoResolution(const QSize &resolution) +{ + m_videoSettings.setResolution(resolution); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::doSetVideoResolution(const QSize &resolution) +{ + TSize size((TInt)resolution.width(), (TInt)resolution.height()); + + // Make sure resolution is not too big if main camera is not used + if (m_cameraEngine->CurrentCameraIndex() != 0) { + TCameraInfo *info = m_cameraEngine->CameraInfo(); + if (info) { + TInt videoResolutionCount = info->iNumVideoFrameSizesSupported; + TSize maxCameraVideoResolution = TSize(0,0); + CCamera *camera = m_cameraEngine->Camera(); + if (camera) { + for (TInt i = 0; i < videoResolutionCount; ++i) { + TSize checkedResolution; + // Use YUV video max frame size in the check (Through + // CVideoRecorderUtility/DevVideoRecord it is possible to + // query only encoder maximums) + camera->EnumerateVideoFrameSizes(checkedResolution, i, CCamera::EFormatYUV420Planar); + if ((checkedResolution.iWidth * checkedResolution.iHeight) > + (maxCameraVideoResolution.iWidth * maxCameraVideoResolution.iHeight)) + maxCameraVideoResolution = checkedResolution; + } + if ((maxCameraVideoResolution.iWidth * maxCameraVideoResolution.iHeight) < + (size.iWidth * size.iHeight)) { + size = maxCameraVideoResolution; + setError(KErrNotSupported, tr("Requested resolution is not supported for this camera.")); + } + } + else + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + }else + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + } + + if (resolution.width() != -1 && resolution.height() != -1) { + if (m_videoRecorder) { + TRAPD(err, m_videoRecorder->SetVideoFrameSizeL((TSize)size)); + if (err == KErrNotSupported || err == KErrArgument) { + setError(KErrNotSupported, tr("Requested video resolution is not supported.")); + TSize fallBack(640,480); + TRAPD(err, m_videoRecorder->SetVideoFrameSizeL(fallBack)); + if (err == KErrNone) { + m_videoSettings.setResolution(QSize(fallBack.iWidth,fallBack.iHeight)); + } else { + fallBack = TSize(176,144); + TRAPD(err, m_videoRecorder->SetVideoFrameSizeL(fallBack)); + if (err == KErrNone) + m_videoSettings.setResolution(QSize(fallBack.iWidth,fallBack.iHeight)); + } + } else { + setError(err, tr("Failed to set video resolution.")); + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } + } +} + +void S60VideoCaptureSession::setFrameRate(qreal rate) +{ + m_videoSettings.setFrameRate(rate); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::doSetFrameRate(qreal rate) +{ + if (rate != 0) { + if (m_videoRecorder) { + bool continuous = false; + QList<qreal> list = supportedVideoFrameRates(&continuous); + qreal maxRate = 0.0; + foreach (qreal fRate, list) + if (fRate > maxRate) + maxRate = fRate; + if (maxRate >= rate && rate > 0) { + TRAPD(err, m_videoRecorder->SetVideoFrameRateL((TReal32)rate)); + if (err == KErrNotSupported) { + setError(KErrNotSupported, tr("Requested framerate is not supported.")); + TReal32 fallBack = 15.0; + TRAPD(err, m_videoRecorder->SetVideoFrameRateL(fallBack)); + if (err == KErrNone) + m_videoSettings.setFrameRate((qreal)fallBack); + } else { + if (err == KErrArgument) { + setError(KErrNotSupported, tr("Requested framerate is not supported.")); + m_videoSettings.setFrameRate(15.0); // Reset + } else { + setError(err, tr("Failed to set video framerate.")); + } + } + } else { + setError(KErrNotSupported, tr("Requested framerate is not supported.")); + m_videoSettings.setFrameRate(15.0); // Reset + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } + } +} + +void S60VideoCaptureSession::setVideoEncodingMode(const QtMultimediaKit::EncodingMode mode) +{ + // This has no effect as it has no support in Symbian + + if (mode == QtMultimediaKit::ConstantQualityEncoding) { + m_videoSettings.setEncodingMode(mode); + return; + } + + setError(KErrNotSupported, tr("Requested video encoding mode is not supported")); + + // m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setAudioEncodingMode(const QtMultimediaKit::EncodingMode mode) +{ + // This has no effect as it has no support in Symbian + + if (mode == QtMultimediaKit::ConstantQualityEncoding) { + m_audioSettings.setEncodingMode(mode); + return; + } + + setError(KErrNotSupported, tr("Requested audio encoding mode is not supported")); + + // m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::initializeVideoCaptureSettings() +{ + // Check if user has already requested some settings + if (m_captureSettingsSet) + return; + + QSize resolution(-1, -1); + qreal frameRate(0); + int bitRate(-1); + + if (m_cameraEngine) { + + if (m_videoRecorder && m_captureState >= EInitialized) { + + // Resolution + QList<QSize> resos = supportedVideoResolutions(0); + foreach (QSize reso, resos) { + if ((reso.width() * reso.height()) > (resolution.width() * resolution.height())) + resolution = reso; + } + + // Needed to query supported framerates for this codec/resolution pair + m_videoSettings.setCodec(KMimeTypeDefaultVideoCodec); + m_videoSettings.setResolution(resolution); + + // FrameRate + QList<qreal> fRates = supportedVideoFrameRates(m_videoSettings, 0); + foreach (qreal rate, fRates) { + if (rate > frameRate) + frameRate = rate; + } + + // BitRate +#ifdef SYMBIAN_3_PLATFORM + if (m_cameraEngine->CurrentCameraIndex() == 0) + bitRate = KBiR_H264_PLID_42801F // 14Mbps + else + bitRate = KBiR_H264_PLID_428016 // 4Mbps +#else // Other platforms + if (m_cameraEngine->CurrentCameraIndex() == 0) + bitRate = KBiR_MPEG4_PLID_4 // 2/4Mbps + else + bitRate = KBiR_MPEG4_PLID_3 // 384kbps +#endif // SYMBIAN_3_PLATFORM + + } else { +#ifdef SYMBIAN_3_PLATFORM + if (m_cameraEngine->CurrentCameraIndex() == 0) { + // Primary camera + resolution = KResH264_PLID_42801F; // 1280x720 + frameRate = KFrR_H264_PLID_42801F; // 30fps + bitRate = KBiR_H264_PLID_42801F; // 14Mbps + } else { + // Other cameras + resolution = KResH264_PLID_42801E; // 640x480 + frameRate = KFrR_H264_PLID_428014; // 30fps + bitRate = KBiR_H264_PLID_428016; // 4Mbps + } +#else // Other platforms + if (m_cameraEngine->CurrentCameraIndex() == 0) { + // Primary camera + resolution = KResMPEG4_PLID_4; // 640x480 + frameRate = KFrR_MPEG4_PLID_4; // 15/30fps + bitRate = KBiR_MPEG4_PLID_4; // 2/4Mbps + } else { + // Other cameras + resolution = KResMPEG4_PLID_3; // 352x288 + frameRate = KFrR_MPEG4; // 15fps + bitRate = KBiR_MPEG4_PLID_3; // 384kbps + } +#endif // SYMBIAN_3_PLATFORM + } + } else { +#ifdef SYMBIAN_3_PLATFORM + resolution = KResH264_PLID_42801F; + frameRate = KFrR_H264_PLID_42801F; + bitRate = KBiR_H264_PLID_42801F; +#else // Pre-Symbian3 Platforms + resolution = KResMPEG4_PLID_4; + frameRate = KFrR_MPEG4_PLID_4; + bitRate = KBiR_MPEG4_PLID_4; +#endif // SYMBIAN_3_PLATFORM + } + + // Set specified settings (Resolution, FrameRate and BitRate) + m_videoSettings.setResolution(resolution); + m_videoSettings.setFrameRate(frameRate); + m_videoSettings.setBitRate(bitRate); + + // Video Settings: Codec, EncodingMode and Quality + m_videoSettings.setCodec(KMimeTypeDefaultVideoCodec); + m_videoSettings.setEncodingMode(QtMultimediaKit::ConstantQualityEncoding); + m_videoSettings.setQuality(QtMultimediaKit::VeryHighQuality); + + // Audio Settings + m_audioSettings.setCodec(KMimeTypeDefaultAudioCodec); + m_audioSettings.setBitRate(KDefaultBitRate); + m_audioSettings.setSampleRate(KDefaultSampleRate); + m_audioSettings.setChannelCount(KDefaultChannelCount); + m_audioSettings.setEncodingMode(QtMultimediaKit::ConstantQualityEncoding); + m_audioSettings.setQuality(QtMultimediaKit::VeryHighQuality); +} + +QSize S60VideoCaptureSession::pixelAspectRatio() +{ +#ifndef S60_31_PLATFORM + TVideoAspectRatio par; + TRAPD(err, m_videoRecorder->GetPixelAspectRatioL(par)); + if (err) + setError(err, tr("Failed to query current pixel aspect ratio.")); + return QSize(par.iNumerator, par.iDenominator); +#else // S60_31_PLATFORM + return QSize(); +#endif // !S60_31_PLATFORM +} + +void S60VideoCaptureSession::setPixelAspectRatio(const QSize par) +{ +#ifndef S60_31_PLATFORM + + const TVideoAspectRatio videoPar(par.width(), par.height()); + TRAPD(err, m_videoRecorder->SetPixelAspectRatioL(videoPar)); + if (err) + setError(err, tr("Failed to set pixel aspect ratio.")); +#else // S60_31_PLATFORM + Q_UNUSED(par); +#endif // !S60_31_PLATFORM + + m_uncommittedSettings = true; +} + +int S60VideoCaptureSession::gain() +{ + TInt gain = 0; + TRAPD(err, gain = m_videoRecorder->GainL()); + if (err) + setError(err, tr("Failed to query video gain.")); + return (int)gain; +} + +void S60VideoCaptureSession::setGain(const int gain) +{ + TRAPD(err, m_videoRecorder->SetGainL(gain)); + if (err) + setError(err, tr("Failed to set video gain.")); + + m_uncommittedSettings = true; +} + +int S60VideoCaptureSession::maxClipSizeInBytes() const +{ + return m_maxClipSize; +} + +void S60VideoCaptureSession::setMaxClipSizeInBytes(const int size) +{ + TRAPD(err, m_videoRecorder->SetMaxClipSizeL(size)); + if (err) { + setError(err, tr("Failed to set maximum video size.")); + } else + m_maxClipSize = size; + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::MvruoOpenComplete(TInt aError) +{ + if (m_error) + return; + + if (aError == KErrNone && m_videoRecorder) { + if (m_captureState == EInitializing) { + // Dummy file open completed, initialize settings + TRAPD(err, doPopulateAudioCodecsL()); + setError(err, tr("Failed to gather information of supported audio codecs.")); + + // For DevVideoRecord codecs are populated during + // doPopulateVideoCodecsDataL() + TRAP(err, doPopulateVideoCodecsL()); + setError(err, tr("Failed to gather information of supported video codecs.")); +#ifndef S60_DEVVIDEO_RECORDING_SUPPORTED + // Max parameters needed to be populated, if not using DevVideoRecord + // Otherwise done already in constructor + doPopulateMaxVideoParameters(); +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + + m_captureState = EInitialized; + emit stateChanged(m_captureState); + + // Initialize settings if not already done + initializeVideoCaptureSettings(); + + // Validate codecs to be used + validateRequestedCodecs(); + + if (m_openWhenReady || m_prepareAfterOpenComplete || m_startAfterPrepareComplete) { + setOutputLocation(m_requestedSink); + m_openWhenReady = false; // Reset + } + if (m_commitSettingsWhenReady) { + applyAllSettings(); + m_commitSettingsWhenReady = false; // Reset + } + return; + + } else if (m_captureState == EOpening) { + // Actual file open completed + m_captureState = EOpenComplete; + emit stateChanged(m_captureState); + + // Prepare right away + if (m_startAfterPrepareComplete || m_prepareAfterOpenComplete) { + m_prepareAfterOpenComplete = false; // Reset + + // Commit settings and prepare with them + applyAllSettings(); + } + return; + + } else if (m_captureState == ENotInitialized) { + // Resources released while waiting OpenFileL to complete + m_videoRecorder->Close(); + return; + + } else { + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + } + + m_videoRecorder->Close(); + if (aError == KErrNotFound || aError == KErrNotSupported || aError == KErrArgument) + setError(KErrGeneral, tr("Requested video container or controller is not supported.")); + else + setError(KErrGeneral, tr("Failure during video recorder initialization.")); +} + +void S60VideoCaptureSession::MvruoPrepareComplete(TInt aError) +{ + if (m_error) + return; + + if(aError == KErrNone) { + if (m_captureState == ENotInitialized) { + // Resources released while waiting for Prepare to complete + m_videoRecorder->Close(); + return; + } + + emit captureSizeChanged(m_videoSettings.resolution()); + + m_captureState = EPrepared; + emit stateChanged(EPrepared); + + // Check the actual active settings + queryAudioEncoderSettings(); + queryVideoEncoderSettings(); + + if (m_openWhenReady == true) { + setOutputLocation(m_requestedSink); + m_openWhenReady = false; // Reset + } + + if (m_commitSettingsWhenReady) { + applyAllSettings(); + m_commitSettingsWhenReady = false; // Reset + } + + if (m_startAfterPrepareComplete) { + m_startAfterPrepareComplete = false; // Reset + startRecording(); + } + } else { + m_videoRecorder->Close(); + if (aError == KErrNotSupported) + setError(aError, tr("Camera preparation for video recording failed because of unsupported setting.")); + else + setError(aError, tr("Failed to prepare camera for video recording.")); + } +} + +void S60VideoCaptureSession::MvruoRecordComplete(TInt aError) +{ + if (!m_videoRecorder) { + setError(KErrNotReady, tr("Unexpected camera error.")); + return; + } + + if((aError == KErrNone || aError == KErrCompletion)) { + m_videoRecorder->Stop(); + + // Reset state + if (m_captureState != ENotInitialized) { + m_captureState = ENotInitialized; + emit stateChanged(m_captureState); + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + } + + if (m_cameraEngine->IsCameraReady()) + initializeVideoRecording(); + } + m_videoRecorder->Close(); + + // Notify muting is disabled if needed + if (m_muted) + emit mutedChanged(false); + + if (aError == KErrDiskFull) + setError(aError, tr("Not enough space for video, recording stopped.")); + else + setError(aError, tr("Recording stopped due to unexpected error.")); +} + +void S60VideoCaptureSession::MvruoEvent(const TMMFEvent& aEvent) +{ + Q_UNUSED(aEvent); +} + +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED +void S60VideoCaptureSession::MdvroReturnPicture(TVideoPicture *aPicture) +{ + // Not used + Q_UNUSED(aPicture); +} + +void S60VideoCaptureSession::MdvroSupplementalInfoSent() +{ + // Not used +} + +void S60VideoCaptureSession::MdvroNewBuffers() +{ + // Not used +} + +void S60VideoCaptureSession::MdvroFatalError(TInt aError) +{ + setError(aError, tr("Unexpected camera error.")); +} + +void S60VideoCaptureSession::MdvroInitializeComplete(TInt aError) +{ + // Not used + Q_UNUSED(aError); +} + +void S60VideoCaptureSession::MdvroStreamEnd() +{ + // Not used +} + +/* + * This populates video codec information (supported codecs, resolutions, + * framerates, etc.) using DevVideoRecord API. + */ +void S60VideoCaptureSession::doPopulateVideoCodecsDataL() +{ + RArray<TUid> encoders; + CleanupClosePushL(encoders); + + CMMFDevVideoRecord *mDevVideoRecord = CMMFDevVideoRecord::NewL(*this); + CleanupStack::PushL(mDevVideoRecord); + + // Retrieve list of all encoders provided by the platform + mDevVideoRecord->GetEncoderListL(encoders); + + for (int i = 0; i < encoders.Count(); ++i ) { + + CVideoEncoderInfo *encoderInfo = mDevVideoRecord->VideoEncoderInfoLC(encoders[i]); + + // Discard encoders that are not HW accelerated and do not support direct capture + if (encoderInfo->Accelerated() == false || encoderInfo->SupportsDirectCapture() == false) { + CleanupStack::Check(encoderInfo); + CleanupStack::PopAndDestroy(encoderInfo); + continue; + } + + m_videoParametersForEncoder.append(MaxResolutionRatesAndTypes()); + int newIndex = m_videoParametersForEncoder.count() - 1; + + m_videoParametersForEncoder[newIndex].bitRate = (int)encoderInfo->MaxBitrate(); + + // Get supported MIME Types + const RPointerArray<CCompressedVideoFormat> &videoFormats = encoderInfo->SupportedOutputFormats(); + for(int x = 0; x < videoFormats.Count(); ++x) { + QString codecMimeType = QString::fromUtf8((char *)videoFormats[x]->MimeType().Ptr(),videoFormats[x]->MimeType().Length()); + + m_videoParametersForEncoder[newIndex].mimeTypes.append(codecMimeType); + } + + // Get supported maximum Resolution/Framerate pairs + const RArray<TPictureRateAndSize> &ratesAndSizes = encoderInfo->MaxPictureRates(); + SupportedFrameRatePictureSize data; + for(int j = 0; j < ratesAndSizes.Count(); ++j) { + data.frameRate = ratesAndSizes[j].iPictureRate; + data.frameSize = QSize(ratesAndSizes[j].iPictureSize.iWidth, ratesAndSizes[j].iPictureSize.iHeight); + + // Save data to the hash + m_videoParametersForEncoder[newIndex].frameRatePictureSizePair.append(data); + } + + CleanupStack::Check(encoderInfo); + CleanupStack::PopAndDestroy(encoderInfo); + } + + CleanupStack::Check(mDevVideoRecord); + CleanupStack::PopAndDestroy(mDevVideoRecord); + CleanupStack::PopAndDestroy(); // RArray<TUid> encoders +} +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + +QStringList S60VideoCaptureSession::supportedVideoContainers() +{ + QStringList containers; + + QList<TInt> controllers = m_videoControllerMap.keys(); + for (int i = 0; i < controllers.count(); ++i) { + foreach (VideoFormatData formatData, m_videoControllerMap[controllers[i]]) { + for (int j = 0; j < formatData.supportedMimeTypes.count(); ++j) { + if (containers.contains(formatData.supportedMimeTypes[j], Qt::CaseInsensitive) == false) + containers.append(formatData.supportedMimeTypes[j]); + } + } + } + + return containers; +} + +bool S60VideoCaptureSession::isSupportedVideoContainer(const QString &containerName) +{ + return supportedVideoContainers().contains(containerName, Qt::CaseInsensitive); +} + +QString S60VideoCaptureSession::videoContainer() const +{ + return m_container; +} + +void S60VideoCaptureSession::setVideoContainer(const QString &containerName) +{ + if (containerName == m_requestedContainer) + return; + + if (containerName.isEmpty()) { + m_requestedContainer = KMimeTypeDefaultContainer; // Use default + } else { + if (supportedVideoContainers().contains(containerName)) { + m_requestedContainer = containerName; + } else { + setError(KErrNotSupported, tr("Requested video container is not supported.")); + m_requestedContainer = KMimeTypeDefaultContainer; // Reset to default + } + } + + m_uncommittedSettings = true; +} + +QString S60VideoCaptureSession::videoContainerDescription(const QString &containerName) +{ + QList<TInt> formats; + QList<TInt> encoders = m_videoControllerMap.keys(); + for (int i = 0; i < encoders.count(); ++i) { + formats = m_videoControllerMap[encoders[i]].keys(); + for (int j = 0; j < formats.count(); ++j) { + if (m_videoControllerMap[encoders[i]][formats[j]].supportedMimeTypes.contains(containerName, Qt::CaseInsensitive)) + return m_videoControllerMap[encoders[i]][formats[j]].description; + } + } + + return QString(); +} + +void S60VideoCaptureSession::cameraStatusChanged(QCamera::Status status) +{ + if (status == QCamera::ActiveStatus) { + m_cameraStarted = true; + + // Continue preparation or start recording if previously requested + if (m_captureState == EInitialized + && (m_openWhenReady || m_prepareAfterOpenComplete || m_startAfterPrepareComplete)) { + setOutputLocation(m_requestedSink); + m_openWhenReady = false; // Reset + } else if ((m_captureState == EOpenComplete || m_captureState == EPrepared) + && (m_prepareAfterOpenComplete || m_startAfterPrepareComplete)) { + startRecording(); + m_prepareAfterOpenComplete = false; // Reset + } + + } else if (status == QCamera::UnloadedStatus) { + m_cameraStarted = false; + releaseVideoRecording(); + } else { + m_cameraStarted = false; + } +} + +void S60VideoCaptureSession::durationTimerTriggered() +{ + // Update position only if recording is ongoing + if ((m_captureState == ERecording) && m_videoRecorder) { + // Signal will be automatically emitted of position changes + TRAPD(err, m_position = m_videoRecorder->DurationL().Int64() / 1000); + setError(err, tr("Cannot retrieve video position.")); + + emit positionChanged(m_position); + } +} + +void S60VideoCaptureSession::doPopulateAudioCodecsL() +{ + if (m_captureState == EInitializing) { + m_audioCodecList.clear(); + + RArray<TFourCC> audioTypes; + CleanupClosePushL(audioTypes); + + if (m_videoRecorder) + m_videoRecorder->GetSupportedAudioTypesL(audioTypes); + else + setError(KErrNotReady, tr("Unexpected camera error.")); + + for (TInt i = 0; i < audioTypes.Count(); i++) { + TUint32 codec = audioTypes[i].FourCC(); + + if (codec == KMMFFourCCCodeAMR) + m_audioCodecList.insert(QString("audio/amr"), KMMFFourCCCodeAMR); + if (codec == KMMFFourCCCodeAAC) + m_audioCodecList.insert(QString("audio/aac"), KMMFFourCCCodeAAC); + } + CleanupStack::PopAndDestroy(&audioTypes); + } +} + +void S60VideoCaptureSession::doPopulateVideoCodecsL() +{ + if (m_captureState == EInitializing) { + m_videoCodecList.clear(); + + CDesC8ArrayFlat* videoTypes = new (ELeave) CDesC8ArrayFlat(10); + CleanupStack::PushL(videoTypes); + + if (m_videoRecorder) + m_videoRecorder->GetSupportedVideoTypesL(*videoTypes); + else + setError(KErrNotReady, tr("Unexpected camera error.")); + + for (TInt i = 0; i < videoTypes->Count(); i++) { + TPtrC8 videoType = videoTypes->MdcaPoint(i); + QString codecMimeType = QString::fromUtf8((char *)videoType.Ptr(), videoType.Length()); +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED + for (int j = 0; j < m_videoParametersForEncoder.size(); ++j) { + if (m_videoParametersForEncoder[j].mimeTypes.contains(codecMimeType, Qt::CaseInsensitive)) { + m_videoCodecList << codecMimeType; + break; + } + } +#else // CVideoRecorderUtility + m_videoCodecList << codecMimeType; +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + } + CleanupStack::PopAndDestroy(videoTypes); + } +} + +#ifndef S60_DEVVIDEO_RECORDING_SUPPORTED +/* + * Maximum resolution, framerate and bitrate can not be queried via MMF or + * ECam, but needs to be set according to the definitions of the video + * standard in question. In video standards, the values often depend on each + * other, but the below function defines constant maximums. + */ +void S60VideoCaptureSession::doPopulateMaxVideoParameters() +{ + m_videoParametersForEncoder.append(MaxResolutionRatesAndTypes()); // For H.263 + m_videoParametersForEncoder.append(MaxResolutionRatesAndTypes()); // For MPEG-4 + m_videoParametersForEncoder.append(MaxResolutionRatesAndTypes()); // For H.264 + + for (int i = 0; i < m_videoCodecList.count(); ++i) { + + // Use all lower case for comparisons + QString codec = m_videoCodecList[i].toLower(); + + if (codec.contains("video/h263-2000", Qt::CaseInsensitive)) { + // H.263 + if (codec == "video/h263-2000" || + codec == "video/h263-2000; profile=0" || + codec == "video/h263-2000; profile=0; level=10" || + codec == "video/h263-2000; profile=3") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(176,144))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 64000) + m_videoParametersForEncoder[0].bitRate = 64000; + continue; + } else if (codec == "video/h263-2000; profile=0; level=20") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 128000) + m_videoParametersForEncoder[0].bitRate = 128000; + continue; + } else if (codec == "video/h263-2000; profile=0; level=30") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 384000) + m_videoParametersForEncoder[0].bitRate = 384000; + continue; + } else if (codec == "video/h263-2000; profile=0; level=40") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 2048000) + m_videoParametersForEncoder[0].bitRate = 2048000; + continue; + } else if (codec == "video/h263-2000; profile=0; level=45") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(176,144))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 128000) + m_videoParametersForEncoder[0].bitRate = 128000; + continue; + } else if (codec == "video/h263-2000; profile=0; level=50") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 4096000) + m_videoParametersForEncoder[0].bitRate = 4096000; + continue; + } + + } else if (codec.contains("video/mp4v-es", Qt::CaseInsensitive)) { + // Mpeg-4 + if (codec == "video/mp4v-es" || + codec == "video/mp4v-es; profile-level-id=1" || + codec == "video/mp4v-es; profile-level-id=8") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(176,144))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 64000) + m_videoParametersForEncoder[0].bitRate = 64000; + continue; + } else if (codec == "video/mp4v-es; profile-level-id=2" || + codec == "video/mp4v-es; profile-level-id=9") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 128000) + m_videoParametersForEncoder[0].bitRate = 128000; + continue; + } else if (codec == "video/mp4v-es; profile-level-id=3") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 384000) + m_videoParametersForEncoder[0].bitRate = 384000; + continue; + } else if (codec == "video/mp4v-es; profile-level-id=4") { +#if (defined(S60_31_PLATFORM) | defined(S60_32_PLATFORM)) + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(640,480))); +#else // S60 5.0 and later platforms + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(640,480))); +#endif + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 4000000) + m_videoParametersForEncoder[0].bitRate = 4000000; + continue; + } else if (codec == "video/mp4v-es; profile-level-id=5") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(25.0, QSize(720,576))); + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(720,480))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 8000000) + m_videoParametersForEncoder[0].bitRate = 8000000; + continue; + } else if (codec == "video/mp4v-es; profile-level-id=6") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(1280,720))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 12000000) + m_videoParametersForEncoder[0].bitRate = 12000000; + continue; + } + + } else if (codec.contains("video/h264", Qt::CaseInsensitive)) { + // H.264 + if (codec == "video/h264" || + codec == "video/h264; profile-level-id=42800a") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(176,144))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 64000) + m_videoParametersForEncoder[0].bitRate = 64000; + continue; + } else if (codec == "video/h264; profile-level-id=42900b") { // BP, L1b + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(176,144))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 128000) + m_videoParametersForEncoder[0].bitRate = 128000; + continue; + } else if (codec == "video/h264; profile-level-id=42800b") { // BP, L1.1 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(7.5, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 192000) + m_videoParametersForEncoder[0].bitRate = 192000; + continue; + } else if (codec == "video/h264; profile-level-id=42800c") { // BP, L1.2 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 384000) + m_videoParametersForEncoder[0].bitRate = 384000; + continue; + } else if (codec == "video/h264; profile-level-id=42800d") { // BP, L1.3 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 768000) + m_videoParametersForEncoder[0].bitRate = 768000; + continue; + } else if (codec == "video/h264; profile-level-id=428014") { // BP, L2 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 2000000) + m_videoParametersForEncoder[0].bitRate = 2000000; + continue; + } else if (codec == "video/h264; profile-level-id=428015") { // BP, L2.1 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(50.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 4000000) + m_videoParametersForEncoder[0].bitRate = 4000000; + continue; + } else if (codec == "video/h264; profile-level-id=428016") { // BP, L2.2 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(16.9, QSize(640,480))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 4000000) + m_videoParametersForEncoder[0].bitRate = 4000000; + continue; + } else if (codec == "video/h264; profile-level-id=42801e") { // BP, L3 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(33.8, QSize(640,480))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 10000000) + m_videoParametersForEncoder[0].bitRate = 10000000; + continue; + } else if (codec == "video/h264; profile-level-id=42801f") { // BP, L3.1 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(1280,720))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 14000000) + m_videoParametersForEncoder[0].bitRate = 14000000; + continue; + } + } + } +} +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + +/* + * This function returns the maximum resolution defined by the video standards + * for different MIME Types. + */ +QSize S60VideoCaptureSession::maximumResolutionForMimeType(const QString &mimeType) const +{ + QSize maxSize(-1,-1); + // Use all lower case for comparisons + QString lowerMimeType = mimeType.toLower(); + + if (lowerMimeType == "video/h263-2000") { + maxSize = KResH263; + } else if (lowerMimeType == "video/h263-2000; profile=0") { + maxSize = KResH263_Profile0; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=10") { + maxSize = KResH263_Profile0_Level10; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=20") { + maxSize = KResH263_Profile0_Level20; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=30") { + maxSize = KResH263_Profile0_Level30; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=40") { + maxSize = KResH263_Profile0_Level40; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=45") { + maxSize = KResH263_Profile0_Level45; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=50") { + maxSize = KResH263_Profile0_Level50; + } else if (lowerMimeType == "video/h263-2000; profile=3") { + maxSize = KResH263_Profile3; + } else if (lowerMimeType == "video/mp4v-es") { + maxSize = KResMPEG4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=1") { + maxSize = KResMPEG4_PLID_1; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=2") { + maxSize = KResMPEG4_PLID_2; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=3") { + maxSize = KResMPEG4_PLID_3; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=4") { + maxSize = KResMPEG4_PLID_4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=5") { + maxSize = KResMPEG4_PLID_5; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=6") { + maxSize = KResMPEG4_PLID_6; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=8") { + maxSize = KResMPEG4_PLID_8; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=9") { + maxSize = KResMPEG4_PLID_9; + } else if (lowerMimeType == "video/h264") { + maxSize = KResH264; + } else if (lowerMimeType == "video/h264; profile-level-id=42800a" || + lowerMimeType == "video/h264; profile-level-id=4d400a" || + lowerMimeType == "video/h264; profile-level-id=64400a") { // L1 + maxSize = KResH264_PLID_42800A; + } else if (lowerMimeType == "video/h264; profile-level-id=42900b" || + lowerMimeType == "video/h264; profile-level-id=4d500b" || + lowerMimeType == "video/h264; profile-level-id=644009") { // L1.b + maxSize = KResH264_PLID_42900B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800b" || + lowerMimeType == "video/h264; profile-level-id=4d400b" || + lowerMimeType == "video/h264; profile-level-id=64400b") { // L1.1 + maxSize = KResH264_PLID_42800B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800c" || + lowerMimeType == "video/h264; profile-level-id=4d400c" || + lowerMimeType == "video/h264; profile-level-id=64400c") { // L1.2 + maxSize = KResH264_PLID_42800C; + } else if (lowerMimeType == "video/h264; profile-level-id=42800d" || + lowerMimeType == "video/h264; profile-level-id=4d400d" || + lowerMimeType == "video/h264; profile-level-id=64400d") { // L1.3 + maxSize = KResH264_PLID_42800D; + } else if (lowerMimeType == "video/h264; profile-level-id=428014" || + lowerMimeType == "video/h264; profile-level-id=4d4014" || + lowerMimeType == "video/h264; profile-level-id=644014") { // L2 + maxSize = KResH264_PLID_428014; + } else if (lowerMimeType == "video/h264; profile-level-id=428015" || + lowerMimeType == "video/h264; profile-level-id=4d4015" || + lowerMimeType == "video/h264; profile-level-id=644015") { // L2.1 + maxSize = KResH264_PLID_428015; + } else if (lowerMimeType == "video/h264; profile-level-id=428016" || + lowerMimeType == "video/h264; profile-level-id=4d4016" || + lowerMimeType == "video/h264; profile-level-id=644016") { // L2.2 + maxSize = KResH264_PLID_428016; + } else if (lowerMimeType == "video/h264; profile-level-id=42801e" || + lowerMimeType == "video/h264; profile-level-id=4d401e" || + lowerMimeType == "video/h264; profile-level-id=64401e") { // L3 + maxSize = KResH264_PLID_42801E; + } else if (lowerMimeType == "video/h264; profile-level-id=42801f" || + lowerMimeType == "video/h264; profile-level-id=4d401f" || + lowerMimeType == "video/h264; profile-level-id=64401f") { // L3.1 + maxSize = KResH264_PLID_42801F; + } else if (lowerMimeType == "video/h264; profile-level-id=428020" || + lowerMimeType == "video/h264; profile-level-id=4d4020" || + lowerMimeType == "video/h264; profile-level-id=644020") { // L3.2 + maxSize = KResH264_PLID_428020; + } else if (lowerMimeType == "video/h264; profile-level-id=428028" || + lowerMimeType == "video/h264; profile-level-id=4d4028" || + lowerMimeType == "video/h264; profile-level-id=644028") { // L4 + maxSize = KResH264_PLID_428028; + } + + return maxSize; +} + +/* + * This function returns the maximum framerate defined by the video standards + * for different MIME Types. + */ + +qreal S60VideoCaptureSession::maximumFrameRateForMimeType(const QString &mimeType) const +{ + qreal maxRate(-1.0); + // Use all lower case for comparisons + QString lowerMimeType = mimeType.toLower(); + + if (lowerMimeType == "video/h263-2000") { + maxRate = KFrR_H263; + } else if (lowerMimeType == "video/h263-2000; profile=0") { + maxRate = KFrR_H263_Profile0; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=10") { + maxRate = KFrR_H263_Profile0_Level10; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=20") { + maxRate = KFrR_H263_Profile0_Level20; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=30") { + maxRate = KFrR_H263_Profile0_Level30; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=40") { + maxRate = KFrR_H263_Profile0_Level40; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=45") { + maxRate = KFrR_H263_Profile0_Level45; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=50") { + maxRate = KFrR_H263_Profile0_Level50; + } else if (lowerMimeType == "video/h263-2000; profile=3") { + maxRate = KFrR_H263_Profile3; + } else if (lowerMimeType == "video/mp4v-es") { + maxRate = KFrR_MPEG4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=1") { + maxRate = KFrR_MPEG4_PLID_1; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=2") { + maxRate = KFrR_MPEG4_PLID_2; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=3") { + maxRate = KFrR_MPEG4_PLID_3; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=4") { + maxRate = KFrR_MPEG4_PLID_4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=5") { + maxRate = KFrR_MPEG4_PLID_5; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=6") { + maxRate = KFrR_MPEG4_PLID_6; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=8") { + maxRate = KFrR_MPEG4_PLID_8; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=9") { + maxRate = KFrR_MPEG4_PLID_9; + } else if (lowerMimeType == "video/h264") { + maxRate = KFrR_H264; + } else if (lowerMimeType == "video/h264; profile-level-id=42800a" || + lowerMimeType == "video/h264; profile-level-id=4d400a" || + lowerMimeType == "video/h264; profile-level-id=64400a") { // L1 + maxRate = KFrR_H264_PLID_42800A; + } else if (lowerMimeType == "video/h264; profile-level-id=42900b" || + lowerMimeType == "video/h264; profile-level-id=4d500b" || + lowerMimeType == "video/h264; profile-level-id=644009") { // L1.b + maxRate = KFrR_H264_PLID_42900B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800b" || + lowerMimeType == "video/h264; profile-level-id=4d400b" || + lowerMimeType == "video/h264; profile-level-id=64400b") { // L1.1 + maxRate = KFrR_H264_PLID_42800B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800c" || + lowerMimeType == "video/h264; profile-level-id=4d400c" || + lowerMimeType == "video/h264; profile-level-id=64400c") { // L1.2 + maxRate = KFrR_H264_PLID_42800C; + } else if (lowerMimeType == "video/h264; profile-level-id=42800d" || + lowerMimeType == "video/h264; profile-level-id=4d400d" || + lowerMimeType == "video/h264; profile-level-id=64400d") { // L1.3 + maxRate = KFrR_H264_PLID_42800D; + } else if (lowerMimeType == "video/h264; profile-level-id=428014" || + lowerMimeType == "video/h264; profile-level-id=4d4014" || + lowerMimeType == "video/h264; profile-level-id=644014") { // L2 + maxRate = KFrR_H264_PLID_428014; + } else if (lowerMimeType == "video/h264; profile-level-id=428015" || + lowerMimeType == "video/h264; profile-level-id=4d4015" || + lowerMimeType == "video/h264; profile-level-id=644015") { // L2.1 + maxRate = KFrR_H264_PLID_428015; + } else if (lowerMimeType == "video/h264; profile-level-id=428016" || + lowerMimeType == "video/h264; profile-level-id=4d4016" || + lowerMimeType == "video/h264; profile-level-id=644016") { // L2.2 + maxRate = KFrR_H264_PLID_428016; + } else if (lowerMimeType == "video/h264; profile-level-id=42801e" || + lowerMimeType == "video/h264; profile-level-id=4d401e" || + lowerMimeType == "video/h264; profile-level-id=64401e") { // L3 + maxRate = KFrR_H264_PLID_42801E; + } else if (lowerMimeType == "video/h264; profile-level-id=42801f" || + lowerMimeType == "video/h264; profile-level-id=4d401f" || + lowerMimeType == "video/h264; profile-level-id=64401f") { // L3.1 + maxRate = KFrR_H264_PLID_42801F; + } else if (lowerMimeType == "video/h264; profile-level-id=428020" || + lowerMimeType == "video/h264; profile-level-id=4d4020" || + lowerMimeType == "video/h264; profile-level-id=644020") { // L3.2 + maxRate = KFrR_H264_PLID_428020; + } else if (lowerMimeType == "video/h264; profile-level-id=428028" || + lowerMimeType == "video/h264; profile-level-id=4d4028" || + lowerMimeType == "video/h264; profile-level-id=644028") { // L4 + maxRate = KFrR_H264_PLID_428028; + } + + return maxRate; +} + +/* + * This function returns the maximum bitrate defined by the video standards + * for different MIME Types. + */ +int S60VideoCaptureSession::maximumBitRateForMimeType(const QString &mimeType) const +{ + int maxRate(-1.0); + // Use all lower case for comparisons + QString lowerMimeType = mimeType.toLower(); + + if (lowerMimeType == "video/h263-2000") { + maxRate = KBiR_H263; + } else if (lowerMimeType == "video/h263-2000; profile=0") { + maxRate = KBiR_H263_Profile0; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=10") { + maxRate = KBiR_H263_Profile0_Level10; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=20") { + maxRate = KBiR_H263_Profile0_Level20; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=30") { + maxRate = KBiR_H263_Profile0_Level30; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=40") { + maxRate = KBiR_H263_Profile0_Level40; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=45") { + maxRate = KBiR_H263_Profile0_Level45; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=50") { + maxRate = KBiR_H263_Profile0_Level50; + } else if (lowerMimeType == "video/h263-2000; profile=3") { + maxRate = KBiR_H263_Profile3; + } else if (lowerMimeType == "video/mp4v-es") { + maxRate = KBiR_MPEG4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=1") { + maxRate = KBiR_MPEG4_PLID_1; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=2") { + maxRate = KBiR_MPEG4_PLID_2; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=3") { + maxRate = KBiR_MPEG4_PLID_3; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=4") { + maxRate = KBiR_MPEG4_PLID_4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=5") { + maxRate = KBiR_MPEG4_PLID_5; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=6") { + maxRate = KBiR_MPEG4_PLID_6; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=8") { + maxRate = KBiR_MPEG4_PLID_8; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=9") { + maxRate = KBiR_MPEG4_PLID_9; + } else if (lowerMimeType == "video/h264") { + maxRate = KBiR_H264; + } else if (lowerMimeType == "video/h264; profile-level-id=42800a" || + lowerMimeType == "video/h264; profile-level-id=4d400a" || + lowerMimeType == "video/h264; profile-level-id=64400a") { // L1 + maxRate = KBiR_H264_PLID_42800A; + } else if (lowerMimeType == "video/h264; profile-level-id=42900b" || + lowerMimeType == "video/h264; profile-level-id=4d500b" || + lowerMimeType == "video/h264; profile-level-id=644009") { // L1.b + maxRate = KBiR_H264_PLID_42900B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800b" || + lowerMimeType == "video/h264; profile-level-id=4d400b" || + lowerMimeType == "video/h264; profile-level-id=64400b") { // L1.1 + maxRate = KBiR_H264_PLID_42800B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800c" || + lowerMimeType == "video/h264; profile-level-id=4d400c" || + lowerMimeType == "video/h264; profile-level-id=64400c") { // L1.2 + maxRate = KBiR_H264_PLID_42800C; + } else if (lowerMimeType == "video/h264; profile-level-id=42800d" || + lowerMimeType == "video/h264; profile-level-id=4d400d" || + lowerMimeType == "video/h264; profile-level-id=64400d") { // L1.3 + maxRate = KBiR_H264_PLID_42800D; + } else if (lowerMimeType == "video/h264; profile-level-id=428014" || + lowerMimeType == "video/h264; profile-level-id=4d4014" || + lowerMimeType == "video/h264; profile-level-id=644014") { // L2 + maxRate = KBiR_H264_PLID_428014; + } else if (lowerMimeType == "video/h264; profile-level-id=428015" || + lowerMimeType == "video/h264; profile-level-id=4d4015" || + lowerMimeType == "video/h264; profile-level-id=644015") { // L2.1 + maxRate = KBiR_H264_PLID_428015; + } else if (lowerMimeType == "video/h264; profile-level-id=428016" || + lowerMimeType == "video/h264; profile-level-id=4d4016" || + lowerMimeType == "video/h264; profile-level-id=644016") { // L2.2 + maxRate = KBiR_H264_PLID_428016; + } else if (lowerMimeType == "video/h264; profile-level-id=42801e" || + lowerMimeType == "video/h264; profile-level-id=4d401e" || + lowerMimeType == "video/h264; profile-level-id=64401e") { // L3 + maxRate = KBiR_H264_PLID_42801E; + } else if (lowerMimeType == "video/h264; profile-level-id=42801f" || + lowerMimeType == "video/h264; profile-level-id=4d401f" || + lowerMimeType == "video/h264; profile-level-id=64401f") { // L3.1 + maxRate = KBiR_H264_PLID_42801F; + } else if (lowerMimeType == "video/h264; profile-level-id=428020" || + lowerMimeType == "video/h264; profile-level-id=4d4020" || + lowerMimeType == "video/h264; profile-level-id=644020") { // L3.2 + maxRate = KBiR_H264_PLID_428020; + } else if (lowerMimeType == "video/h264; profile-level-id=428028" || + lowerMimeType == "video/h264; profile-level-id=4d4028" || + lowerMimeType == "video/h264; profile-level-id=644028") { // L4 + maxRate = KBiR_H264_PLID_428028; + } + + return maxRate; +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60videocapturesession.h b/src/plugins/symbian/ecam/s60videocapturesession.h new file mode 100644 index 000000000..cfa101f57 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videocapturesession.h @@ -0,0 +1,414 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOCAPTURESESSION_H +#define S60VIDEOCAPTURESESSION_H + +#include <QtCore/qurl.h> +#include <QtCore/qhash.h> + +#include <qmediaencodersettings.h> +#include <qcamera.h> +#include <qmediarecorder.h> + +#include "s60cameraengine.h" + +#include <e32base.h> +#include <videorecorder.h> // CVideoRecorderUtility +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED +#include <mmf/devvideo/devvideorecord.h> +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + +QT_USE_NAMESPACE + +class QTimer; + +/* + * VideoSession is the main class handling all video recording related + * operations. It uses mainly CVideoRecorderUtility to do it's tasks, but if + * DevVideoRecord is available it is used to provide more detailed + * information of the supported video settings. + */ +class S60VideoCaptureSession : public QObject, + public MVideoRecorderUtilityObserver +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED + ,public MMMFDevVideoRecordObserver +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED +{ + Q_OBJECT + Q_ENUMS(Error) + Q_ENUMS(EcamErrors) + Q_ENUMS(TVideoCaptureState) + +public: // Enums + + enum TVideoCaptureState + { + ENotInitialized = 0, // 0 - VideoRecording is not initialized, instance may or may not be created + EInitializing, // 1 - Initialization is ongoing + EInitialized, // 2 - VideoRecording is initialized, OpenFile is called with dummy file + EOpening, // 3 - OpenFile called with actual output location, waiting completion + EOpenComplete, // 4 - OpenFile completed with the actual output location + EPreparing, // 5 - Preparing VideoRecording to use set video settings + EPrepared, // 6 - VideoRecording is prepared with the set settings, ready to record + ERecording, // 7 - Video recording is ongoing + EPaused // 8 - Video recording has been started and paused + }; + + enum AudioQualityDefinition + { + ENoAudioQuality = 0, // 0 - Both BitRate and SampleRate settings available + EOnlyAudioQuality, // 1 - No BitRate or SampleRate settings available, use Quality to set them + EAudioQualityAndBitRate, // 2 - BitRate setting available, use Quality to set SampleRate + EAudioQualityAndSampleRate, // 3 - SampleRate setting available, use Quality to set BitRate + }; + + enum VideoQualityDefinition + { + ENoVideoQuality = 0, // 0 - All, Resolution, FrameRate and BitRate available + EOnlyVideoQuality, // 1 - None available, use Quality to set Resolution, FrameRate and BitRate + EVideoQualityAndResolution, // 2 - Only Resolution available, use Quality to set FrameRate and BitRate + EVideoQualityAndFrameRate, // 3 - Only FrameRate available, use Quality to set Resolution and BitRate + EVideoQualityAndBitRate, // 4 - Only BitRate available, use Quality to set Resolution and FrameRate + EVideoQualityAndResolutionAndBitRate, // 5 - No FrameRate available, use Quality to set it + EVideoQualityAndResolutionAndFrameRate, // 6 - No BitRate available, use Quality to set it + EVideoQualityAndFrameRateAndBitRate // 7 - No Resolution available, use Quality to set it + }; + +public: // Constructor & Destructor + + S60VideoCaptureSession(QObject *parent = 0); + ~S60VideoCaptureSession(); + +public: // MVideoRecorderUtilityObserver + + void MvruoOpenComplete(TInt aError); + void MvruoPrepareComplete(TInt aError); + void MvruoRecordComplete(TInt aError); + void MvruoEvent(const TMMFEvent& aEvent); + +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED +public: // MMMFDevVideoRecordObserver + void MdvroReturnPicture(TVideoPicture *aPicture); + void MdvroSupplementalInfoSent(); + void MdvroNewBuffers(); + void MdvroFatalError(TInt aError); + void MdvroInitializeComplete(TInt aError); + void MdvroStreamEnd(); +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + +public: // Methods + + void setError(const TInt error, const QString &description); + void setCameraHandle(CCameraEngine* cameraHandle); + void notifySettingsSet(); + + qint64 position(); + TVideoCaptureState state() const; + bool isMuted() const; + + // Controls + int initializeVideoRecording(); + void releaseVideoRecording(); + void applyAllSettings(); + + void startRecording(); + void pauseRecording(); + void stopRecording(const bool reInitialize = true); + void setMuted(const bool muted); + + // Output Location + bool setOutputLocation(const QUrl &sink); + QUrl outputLocation() const; + + // Resolution + void setVideoResolution(const QSize &resolution); + QList<QSize> supportedVideoResolutions(bool *continuous); + QList<QSize> supportedVideoResolutions(const QVideoEncoderSettings &settings, bool *continuous); + + // Framerate + void setFrameRate(const qreal rate); + QList<qreal> supportedVideoFrameRates(bool *continuous); + QList<qreal> supportedVideoFrameRates(const QVideoEncoderSettings &settings, bool *continuous); + + // Other Video Settings + void setBitrate(const int bitrate); + void setVideoEncodingMode(const QtMultimediaKit::EncodingMode mode); + + // Video Codecs + void setVideoCaptureCodec(const QString &codecName); + QStringList supportedVideoCaptureCodecs(); + QString videoCaptureCodecDescription(const QString &codecName); + + // Audio Codecs + void setAudioCaptureCodec(const QString &codecName); + QStringList supportedAudioCaptureCodecs(); + + // Encoder Settings + void videoEncoderSettings(QVideoEncoderSettings &videoSettings); + void audioEncoderSettings(QAudioEncoderSettings &audioSettings); + + // Quality + void setVideoCaptureQuality(const QtMultimediaKit::EncodingQuality quality, + const VideoQualityDefinition mode); + void setAudioCaptureQuality(const QtMultimediaKit::EncodingQuality quality, + const AudioQualityDefinition mode); + + // Video Containers + QString videoContainer() const; + void setVideoContainer(const QString &containerName); + QStringList supportedVideoContainers(); + bool isSupportedVideoContainer(const QString &containerName); + QString videoContainerDescription(const QString &containerName); + + // Audio Settings + QList<int> supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous); + void setAudioSampleRate(const int sampleRate); + void setAudioBitRate(const int bitRate); + void setAudioChannelCount(const int channelCount); + void setAudioEncodingMode(const QtMultimediaKit::EncodingMode mode); + + // Video Options + QSize pixelAspectRatio(); + void setPixelAspectRatio(const QSize par); + int gain(); + void setGain(const int gain); + int maxClipSizeInBytes() const; + void setMaxClipSizeInBytes(const int size); + +private: // Internal + + QMediaRecorder::Error fromSymbianErrorToQtMultimediaError(int aError); + + void initializeVideoCaptureSettings(); + void doInitializeVideoRecorderL(); + void commitVideoEncoderSettings(); + void queryAudioEncoderSettings(); + void queryVideoEncoderSettings(); + void validateRequestedCodecs(); + void resetSession(bool errorHandling = false); + + void doSetCodecsL(); + QString determineProfileAndLevel(); + void doSetVideoResolution(const QSize &resolution); + void doSetFrameRate(qreal rate); + void doSetBitrate(const int &bitrate); + + void updateVideoCaptureContainers(); + void doUpdateVideoCaptureContainersL(); + void selectController(const QString &format, + TUid &controllerUid, + TUid &formatUid); + + void doPopulateVideoCodecsDataL(); + void doPopulateVideoCodecsL(); +#ifndef S60_DEVVIDEO_RECORDING_SUPPORTED + void doPopulateMaxVideoParameters(); +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + void doPopulateAudioCodecsL(); + + QList<int> doGetSupportedSampleRatesL(const QAudioEncoderSettings &settings, + bool *continuous); + QSize maximumResolutionForMimeType(const QString &mimeType) const; + qreal maximumFrameRateForMimeType(const QString &mimeType) const; + int maximumBitRateForMimeType(const QString &mimeType) const; + +signals: // Notification Signals + + void stateChanged(S60VideoCaptureSession::TVideoCaptureState); + void positionChanged(qint64); + void mutedChanged(bool); + void captureSizeChanged(const QSize&); + void error(int, const QString&); + +private slots: // Internal Slots + + void cameraStatusChanged(QCamera::Status); + void durationTimerTriggered(); + +private: // Structs + + /* + * This structure holds the information of supported video mime types for + * the format and also description for it. + */ + struct VideoFormatData { + QString description; + QStringList supportedMimeTypes; + }; + + /* + * This structure is used to define supported resolutions and framerate + * (depending on each other) for each supported encoder mime type (defining + * encoder, profile and level) + */ + struct SupportedFrameRatePictureSize { + SupportedFrameRatePictureSize() {} + SupportedFrameRatePictureSize(qreal rate, QSize size): + frameRate(rate), + frameSize(size) {} + qreal frameRate; + QSize frameSize; + }; + + /* + * This structure defines supported resolution/framerate pairs and maximum + * bitrate for a single encodec device. It also the supported mime types + * (codec, profile and level) of the encoder device. + * + * Structure defines 2 contructors: + * - First with no attributes + * - Second, which will construct the sructure appending one + * resolution/framerate pair to the list of + * SupportedFrameRatePictureSizes and setting the given bitrate as + * maximum. This second constructor is for convenience. + * + * This struct is used in m_videoParametersForEncoder (QList). + * + * Here's a visualization of an example strcuture: + * STRUCT: + * |-- Resolution/FrameRate Pairs: + * | |- VGA / 30fps + * | |- 720p / 25fps + * | |- Etc. + * | + * |-- MimeTypes: + * | |- video/mp4v-es; profile-level-id=1 + * | |- video/mp4v-es; profile-level-id=2 + * | |- Etc. + * | + * |-- Max BitRate: 1Mbps + */ + struct MaxResolutionRatesAndTypes { + MaxResolutionRatesAndTypes() {} + MaxResolutionRatesAndTypes(QSize size, qreal fRate, int bRate): + bitRate(bRate) + { + frameRatePictureSizePair.append(SupportedFrameRatePictureSize(fRate,size)); + } + QList<SupportedFrameRatePictureSize> frameRatePictureSizePair; + QStringList mimeTypes; + int bitRate; + }; + +private: // Data + + CCameraEngine *m_cameraEngine; + CVideoRecorderUtility *m_videoRecorder; + QTimer *m_durationTimer; + qint64 m_position; + // Symbian ErrorCode + mutable int m_error; + // This defines whether Camera is in ActiveStatus or not + bool m_cameraStarted; + // Internal state of the video recorder + TVideoCaptureState m_captureState; + // Actual output file name/path + QUrl m_sink; + // Requested output file name/path, this may be different from m_sink if + // asynchronous operation was ongoing in the CVideoRecorderUtility when new + // outputLocation was set. + QUrl m_requestedSink; + // Requested videoSettings. The may not be active settings before those are + // committed (with commitVideoEncoderSettings()) + QVideoEncoderSettings m_videoSettings; + // Requested audioSettings. The may not be active settings before those are + // committed (with commitVideoEncoderSettings()) + QAudioEncoderSettings m_audioSettings; + // Tells whether settings should be initialized when changing the camera + bool m_captureSettingsSet; + // Active container + QString m_container; + // Requested container, this may be different from m_container if + // asynchronous operation was ongoing in the CVideoRecorderUtility when new + // container was set. + QString m_requestedContainer; + // Requested muted value. This may not be active value before settings are + // committed (with commitVideoEncoderSettings()) + bool m_muted; + // Maximum ClipSize in Bytes + int m_maxClipSize; + // List of supported video codec mime types + QStringList m_videoCodecList; + // Hash of supported video codec mime types and corresponding FourCC codes + QHash<QString, TFourCC> m_audioCodecList; + // Map of video capture controllers information. It is populated during + // doUpdateVideoCaptureContainersL(). + // + // Here's a visualization of an example strcuture: + // m_videoControllerMap(HASH): + // | + // |-- Controller 1 : HASH + // | |- Container 1 (UID) : FormatData + // | | |- Description + // | | |- List of supported MimeTypes + // | |- Container 2 (UID) : FormatData + // | | |- Description + // | | |- List of supported MimeTypes + // | |- Etc. + // | + // |-- Controller 2: HASH + // | |- Container 1 (UID) : FormatData + // | | |- Description + // | | |- List of supported MimeTypes + // | |- Etc. + // + QHash<TInt, QHash<TInt,VideoFormatData> > m_videoControllerMap; + // List of Encoder information. If DevVideoRecord is available info is + // gathered during doPopulateVideoCodecsDataL() for each encoder (hw + // accelerated and supporting camera input) found. If DevVideoRecord is not + // available, the info is set in doPopulateMaxVideoParameters() based on + // supported codec list received from CVideoRecorderUtility. + QList<MaxResolutionRatesAndTypes> m_videoParametersForEncoder; + // Set if OpenFileL should be executed when currently ongoing operation + // is completed. + bool m_openWhenReady; + // Set if video capture should be prepared after OpenFileL has completed + bool m_prepareAfterOpenComplete; + // Set if video capture should be started when Prepare has completed + bool m_startAfterPrepareComplete; + // Tells if settings have been set after last Prepare() + bool m_uncommittedSettings; + // Tells if settings need to be applied after ongoing operation has finished + bool m_commitSettingsWhenReady; +}; + +#endif // S60VIDEOCAPTURESESSION_H diff --git a/src/plugins/symbian/ecam/s60videodevicecontrol.cpp b/src/plugins/symbian/ecam/s60videodevicecontrol.cpp new file mode 100644 index 000000000..3d55d3110 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videodevicecontrol.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <QtCore/qstring.h> +#include <QtGui/qicon.h> + +#include "s60videodevicecontrol.h" +#include "s60cameracontrol.h" +#include "s60cameraconstants.h" + +S60VideoDeviceControl::S60VideoDeviceControl(QObject *parent) : + QVideoDeviceControl(parent) +{ +} + +S60VideoDeviceControl::S60VideoDeviceControl(S60CameraControl *control, QObject *parent) : + QVideoDeviceControl(parent), + m_selectedDevice(KDefaultCameraDevice) +{ + m_control = control; + connect(m_control, SIGNAL(devicesChanged()), this, SIGNAL(devicesChanged())); +} + +S60VideoDeviceControl::~S60VideoDeviceControl() +{ +} + +int S60VideoDeviceControl::deviceCount() const +{ + return S60CameraControl::deviceCount(); +} + +QString S60VideoDeviceControl::deviceName(int index) const +{ + return S60CameraControl::name(index); +} + +QString S60VideoDeviceControl::deviceDescription(int index) const +{ + return S60CameraControl::description(index); +} + +QIcon S60VideoDeviceControl::deviceIcon(int index) const +{ + Q_UNUSED(index); + return QIcon(); +} + +int S60VideoDeviceControl::defaultDevice() const +{ + return KDefaultCameraDevice; +} + +int S60VideoDeviceControl::selectedDevice() const +{ + return m_selectedDevice; +} + +void S60VideoDeviceControl::setSelectedDevice(int index) +{ + // Inform that we selected new device + if (m_selectedDevice != index) { + m_control->setSelectedDevice(index); + m_selectedDevice = index; + emit selectedDeviceChanged(m_selectedDevice); + emit selectedDeviceChanged(deviceName(m_selectedDevice)); + } +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60videodevicecontrol.h b/src/plugins/symbian/ecam/s60videodevicecontrol.h new file mode 100644 index 000000000..03249441a --- /dev/null +++ b/src/plugins/symbian/ecam/s60videodevicecontrol.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEODEVICECONTROL_H +#define S60VIDEODEVICECONTROL_H + +#include "qvideodevicecontrol.h" + +QT_USE_NAMESPACE + +class S60CameraControl; +class QString; +class QIcon; + +/* + * Control for providing information of the video device (r. camera) and to + * enable other camera device (e.g. secondary camera if one exists). + */ +class S60VideoDeviceControl : public QVideoDeviceControl +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60VideoDeviceControl(QObject *parent); + S60VideoDeviceControl(S60CameraControl *control, QObject *parent = 0); + virtual ~S60VideoDeviceControl(); + +public: // QVideoDeviceControl + + int deviceCount() const; + + QString deviceName(int index) const; + QString deviceDescription(int index) const; + QIcon deviceIcon(int index) const; + + int defaultDevice() const; + int selectedDevice() const; + +public slots: // QVideoDeviceControl + + void setSelectedDevice(int index); + +/* +Q_SIGNALS: +void selectedDeviceChanged(int index); +void selectedDeviceChanged(const QString &deviceName); +void devicesChanged(); +*/ + +private: // Data + + S60CameraControl *m_control; + int m_selectedDevice; +}; + +#endif // S60VIDEODEVICECONTROL_H diff --git a/src/plugins/symbian/ecam/s60videoencodercontrol.cpp b/src/plugins/symbian/ecam/s60videoencodercontrol.cpp new file mode 100644 index 000000000..34b28e28e --- /dev/null +++ b/src/plugins/symbian/ecam/s60videoencodercontrol.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60videoencodercontrol.h" +#include "s60videocapturesession.h" + +S60VideoEncoderControl::S60VideoEncoderControl(QObject *parent) : + QVideoEncoderControl(parent) +{ +} + +S60VideoEncoderControl::S60VideoEncoderControl(S60VideoCaptureSession *session, QObject *parent) : + QVideoEncoderControl(parent) +{ + m_session = session; +} + +S60VideoEncoderControl::~S60VideoEncoderControl() +{ +} + +QStringList S60VideoEncoderControl::supportedVideoCodecs() const +{ + return m_session->supportedVideoCaptureCodecs(); +} + +QString S60VideoEncoderControl::videoCodecDescription(const QString &codecName) const +{ + return m_session->videoCaptureCodecDescription(codecName); +} + +QList<qreal> S60VideoEncoderControl::supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const +{ + if (!settings.isNull()) + return m_session->supportedVideoFrameRates(settings, continuous); + return m_session->supportedVideoFrameRates(continuous); +} + +QList<QSize> S60VideoEncoderControl::supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const +{ + if (!settings.isNull()) + return m_session->supportedVideoResolutions(settings, continuous); + return m_session->supportedVideoResolutions(continuous); +} + +QStringList S60VideoEncoderControl::supportedEncodingOptions(const QString &codec) const +{ + // Possible settings: EncodingMode, Codec, Resolution, FrameRate, BitRate, Quality + // Possible (codec specific) options: PixelAspectRatio, Gain, MaxClipSizeInBytes + + // Following options are valid for all codecs + Q_UNUSED(codec); + + QStringList options; + options.append("pixelAspectRatio"); + options.append("gain"); + options.append("maxClipSizeInBytes"); + + return options; +} + +QVariant S60VideoEncoderControl::encodingOption(const QString &codec, const QString &name) const +{ + Q_UNUSED(codec); + + // Possible settings: EncodingMode, Codec, Resolution, FrameRate, BitRate, Quality + // Possible (codec specific) options: PixelAspectRatio, Gain, MaxClipSizeInBytes + + QVariant returnValue; + + if (qstrcmp(name.toLocal8Bit().constData(), "pixelAspectRatio") == 0) + returnValue.setValue(m_session->pixelAspectRatio()); + else if (qstrcmp(name.toLocal8Bit().constData(), "gain") == 0) + returnValue.setValue((int)m_session->gain()); + else if (qstrcmp(name.toLocal8Bit().constData(), "maxClipSizeInBytes") == 0) + returnValue.setValue(m_session->maxClipSizeInBytes()); + + return returnValue; +} + +void S60VideoEncoderControl::setEncodingOption( + const QString &codec, const QString &name, const QVariant &value) +{ + // Set the codec first if not already set + m_session->setVideoCaptureCodec(codec); + + if (qstrcmp(name.toLocal8Bit().constData(), "pixelAspectRatio") == 0) + m_session->setPixelAspectRatio(value.toSize()); + else if (qstrcmp(name.toLocal8Bit().constData(), "gain") == 0) + m_session->setGain(value.toInt()); + else if (qstrcmp(name.toLocal8Bit().constData(), "maxClipSizeInBytes") == 0) + m_session->setMaxClipSizeInBytes(value.toInt()); + else + m_session->setError(KErrNotSupported, tr("Requested encoding option is not supported")); +} + +QVideoEncoderSettings S60VideoEncoderControl::videoSettings() const +{ + QVideoEncoderSettings settings; + m_session->videoEncoderSettings(settings); + + return settings; +} + +void S60VideoEncoderControl::setVideoSettings(const QVideoEncoderSettings &settings) +{ + // Notify that settings have been implicitly set and there's no need to + // initialize them in case camera is changed + m_session->notifySettingsSet(); + + if (settings.codec().isEmpty() + || (settings.resolution() == QSize(-1,-1) && settings.frameRate() == 0 && settings.bitRate() == -1)) { + if (!settings.codec().isEmpty()) + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EOnlyVideoQuality); + } else if (settings.resolution() != QSize(-1,-1) && settings.frameRate() == 0 && settings.bitRate() == -1) { // Only Resolution + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setVideoResolution(settings.resolution()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndResolution); + + } else if (settings.resolution() == QSize(-1,-1) && settings.frameRate() != 0 && settings.bitRate() == -1) { // Only Framerate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setFrameRate(settings.frameRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndFrameRate); + + } else if (settings.resolution() == QSize(-1,-1) && settings.frameRate() == 0 && settings.bitRate() != -1) { // Only BitRate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setBitrate(settings.bitRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndBitRate); + + } else if (settings.resolution() != QSize(-1,-1) && settings.frameRate() != 0 && settings.bitRate() == -1) { // Resolution and FrameRate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setVideoResolution(settings.resolution()); + m_session->setFrameRate(settings.frameRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndResolutionAndFrameRate); + + } else if (settings.resolution() != QSize(-1,-1) && settings.frameRate() == 0 && settings.bitRate() != -1) { // Resolution and BitRate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setVideoResolution(settings.resolution()); + m_session->setBitrate(settings.bitRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndResolutionAndBitRate); + + } else if (settings.resolution() == QSize(-1,-1) && settings.frameRate() != 0 && settings.bitRate() != -1) { // FrameRate and BitRate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setFrameRate(settings.frameRate()); + m_session->setBitrate(settings.bitRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndFrameRateAndBitRate); + + } else { // All: Resolution, BitRate and FrameRate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setVideoResolution(settings.resolution()); + m_session->setFrameRate(settings.frameRate()); + m_session->setBitrate(settings.bitRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::ENoVideoQuality); + } +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60videoencodercontrol.h b/src/plugins/symbian/ecam/s60videoencodercontrol.h new file mode 100644 index 000000000..4554d9291 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videoencodercontrol.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOENCODERCONTROL_H +#define S60VIDEOENCODERCONTROL_H + +#include <QtCore/qstringlist.h> +#include <QtCore/qmap.h> + +#include "qvideoencodercontrol.h" + +QT_USE_NAMESPACE + +class S60VideoCaptureSession; + +/* + * Control for video settings when recording video using QMediaRecorder. + */ +class S60VideoEncoderControl : public QVideoEncoderControl +{ + Q_OBJECT + +public: // Contructors & Destructor + + S60VideoEncoderControl(QObject *parent = 0); + S60VideoEncoderControl(S60VideoCaptureSession *session, QObject *parent = 0); + virtual ~S60VideoEncoderControl(); + +public: // QVideoEncoderControl + + // Resolution + QList<QSize> supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous = 0) const; + + // Framerate + QList<qreal> supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous = 0) const; + + // Video Codec + QStringList supportedVideoCodecs() const; + QString videoCodecDescription(const QString &codecName) const; + + // Video Settings + QVideoEncoderSettings videoSettings() const; + void setVideoSettings(const QVideoEncoderSettings &settings); + + // Encoding Options + QStringList supportedEncodingOptions(const QString &codec) const; + QVariant encodingOption(const QString &codec, const QString &name) const; + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + +private: // Data + + S60VideoCaptureSession* m_session; + +}; + +#endif // S60VIDEOENCODERCONTROL_H diff --git a/src/plugins/symbian/ecam/s60videorenderercontrol.cpp b/src/plugins/symbian/ecam/s60videorenderercontrol.cpp new file mode 100644 index 000000000..8cc3546b5 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videorenderercontrol.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include <qabstractvideosurface.h> + +#include "s60videorenderercontrol.h" + +S60VideoRendererControl::S60VideoRendererControl(QObject *parent) : + QVideoRendererControl(parent), + m_surface(0) +{ +} + +S60VideoRendererControl::~S60VideoRendererControl() +{ + // Stop surface if still active + if (m_surface) + if (m_surface->isActive()) + m_surface->stop(); +} + +QAbstractVideoSurface *S60VideoRendererControl::surface() const +{ + return m_surface; +} + +void S60VideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + if (surface == 0) { + // Stop current surface if needed + if (m_surface) + if (m_surface->isActive()) + m_surface->stop(); + } + + m_surface = surface; + emit viewFinderSurfaceSet(); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60videorenderercontrol.h b/src/plugins/symbian/ecam/s60videorenderercontrol.h new file mode 100644 index 000000000..b35433f86 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videorenderercontrol.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** 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 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. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEORENDERERCONTROL_H +#define S60VIDEORENDERERCONTROL_H + +#include <qvideorenderercontrol.h> + +/* + * Control for QGraphicsVideoItem. Viewfinder frames are streamed to a surface + * which is drawn to the display by the Qt Graphics Vide Framework. + * VideoRendererControl uses only Bitmap Viewfinder. + */ +class S60VideoRendererControl : public QVideoRendererControl +{ + Q_OBJECT + +public: // Constructor & Destructor + + S60VideoRendererControl(QObject *parent = 0); + virtual ~S60VideoRendererControl(); + +public: // S60VideoRendererControl + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + +signals: // Internal Signals + + void viewFinderSurfaceSet(); + +private: // Data + + QAbstractVideoSurface *m_surface; + +}; + +#endif // S60VIDEORENDERERCONTROL_H |