diff -r 000000000000 -r 876b1a06bc25 plugins/multimedia/symbian/mmf/audiosource/s60audiocapturesession.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/plugins/multimedia/symbian/mmf/audiosource/s60audiocapturesession.cpp Wed Aug 25 15:49:42 2010 +0300 @@ -0,0 +1,608 @@ +/**************************************************************************** +** +** Copyright (C) 2010 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 "s60audiocapturesession.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +_LIT(KAudioDummyFile, "c:\\data\\temp\\temp.wav"); + +S60AudioCaptureSession::S60AudioCaptureSession(QObject *parent): + QObject(parent) + , m_recorderUtility(NULL) + , m_captureState(ENotInitialized) + , m_controllerIdMap(QHash()) + , m_audioCodeclist(QHash()) + , m_error(QMediaRecorder::NoError) + , isMuted(false) +{ + TRAPD(err, initializeSessionL()); + setError(err); +} + +void S60AudioCaptureSession::initializeSessionL() +{ + m_recorderUtility = CMdaAudioRecorderUtility::NewL(*this, 0, 80,(TMdaPriorityPreference) 0x5210001); + updateAudioContainersL(); + populateAudioCodecsDataL(); + setDefaultSettings(); +} + +void S60AudioCaptureSession::setError(TInt aError) +{ + if (aError == KErrNone) + return; + + m_error = aError; + QMediaRecorder::Error recorderError = fromSymbianErrorToMultimediaError(m_error); + + // TODO: fix to user friendly string at some point + // These error string are only dev usable + QString symbianError; + symbianError.append("Symbian:"); + symbianError.append(QString::number(m_error)); + emit error(recorderError, symbianError); +} + +QMediaRecorder::Error S60AudioCaptureSession::fromSymbianErrorToMultimediaError(int error) +{ + switch(error) { + case KErrNoMemory: + case KErrNotFound: + case KErrBadHandle: + case KErrAbort: + case KErrCorrupt: + case KErrGeneral: + case KErrPathNotFound: + case KErrUnknown: + case KErrNotReady: + case KErrInUse: + case KErrAccessDenied: + case KErrLocked: + case KErrPermissionDenied: + case KErrAlreadyExists: + return QMediaRecorder::ResourceError; + case KErrNotSupported: + case KErrArgument: + return QMediaRecorder::FormatError; + case KErrNone: + default: + return QMediaRecorder::NoError; + } +} + +S60AudioCaptureSession::~S60AudioCaptureSession() +{ + delete m_recorderUtility; +} + +QAudioFormat S60AudioCaptureSession::format() const +{ + return m_format; +} + +bool S60AudioCaptureSession::setFormat(const QAudioFormat &format) +{ + m_format = format; + return true; +} + +QStringList S60AudioCaptureSession::supportedAudioCodecs() const +{ + return m_audioCodeclist.keys(); +} + +QStringList S60AudioCaptureSession::supportedAudioContainers() const +{ + return m_controllerIdMap.keys(); +} + +QString S60AudioCaptureSession::codecDescription(const QString &codecName) +{ + if (m_audioCodeclist.keys().contains(codecName)) + return m_audioCodeclist.value(codecName).codecDescription; + else + return QString(); +} + +QString S60AudioCaptureSession::audioContainerDescription(const QString &containerName) +{ + if (m_controllerIdMap.keys().contains(containerName)) + return m_controllerIdMap.value(containerName).destinationFormatDescription; + else + return QString(); +} + +bool S60AudioCaptureSession::setAudioCodec(const QString &codecName) +{ + QStringList codecs = supportedAudioCodecs(); + if(codecs.contains(codecName)) { + m_format.setCodec(codecName); + return true; + } + return false; +} + +bool S60AudioCaptureSession::setAudioContainer(const QString &containerMimeType) +{ + QStringList containers = supportedAudioContainers(); + if(containers.contains(containerMimeType)) { + m_container = containerMimeType; + return true; + } + return false; +} + +QString S60AudioCaptureSession::audioCodec() const +{ + return m_format.codec(); +} + +QString S60AudioCaptureSession::audioContainer() const +{ + return m_container; +} + +QUrl S60AudioCaptureSession::outputLocation() const +{ + return m_sink; +} + +bool S60AudioCaptureSession::setOutputLocation(const QUrl& sink) +{ + QString filename = QDir::toNativeSeparators(m_sink.toString()); + TPtrC16 path(reinterpret_cast(filename.utf16())); + TRAPD(err, BaflUtils::EnsurePathExistsL(CCoeEnv::Static()->FsSession(),path)); + if (err==KErrNone) { + m_sink = sink; + setError(err); + return true; + }else { + setError(err); + return false; + } +} + +qint64 S60AudioCaptureSession::position() const +{ + if ((m_captureState != ERecording) || !m_recorderUtility) + return 0; + + return m_recorderUtility->Duration().Int64() / 1000; +} + +QString S60AudioCaptureSession::initializeSinkL() +{ + if (m_sink.isEmpty()) + m_sink = generateAudioFilePath(); + QString sink = QDir::toNativeSeparators(m_sink.toString()); + int index = sink.lastIndexOf('.'); + if (index != -1) + sink.chop(sink.length()-index); + + sink.append(m_controllerIdMap.value(m_container).fileExtension); + return sink; +} + +void S60AudioCaptureSession::record() +{ + if (!m_recorderUtility) + return; + + if (m_captureState == EInitialized || m_captureState == ERecordComplete) { + QString filename = initializeSinkL(); + TPtrC16 sink(reinterpret_cast(filename.utf16())); + TUid controllerUid(TUid::Uid(m_controllerIdMap.value(m_container).controllerUid)); + TUid formatUid(TUid::Uid(m_controllerIdMap.value(m_container).destinationFormatUid)); + TRAPD(err, + if (m_container != "audio/amr") + m_recorderUtility->OpenFileL(sink, controllerUid, KNullUid, formatUid); + else + m_recorderUtility->OpenFileL(sink); + ); + setError(err); + }else if (m_captureState == EPaused) { + m_recorderUtility->SetPosition(m_pausedPosition); + TRAPD(error, m_recorderUtility->RecordL()); + setError(error); + m_captureState = ERecording; + emit stateChanged(m_captureState); + } +} + +void S60AudioCaptureSession::mute(bool muted) +{ + if (!m_recorderUtility) + return; + + if (muted) + m_recorderUtility->SetGain(0); + else + m_recorderUtility->SetGain(m_recorderUtility->MaxGain()); + + isMuted = muted; +} + +bool S60AudioCaptureSession::muted() +{ + return isMuted; +} + +void S60AudioCaptureSession::setDefaultSettings() +{ + // Setting AMR to default format if supported + if (m_controllerIdMap.count() > 0) { + if ( m_controllerIdMap.contains("audio/amr")) + m_container = QString("audio/amr"); + else + m_container = m_controllerIdMap.keys()[0]; + } + if (m_audioCodeclist.keys().count() > 0) { + if (m_audioCodeclist.keys().contains("AMR")) { + m_format.setSampleSize(8); + m_format.setChannels(1); + m_format.setFrequency(8000); + m_format.setSampleType(QAudioFormat::SignedInt); + m_format.setCodec("AMR"); + }else + m_format.setCodec(m_audioCodeclist.keys()[0]); + } +} + +void S60AudioCaptureSession::pause() +{ + if (!m_recorderUtility) + return; + + m_pausedPosition = m_recorderUtility->Position(); + m_recorderUtility->Stop(); + m_captureState = EPaused; + emit stateChanged(m_captureState); +} + +void S60AudioCaptureSession::stop() +{ + if (!m_recorderUtility) + return; + + m_recorderUtility->Stop(); + m_recorderUtility->Close(); + m_captureState = ERecordComplete; + emit stateChanged(m_captureState); +} + +void S60AudioCaptureSession::setCaptureDevice(const QString &deviceName) +{ + m_captureDevice = deviceName; +} + +void S60AudioCaptureSession::MoscoStateChangeEvent(CBase* aObject, + TInt aPreviousState, TInt aCurrentState, TInt aErrorCode) +{ + if (aErrorCode==KErrNone) { + TRAPD(err, MoscoStateChangeEventL(aObject, aPreviousState, aCurrentState, NULL)); + setError(err); + } + setError(aErrorCode); +} + +void S60AudioCaptureSession::MoscoStateChangeEventL(CBase* aObject, + TInt aPreviousState, TInt aCurrentState, TInt aErrorCode) +{ + if (aObject != m_recorderUtility) + return; + + switch(aCurrentState) { + case CMdaAudioClipUtility::EOpen: { + if(aPreviousState == CMdaAudioClipUtility::ENotReady) { + if (m_captureState == EInitializing) { + retrieveSupportedAudioSampleRatesL(); + m_recorderUtility->Close(); + m_captureState = EInitialized; + emit stateChanged(m_captureState); + }else { + applyAudioSettingsL(); + m_recorderUtility->SetGain(m_recorderUtility->MaxGain()); + m_recorderUtility->RecordL(); + m_captureState = EOpenCompelete; + emit stateChanged(m_captureState); + } + break; + } + } + case CMdaAudioClipUtility::ENotReady: { + m_captureState = EInitialized; + emit stateChanged(m_captureState); + break; + } + case CMdaAudioClipUtility::ERecording: { + m_captureState = ERecording; + emit stateChanged(m_captureState); + break; + } + default: { + break; + } + } +} + +void S60AudioCaptureSession::updateAudioContainersL() +{ + CMMFControllerPluginSelectionParameters* pluginParameters = + CMMFControllerPluginSelectionParameters::NewLC(); + CMMFFormatSelectionParameters* formatParameters = + CMMFFormatSelectionParameters::NewLC(); + + pluginParameters->SetRequiredRecordFormatSupportL(*formatParameters); + + RArray ids; + CleanupClosePushL(ids); + User::LeaveIfError(ids.Append(KUidMediaTypeAudio)); + + pluginParameters->SetMediaIdsL(ids, + CMMFPluginSelectionParameters::EAllowOnlySuppliedMediaIds); + + RMMFControllerImplInfoArray controllers; + CleanupResetAndDestroyPushL(controllers); + + //Get all audio record controllers/formats that are supported + pluginParameters->ListImplementationsL(controllers); + + for (TInt index=0; indexRecordFormats(); + for (TInt j=0; jSupportedMimeTypes(); + const CDesC8Array& fileExtensions = recordFormats[j]->SupportedFileExtensions(); + TInt mimeCount = mimeTypes.Count(); + TInt fileExtCount = fileExtensions.Count(); + + if (mimeCount > 0 && fileExtCount > 0) { + TPtrC8 extension = fileExtensions[0]; + TPtrC8 mimeType = mimeTypes[0]; + QString type = QString::fromUtf8((char *)mimeType.Ptr(), mimeType.Length()); + + if (type != "audio/mp4" && type != "audio/basic") { + ControllerData data; + data.controllerUid = controllers[index]->Uid().iUid; + data.destinationFormatUid = recordFormats[j]->Uid().iUid; + data.destinationFormatDescription = QString::fromUtf16( + recordFormats[j]->DisplayName().Ptr(), + recordFormats[j]->DisplayName().Length()); + data.fileExtension = QString::fromUtf8((char *)extension.Ptr(), extension.Length()); + m_controllerIdMap[type] = data; + } + } + } + } + CleanupStack::PopAndDestroy(4);//controllers, ids, formatParameters, pluginParameters +} + +void S60AudioCaptureSession::retrieveSupportedAudioSampleRatesL() +{ + if (!m_recorderUtility || m_captureState != EInitializing) + return; + + m_supportedSampleRates.clear(); + + RArray supportedSampleRates; + CleanupClosePushL(supportedSampleRates); + m_recorderUtility->GetSupportedSampleRatesL(supportedSampleRates); + for (TInt j = 0; j < supportedSampleRates.Count(); j++ ) + m_supportedSampleRates.append(supportedSampleRates[j]); + + CleanupStack::PopAndDestroy(&supportedSampleRates); +} + +QList S60AudioCaptureSession::supportedAudioSampleRates(const QAudioEncoderSettings &settings) const +{ + QList supportedSampleRates; + + if (!settings.codec().isEmpty()) { + if (settings.codec() == "AMR") + supportedSampleRates.append(8000); + else + supportedSampleRates = m_supportedSampleRates; + }else + supportedSampleRates = m_supportedSampleRates; + + return supportedSampleRates; +} + +void S60AudioCaptureSession::populateAudioCodecsDataL() +{ + if (!m_recorderUtility) + return; + + if (m_controllerIdMap.contains("audio/amr")) { + CodecData data; + data.codecDescription = QString("GSM AMR Codec"); + m_audioCodeclist[QString("AMR")]=data; + } + if (m_controllerIdMap.contains("audio/basic")) { + CodecData data; + data.fourCC = KMMFFourCCCodeALAW; + data.codecDescription = QString("Sun/Next ""Au"" audio codec"); + m_audioCodeclist[QString("AULAW")]=data; + } + if (m_controllerIdMap.contains("audio/wav")) { + CodecData data; + data.fourCC = KMMFFourCCCodePCM16; + data.codecDescription = QString("Pulse code modulation"); + m_audioCodeclist[QString("PCM")]=data; + } + if (m_controllerIdMap.contains("audio/mp4")) { + CodecData data; + data.fourCC = KMMFFourCCCodeAAC; + data.codecDescription = QString("Advanced Audio Codec"); + m_audioCodeclist[QString("AAC")]=data; + } + + // default samplerates + m_supportedSampleRates << 96000 << 88200 << 64000 << 48000 << 44100 << 32000 << 24000 << 22050 << 16000 << 12000 << 11025 << 8000; + m_captureState = EInitialized; + if (m_controllerIdMap.contains("audio/wav") && m_captureState != EInitialized) { + TMdaFileClipLocation location; + location.iName = KAudioDummyFile(); + TMdaWavClipFormat format; + m_captureState = EInitializing; + m_recorderUtility->OpenL(&location, &format); + } +} + +void S60AudioCaptureSession::applyAudioSettingsL() +{ + if (!m_recorderUtility || m_format.codec() == "AMR") + return; + + TFourCC fourCC = m_audioCodeclist.value(m_format.codec()).fourCC; + + if (m_format.codec() == "PCM") + fourCC = determinePCMFormat(); + + RArray supportedDataTypes; + CleanupClosePushL(supportedDataTypes); + m_recorderUtility->GetSupportedDestinationDataTypesL(supportedDataTypes); + + for (TInt k = 0; k < supportedDataTypes.Count(); k++ ) { + if (supportedDataTypes[k].FourCC() == fourCC.FourCC()) { + m_recorderUtility->SetDestinationDataTypeL(supportedDataTypes[k]); + break; + } + } + CleanupStack::PopAndDestroy(&supportedDataTypes); + + RArray supportedSampleRates; + CleanupClosePushL(supportedSampleRates); + m_recorderUtility->GetSupportedSampleRatesL(supportedSampleRates); + for (TInt i = 0; i < supportedSampleRates.Count(); i++ ) { + TUint supportedSampleRate = supportedSampleRates[i]; + if (supportedSampleRate == m_format.frequency()) { + m_recorderUtility->SetDestinationSampleRateL(m_format.frequency()); + break; + } + } + CleanupStack::PopAndDestroy(&supportedSampleRates); + + RArray supportedChannels; + CleanupClosePushL(supportedChannels); + m_recorderUtility->GetSupportedNumberOfChannelsL(supportedChannels); + for (TInt l = 0; l < supportedChannels.Count(); l++ ) { + if (supportedChannels[l] == m_format.channels()) { + m_recorderUtility->SetDestinationNumberOfChannelsL(m_format.channels()); + break; + } + } + CleanupStack::PopAndDestroy(&supportedChannels); +} + +TFourCC S60AudioCaptureSession::determinePCMFormat() +{ + TFourCC fourCC; + + if (m_format.sampleSize() == 8) { + // 8 bit + switch (m_format.sampleType()) { + case QAudioFormat::SignedInt: { + fourCC.Set(KMMFFourCCCodePCM8); + break; + } + case QAudioFormat::UnSignedInt: { + fourCC.Set(KMMFFourCCCodePCMU8); + break; + } + case QAudioFormat::Float: + case QAudioFormat::Unknown: + default: { + fourCC.Set(KMMFFourCCCodePCM8); + break; + } + } + } else if (m_format.sampleSize() == 16) { + // 16 bit + switch (m_format.sampleType()) { + case QAudioFormat::SignedInt: { + fourCC.Set(m_format.byteOrder()==QAudioFormat::BigEndian? + KMMFFourCCCodePCM16B:KMMFFourCCCodePCM16); + break; + } + case QAudioFormat::UnSignedInt: { + fourCC.Set(m_format.byteOrder()==QAudioFormat::BigEndian? + KMMFFourCCCodePCMU16B:KMMFFourCCCodePCMU16); + break; + } + default: { + fourCC.Set(KMMFFourCCCodePCM16); + break; + } + } + } + return fourCC; +} + +QUrl S60AudioCaptureSession::generateAudioFilePath() +{ + QDir outputDir(QDir::rootPath()); + + int lastImage = 0; + int fileCount = 0; + foreach(QString fileName, outputDir.entryList(QStringList() << "testclip_*")) { + int imgNumber = fileName.mid(5, fileName.size()-9).toInt(); + lastImage = qMax(lastImage, imgNumber); + if (outputDir.exists(fileName)) + fileCount+=1; + } + lastImage+=fileCount; + QUrl location(QDir::toNativeSeparators(outputDir.canonicalPath()+QString("/testclip_%1").arg(lastImage+1,4,10,QLatin1Char('0')))); + return location; +} +