diff options
Diffstat (limited to 'src/multimedia/multimedia')
61 files changed, 16142 insertions, 0 deletions
diff --git a/src/multimedia/multimedia/audio/audio.pri b/src/multimedia/multimedia/audio/audio.pri new file mode 100644 index 0000000..ae28a26 --- /dev/null +++ b/src/multimedia/multimedia/audio/audio.pri @@ -0,0 +1,73 @@ +HEADERS += $$PWD/qaudio.h \ + $$PWD/qaudioformat.h \ + $$PWD/qaudioinput.h \ + $$PWD/qaudiooutput.h \ + $$PWD/qaudiodeviceinfo.h \ + $$PWD/qaudioengineplugin.h \ + $$PWD/qaudioengine.h \ + $$PWD/qaudiodevicefactory_p.h + + +SOURCES += $$PWD/qaudio.cpp \ + $$PWD/qaudioformat.cpp \ + $$PWD/qaudiodeviceinfo.cpp \ + $$PWD/qaudiooutput.cpp \ + $$PWD/qaudioinput.cpp \ + $$PWD/qaudioengineplugin.cpp \ + $$PWD/qaudioengine.cpp \ + $$PWD/qaudiodevicefactory.cpp + +contains(QT_CONFIG, audio-backend) { + +mac { + HEADERS += $$PWD/qaudioinput_mac_p.h \ + $$PWD/qaudiooutput_mac_p.h \ + $$PWD/qaudiodeviceinfo_mac_p.h \ + $$PWD/qaudio_mac_p.h + + SOURCES += $$PWD/qaudiodeviceinfo_mac_p.cpp \ + $$PWD/qaudiooutput_mac_p.cpp \ + $$PWD/qaudioinput_mac_p.cpp \ + $$PWD/qaudio_mac.cpp + + LIBS += -framework ApplicationServices -framework CoreAudio -framework AudioUnit -framework AudioToolbox + +} else:win32 { + + HEADERS += $$PWD/qaudioinput_win32_p.h $$PWD/qaudiooutput_win32_p.h $$PWD/qaudiodeviceinfo_win32_p.h + SOURCES += $$PWD/qaudiodeviceinfo_win32_p.cpp \ + $$PWD/qaudiooutput_win32_p.cpp \ + $$PWD/qaudioinput_win32_p.cpp + !wince*:LIBS += -lwinmm + wince*:LIBS += -lcoredll + +} else:symbian { + INCLUDEPATH += /epoc32/include/mmf/common + INCLUDEPATH += /epoc32/include/mmf/server + + HEADERS += $$PWD/qaudio_symbian_p.h \ + $$PWD/qaudiodeviceinfo_symbian_p.h \ + $$PWD/qaudioinput_symbian_p.h \ + $$PWD/qaudiooutput_symbian_p.h + + SOURCES += $$PWD/qaudio_symbian_p.cpp \ + $$PWD/qaudiodeviceinfo_symbian_p.cpp \ + $$PWD/qaudioinput_symbian_p.cpp \ + $$PWD/qaudiooutput_symbian_p.cpp + + LIBS += -lmmfdevsound +} else:unix { + unix:contains(QT_CONFIG, alsa) { + linux-*|freebsd-*|openbsd-*:{ + DEFINES += HAS_ALSA + HEADERS += $$PWD/qaudiooutput_alsa_p.h $$PWD/qaudioinput_alsa_p.h $$PWD/qaudiodeviceinfo_alsa_p.h + SOURCES += $$PWD/qaudiodeviceinfo_alsa_p.cpp \ + $$PWD/qaudiooutput_alsa_p.cpp \ + $$PWD/qaudioinput_alsa_p.cpp + LIBS_PRIVATE += -lasound + } + } +} +} else { + DEFINES += QT_NO_AUDIO_BACKEND +} diff --git a/src/multimedia/multimedia/audio/qaudio.cpp b/src/multimedia/multimedia/audio/qaudio.cpp new file mode 100644 index 0000000..e0f24ce --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudio.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 <QtMultimedia/qaudio.h> + + +QT_BEGIN_NAMESPACE + +namespace QAudio +{ + +class RegisterMetaTypes +{ +public: + RegisterMetaTypes() + { + qRegisterMetaType<QAudio::Error>(); + qRegisterMetaType<QAudio::State>(); + qRegisterMetaType<QAudio::Mode>(); + } + +} _register; + +} + +/*! + \namespace QAudio + \brief The QAudio namespace contains enums used by the audio classes. + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 +*/ + +/*! + \enum QAudio::Error + + \value NoError No errors have occurred + \value OpenError An error opening the audio device + \value IOError An error occurred during read/write of audio device + \value UnderrunError Audio data is not being fed to the audio device at a fast enough rate + \value FatalError A non-recoverable error has occurred, the audio device is not usable at this time. +*/ + +/*! + \enum QAudio::State + + \value ActiveState Audio data is being processed, this state is set after start() is called + and while audio data is available to be processed. + \value SuspendedState The audio device is in a suspended state, this state will only be entered + after suspend() is called. + \value StoppedState The audio device is closed, not processing any audio data + \value IdleState The QIODevice passed in has no data and audio system's buffer is empty, this state + is set after start() is called and while no audio data is available to be processed. +*/ + +/*! + \enum QAudio::Mode + + \value AudioOutput audio output device + \value AudioInput audio input device +*/ + + +QT_END_NAMESPACE + diff --git a/src/multimedia/multimedia/audio/qaudio.h b/src/multimedia/multimedia/audio/qaudio.h new file mode 100644 index 0000000..9ca1dff --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudio.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QAUDIO_H +#define QAUDIO_H + + +#include <QtCore/qglobal.h> +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +namespace QAudio +{ + enum Error { NoError, OpenError, IOError, UnderrunError, FatalError }; + enum State { ActiveState, SuspendedState, StoppedState, IdleState }; + enum Mode { AudioInput, AudioOutput }; +} + +QT_END_NAMESPACE + +QT_END_HEADER + +Q_DECLARE_METATYPE(QAudio::Error) +Q_DECLARE_METATYPE(QAudio::State) +Q_DECLARE_METATYPE(QAudio::Mode) + +#endif // QAUDIO_H diff --git a/src/multimedia/multimedia/audio/qaudio_mac.cpp b/src/multimedia/multimedia/audio/qaudio_mac.cpp new file mode 100644 index 0000000..14fee8b --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudio_mac.cpp @@ -0,0 +1,142 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 "qaudio_mac_p.h" + +QT_BEGIN_NAMESPACE + +// Debugging +QDebug operator<<(QDebug dbg, const QAudioFormat& audioFormat) +{ + dbg.nospace() << "QAudioFormat(" << + audioFormat.frequency() << "," << + audioFormat.channels() << "," << + audioFormat.sampleSize()<< "," << + audioFormat.codec() << "," << + audioFormat.byteOrder() << "," << + audioFormat.sampleType() << ")"; + + return dbg.space(); +} + + +// Conversion +QAudioFormat toQAudioFormat(AudioStreamBasicDescription const& sf) +{ + QAudioFormat audioFormat; + + audioFormat.setFrequency(sf.mSampleRate); + audioFormat.setChannels(sf.mChannelsPerFrame); + audioFormat.setSampleSize(sf.mBitsPerChannel); + audioFormat.setCodec(QString::fromLatin1("audio/pcm")); + audioFormat.setByteOrder(sf.mFormatFlags & kLinearPCMFormatFlagIsBigEndian != 0 ? QAudioFormat::BigEndian : QAudioFormat::LittleEndian); + QAudioFormat::SampleType type = QAudioFormat::UnSignedInt; + if ((sf.mFormatFlags & kLinearPCMFormatFlagIsSignedInteger) != 0) + type = QAudioFormat::SignedInt; + else if ((sf.mFormatFlags & kLinearPCMFormatFlagIsFloat) != 0) + type = QAudioFormat::Float; + audioFormat.setSampleType(type); + + return audioFormat; +} + +AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat) +{ + AudioStreamBasicDescription sf; + + sf.mFormatFlags = kAudioFormatFlagIsPacked; + sf.mSampleRate = audioFormat.frequency(); + sf.mFramesPerPacket = 1; + sf.mChannelsPerFrame = audioFormat.channels(); + sf.mBitsPerChannel = audioFormat.sampleSize(); + sf.mBytesPerFrame = sf.mChannelsPerFrame * (sf.mBitsPerChannel / 8); + sf.mBytesPerPacket = sf.mFramesPerPacket * sf.mBytesPerFrame; + sf.mFormatID = kAudioFormatLinearPCM; + + switch (audioFormat.sampleType()) { + case QAudioFormat::SignedInt: sf.mFormatFlags |= kAudioFormatFlagIsSignedInteger; break; + case QAudioFormat::UnSignedInt: /* default */ break; + case QAudioFormat::Float: sf.mFormatFlags |= kAudioFormatFlagIsFloat; break; + case QAudioFormat::Unknown: default: break; + } + + return sf; +} + +// QAudioRingBuffer +QAudioRingBuffer::QAudioRingBuffer(int bufferSize): + m_bufferSize(bufferSize) +{ + m_buffer = new char[m_bufferSize]; + reset(); +} + +QAudioRingBuffer::~QAudioRingBuffer() +{ + delete m_buffer; +} + +int QAudioRingBuffer::used() const +{ + return m_bufferUsed; +} + +int QAudioRingBuffer::free() const +{ + return m_bufferSize - m_bufferUsed; +} + +int QAudioRingBuffer::size() const +{ + return m_bufferSize; +} + +void QAudioRingBuffer::reset() +{ + m_readPos = 0; + m_writePos = 0; + m_bufferUsed = 0; +} + +QT_END_NAMESPACE + + diff --git a/src/multimedia/multimedia/audio/qaudio_mac_p.h b/src/multimedia/multimedia/audio/qaudio_mac_p.h new file mode 100644 index 0000000..4e7d688 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudio_mac_p.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIO_MAC_P_H +#define QAUDIO_MAC_P_H + +#include <CoreAudio/CoreAudio.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qatomic.h> + +#include <QtMultimedia/qaudioformat.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +extern QDebug operator<<(QDebug dbg, const QAudioFormat& audioFormat); + +extern QAudioFormat toQAudioFormat(const AudioStreamBasicDescription& streamFormat); +extern AudioStreamBasicDescription toAudioStreamBasicDescription(QAudioFormat const& audioFormat); + +class QAudioRingBuffer +{ +public: + typedef QPair<char*, int> Region; + + QAudioRingBuffer(int bufferSize); + ~QAudioRingBuffer(); + + Region acquireReadRegion(int size) + { + const int used = m_bufferUsed.fetchAndAddAcquire(0); + + if (used > 0) { + const int readSize = qMin(size, qMin(m_bufferSize - m_readPos, used)); + + return readSize > 0 ? Region(m_buffer + m_readPos, readSize) : Region(0, 0); + } + + return Region(0, 0); + } + + void releaseReadRegion(Region const& region) + { + m_readPos = (m_readPos + region.second) % m_bufferSize; + + m_bufferUsed.fetchAndAddRelease(-region.second); + } + + Region acquireWriteRegion(int size) + { + const int free = m_bufferSize - m_bufferUsed.fetchAndAddAcquire(0); + + if (free > 0) { + const int writeSize = qMin(size, qMin(m_bufferSize - m_writePos, free)); + + return writeSize > 0 ? Region(m_buffer + m_writePos, writeSize) : Region(0, 0); + } + + return Region(0, 0); + } + + void releaseWriteRegion(Region const& region) + { + m_writePos = (m_writePos + region.second) % m_bufferSize; + + m_bufferUsed.fetchAndAddRelease(region.second); + } + + int used() const; + int free() const; + int size() const; + + void reset(); + +private: + int m_bufferSize; + int m_readPos; + int m_writePos; + char* m_buffer; + QAtomicInt m_bufferUsed; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIO_MAC_P_H + + diff --git a/src/multimedia/multimedia/audio/qaudio_symbian_p.cpp b/src/multimedia/multimedia/audio/qaudio_symbian_p.cpp new file mode 100644 index 0000000..58e3745 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudio_symbian_p.cpp @@ -0,0 +1,395 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 "qaudio_symbian_p.h" +#include <mmffourcc.h> + +QT_BEGIN_NAMESPACE + +namespace SymbianAudio { + +DevSoundCapabilities::DevSoundCapabilities(CMMFDevSound &devsound, + QAudio::Mode mode) +{ + QT_TRAP_THROWING(constructL(devsound, mode)); +} + +DevSoundCapabilities::~DevSoundCapabilities() +{ + m_fourCC.Close(); +} + +void DevSoundCapabilities::constructL(CMMFDevSound &devsound, + QAudio::Mode mode) +{ + m_caps = devsound.Capabilities(); + + TMMFPrioritySettings settings; + + switch (mode) { + case QAudio::AudioOutput: + settings.iState = EMMFStatePlaying; + devsound.GetSupportedInputDataTypesL(m_fourCC, settings); + break; + + case QAudio::AudioInput: + settings.iState = EMMFStateRecording; + devsound.GetSupportedInputDataTypesL(m_fourCC, settings); + break; + + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode"); + } +} + +namespace Utils { + +//----------------------------------------------------------------------------- +// Static data +//----------------------------------------------------------------------------- + +// Sample rate / frequency + +typedef TMMFSampleRate SampleRateNative; +typedef int SampleRateQt; + +const int SampleRateCount = 12; + +const SampleRateNative SampleRateListNative[SampleRateCount] = { + EMMFSampleRate8000Hz + , EMMFSampleRate11025Hz + , EMMFSampleRate12000Hz + , EMMFSampleRate16000Hz + , EMMFSampleRate22050Hz + , EMMFSampleRate24000Hz + , EMMFSampleRate32000Hz + , EMMFSampleRate44100Hz + , EMMFSampleRate48000Hz + , EMMFSampleRate64000Hz + , EMMFSampleRate88200Hz + , EMMFSampleRate96000Hz +}; + +const SampleRateQt SampleRateListQt[SampleRateCount] = { + 8000 + , 11025 + , 12000 + , 16000 + , 22050 + , 24000 + , 32000 + , 44100 + , 48000 + , 64000 + , 88200 + , 96000 +}; + +// Channels + +typedef TMMFMonoStereo ChannelsNative; +typedef int ChannelsQt; + +const int ChannelsCount = 2; + +const ChannelsNative ChannelsListNative[ChannelsCount] = { + EMMFMono + , EMMFStereo +}; + +const ChannelsQt ChannelsListQt[ChannelsCount] = { + 1 + , 2 +}; + +// Encoding + +const int EncodingCount = 6; + +const TUint32 EncodingFourCC[EncodingCount] = { + KMMFFourCCCodePCM8 // 0 + , KMMFFourCCCodePCMU8 // 1 + , KMMFFourCCCodePCM16 // 2 + , KMMFFourCCCodePCMU16 // 3 + , KMMFFourCCCodePCM16B // 4 + , KMMFFourCCCodePCMU16B // 5 +}; + +// The characterised DevSound API specification states that the iEncoding +// field in TMMFCapabilities is ignored, and that the FourCC should be used +// to specify the PCM encoding. +// See "SGL.GT0287.102 Multimedia DevSound Baseline Compatibility.doc" in the +// mm_info/mm_docs repository. +const TMMFSoundEncoding EncodingNative[EncodingCount] = { + EMMFSoundEncoding16BitPCM // 0 + , EMMFSoundEncoding16BitPCM // 1 + , EMMFSoundEncoding16BitPCM // 2 + , EMMFSoundEncoding16BitPCM // 3 + , EMMFSoundEncoding16BitPCM // 4 + , EMMFSoundEncoding16BitPCM // 5 +}; + + +const int EncodingSampleSize[EncodingCount] = { + 8 // 0 + , 8 // 1 + , 16 // 2 + , 16 // 3 + , 16 // 4 + , 16 // 5 +}; + +const QAudioFormat::Endian EncodingByteOrder[EncodingCount] = { + QAudioFormat::LittleEndian // 0 + , QAudioFormat::LittleEndian // 1 + , QAudioFormat::LittleEndian // 2 + , QAudioFormat::LittleEndian // 3 + , QAudioFormat::BigEndian // 4 + , QAudioFormat::BigEndian // 5 +}; + +const QAudioFormat::SampleType EncodingSampleType[EncodingCount] = { + QAudioFormat::SignedInt // 0 + , QAudioFormat::UnSignedInt // 1 + , QAudioFormat::SignedInt // 2 + , QAudioFormat::UnSignedInt // 3 + , QAudioFormat::SignedInt // 4 + , QAudioFormat::UnSignedInt // 5 +}; + + +//----------------------------------------------------------------------------- +// Private functions +//----------------------------------------------------------------------------- + +// Helper functions for implementing parameter conversions + +template<typename Input> +bool findValue(const Input *inputArray, int length, Input input, int &index) { + bool result = false; + for (int i=0; !result && i<length; ++i) + if (inputArray[i] == input) { + index = i; + result = true; + } + return result; +} + +template<typename Input, typename Output> +bool convertValue(const Input *inputArray, const Output *outputArray, + int length, Input input, Output &output) { + int index; + const bool result = findValue<Input>(inputArray, length, input, index); + if (result) + output = outputArray[index]; + return result; +} + +/** + * Macro which is used to generate the implementation of the conversion + * functions. The implementation is just a wrapper around the templated + * convertValue function, e.g. + * + * CONVERSION_FUNCTION_IMPL(SampleRate, Qt, Native) + * + * expands to + * + * bool SampleRateQtToNative(int input, TMMFSampleRate &output) { + * return convertValue<SampleRateQt, SampleRateNative> + * (SampleRateListQt, SampleRateListNative, SampleRateCount, + * input, output); + * } + */ +#define CONVERSION_FUNCTION_IMPL(FieldLc, Field, Input, Output) \ +bool FieldLc##Input##To##Output(Field##Input input, Field##Output &output) { \ + return convertValue<Field##Input, Field##Output>(Field##List##Input, \ + Field##List##Output, Field##Count, input, output); \ +} + +//----------------------------------------------------------------------------- +// Local helper functions +//----------------------------------------------------------------------------- + +CONVERSION_FUNCTION_IMPL(sampleRate, SampleRate, Qt, Native) +CONVERSION_FUNCTION_IMPL(sampleRate, SampleRate, Native, Qt) +CONVERSION_FUNCTION_IMPL(channels, Channels, Qt, Native) +CONVERSION_FUNCTION_IMPL(channels, Channels, Native, Qt) + +bool sampleInfoQtToNative(int inputSampleSize, + QAudioFormat::Endian inputByteOrder, + QAudioFormat::SampleType inputSampleType, + TUint32 &outputFourCC, + TMMFSoundEncoding &outputEncoding) { + + bool found = false; + + for (int i=0; i<EncodingCount && !found; ++i) { + if ( EncodingSampleSize[i] == inputSampleSize + && EncodingByteOrder[i] == inputByteOrder + && EncodingSampleType[i] == inputSampleType) { + outputFourCC = EncodingFourCC[i]; + outputEncoding = EncodingNative[i]; // EMMFSoundEncoding16BitPCM + found = true; + } + } + + return found; +} + +//----------------------------------------------------------------------------- +// Public functions +//----------------------------------------------------------------------------- + +void capabilitiesNativeToQt(const DevSoundCapabilities &caps, + QList<int> &frequencies, + QList<int> &channels, + QList<int> &sampleSizes, + QList<QAudioFormat::Endian> &byteOrders, + QList<QAudioFormat::SampleType> &sampleTypes) { + + frequencies.clear(); + sampleSizes.clear(); + byteOrders.clear(); + sampleTypes.clear(); + channels.clear(); + + for (int i=0; i<SampleRateCount; ++i) + if (caps.caps().iRate & SampleRateListNative[i]) + frequencies += SampleRateListQt[i]; + + for (int i=0; i<ChannelsCount; ++i) + if (caps.caps().iChannels & ChannelsListNative[i]) + channels += ChannelsListQt[i]; + + for (int i=0; i<EncodingCount; ++i) { + if (caps.fourCC().Find(EncodingFourCC[i]) != KErrNotFound) { + sampleSizes += EncodingSampleSize[i]; + byteOrders += EncodingByteOrder[i]; + sampleTypes += EncodingSampleType[i]; + } + } + +} + +bool isFormatSupported(const QAudioFormat &formatQt, + const DevSoundCapabilities &caps) { + TMMFCapabilities formatNative; + TUint32 fourCC; + + bool result = false; + if (formatQt.codec() == "audio/pcm" && + formatQtToNative(formatQt, fourCC, formatNative)) { + result = + (formatNative.iRate & caps.caps().iRate) + && (formatNative.iChannels & caps.caps().iChannels) + && (caps.fourCC().Find(fourCC) != KErrNotFound); + } + return result; +} + +bool formatQtToNative(const QAudioFormat &inputFormat, + TUint32 &outputFourCC, + TMMFCapabilities &outputFormat) { + + bool result = false; + + // Need to use temporary variables because TMMFCapabilities fields are all + // TInt, rather than MMF enumerated types. + TMMFSampleRate outputSampleRate; + TMMFMonoStereo outputChannels; + TMMFSoundEncoding outputEncoding; + + if (inputFormat.codec() == "audio/pcm") { + result = + sampleRateQtToNative(inputFormat.frequency(), outputSampleRate) + && channelsQtToNative(inputFormat.channels(), outputChannels) + && sampleInfoQtToNative(inputFormat.sampleSize(), + inputFormat.byteOrder(), + inputFormat.sampleType(), + outputFourCC, + outputEncoding); + } + + if (result) { + outputFormat.iRate = outputSampleRate; + outputFormat.iChannels = outputChannels; + outputFormat.iEncoding = outputEncoding; + } + + return result; +} + +QAudio::State stateNativeToQt(State nativeState, + QAudio::State initializingState) +{ + switch (nativeState) { + case ClosedState: + return QAudio::StoppedState; + case InitializingState: + return initializingState; + case ActiveState: + return QAudio::ActiveState; + case IdleState: + return QAudio::IdleState; + case SuspendedState: + return QAudio::SuspendedState; + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid state"); + return QAudio::StoppedState; // suppress compiler warning + } +} + +qint64 bytesToSamples(const QAudioFormat &format, qint64 length) +{ + return length / ((format.sampleSize() / 8) * format.channels()); +} + +qint64 samplesToBytes(const QAudioFormat &format, qint64 samples) +{ + return samples * (format.sampleSize() / 8) * format.channels(); +} + +} // namespace Utils +} // namespace SymbianAudio + +QT_END_NAMESPACE + + diff --git a/src/multimedia/multimedia/audio/qaudio_symbian_p.h b/src/multimedia/multimedia/audio/qaudio_symbian_p.h new file mode 100644 index 0000000..d5238b4 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudio_symbian_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIO_SYMBIAN_P_H +#define QAUDIO_SYMBIAN_P_H + +#include <QtCore/qnamespace.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudio.h> +#include <sounddevice.h> + +QT_BEGIN_NAMESPACE + +namespace SymbianAudio { + +/** + * Default values used by audio input and output classes, when underlying + * DevSound instance has not yet been created. + */ + +const int DefaultBufferSize = 4096; // bytes +const int DefaultNotifyInterval = 1000; // ms + +/** + * Enumeration used to track state of internal DevSound instances. + * Values are translated to the corresponding QAudio::State values by + * SymbianAudio::Utils::stateNativeToQt. + */ +enum State { + ClosedState + , InitializingState + , ActiveState + , IdleState + , SuspendedState +}; + +/* + * Helper class for querying DevSound codec / format support + */ +class DevSoundCapabilities { +public: + DevSoundCapabilities(CMMFDevSound &devsound, QAudio::Mode mode); + ~DevSoundCapabilities(); + + const RArray<TFourCC>& fourCC() const { return m_fourCC; } + const TMMFCapabilities& caps() const { return m_caps; } + +private: + void constructL(CMMFDevSound &devsound, QAudio::Mode mode); + +private: + RArray<TFourCC> m_fourCC; + TMMFCapabilities m_caps; +}; + +namespace Utils { + +/** + * Convert native audio capabilities to QAudio lists. + */ +void capabilitiesNativeToQt(const DevSoundCapabilities &caps, + QList<int> &frequencies, + QList<int> &channels, + QList<int> &sampleSizes, + QList<QAudioFormat::Endian> &byteOrders, + QList<QAudioFormat::SampleType> &sampleTypes); + +/** + * Check whether format is supported. + */ +bool isFormatSupported(const QAudioFormat &format, + const DevSoundCapabilities &caps); + +/** + * Convert QAudioFormat to native format types. + * + * Note that, despite the name, DevSound uses TMMFCapabilities to specify + * single formats as well as capabilities. + * + * Note that this function does not modify outputFormat.iBufferSize. + */ +bool formatQtToNative(const QAudioFormat &inputFormat, + TUint32 &outputFourCC, + TMMFCapabilities &outputFormat); + +/** + * Convert internal states to QAudio states. + */ +QAudio::State stateNativeToQt(State nativeState, + QAudio::State initializingState); + +/** + * Convert data length to number of samples. + */ +qint64 bytesToSamples(const QAudioFormat &format, qint64 length); + +/** + * Convert number of samples to data length. + */ +qint64 samplesToBytes(const QAudioFormat &format, qint64 samples); + +} // namespace Utils +} // namespace SymbianAudio + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/audio/qaudiodevicefactory.cpp b/src/multimedia/multimedia/audio/qaudiodevicefactory.cpp new file mode 100644 index 0000000..4f45110 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodevicefactory.cpp @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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/qdebug.h> +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudioengineplugin.h> +#include <private/qfactoryloader_p.h> +#include "qaudiodevicefactory_p.h" + +#ifndef QT_NO_AUDIO_BACKEND +#if defined(Q_OS_WIN) +#include "qaudiodeviceinfo_win32_p.h" +#include "qaudiooutput_win32_p.h" +#include "qaudioinput_win32_p.h" +#elif defined(Q_OS_MAC) +#include "qaudiodeviceinfo_mac_p.h" +#include "qaudiooutput_mac_p.h" +#include "qaudioinput_mac_p.h" +#elif defined(HAS_ALSA) +#include "qaudiodeviceinfo_alsa_p.h" +#include "qaudiooutput_alsa_p.h" +#include "qaudioinput_alsa_p.h" +#elif defined(Q_OS_SYMBIAN) +#include "qaudiodeviceinfo_symbian_p.h" +#include "qaudiooutput_symbian_p.h" +#include "qaudioinput_symbian_p.h" +#endif +#endif + +QT_BEGIN_NAMESPACE + +Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader, + (QAudioEngineFactoryInterface_iid, QLatin1String("/audio"), Qt::CaseInsensitive)) + + +class QNullDeviceInfo : public QAbstractAudioDeviceInfo +{ +public: + QAudioFormat preferredFormat() const { qWarning()<<"using null deviceinfo, none available"; return QAudioFormat(); } + bool isFormatSupported(const QAudioFormat& ) const { return false; } + QAudioFormat nearestFormat(const QAudioFormat& ) const { return QAudioFormat(); } + QString deviceName() const { return QString(); } + QStringList codecList() { return QStringList(); } + QList<int> frequencyList() { return QList<int>(); } + QList<int> channelsList() { return QList<int>(); } + QList<int> sampleSizeList() { return QList<int>(); } + QList<QAudioFormat::Endian> byteOrderList() { return QList<QAudioFormat::Endian>(); } + QList<QAudioFormat::SampleType> sampleTypeList() { return QList<QAudioFormat::SampleType>(); } +}; + +class QNullInputDevice : public QAbstractAudioInput +{ +public: + QIODevice* start(QIODevice* ) { qWarning()<<"using null input device, none available"; return 0; } + void stop() {} + void reset() {} + void suspend() {} + void resume() {} + int bytesReady() const { return 0; } + int periodSize() const { return 0; } + void setBufferSize(int ) {} + int bufferSize() const { return 0; } + void setNotifyInterval(int ) {} + int notifyInterval() const { return 0; } + qint64 processedUSecs() const { return 0; } + qint64 elapsedUSecs() const { return 0; } + QAudio::Error error() const { return QAudio::OpenError; } + QAudio::State state() const { return QAudio::StoppedState; } + QAudioFormat format() const { return QAudioFormat(); } +}; + +class QNullOutputDevice : public QAbstractAudioOutput +{ +public: + QIODevice* start(QIODevice* ) { qWarning()<<"using null output device, none available"; return 0; } + void stop() {} + void reset() {} + void suspend() {} + void resume() {} + int bytesFree() const { return 0; } + int periodSize() const { return 0; } + void setBufferSize(int ) {} + int bufferSize() const { return 0; } + void setNotifyInterval(int ) {} + int notifyInterval() const { return 0; } + qint64 processedUSecs() const { return 0; } + qint64 elapsedUSecs() const { return 0; } + QAudio::Error error() const { return QAudio::OpenError; } + QAudio::State state() const { return QAudio::StoppedState; } + QAudioFormat format() const { return QAudioFormat(); } +}; + +QList<QAudioDeviceInfo> QAudioDeviceFactory::availableDevices(QAudio::Mode mode) +{ + QList<QAudioDeviceInfo> devices; +#ifndef QT_NO_AUDIO_BACKEND +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN)) + foreach (const QByteArray &handle, QAudioDeviceInfoInternal::availableDevices(mode)) + devices << QAudioDeviceInfo(QLatin1String("builtin"), handle, mode); +#endif +#endif + QFactoryLoader* l = loader(); + + foreach (QString const& key, l->keys()) { + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(l->instance(key)); + if (plugin) { + foreach (QByteArray const& handle, plugin->availableDevices(mode)) + devices << QAudioDeviceInfo(key, handle, mode); + } + + delete plugin; + } + + return devices; +} + +QAudioDeviceInfo QAudioDeviceFactory::defaultInputDevice() +{ + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(QLatin1String("default"))); + + if (plugin) { + QList<QByteArray> list = plugin->availableDevices(QAudio::AudioInput); + if (list.size() > 0) + return QAudioDeviceInfo(QLatin1String("default"), list.at(0), QAudio::AudioInput); + } +#ifndef QT_NO_AUDIO_BACKEND +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN)) + return QAudioDeviceInfo(QLatin1String("builtin"), QAudioDeviceInfoInternal::defaultInputDevice(), QAudio::AudioInput); +#endif +#endif + return QAudioDeviceInfo(); +} + +QAudioDeviceInfo QAudioDeviceFactory::defaultOutputDevice() +{ + QAudioEngineFactoryInterface* plugin = qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(QLatin1String("default"))); + + if (plugin) { + QList<QByteArray> list = plugin->availableDevices(QAudio::AudioOutput); + if (list.size() > 0) + return QAudioDeviceInfo(QLatin1String("default"), list.at(0), QAudio::AudioOutput); + } +#ifndef QT_NO_AUDIO_BACKEND +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN)) + return QAudioDeviceInfo(QLatin1String("builtin"), QAudioDeviceInfoInternal::defaultOutputDevice(), QAudio::AudioOutput); +#endif +#endif + return QAudioDeviceInfo(); +} + +QAbstractAudioDeviceInfo* QAudioDeviceFactory::audioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode) +{ + QAbstractAudioDeviceInfo *rc = 0; + +#ifndef QT_NO_AUDIO_BACKEND +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN)) + if (realm == QLatin1String("builtin")) + return new QAudioDeviceInfoInternal(handle, mode); +#endif +#endif + QAudioEngineFactoryInterface* plugin = + qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(realm)); + + if (plugin) + rc = plugin->createDeviceInfo(handle, mode); + + return rc == 0 ? new QNullDeviceInfo() : rc; +} + +QAbstractAudioInput* QAudioDeviceFactory::createDefaultInputDevice(QAudioFormat const &format) +{ + return createInputDevice(defaultInputDevice(), format); +} + +QAbstractAudioOutput* QAudioDeviceFactory::createDefaultOutputDevice(QAudioFormat const &format) +{ + return createOutputDevice(defaultOutputDevice(), format); +} + +QAbstractAudioInput* QAudioDeviceFactory::createInputDevice(QAudioDeviceInfo const& deviceInfo, QAudioFormat const &format) +{ + if (deviceInfo.isNull()) + return new QNullInputDevice(); +#ifndef QT_NO_AUDIO_BACKEND +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN)) + if (deviceInfo.realm() == QLatin1String("builtin")) + return new QAudioInputPrivate(deviceInfo.handle(), format); +#endif +#endif + QAudioEngineFactoryInterface* plugin = + qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(deviceInfo.realm())); + + if (plugin) + return plugin->createInput(deviceInfo.handle(), format); + + return new QNullInputDevice(); +} + +QAbstractAudioOutput* QAudioDeviceFactory::createOutputDevice(QAudioDeviceInfo const& deviceInfo, QAudioFormat const &format) +{ + if (deviceInfo.isNull()) + return new QNullOutputDevice(); +#ifndef QT_NO_AUDIO_BACKEND +#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN)) + if (deviceInfo.realm() == QLatin1String("builtin")) + return new QAudioOutputPrivate(deviceInfo.handle(), format); +#endif +#endif + QAudioEngineFactoryInterface* plugin = + qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(deviceInfo.realm())); + + if (plugin) + return plugin->createOutput(deviceInfo.handle(), format); + + return new QNullOutputDevice(); +} + +QT_END_NAMESPACE + diff --git a/src/multimedia/multimedia/audio/qaudiodevicefactory_p.h b/src/multimedia/multimedia/audio/qaudiodevicefactory_p.h new file mode 100644 index 0000000..8ee8b05 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodevicefactory_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIODEVICEFACTORY_P_H +#define QAUDIODEVICEFACTORY_P_H + +#include <QtCore/qglobal.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qlist.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class QAbstractAudioInput; +class QAbstractAudioOutput; +class QAbstractAudioDeviceInfo; + +class QAudioDeviceFactory +{ +public: + static QList<QAudioDeviceInfo> availableDevices(QAudio::Mode mode); + + static QAudioDeviceInfo defaultInputDevice(); + static QAudioDeviceInfo defaultOutputDevice(); + + static QAbstractAudioDeviceInfo* audioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode); + + static QAbstractAudioInput* createDefaultInputDevice(QAudioFormat const &format); + static QAbstractAudioOutput* createDefaultOutputDevice(QAudioFormat const &format); + + static QAbstractAudioInput* createInputDevice(QAudioDeviceInfo const &device, QAudioFormat const &format); + static QAbstractAudioOutput* createOutputDevice(QAudioDeviceInfo const &device, QAudioFormat const &format); + + static QAbstractAudioInput* createNullInput(); + static QAbstractAudioOutput* createNullOutput(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIODEVICEFACTORY_P_H + diff --git a/src/multimedia/multimedia/audio/qaudiodeviceinfo.cpp b/src/multimedia/multimedia/audio/qaudiodeviceinfo.cpp new file mode 100644 index 0000000..ff04b4e --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodeviceinfo.cpp @@ -0,0 +1,400 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 "qaudiodevicefactory_p.h" +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + + +QT_BEGIN_NAMESPACE + +class QAudioDeviceInfoPrivate : public QSharedData +{ +public: + QAudioDeviceInfoPrivate():info(0) {} + QAudioDeviceInfoPrivate(const QString &r, const QByteArray &h, QAudio::Mode m): + realm(r), handle(h), mode(m) + { + info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode); + } + + QAudioDeviceInfoPrivate(const QAudioDeviceInfoPrivate &other): + QSharedData(other), + realm(other.realm), handle(other.handle), mode(other.mode) + { + info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode); + } + + QAudioDeviceInfoPrivate& operator=(const QAudioDeviceInfoPrivate &other) + { + delete info; + + realm = other.realm; + handle = other.handle; + mode = other.mode; + info = QAudioDeviceFactory::audioDeviceInfo(realm, handle, mode); + return *this; + } + + ~QAudioDeviceInfoPrivate() + { + delete info; + } + + QString realm; + QByteArray handle; + QAudio::Mode mode; + QAbstractAudioDeviceInfo* info; +}; + + +/*! + \class QAudioDeviceInfo + \brief The QAudioDeviceInfo class provides an interface to query audio devices and their functionality. + \inmodule QtMultimedia + \ingroup multimedia + + \since 4.6 + + QAudioDeviceInfo lets you query for audio devices--such as sound + cards and USB headsets--that are currently available on the system. + The audio devices available are dependent on the platform or audio plugins installed. + + You can also query each device for the formats it supports. A + format in this context is a set consisting of a specific byte + order, channel, codec, frequency, sample rate, and sample type. A + format is represented by the QAudioFormat class. + + The values supported by the the device for each of these + parameters can be fetched with + supportedByteOrders(), supportedChannelCounts(), supportedCodecs(), + supportedSampleRates(), supportedSampleSizes(), and + supportedSampleTypes(). The combinations supported are dependent on the platform, + audio plugins installed and the audio device capabilities. If you need a specific format, you can check if + the device supports it with isFormatSupported(), or fetch a + supported format that is as close as possible to the format with + nearestFormat(). For instance: + + \snippet doc/src/snippets/audio/main.cpp 1 + \dots 8 + \snippet doc/src/snippets/audio/main.cpp 2 + + A QAudioDeviceInfo is used by Qt to construct + classes that communicate with the device--such as + QAudioInput, and QAudioOutput. The static + functions defaultInputDevice(), defaultOutputDevice(), and + availableDevices() let you get a list of all available + devices. Devices are fetch according to the value of mode + this is specified by the QAudio::Mode enum. + The QAudioDeviceInfo returned are only valid for the QAudio::Mode. + + For instance: + + \code + foreach(const QAudioDeviceInfo &deviceInfo, QAudioDeviceInfo::availableDevices(QAudio::AudioOutput)) + qDebug() << "Device name: " << deviceInfo.deviceName(); + \endcode + + In this code sample, we loop through all devices that are able to output + sound, i.e., play an audio stream in a supported format. For each device we + find, we simply print the deviceName(). + + \sa QAudioOutput, QAudioInput +*/ + +/*! + Constructs an empty QAudioDeviceInfo object. +*/ + +QAudioDeviceInfo::QAudioDeviceInfo(): + d(new QAudioDeviceInfoPrivate) +{ +} + +/*! + Constructs a copy of \a other. +*/ + +QAudioDeviceInfo::QAudioDeviceInfo(const QAudioDeviceInfo& other): + d(other.d) +{ +} + +/*! + Destroy this audio device info. +*/ + +QAudioDeviceInfo::~QAudioDeviceInfo() +{ +} + +/*! + Sets the QAudioDeviceInfo object to be equal to \a other. +*/ + +QAudioDeviceInfo& QAudioDeviceInfo::operator=(const QAudioDeviceInfo &other) +{ + d = other.d; + return *this; +} + +/*! + Returns whether this QAudioDeviceInfo object holds a device definition. +*/ + +bool QAudioDeviceInfo::isNull() const +{ + return d->info == 0; +} + +/*! + Returns human readable name of audio device. + + Device names vary depending on platform/audio plugin being used. + + They are a unique string identifiers for the audio device. + + eg. default, Intel, U0x46d0x9a4 +*/ + +QString QAudioDeviceInfo::deviceName() const +{ + return isNull() ? QString() : d->info->deviceName(); +} + +/*! + Returns true if \a settings are supported by the audio device of this QAudioDeviceInfo. +*/ + +bool QAudioDeviceInfo::isFormatSupported(const QAudioFormat &settings) const +{ + return isNull() ? false : d->info->isFormatSupported(settings); +} + +/*! + Returns QAudioFormat of default settings. + + These settings are provided by the platform/audio plugin being used. + + They also are dependent on the QAudio::Mode being used. + + A typical audio system would provide something like: + \list + \o Input settings: 8000Hz mono 8 bit. + \o Output settings: 44100Hz stereo 16 bit little endian. + \endlist +*/ + +QAudioFormat QAudioDeviceInfo::preferredFormat() const +{ + return isNull() ? QAudioFormat() : d->info->preferredFormat(); +} + +/*! + Returns closest QAudioFormat to \a settings that system audio supports. + + These settings are provided by the platform/audio plugin being used. + + They also are dependent on the QAudio::Mode being used. +*/ + +QAudioFormat QAudioDeviceInfo::nearestFormat(const QAudioFormat &settings) const +{ + return isNull() ? QAudioFormat() : d->info->nearestFormat(settings); +} + +/*! + Returns a list of supported codecs. + + All platform and plugin implementations should provide support for: + + "audio/pcm" - Linear PCM + + For writing plugins to support additional codecs refer to: + + http://www.iana.org/assignments/media-types/audio/ +*/ + +QStringList QAudioDeviceInfo::supportedCodecs() const +{ + return isNull() ? QStringList() : d->info->codecList(); +} + +/*! + Returns a list of supported sample rates. + + \since 4.7 +*/ + +QList<int> QAudioDeviceInfo::supportedSampleRates() const +{ + return supportedFrequencies(); +} + +/*! + \obsolete + + Use supportedSampleRates() instead. +*/ + +QList<int> QAudioDeviceInfo::supportedFrequencies() const +{ + return isNull() ? QList<int>() : d->info->frequencyList(); +} + +/*! + Returns a list of supported channel counts. + + \since 4.7 +*/ + +QList<int> QAudioDeviceInfo::supportedChannelCounts() const +{ + return supportedChannels(); +} + +/*! + \obsolete + + Use supportedChannelCount() instead. +*/ + +QList<int> QAudioDeviceInfo::supportedChannels() const +{ + return isNull() ? QList<int>() : d->info->channelsList(); +} + +/*! + Returns a list of supported sample sizes. +*/ + +QList<int> QAudioDeviceInfo::supportedSampleSizes() const +{ + return isNull() ? QList<int>() : d->info->sampleSizeList(); +} + +/*! + Returns a list of supported byte orders. +*/ + +QList<QAudioFormat::Endian> QAudioDeviceInfo::supportedByteOrders() const +{ + return isNull() ? QList<QAudioFormat::Endian>() : d->info->byteOrderList(); +} + +/*! + Returns a list of supported sample types. +*/ + +QList<QAudioFormat::SampleType> QAudioDeviceInfo::supportedSampleTypes() const +{ + return isNull() ? QList<QAudioFormat::SampleType>() : d->info->sampleTypeList(); +} + +/*! + Returns the name of the default input audio device. + All platform and audio plugin implementations provide a default audio device to use. +*/ + +QAudioDeviceInfo QAudioDeviceInfo::defaultInputDevice() +{ + return QAudioDeviceFactory::defaultInputDevice(); +} + +/*! + Returns the name of the default output audio device. + All platform and audio plugin implementations provide a default audio device to use. +*/ + +QAudioDeviceInfo QAudioDeviceInfo::defaultOutputDevice() +{ + return QAudioDeviceFactory::defaultOutputDevice(); +} + +/*! + Returns a list of audio devices that support \a mode. +*/ + +QList<QAudioDeviceInfo> QAudioDeviceInfo::availableDevices(QAudio::Mode mode) +{ + return QAudioDeviceFactory::availableDevices(mode); +} + + +/*! + \internal +*/ + +QAudioDeviceInfo::QAudioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode): + d(new QAudioDeviceInfoPrivate(realm, handle, mode)) +{ +} + +/*! + \internal +*/ + +QString QAudioDeviceInfo::realm() const +{ + return d->realm; +} + +/*! + \internal +*/ + +QByteArray QAudioDeviceInfo::handle() const +{ + return d->handle; +} + + +/*! + \internal +*/ + +QAudio::Mode QAudioDeviceInfo::mode() const +{ + return d->mode; +} + +QT_END_NAMESPACE + diff --git a/src/multimedia/multimedia/audio/qaudiodeviceinfo.h b/src/multimedia/multimedia/audio/qaudiodeviceinfo.h new file mode 100644 index 0000000..1cc0731 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodeviceinfo.h @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QAUDIODEVICEINFO_H +#define QAUDIODEVICEINFO_H + +#include <QtCore/qobject.h> +#include <QtCore/qglobal.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class QAudioDeviceFactory; + +class QAudioDeviceInfoPrivate; +class Q_MULTIMEDIA_EXPORT QAudioDeviceInfo +{ + friend class QAudioDeviceFactory; + +public: + QAudioDeviceInfo(); + QAudioDeviceInfo(const QAudioDeviceInfo& other); + ~QAudioDeviceInfo(); + + QAudioDeviceInfo& operator=(const QAudioDeviceInfo& other); + + bool isNull() const; + + QString deviceName() const; + + bool isFormatSupported(const QAudioFormat &format) const; + QAudioFormat preferredFormat() const; + QAudioFormat nearestFormat(const QAudioFormat &format) const; + + QStringList supportedCodecs() const; + QList<int> supportedFrequencies() const; + QList<int> supportedSampleRates() const; + QList<int> supportedChannels() const; + QList<int> supportedChannelCounts() const; + QList<int> supportedSampleSizes() const; + QList<QAudioFormat::Endian> supportedByteOrders() const; + QList<QAudioFormat::SampleType> supportedSampleTypes() const; + + static QAudioDeviceInfo defaultInputDevice(); + static QAudioDeviceInfo defaultOutputDevice(); + + static QList<QAudioDeviceInfo> availableDevices(QAudio::Mode mode); + +private: + QAudioDeviceInfo(const QString &realm, const QByteArray &handle, QAudio::Mode mode); + QString realm() const; + QByteArray handle() const; + QAudio::Mode mode() const; + + QSharedDataPointer<QAudioDeviceInfoPrivate> d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +Q_DECLARE_METATYPE(QAudioDeviceInfo) + +#endif // QAUDIODEVICEINFO_H diff --git a/src/multimedia/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp b/src/multimedia/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp new file mode 100644 index 0000000..36270a7 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodeviceinfo_alsa_p.cpp @@ -0,0 +1,486 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qaudiodeviceinfo_alsa_p.h" + +#include <alsa/version.h> + +QT_BEGIN_NAMESPACE + +QAudioDeviceInfoInternal::QAudioDeviceInfoInternal(QByteArray dev, QAudio::Mode mode) +{ + handle = 0; + + device = QLatin1String(dev); + this->mode = mode; +} + +QAudioDeviceInfoInternal::~QAudioDeviceInfoInternal() +{ + close(); +} + +bool QAudioDeviceInfoInternal::isFormatSupported(const QAudioFormat& format) const +{ + return testSettings(format); +} + +QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const +{ + QAudioFormat nearest; + if(mode == QAudio::AudioOutput) { + nearest.setFrequency(44100); + nearest.setChannels(2); + nearest.setByteOrder(QAudioFormat::LittleEndian); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(16); + nearest.setCodec(QLatin1String("audio/pcm")); + } else { + nearest.setFrequency(8000); + nearest.setChannels(1); + nearest.setSampleType(QAudioFormat::UnSignedInt); + nearest.setSampleSize(8); + nearest.setCodec(QLatin1String("audio/pcm")); + if(!testSettings(nearest)) { + nearest.setChannels(2); + nearest.setSampleSize(16); + nearest.setSampleType(QAudioFormat::SignedInt); + } + } + return nearest; +} + +QAudioFormat QAudioDeviceInfoInternal::nearestFormat(const QAudioFormat& format) const +{ + if(testSettings(format)) + return format; + else + return preferredFormat(); +} + +QString QAudioDeviceInfoInternal::deviceName() const +{ + return device; +} + +QStringList QAudioDeviceInfoInternal::codecList() +{ + updateLists(); + return codecz; +} + +QList<int> QAudioDeviceInfoInternal::frequencyList() +{ + updateLists(); + return freqz; +} + +QList<int> QAudioDeviceInfoInternal::channelsList() +{ + updateLists(); + return channelz; +} + +QList<int> QAudioDeviceInfoInternal::sampleSizeList() +{ + updateLists(); + return sizez; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::byteOrderList() +{ + updateLists(); + return byteOrderz; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::sampleTypeList() +{ + updateLists(); + return typez; +} + +bool QAudioDeviceInfoInternal::open() +{ + int err = 0; + QString dev = device; + QList<QByteArray> devices = availableDevices(mode); + + if(dev.compare(QLatin1String("default")) == 0) { +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + dev = QLatin1String(devices.first().constData()); +#else + dev = QLatin1String("hw:0,0"); +#endif + } else { +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + dev = device; +#else + int idx = 0; + char *name; + + QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1); + + while(snd_card_get_name(idx,&name) == 0) { + if(dev.contains(QLatin1String(name))) + break; + idx++; + } + dev = QString(QLatin1String("hw:%1,0")).arg(idx); +#endif + } + if(mode == QAudio::AudioOutput) { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); + } else { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); + } + if(err < 0) { + handle = 0; + return false; + } + return true; +} + +void QAudioDeviceInfoInternal::close() +{ + if(handle) + snd_pcm_close(handle); + handle = 0; +} + +bool QAudioDeviceInfoInternal::testSettings(const QAudioFormat& format) const +{ + // Set nearest to closest settings that do work. + // See if what is in settings will work (return value). + int err = 0; + snd_pcm_t* handle; + snd_pcm_hw_params_t *params; + QString dev = device; + + QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioOutput); + + if(dev.compare(QLatin1String("default")) == 0) { +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + dev = QLatin1String(devices.first().constData()); +#else + dev = QLatin1String("hw:0,0"); +#endif + } else { +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + dev = device; +#else + int idx = 0; + char *name; + + QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1); + + while(snd_card_get_name(idx,&name) == 0) { + if(shortName.compare(QLatin1String(name)) == 0) + break; + idx++; + } + dev = QString(QLatin1String("hw:%1,0")).arg(idx); +#endif + } + if(mode == QAudio::AudioOutput) { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); + } else { + err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); + } + if(err < 0) { + handle = 0; + return false; + } + + bool testChannel = false; + bool testCodec = false; + bool testFreq = false; + bool testType = false; + bool testSize = false; + + int dir = 0; + + snd_pcm_nonblock( handle, 0 ); + snd_pcm_hw_params_alloca( ¶ms ); + snd_pcm_hw_params_any( handle, params ); + + // set the values! + snd_pcm_hw_params_set_channels(handle,params,format.channels()); + snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir); + switch(format.sampleSize()) { + case 8: + if(format.sampleType() == QAudioFormat::SignedInt) + snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8); + else if(format.sampleType() == QAudioFormat::UnSignedInt) + snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8); + break; + case 16: + if(format.sampleType() == QAudioFormat::SignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE); + } else if(format.sampleType() == QAudioFormat::UnSignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE); + } + break; + case 32: + if(format.sampleType() == QAudioFormat::SignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE); + } else if(format.sampleType() == QAudioFormat::UnSignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE); + } + } + + // For now, just accept only audio/pcm codec + if(!format.codec().startsWith(QLatin1String("audio/pcm"))) { + err=-1; + } else + testCodec = true; + + if(err>=0 && format.channels() != -1) { + err = snd_pcm_hw_params_test_channels(handle,params,format.channels()); + if(err>=0) + err = snd_pcm_hw_params_set_channels(handle,params,format.channels()); + if(err>=0) + testChannel = true; + } + + if(err>=0 && format.frequency() != -1) { + err = snd_pcm_hw_params_test_rate(handle,params,format.frequency(),0); + if(err>=0) + err = snd_pcm_hw_params_set_rate(handle,params,format.frequency(),dir); + if(err>=0) + testFreq = true; + } + + if((err>=0 && format.sampleSize() != -1) && + (format.sampleType() != QAudioFormat::Unknown)) { + switch(format.sampleSize()) { + case 8: + if(format.sampleType() == QAudioFormat::SignedInt) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S8); + else if(format.sampleType() == QAudioFormat::UnSignedInt) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U8); + break; + case 16: + if(format.sampleType() == QAudioFormat::SignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S16_BE); + } else if(format.sampleType() == QAudioFormat::UnSignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U16_BE); + } + break; + case 32: + if(format.sampleType() == QAudioFormat::SignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_S32_BE); + } else if(format.sampleType() == QAudioFormat::UnSignedInt) { + if(format.byteOrder() == QAudioFormat::LittleEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_LE); + else if(format.byteOrder() == QAudioFormat::BigEndian) + err = snd_pcm_hw_params_set_format(handle,params,SND_PCM_FORMAT_U32_BE); + } + } + if(err>=0) { + testSize = true; + testType = true; + } + } + if(err>=0) + err = snd_pcm_hw_params(handle, params); + + if(err == 0) { + // settings work + // close() + if(handle) + snd_pcm_close(handle); + return true; + } + if(handle) + snd_pcm_close(handle); + + return false; +} + +void QAudioDeviceInfoInternal::updateLists() +{ + // redo all lists based on current settings + freqz.clear(); + channelz.clear(); + sizez.clear(); + byteOrderz.clear(); + typez.clear(); + codecz.clear(); + + if(!handle) + open(); + + if(!handle) + return; + + for(int i=0; i<(int)MAX_SAMPLE_RATES; i++) { + //if(snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0) + freqz.append(SAMPLE_RATES[i]); + } + channelz.append(1); + channelz.append(2); + sizez.append(8); + sizez.append(16); + sizez.append(32); + byteOrderz.append(QAudioFormat::LittleEndian); + byteOrderz.append(QAudioFormat::BigEndian); + typez.append(QAudioFormat::SignedInt); + typez.append(QAudioFormat::UnSignedInt); + typez.append(QAudioFormat::Float); + codecz.append(QLatin1String("audio/pcm")); + close(); +} + +QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode mode) +{ + QList<QByteArray> allDevices; + QList<QByteArray> devices; + QByteArray filter; + +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + // Create a list of all current audio devices that support mode + void **hints, **n; + char *name, *descr, *io; + + if(snd_device_name_hint(-1, "pcm", &hints) < 0) { + qWarning() << "no alsa devices available"; + return devices; + } + n = hints; + + if(mode == QAudio::AudioInput) { + filter = "Input"; + } else { + filter = "Output"; + } + + while (*n != NULL) { + name = snd_device_name_get_hint(*n, "NAME"); + descr = snd_device_name_get_hint(*n, "DESC"); + io = snd_device_name_get_hint(*n, "IOID"); + if((name != NULL) && (descr != NULL) && ((io == NULL) || (io == filter))) { + QString deviceName = QLatin1String(name); + QString deviceDescription = QLatin1String(descr); + allDevices.append(deviceName.toLocal8Bit().constData()); + if(deviceDescription.contains(QLatin1String("Default Audio Device"))) + devices.append(deviceName.toLocal8Bit().constData()); + } + if(name != NULL) + free(name); + if(descr != NULL) + free(descr); + if(io != NULL) + free(io); + ++n; + } + snd_device_name_free_hint(hints); + + if(devices.size() > 0) { + devices.append("default"); + } +#else + int idx = 0; + char* name; + + while(snd_card_get_name(idx,&name) == 0) { + devices.append(name); + idx++; + } + if (idx > 0) + devices.append("default"); +#endif + if (devices.size() == 0 && allDevices.size() > 0) + return allDevices; + + return devices; +} + +QByteArray QAudioDeviceInfoInternal::defaultInputDevice() +{ + QList<QByteArray> devices = availableDevices(QAudio::AudioInput); + if(devices.size() == 0) + return QByteArray(); + + return devices.first(); +} + +QByteArray QAudioDeviceInfoInternal::defaultOutputDevice() +{ + QList<QByteArray> devices = availableDevices(QAudio::AudioOutput); + if(devices.size() == 0) + return QByteArray(); + + return devices.first(); +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudiodeviceinfo_alsa_p.h b/src/multimedia/multimedia/audio/qaudiodeviceinfo_alsa_p.h new file mode 100644 index 0000000..6f9a459 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodeviceinfo_alsa_p.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIODEVICEINFOALSA_H +#define QAUDIODEVICEINFOALSA_H + +#include <alsa/asoundlib.h> + +#include <QtCore/qbytearray.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_NAMESPACE + +const unsigned int MAX_SAMPLE_RATES = 5; +const unsigned int SAMPLE_RATES[] = + { 8000, 11025, 22050, 44100, 48000 }; + +class QAudioDeviceInfoInternal : public QAbstractAudioDeviceInfo +{ + Q_OBJECT +public: + QAudioDeviceInfoInternal(QByteArray dev,QAudio::Mode mode); + ~QAudioDeviceInfoInternal(); + + bool testSettings(const QAudioFormat& format) const; + void updateLists(); + QAudioFormat preferredFormat() const; + bool isFormatSupported(const QAudioFormat& format) const; + QAudioFormat nearestFormat(const QAudioFormat& format) const; + QString deviceName() const; + QStringList codecList(); + QList<int> frequencyList(); + QList<int> channelsList(); + QList<int> sampleSizeList(); + QList<QAudioFormat::Endian> byteOrderList(); + QList<QAudioFormat::SampleType> sampleTypeList(); + static QByteArray defaultInputDevice(); + static QByteArray defaultOutputDevice(); + static QList<QByteArray> availableDevices(QAudio::Mode); + +private: + bool open(); + void close(); + + QString device; + QAudio::Mode mode; + QAudioFormat nearest; + QList<int> freqz; + QList<int> channelz; + QList<int> sizez; + QList<QAudioFormat::Endian> byteOrderz; + QStringList codecz; + QList<QAudioFormat::SampleType> typez; + snd_pcm_t* handle; + snd_pcm_hw_params_t *params; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/multimedia/multimedia/audio/qaudiodeviceinfo_mac_p.cpp b/src/multimedia/multimedia/audio/qaudiodeviceinfo_mac_p.cpp new file mode 100644 index 0000000..ecd03e5 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodeviceinfo_mac_p.cpp @@ -0,0 +1,357 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> +#include <QtCore/qbytearray.h> +#include <QtCore/qdatastream.h> +#include <QtCore/qdebug.h> +#include <private/qcore_mac_p.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> +#include "qaudio_mac_p.h" +#include "qaudiodeviceinfo_mac_p.h" + + + +QT_BEGIN_NAMESPACE + + +QAudioDeviceInfoInternal::QAudioDeviceInfoInternal(QByteArray const& handle, QAudio::Mode) +{ + QDataStream ds(handle); + quint32 did, tm; + + ds >> did >> tm >> name; + deviceId = AudioDeviceID(did); + mode = QAudio::Mode(tm); +} + +bool QAudioDeviceInfoInternal::isFormatSupported(const QAudioFormat& format) const +{ + return format.codec() == QString::fromLatin1("audio/pcm"); +} + +QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const +{ + QAudioFormat rc; + + UInt32 propSize = 0; + + if (AudioDeviceGetPropertyInfo(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreams, + &propSize, + 0) == noErr) { + + const int sc = propSize / sizeof(AudioStreamID); + + if (sc > 0) { + AudioStreamID* streams = new AudioStreamID[sc]; + + if (AudioDeviceGetProperty(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreams, + &propSize, + streams) == noErr) { + + for (int i = 0; i < sc; ++i) { + if (AudioStreamGetPropertyInfo(streams[i], + 0, + kAudioStreamPropertyPhysicalFormat, + &propSize, + 0) == noErr) { + + AudioStreamBasicDescription sf; + + if (AudioStreamGetProperty(streams[i], + 0, + kAudioStreamPropertyPhysicalFormat, + &propSize, + &sf) == noErr) { + rc = toQAudioFormat(sf); + break; + } + } + } + } + + delete streams; + } + } + + return rc; +} + +QAudioFormat QAudioDeviceInfoInternal::nearestFormat(const QAudioFormat& format) const +{ + QAudioFormat rc(format); + QAudioFormat target = preferredFormat(); + + if (!format.codec().isEmpty() && format.codec() != QString::fromLatin1("audio/pcm")) + return QAudioFormat(); + + rc.setCodec(QString::fromLatin1("audio/pcm")); + + if (rc.frequency() != target.frequency()) + rc.setFrequency(target.frequency()); + if (rc.channels() != target.channels()) + rc.setChannels(target.channels()); + if (rc.sampleSize() != target.sampleSize()) + rc.setSampleSize(target.sampleSize()); + if (rc.byteOrder() != target.byteOrder()) + rc.setByteOrder(target.byteOrder()); + if (rc.sampleType() != target.sampleType()) + rc.setSampleType(target.sampleType()); + + return rc; +} + +QString QAudioDeviceInfoInternal::deviceName() const +{ + return name; +} + +QStringList QAudioDeviceInfoInternal::codecList() +{ + return QStringList() << QString::fromLatin1("audio/pcm"); +} + +QList<int> QAudioDeviceInfoInternal::frequencyList() +{ + QSet<int> rc; + + // Add some common frequencies + rc << 8000 << 11025 << 22050 << 44100; + + // + UInt32 propSize = 0; + + if (AudioDeviceGetPropertyInfo(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyAvailableNominalSampleRates, + &propSize, + 0) == noErr) { + + const int pc = propSize / sizeof(AudioValueRange); + + if (pc > 0) { + AudioValueRange* vr = new AudioValueRange[pc]; + + if (AudioDeviceGetProperty(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyAvailableNominalSampleRates, + &propSize, + vr) == noErr) { + + for (int i = 0; i < pc; ++i) + rc << vr[i].mMaximum; + } + + delete vr; + } + } + + return rc.toList(); +} + +QList<int> QAudioDeviceInfoInternal::channelsList() +{ + QList<int> rc; + + // Can mix down to 1 channel + rc << 1; + + UInt32 propSize = 0; + int channels = 0; + + if (AudioDeviceGetPropertyInfo(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreamConfiguration, + &propSize, + 0) == noErr) { + + AudioBufferList* audioBufferList = static_cast<AudioBufferList*>(qMalloc(propSize)); + + if (audioBufferList != 0) { + if (AudioDeviceGetProperty(deviceId, + 0, + mode == QAudio::AudioInput, + kAudioDevicePropertyStreamConfiguration, + &propSize, + audioBufferList) == noErr) { + + for (int i = 0; i < int(audioBufferList->mNumberBuffers); ++i) { + channels += audioBufferList->mBuffers[i].mNumberChannels; + rc << channels; + } + } + + qFree(audioBufferList); + } + } + + return rc; +} + +QList<int> QAudioDeviceInfoInternal::sampleSizeList() +{ + return QList<int>() << 8 << 16 << 24 << 32 << 64; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::byteOrderList() +{ + return QList<QAudioFormat::Endian>() << QAudioFormat::LittleEndian << QAudioFormat::BigEndian; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::sampleTypeList() +{ + return QList<QAudioFormat::SampleType>() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float; +} + +static QByteArray get_device_info(AudioDeviceID audioDevice, QAudio::Mode mode) +{ + UInt32 size; + QByteArray device; + QDataStream ds(&device, QIODevice::WriteOnly); + AudioStreamBasicDescription sf; + CFStringRef name; + Boolean isInput = mode == QAudio::AudioInput; + + // Id + ds << quint32(audioDevice); + + // Mode + size = sizeof(AudioStreamBasicDescription); + if (AudioDeviceGetProperty(audioDevice, 0, isInput, kAudioDevicePropertyStreamFormat, + &size, &sf) != noErr) { + return QByteArray(); + } + ds << quint32(mode); + + // Name + size = sizeof(CFStringRef); + if (AudioDeviceGetProperty(audioDevice, 0, isInput, kAudioObjectPropertyName, + &size, &name) != noErr) { + qWarning() << "QAudioDeviceInfo: Unable to find device name"; + } + ds << QCFString::toQString(name); + + CFRelease(name); + + return device; +} + +QByteArray QAudioDeviceInfoInternal::defaultInputDevice() +{ + AudioDeviceID audioDevice; + UInt32 size = sizeof(audioDevice); + + if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultInputDevice, &size, + &audioDevice) != noErr) { + qWarning() << "QAudioDeviceInfo: Unable to find default input device"; + return QByteArray(); + } + + return get_device_info(audioDevice, QAudio::AudioInput); +} + +QByteArray QAudioDeviceInfoInternal::defaultOutputDevice() +{ + AudioDeviceID audioDevice; + UInt32 size = sizeof(audioDevice); + + if (AudioHardwareGetProperty(kAudioHardwarePropertyDefaultOutputDevice, &size, + &audioDevice) != noErr) { + qWarning() << "QAudioDeviceInfo: Unable to find default output device"; + return QByteArray(); + } + + return get_device_info(audioDevice, QAudio::AudioOutput); +} + +QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode mode) +{ + QList<QByteArray> devices; + + UInt32 propSize = 0; + + if (AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices, &propSize, 0) == noErr) { + + const int dc = propSize / sizeof(AudioDeviceID); + + if (dc > 0) { + AudioDeviceID* audioDevices = new AudioDeviceID[dc]; + + if (AudioHardwareGetProperty(kAudioHardwarePropertyDevices, &propSize, audioDevices) == noErr) { + for (int i = 0; i < dc; ++i) { + QByteArray info = get_device_info(audioDevices[i], mode); + if (!info.isNull()) + devices << info; + } + } + + delete audioDevices; + } + } + + return devices; +} + + +QT_END_NAMESPACE + diff --git a/src/multimedia/multimedia/audio/qaudiodeviceinfo_mac_p.h b/src/multimedia/multimedia/audio/qaudiodeviceinfo_mac_p.h new file mode 100644 index 0000000..e234384 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodeviceinfo_mac_p.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QDEVICEINFO_MAC_P_H +#define QDEVICEINFO_MAC_P_H + +#include <CoreAudio/CoreAudio.h> + +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QAudioDeviceInfoInternal : public QAbstractAudioDeviceInfo +{ +public: + AudioDeviceID deviceId; + QString name; + QAudio::Mode mode; + + QAudioDeviceInfoInternal(QByteArray const& handle, QAudio::Mode mode); + + bool isFormatSupported(const QAudioFormat& format) const; + QAudioFormat preferredFormat() const; + QAudioFormat nearestFormat(const QAudioFormat& format) const; + + QString deviceName() const; + + QStringList codecList(); + QList<int> frequencyList(); + QList<int> channelsList(); + QList<int> sampleSizeList(); + QList<QAudioFormat::Endian> byteOrderList(); + QList<QAudioFormat::SampleType> sampleTypeList(); + + static QByteArray defaultInputDevice(); + static QByteArray defaultOutputDevice(); + + static QList<QByteArray> availableDevices(QAudio::Mode mode); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QDEVICEINFO_MAC_P_H diff --git a/src/multimedia/multimedia/audio/qaudiodeviceinfo_symbian_p.cpp b/src/multimedia/multimedia/audio/qaudiodeviceinfo_symbian_p.cpp new file mode 100644 index 0000000..36284d3 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodeviceinfo_symbian_p.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 "qaudiodeviceinfo_symbian_p.h" +#include "qaudio_symbian_p.h" + +QT_BEGIN_NAMESPACE + +QAudioDeviceInfoInternal::QAudioDeviceInfoInternal(QByteArray device, + QAudio::Mode mode) + : m_deviceName(QLatin1String(device)) + , m_mode(mode) + , m_updated(false) +{ + QT_TRAP_THROWING(m_devsound.reset(CMMFDevSound::NewL())); +} + +QAudioDeviceInfoInternal::~QAudioDeviceInfoInternal() +{ + +} + +QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const +{ + QAudioFormat format; + switch (m_mode) { + case QAudio::AudioOutput: + format.setFrequency(44100); + format.setChannels(2); + format.setSampleSize(16); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setCodec(QLatin1String("audio/pcm")); + break; + + case QAudio::AudioInput: + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(16); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setCodec(QLatin1String("audio/pcm")); + break; + + default: + Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode"); + } + + if (!isFormatSupported(format)) { + if (m_frequencies.size()) + format.setFrequency(m_frequencies[0]); + if (m_channels.size()) + format.setChannels(m_channels[0]); + if (m_sampleSizes.size()) + format.setSampleSize(m_sampleSizes[0]); + if (m_byteOrders.size()) + format.setByteOrder(m_byteOrders[0]); + if (m_sampleTypes.size()) + format.setSampleType(m_sampleTypes[0]); + } + + return format; +} + +bool QAudioDeviceInfoInternal::isFormatSupported( + const QAudioFormat &format) const +{ + getSupportedFormats(); + const bool supported = + m_codecs.contains(format.codec()) + && m_frequencies.contains(format.frequency()) + && m_channels.contains(format.channels()) + && m_sampleSizes.contains(format.sampleSize()) + && m_byteOrders.contains(format.byteOrder()) + && m_sampleTypes.contains(format.sampleType()); + + return supported; +} + +QAudioFormat QAudioDeviceInfoInternal::nearestFormat(const QAudioFormat &format) const +{ + if (isFormatSupported(format)) + return format; + else + return preferredFormat(); +} + +QString QAudioDeviceInfoInternal::deviceName() const +{ + return m_deviceName; +} + +QStringList QAudioDeviceInfoInternal::codecList() +{ + getSupportedFormats(); + return m_codecs; +} + +QList<int> QAudioDeviceInfoInternal::frequencyList() +{ + getSupportedFormats(); + return m_frequencies; +} + +QList<int> QAudioDeviceInfoInternal::channelsList() +{ + getSupportedFormats(); + return m_channels; +} + +QList<int> QAudioDeviceInfoInternal::sampleSizeList() +{ + getSupportedFormats(); + return m_sampleSizes; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::byteOrderList() +{ + getSupportedFormats(); + return m_byteOrders; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::sampleTypeList() +{ + getSupportedFormats(); + return m_sampleTypes; +} + +QByteArray QAudioDeviceInfoInternal::defaultInputDevice() +{ + return QByteArray("default"); +} + +QByteArray QAudioDeviceInfoInternal::defaultOutputDevice() +{ + return QByteArray("default"); +} + +QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode) +{ + QList<QByteArray> result; + result += QByteArray("default"); + return result; +} + +void QAudioDeviceInfoInternal::getSupportedFormats() const +{ + if (!m_updated) { + QScopedPointer<SymbianAudio::DevSoundCapabilities> caps( + new SymbianAudio::DevSoundCapabilities(*m_devsound, m_mode)); + + SymbianAudio::Utils::capabilitiesNativeToQt(*caps, + m_frequencies, m_channels, m_sampleSizes, + m_byteOrders, m_sampleTypes); + + m_codecs.append(QLatin1String("audio/pcm")); + + m_updated = true; + } +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudiodeviceinfo_symbian_p.h b/src/multimedia/multimedia/audio/qaudiodeviceinfo_symbian_p.h new file mode 100644 index 0000000..89e539f --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodeviceinfo_symbian_p.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIODEVICEINFO_SYMBIAN_P_H +#define QAUDIODEVICEINFO_SYMBIAN_P_H + +#include <QtMultimedia/qaudioengine.h> +#include <sounddevice.h> + +QT_BEGIN_NAMESPACE + +class QAudioDeviceInfoInternal + : public QAbstractAudioDeviceInfo +{ + Q_OBJECT + +public: + QAudioDeviceInfoInternal(QByteArray device, QAudio::Mode mode); + ~QAudioDeviceInfoInternal(); + + // QAbstractAudioDeviceInfo + QAudioFormat preferredFormat() const; + bool isFormatSupported(const QAudioFormat &format) const; + QAudioFormat nearestFormat(const QAudioFormat &format) const; + QString deviceName() const; + QStringList codecList(); + QList<int> frequencyList(); + QList<int> channelsList(); + QList<int> sampleSizeList(); + QList<QAudioFormat::Endian> byteOrderList(); + QList<QAudioFormat::SampleType> sampleTypeList(); + static QByteArray defaultInputDevice(); + static QByteArray defaultOutputDevice(); + static QList<QByteArray> availableDevices(QAudio::Mode); + +private: + void getSupportedFormats() const; + +private: + QScopedPointer<CMMFDevSound> m_devsound; + + QString m_deviceName; + QAudio::Mode m_mode; + + // Mutable to allow lazy initialization when called from const-qualified + // public functions (isFormatSupported, nearestFormat) + mutable bool m_updated; + mutable QStringList m_codecs; + mutable QList<int> m_frequencies; + mutable QList<int> m_channels; + mutable QList<int> m_sampleSizes; + mutable QList<QAudioFormat::Endian> m_byteOrders; + mutable QList<QAudioFormat::SampleType> m_sampleTypes; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/audio/qaudiodeviceinfo_win32_p.cpp b/src/multimedia/multimedia/audio/qaudiodeviceinfo_win32_p.cpp new file mode 100644 index 0000000..aee0807 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodeviceinfo_win32_p.cpp @@ -0,0 +1,433 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include <windows.h> +#include <mmsystem.h> +#include "qaudiodeviceinfo_win32_p.h" + +QT_BEGIN_NAMESPACE + +// For mingw toolchain mmsystem.h only defines half the defines, so add if needed. +#ifndef WAVE_FORMAT_44M08 +#define WAVE_FORMAT_44M08 0x00000100 +#define WAVE_FORMAT_44S08 0x00000200 +#define WAVE_FORMAT_44M16 0x00000400 +#define WAVE_FORMAT_44S16 0x00000800 +#define WAVE_FORMAT_48M08 0x00001000 +#define WAVE_FORMAT_48S08 0x00002000 +#define WAVE_FORMAT_48M16 0x00004000 +#define WAVE_FORMAT_48S16 0x00008000 +#define WAVE_FORMAT_96M08 0x00010000 +#define WAVE_FORMAT_96S08 0x00020000 +#define WAVE_FORMAT_96M16 0x00040000 +#define WAVE_FORMAT_96S16 0x00080000 +#endif + + +QAudioDeviceInfoInternal::QAudioDeviceInfoInternal(QByteArray dev, QAudio::Mode mode) +{ + device = QLatin1String(dev); + this->mode = mode; + + updateLists(); +} + +QAudioDeviceInfoInternal::~QAudioDeviceInfoInternal() +{ + close(); +} + +bool QAudioDeviceInfoInternal::isFormatSupported(const QAudioFormat& format) const +{ + return testSettings(format); +} + +QAudioFormat QAudioDeviceInfoInternal::preferredFormat() const +{ + QAudioFormat nearest; + if(mode == QAudio::AudioOutput) { + nearest.setFrequency(44100); + nearest.setChannelCount(2); + nearest.setByteOrder(QAudioFormat::LittleEndian); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(16); + nearest.setCodec(QLatin1String("audio/pcm")); + } else { + nearest.setFrequency(11025); + nearest.setChannelCount(1); + nearest.setByteOrder(QAudioFormat::LittleEndian); + nearest.setSampleType(QAudioFormat::SignedInt); + nearest.setSampleSize(8); + nearest.setCodec(QLatin1String("audio/pcm")); + } + return nearest; +} + +QAudioFormat QAudioDeviceInfoInternal::nearestFormat(const QAudioFormat& format) const +{ + if(testSettings(format)) + return format; + else + return preferredFormat(); +} + +QString QAudioDeviceInfoInternal::deviceName() const +{ + return device; +} + +QStringList QAudioDeviceInfoInternal::codecList() +{ + updateLists(); + return codecz; +} + +QList<int> QAudioDeviceInfoInternal::frequencyList() +{ + updateLists(); + return freqz; +} + +QList<int> QAudioDeviceInfoInternal::channelsList() +{ + updateLists(); + return channelz; +} + +QList<int> QAudioDeviceInfoInternal::sampleSizeList() +{ + updateLists(); + return sizez; +} + +QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::byteOrderList() +{ + updateLists(); + return byteOrderz; +} + +QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::sampleTypeList() +{ + updateLists(); + return typez; +} + + +bool QAudioDeviceInfoInternal::open() +{ + return true; +} + +void QAudioDeviceInfoInternal::close() +{ +} + +bool QAudioDeviceInfoInternal::testSettings(const QAudioFormat& format) const +{ + // Set nearest to closest settings that do work. + // See if what is in settings will work (return value). + + bool failed = false; + bool match = false; + + // check codec + for( int i = 0; i < codecz.count(); i++) { + if (format.codec() == codecz.at(i)) + match = true; + } + if (!match) failed = true; + + // check channel + match = false; + if (!failed) { + for( int i = 0; i < channelz.count(); i++) { + if (format.channels() == channelz.at(i)) { + match = true; + break; + } + } + } + if (!match) failed = true; + + // check frequency + match = false; + if (!failed) { + for( int i = 0; i < freqz.count(); i++) { + if (format.frequency() == freqz.at(i)) { + match = true; + break; + } + } + } + + // check sample size + match = false; + if (!failed) { + for( int i = 0; i < sizez.count(); i++) { + if (format.sampleSize() == sizez.at(i)) { + match = true; + break; + } + } + } + + // check byte order + match = false; + if (!failed) { + for( int i = 0; i < byteOrderz.count(); i++) { + if (format.byteOrder() == byteOrderz.at(i)) { + match = true; + break; + } + } + } + + // check sample type + match = false; + if (!failed) { + for( int i = 0; i < typez.count(); i++) { + if (format.sampleType() == typez.at(i)) { + match = true; + break; + } + } + } + + if(!failed) { + // settings work + return true; + } + return false; +} + +void QAudioDeviceInfoInternal::updateLists() +{ + // redo all lists based on current settings + bool base = false; + bool match = false; + DWORD fmt = NULL; + QString tmp; + + if(device.compare(QLatin1String("default")) == 0) + base = true; + + if(mode == QAudio::AudioOutput) { + WAVEOUTCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveOutGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveOutGetDevCaps(i, &woc, sizeof(WAVEOUTCAPS)) + == MMSYSERR_NOERROR) { + tmp = QString((const QChar *)woc.szPname); + if(tmp.compare(device) == 0) { + match = true; + fmt = woc.dwFormats; + break; + } + if(base) { + match = true; + fmt = woc.dwFormats; + break; + } + } + } + } else { + WAVEINCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveInGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveInGetDevCaps(i, &woc, sizeof(WAVEINCAPS)) + == MMSYSERR_NOERROR) { + tmp = QString((const QChar *)woc.szPname); + if(tmp.compare(device) == 0) { + match = true; + fmt = woc.dwFormats; + break; + } + if(base) { + match = true; + fmt = woc.dwFormats; + break; + } + } + } + } + sizez.clear(); + freqz.clear(); + channelz.clear(); + byteOrderz.clear(); + typez.clear(); + codecz.clear(); + + if(match) { + if((fmt && WAVE_FORMAT_1M08) + || (fmt && WAVE_FORMAT_1S08) + || (fmt && WAVE_FORMAT_2M08) + || (fmt && WAVE_FORMAT_2S08) + || (fmt && WAVE_FORMAT_4M08) + || (fmt && WAVE_FORMAT_4S08) +#ifndef Q_OS_WINCE + || (fmt && WAVE_FORMAT_48M08) + || (fmt && WAVE_FORMAT_48S08) + || (fmt && WAVE_FORMAT_96M08) + || (fmt && WAVE_FORMAT_96S08) +#endif + ) { + sizez.append(8); + } + if((fmt && WAVE_FORMAT_1M16) + || (fmt && WAVE_FORMAT_1S16) + || (fmt && WAVE_FORMAT_2M16) + || (fmt && WAVE_FORMAT_2S16) + || (fmt && WAVE_FORMAT_4M16) + || (fmt && WAVE_FORMAT_4S16) +#ifndef Q_OS_WINCE + || (fmt && WAVE_FORMAT_48M16) + || (fmt && WAVE_FORMAT_48S16) + || (fmt && WAVE_FORMAT_96M16) + || (fmt && WAVE_FORMAT_96S16) +#endif + ) { + sizez.append(16); + } + if((fmt && WAVE_FORMAT_1M08) + || (fmt && WAVE_FORMAT_1S08) + || (fmt && WAVE_FORMAT_1M16) + || (fmt && WAVE_FORMAT_1S16)) { + freqz.append(11025); + } + if((fmt && WAVE_FORMAT_2M08) + || (fmt && WAVE_FORMAT_2S08) + || (fmt && WAVE_FORMAT_2M16) + || (fmt && WAVE_FORMAT_2S16)) { + freqz.append(22050); + } + if((fmt && WAVE_FORMAT_4M08) + || (fmt && WAVE_FORMAT_4S08) + || (fmt && WAVE_FORMAT_4M16) + || (fmt && WAVE_FORMAT_4S16)) { + freqz.append(44100); + } +#ifndef Q_OS_WINCE + if((fmt && WAVE_FORMAT_48M08) + || (fmt && WAVE_FORMAT_48S08) + || (fmt && WAVE_FORMAT_48M16) + || (fmt && WAVE_FORMAT_48S16)) { + freqz.append(48000); + } + if((fmt && WAVE_FORMAT_96M08) + || (fmt && WAVE_FORMAT_96S08) + || (fmt && WAVE_FORMAT_96M16) + || (fmt && WAVE_FORMAT_96S16)) { + freqz.append(96000); + } +#endif + channelz.append(1); + channelz.append(2); + + byteOrderz.append(QAudioFormat::LittleEndian); + + typez.append(QAudioFormat::SignedInt); + typez.append(QAudioFormat::UnSignedInt); + + codecz.append(QLatin1String("audio/pcm")); + } + if (freqz.count() > 0) + freqz.prepend(8000); +} + +QList<QByteArray> QAudioDeviceInfoInternal::availableDevices(QAudio::Mode mode) +{ + Q_UNUSED(mode) + + QList<QByteArray> devices; + + if(mode == QAudio::AudioOutput) { + WAVEOUTCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveOutGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveOutGetDevCaps(i, &woc, sizeof(WAVEOUTCAPS)) + == MMSYSERR_NOERROR) { + devices.append(QString((const QChar *)woc.szPname).toLocal8Bit().constData()); + } + } + } else { + WAVEINCAPS woc; + unsigned long iNumDevs,i; + iNumDevs = waveInGetNumDevs(); + for(i=0;i<iNumDevs;i++) { + if(waveInGetDevCaps(i, &woc, sizeof(WAVEINCAPS)) + == MMSYSERR_NOERROR) { + devices.append(QString((const QChar *)woc.szPname).toLocal8Bit().constData()); + } + } + + } + if(devices.count() > 0) + devices.append("default"); + + return devices; +} + +QByteArray QAudioDeviceInfoInternal::defaultOutputDevice() +{ + return QByteArray("default"); +} + +QByteArray QAudioDeviceInfoInternal::defaultInputDevice() +{ + return QByteArray("default"); +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudiodeviceinfo_win32_p.h b/src/multimedia/multimedia/audio/qaudiodeviceinfo_win32_p.h new file mode 100644 index 0000000..cb6dd91 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiodeviceinfo_win32_p.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIODEVICEINFOWIN_H +#define QAUDIODEVICEINFOWIN_H + +#include <QtCore/qbytearray.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qlist.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +QT_BEGIN_NAMESPACE + +const unsigned int MAX_SAMPLE_RATES = 5; +const unsigned int SAMPLE_RATES[] = { 8000, 11025, 22050, 44100, 48000 }; + +class QAudioDeviceInfoInternal : public QAbstractAudioDeviceInfo +{ + Q_OBJECT + +public: + QAudioDeviceInfoInternal(QByteArray dev,QAudio::Mode mode); + ~QAudioDeviceInfoInternal(); + + bool open(); + void close(); + + bool testSettings(const QAudioFormat& format) const; + void updateLists(); + QAudioFormat preferredFormat() const; + bool isFormatSupported(const QAudioFormat& format) const; + QAudioFormat nearestFormat(const QAudioFormat& format) const; + QString deviceName() const; + QStringList codecList(); + QList<int> frequencyList(); + QList<int> channelsList(); + QList<int> sampleSizeList(); + QList<QAudioFormat::Endian> byteOrderList(); + QList<QAudioFormat::SampleType> sampleTypeList(); + static QByteArray defaultInputDevice(); + static QByteArray defaultOutputDevice(); + static QList<QByteArray> availableDevices(QAudio::Mode); + +private: + QAudio::Mode mode; + QString device; + QAudioFormat nearest; + QList<int> freqz; + QList<int> channelz; + QList<int> sizez; + QList<QAudioFormat::Endian> byteOrderz; + QStringList codecz; + QList<QAudioFormat::SampleType> typez; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/audio/qaudioengine.cpp b/src/multimedia/multimedia/audio/qaudioengine.cpp new file mode 100644 index 0000000..7f1f5d3 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioengine.cpp @@ -0,0 +1,343 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 <QtMultimedia/qaudioengine.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractAudioDeviceInfo + \brief The QAbstractAudioDeviceInfo class provides access for QAudioDeviceInfo to access the audio + device provided by the plugin. + \internal + + \ingroup multimedia + + This class implements the audio functionality for + QAudioDeviceInfo, i.e., QAudioDeviceInfo keeps a + QAbstractAudioDeviceInfo and routes function calls to it. For a + description of the functionality that QAbstractAudioDeviceInfo + implements, you can read the class and functions documentation of + QAudioDeviceInfo. + + \sa QAudioDeviceInfo +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioDeviceInfo::preferredFormat() const + Returns the nearest settings. +*/ + +/*! + \fn virtual bool QAbstractAudioDeviceInfo::isFormatSupported(const QAudioFormat& format) const + Returns true if \a format is available from audio device. +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioDeviceInfo::nearestFormat(const QAudioFormat& format) const + Returns the nearest settings \a format. +*/ + +/*! + \fn virtual QString QAbstractAudioDeviceInfo::deviceName() const + Returns the audio device name. +*/ + +/*! + \fn virtual QStringList QAbstractAudioDeviceInfo::codecList() + Returns the list of currently available codecs. +*/ + +/*! + \fn virtual QList<int> QAbstractAudioDeviceInfo::frequencyList() + Returns the list of currently available frequencies. +*/ + +/*! + \fn virtual QList<int> QAbstractAudioDeviceInfo::channelsList() + Returns the list of currently available channels. +*/ + +/*! + \fn virtual QList<int> QAbstractAudioDeviceInfo::sampleSizeList() + Returns the list of currently available sample sizes. +*/ + +/*! + \fn virtual QList<QAudioFormat::Endian> QAbstractAudioDeviceInfo::byteOrderList() + Returns the list of currently available byte orders. +*/ + +/*! + \fn virtual QList<QAudioFormat::SampleType> QAbstractAudioDeviceInfo::sampleTypeList() + Returns the list of currently available sample types. +*/ + +/*! + \class QAbstractAudioOutput + \brief The QAbstractAudioOutput class provides access for QAudioOutput to access the audio + device provided by the plugin. + \internal + + \ingroup multimedia + + QAbstractAudioOutput implements audio functionality for + QAudioOutput, i.e., QAudioOutput routes function calls to + QAbstractAudioOutput. For a description of the functionality that + is implemented, see the QAudioOutput class and function + descriptions. + + \sa QAudioOutput +*/ + +/*! + \fn virtual QIODevice* QAbstractAudioOutput::start(QIODevice* device) + Uses the \a device as the QIODevice to transfer data. If \a device is null then the class + creates an internal QIODevice. Returns a pointer to the QIODevice being used to handle + the data transfer. This QIODevice can be used to write() audio data directly. Passing a + QIODevice allows the data to be transfered without any extra code. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::stop() + Stops the audio output. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::reset() + Drops all audio data in the buffers, resets buffers to zero. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::suspend() + Stops processing audio data, preserving buffered audio data. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::resume() + Resumes processing audio data after a suspend() +*/ + +/*! + \fn virtual int QAbstractAudioOutput::bytesFree() const + Returns the free space available in bytes in the audio buffer. +*/ + +/*! + \fn virtual int QAbstractAudioOutput::periodSize() const + Returns the period size in bytes. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::setBufferSize(int value) + Sets the audio buffer size to \a value in bytes. +*/ + +/*! + \fn virtual int QAbstractAudioOutput::bufferSize() const + Returns the audio buffer size in bytes. +*/ + +/*! + \fn virtual void QAbstractAudioOutput::setNotifyInterval(int ms) + Sets the interval for notify() signal to be emitted. This is based on the \a ms + of audio data processed not on actual real-time. The resolution of the timer + is platform specific. +*/ + +/*! + \fn virtual int QAbstractAudioOutput::notifyInterval() const + Returns the notify interval in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioOutput::processedUSecs() const + Returns the amount of audio data processed since start() was called in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioOutput::elapsedUSecs() const + Returns the milliseconds since start() was called, including time in Idle and suspend states. +*/ + +/*! + \fn virtual QAudio::Error QAbstractAudioOutput::error() const + Returns the error state. +*/ + +/*! + \fn virtual QAudio::State QAbstractAudioOutput::state() const + Returns the state of audio processing. +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioOutput::format() const + Returns the QAudioFormat being used. +*/ + +/*! + \fn QAbstractAudioOutput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. +*/ + +/*! + \fn QAbstractAudioOutput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + + +/*! + \class QAbstractAudioInput + \brief The QAbstractAudioInput class provides access for QAudioInput to access the audio + device provided by the plugin. + \internal + + \ingroup multimedia + + QAudioDeviceInput keeps an instance of QAbstractAudioInput and + routes calls to functions of the same name to QAbstractAudioInput. + This means that it is QAbstractAudioInput that implements the + audio functionality. For a description of the functionality, see + the QAudioInput class description. + + \sa QAudioInput +*/ + +/*! + \fn virtual QIODevice* QAbstractAudioInput::start(QIODevice* device) + Uses the \a device as the QIODevice to transfer data. If \a device is null + then the class creates an internal QIODevice. Returns a pointer to the + QIODevice being used to handle the data transfer. This QIODevice can be used to + read() audio data directly. Passing a QIODevice allows the data to be transfered + without any extra code. +*/ + +/*! + \fn virtual void QAbstractAudioInput::stop() + Stops the audio input. +*/ + +/*! + \fn virtual void QAbstractAudioInput::reset() + Drops all audio data in the buffers, resets buffers to zero. +*/ + +/*! + \fn virtual void QAbstractAudioInput::suspend() + Stops processing audio data, preserving buffered audio data. +*/ + +/*! + \fn virtual void QAbstractAudioInput::resume() + Resumes processing audio data after a suspend(). +*/ + +/*! + \fn virtual int QAbstractAudioInput::bytesReady() const + Returns the amount of audio data available to read in bytes. +*/ + +/*! + \fn virtual int QAbstractAudioInput::periodSize() const + Returns the period size in bytes. +*/ + +/*! + \fn virtual void QAbstractAudioInput::setBufferSize(int value) + Sets the audio buffer size to \a value in milliseconds. +*/ + +/*! + \fn virtual int QAbstractAudioInput::bufferSize() const + Returns the audio buffer size in milliseconds. +*/ + +/*! + \fn virtual void QAbstractAudioInput::setNotifyInterval(int ms) + Sets the interval for notify() signal to be emitted. This is based + on the \a ms of audio data processed not on actual real-time. + The resolution of the timer is platform specific. +*/ + +/*! + \fn virtual int QAbstractAudioInput::notifyInterval() const + Returns the notify interval in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioInput::processedUSecs() const + Returns the amount of audio data processed since start() was called in milliseconds. +*/ + +/*! + \fn virtual qint64 QAbstractAudioInput::elapsedUSecs() const + Returns the milliseconds since start() was called, including time in Idle and suspend states. +*/ + +/*! + \fn virtual QAudio::Error QAbstractAudioInput::error() const + Returns the error state. +*/ + +/*! + \fn virtual QAudio::State QAbstractAudioInput::state() const + Returns the state of audio processing. +*/ + +/*! + \fn virtual QAudioFormat QAbstractAudioInput::format() const + Returns the QAudioFormat being used +*/ + +/*! + \fn QAbstractAudioInput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. +*/ + +/*! + \fn QAbstractAudioInput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudioengine.h b/src/multimedia/multimedia/audio/qaudioengine.h new file mode 100644 index 0000000..df9d09d --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioengine.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QAUDIOENGINE_H +#define QAUDIOENGINE_H + +#include <QtCore/qglobal.h> +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class Q_MULTIMEDIA_EXPORT QAbstractAudioDeviceInfo : public QObject +{ + Q_OBJECT + +public: + virtual QAudioFormat preferredFormat() const = 0; + virtual bool isFormatSupported(const QAudioFormat &format) const = 0; + virtual QAudioFormat nearestFormat(const QAudioFormat &format) const = 0; + virtual QString deviceName() const = 0; + virtual QStringList codecList() = 0; + virtual QList<int> frequencyList() = 0; + virtual QList<int> channelsList() = 0; + virtual QList<int> sampleSizeList() = 0; + virtual QList<QAudioFormat::Endian> byteOrderList() = 0; + virtual QList<QAudioFormat::SampleType> sampleTypeList() = 0; +}; + +class Q_MULTIMEDIA_EXPORT QAbstractAudioOutput : public QObject +{ + Q_OBJECT + +public: + virtual QIODevice* start(QIODevice* device) = 0; + virtual void stop() = 0; + virtual void reset() = 0; + virtual void suspend() = 0; + virtual void resume() = 0; + virtual int bytesFree() const = 0; + virtual int periodSize() const = 0; + virtual void setBufferSize(int value) = 0; + virtual int bufferSize() const = 0; + virtual void setNotifyInterval(int milliSeconds) = 0; + virtual int notifyInterval() const = 0; + virtual qint64 processedUSecs() const = 0; + virtual qint64 elapsedUSecs() const = 0; + virtual QAudio::Error error() const = 0; + virtual QAudio::State state() const = 0; + virtual QAudioFormat format() const = 0; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); +}; + +class Q_MULTIMEDIA_EXPORT QAbstractAudioInput : public QObject +{ + Q_OBJECT + +public: + virtual QIODevice* start(QIODevice* device) = 0; + virtual void stop() = 0; + virtual void reset() = 0; + virtual void suspend() = 0; + virtual void resume() = 0; + virtual int bytesReady() const = 0; + virtual int periodSize() const = 0; + virtual void setBufferSize(int value) = 0; + virtual int bufferSize() const = 0; + virtual void setNotifyInterval(int milliSeconds) = 0; + virtual int notifyInterval() const = 0; + virtual qint64 processedUSecs() const = 0; + virtual qint64 elapsedUSecs() const = 0; + virtual QAudio::Error error() const = 0; + virtual QAudio::State state() const = 0; + virtual QAudioFormat format() const = 0; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOENGINE_H diff --git a/src/multimedia/multimedia/audio/qaudioengineplugin.cpp b/src/multimedia/multimedia/audio/qaudioengineplugin.cpp new file mode 100644 index 0000000..82324b5 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioengineplugin.cpp @@ -0,0 +1,54 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 <QtMultimedia/qaudioengineplugin.h> + +QT_BEGIN_NAMESPACE + +QAudioEnginePlugin::QAudioEnginePlugin(QObject* parent) : + QObject(parent) +{} + +QAudioEnginePlugin::~QAudioEnginePlugin() +{} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudioengineplugin.h b/src/multimedia/multimedia/audio/qaudioengineplugin.h new file mode 100644 index 0000000..2322d2a --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioengineplugin.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QAUDIOENGINEPLUGIN_H +#define QAUDIOENGINEPLUGIN_H + +#include <QtCore/qstring.h> +#include <QtCore/qplugin.h> +#include <QtCore/qfactoryinterface.h> + +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +struct Q_MULTIMEDIA_EXPORT QAudioEngineFactoryInterface : public QFactoryInterface +{ + virtual QList<QByteArray> availableDevices(QAudio::Mode) const = 0; + virtual QAbstractAudioInput* createInput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioOutput* createOutput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioDeviceInfo* createDeviceInfo(const QByteArray& device, QAudio::Mode mode) = 0; +}; + +#define QAudioEngineFactoryInterface_iid \ + "com.nokia.qt.QAudioEngineFactoryInterface" +Q_DECLARE_INTERFACE(QAudioEngineFactoryInterface, QAudioEngineFactoryInterface_iid) + +class Q_MULTIMEDIA_EXPORT QAudioEnginePlugin : public QObject, public QAudioEngineFactoryInterface +{ + Q_OBJECT + Q_INTERFACES(QAudioEngineFactoryInterface:QFactoryInterface) + +public: + QAudioEnginePlugin(QObject *parent = 0); + ~QAudioEnginePlugin(); + + virtual QStringList keys() const = 0; + virtual QList<QByteArray> availableDevices(QAudio::Mode) const = 0; + virtual QAbstractAudioInput* createInput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioOutput* createOutput(const QByteArray& device, const QAudioFormat& format = QAudioFormat()) = 0; + virtual QAbstractAudioDeviceInfo* createDeviceInfo(const QByteArray& device, QAudio::Mode mode) = 0; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOENGINEPLUGIN_H diff --git a/src/multimedia/multimedia/audio/qaudioformat.cpp b/src/multimedia/multimedia/audio/qaudioformat.cpp new file mode 100644 index 0000000..86d72f6 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioformat.cpp @@ -0,0 +1,407 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 <QDebug> +#include <QtMultimedia/qaudioformat.h> + + +QT_BEGIN_NAMESPACE + + +class QAudioFormatPrivate : public QSharedData +{ +public: + QAudioFormatPrivate() + { + frequency = -1; + channels = -1; + sampleSize = -1; + byteOrder = QAudioFormat::Endian(QSysInfo::ByteOrder); + sampleType = QAudioFormat::Unknown; + } + + QAudioFormatPrivate(const QAudioFormatPrivate &other): + QSharedData(other), + codec(other.codec), + byteOrder(other.byteOrder), + sampleType(other.sampleType), + frequency(other.frequency), + channels(other.channels), + sampleSize(other.sampleSize) + { + } + + QAudioFormatPrivate& operator=(const QAudioFormatPrivate &other) + { + codec = other.codec; + byteOrder = other.byteOrder; + sampleType = other.sampleType; + frequency = other.frequency; + channels = other.channels; + sampleSize = other.sampleSize; + + return *this; + } + + QString codec; + QAudioFormat::Endian byteOrder; + QAudioFormat::SampleType sampleType; + int frequency; + int channels; + int sampleSize; +}; + +/*! + \class QAudioFormat + \brief The QAudioFormat class stores audio parameter information. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + An audio format specifies how data in an audio stream is arranged, + i.e, how the stream is to be interpreted. The encoding itself is + specified by the codec() used for the stream. + + In addition to the encoding, QAudioFormat contains other + parameters that further specify how the audio data is arranged. + These are the frequency, the number of channels, the sample size, + the sample type, and the byte order. The following table describes + these in more detail. + + \table + \header + \o Parameter + \o Description + \row + \o Sample Rate + \o Samples per second of audio data in Hertz. + \row + \o Number of channels + \o The number of audio channels (typically one for mono + or two for stereo) + \row + \o Sample size + \o How much data is stored in each sample (typically 8 + or 16 bits) + \row + \o Sample type + \o Numerical representation of sample (typically signed integer, + unsigned integer or float) + \row + \o Byte order + \o Byte ordering of sample (typically little endian, big endian) + \endtable + + You can obtain audio formats compatible with the audio device used + through functions in QAudioDeviceInfo. This class also lets you + query available parameter values for a device, so that you can set + the parameters yourself. See the QAudioDeviceInfo class + description for details. You need to know the format of the audio + streams you wish to play. Qt does not set up formats for you. +*/ + +/*! + Construct a new audio format. + + Values are initialized as follows: + \list + \o sampleRate() = -1 + \o channelCount() = -1 + \o sampleSize() = -1 + \o byteOrder() = QAudioFormat::Endian(QSysInfo::ByteOrder) + \o sampleType() = QAudioFormat::Unknown + \c codec() = "" + \endlist +*/ + +QAudioFormat::QAudioFormat(): + d(new QAudioFormatPrivate) +{ +} + +/*! + Construct a new audio format using \a other. +*/ + +QAudioFormat::QAudioFormat(const QAudioFormat &other): + d(other.d) +{ +} + +/*! + Destroy this audio format. +*/ + +QAudioFormat::~QAudioFormat() +{ +} + +/*! + Assigns \a other to this QAudioFormat implementation. +*/ + +QAudioFormat& QAudioFormat::operator=(const QAudioFormat &other) +{ + d = other.d; + return *this; +} + +/*! + Returns true if this QAudioFormat is equal to the \a other + QAudioFormat; otherwise returns false. + + All elements of QAudioFormat are used for the comparison. +*/ + +bool QAudioFormat::operator==(const QAudioFormat &other) const +{ + return d->frequency == other.d->frequency && + d->channels == other.d->channels && + d->sampleSize == other.d->sampleSize && + d->byteOrder == other.d->byteOrder && + d->codec == other.d->codec && + d->sampleType == other.d->sampleType; +} + +/*! + Returns true if this QAudioFormat is not equal to the \a other + QAudioFormat; otherwise returns false. + + All elements of QAudioFormat are used for the comparison. +*/ + +bool QAudioFormat::operator!=(const QAudioFormat& other) const +{ + return !(*this == other); +} + +/*! + Returns true if all of the parameters are valid. +*/ + +bool QAudioFormat::isValid() const +{ + return d->frequency != -1 && d->channels != -1 && d->sampleSize != -1 && + d->sampleType != QAudioFormat::Unknown && !d->codec.isEmpty(); +} + +/*! + Sets the sample rate to \a samplerate Hertz. + + \since 4.7 +*/ + +void QAudioFormat::setSampleRate(int samplerate) +{ + d->frequency = samplerate; +} + +/*! + \obsolete + + Use setSampleRate() instead. +*/ + +void QAudioFormat::setFrequency(int frequency) +{ + d->frequency = frequency; +} + +/*! + Returns the current sample rate in Hertz. + + \since 4.7 +*/ + +int QAudioFormat::sampleRate() const +{ + return d->frequency; +} + +/*! + \obsolete + + Use sampleRate() instead. +*/ + +int QAudioFormat::frequency() const +{ + return d->frequency; +} + +/*! + Sets the channel count to \a channels. + + \since 4.7 +*/ + +void QAudioFormat::setChannelCount(int channels) +{ + d->channels = channels; +} + +/*! + \obsolete + + Use setChannelCount() instead. +*/ + +void QAudioFormat::setChannels(int channels) +{ + d->channels = channels; +} + +/*! + Returns the current channel count value. + + \since 4.7 +*/ + +int QAudioFormat::channelCount() const +{ + return d->channels; +} + +/*! + \obsolete + + Use channelCount() instead. +*/ + +int QAudioFormat::channels() const +{ + return d->channels; +} + +/*! + Sets the sample size to the \a sampleSize specified. +*/ + +void QAudioFormat::setSampleSize(int sampleSize) +{ + d->sampleSize = sampleSize; +} + +/*! + Returns the current sample size value. +*/ + +int QAudioFormat::sampleSize() const +{ + return d->sampleSize; +} + +/*! + Sets the codec to \a codec. + + \sa QAudioDeviceInfo::supportedCodecs() +*/ + +void QAudioFormat::setCodec(const QString &codec) +{ + d->codec = codec; +} + +/*! + Returns the current codec value. + + \sa QAudioDeviceInfo::supportedCodecs() +*/ + +QString QAudioFormat::codec() const +{ + return d->codec; +} + +/*! + Sets the byteOrder to \a byteOrder. +*/ + +void QAudioFormat::setByteOrder(QAudioFormat::Endian byteOrder) +{ + d->byteOrder = byteOrder; +} + +/*! + Returns the current byteOrder value. +*/ + +QAudioFormat::Endian QAudioFormat::byteOrder() const +{ + return d->byteOrder; +} + +/*! + Sets the sampleType to \a sampleType. +*/ + +void QAudioFormat::setSampleType(QAudioFormat::SampleType sampleType) +{ + d->sampleType = sampleType; +} + +/*! + Returns the current SampleType value. +*/ + +QAudioFormat::SampleType QAudioFormat::sampleType() const +{ + return d->sampleType; +} + +/*! + \enum QAudioFormat::SampleType + + \value Unknown Not Set + \value SignedInt samples are signed integers + \value UnSignedInt samples are unsigned intergers + \value Float samples are floats +*/ + +/*! + \enum QAudioFormat::Endian + + \value BigEndian samples are big endian byte order + \value LittleEndian samples are little endian byte order +*/ + +QT_END_NAMESPACE + diff --git a/src/multimedia/multimedia/audio/qaudioformat.h b/src/multimedia/multimedia/audio/qaudioformat.h new file mode 100644 index 0000000..6c835b7 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioformat.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QAUDIOFORMAT_H +#define QAUDIOFORMAT_H + +#include <QtCore/qobject.h> +#include <QtCore/qglobal.h> +#include <QtCore/qshareddata.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAudioFormatPrivate; + +class Q_MULTIMEDIA_EXPORT QAudioFormat +{ +public: + enum SampleType { Unknown, SignedInt, UnSignedInt, Float }; + enum Endian { BigEndian = QSysInfo::BigEndian, LittleEndian = QSysInfo::LittleEndian }; + + QAudioFormat(); + QAudioFormat(const QAudioFormat &other); + ~QAudioFormat(); + + QAudioFormat& operator=(const QAudioFormat &other); + bool operator==(const QAudioFormat &other) const; + bool operator!=(const QAudioFormat &other) const; + + bool isValid() const; + + void setFrequency(int frequency); + int frequency() const; + void setSampleRate(int sampleRate); + int sampleRate() const; + + void setChannels(int channels); + int channels() const; + void setChannelCount(int channelCount); + int channelCount() const; + + void setSampleSize(int sampleSize); + int sampleSize() const; + + void setCodec(const QString &codec); + QString codec() const; + + void setByteOrder(QAudioFormat::Endian byteOrder); + QAudioFormat::Endian byteOrder() const; + + void setSampleType(QAudioFormat::SampleType sampleType); + QAudioFormat::SampleType sampleType() const; + +private: + QSharedDataPointer<QAudioFormatPrivate> d; +}; + + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOFORMAT_H diff --git a/src/multimedia/multimedia/audio/qaudioinput.cpp b/src/multimedia/multimedia/audio/qaudioinput.cpp new file mode 100644 index 0000000..c99e870 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioinput.cpp @@ -0,0 +1,432 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudioinput.h> + +#include "qaudiodevicefactory_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioInput + \brief The QAudioInput class provides an interface for receiving audio data from an audio input device. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + You can construct an audio input with the system's + \l{QAudioDeviceInfo::defaultInputDevice()}{default audio input + device}. It is also possible to create QAudioInput with a + specific QAudioDeviceInfo. When you create the audio input, you + should also send in the QAudioFormat to be used for the recording + (see the QAudioFormat class description for details). + + To record to a file: + + QAudioInput lets you record audio with an audio input device. The + default constructor of this class will use the systems default + audio device, but you can also specify a QAudioDeviceInfo for a + specific device. You also need to pass in the QAudioFormat in + which you wish to record. + + Starting up the QAudioInput is simply a matter of calling start() + with a QIODevice opened for writing. For instance, to record to a + file, you can: + + \code + QFile outputFile; // class member. + QAudioInput* audio; // class member. + \endcode + + \code + { + outputFile.setFileName("/tmp/test.raw"); + outputFile.open( QIODevice::WriteOnly | QIODevice::Truncate ); + + QAudioFormat format; + // set up the format you want, eg. + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::UnSignedInt); + + QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice(); + if (!info.isFormatSupported(format)) { + qWarning()<<"default format not supported try to use nearest"; + format = info.nearestFormat(format); + } + + audio = new QAudioInput(format, this); + QTimer::singleShot(3000, this, SLOT(stopRecording())); + audio->start(&outputFile); + // Records audio for 3000ms + } + \endcode + + This will start recording if the format specified is supported by + the input device (you can check this with + QAudioDeviceInfo::isFormatSupported(). In case there are any + snags, use the error() function to check what went wrong. We stop + recording in the \c stopRecording() slot. + + \code + void stopRecording() + { + audio->stop(); + outputFile->close(); + delete audio; + } + \endcode + + At any point in time, QAudioInput will be in one of four states: + active, suspended, stopped, or idle. These states are specified by + the QAudio::State enum. You can request a state change directly through + suspend(), resume(), stop(), reset(), and start(). The current + state is reported by state(). QAudioOutput will also signal you + when the state changes (stateChanged()). + + QAudioInput provides several ways of measuring the time that has + passed since the start() of the recording. The \c processedUSecs() + function returns the length of the stream in microseconds written, + i.e., it leaves out the times the audio input was suspended or idle. + The elapsedUSecs() function returns the time elapsed since start() was called regardless of + which states the QAudioInput has been in. + + If an error should occur, you can fetch its reason with error(). + The possible error reasons are described by the QAudio::Error + enum. The QAudioInput will enter the \l{QAudio::}{StoppedState} when + an error is encountered. Connect to the stateChanged() signal to + handle the error: + + \snippet doc/src/snippets/audio/main.cpp 0 + + \sa QAudioOutput, QAudioDeviceInfo + + \section1 Symbian Platform Security Requirements + + On Symbian, processes which use this class must have the + \c UserEnvironment platform security capability. If the client + process lacks this capability, calls to either overload of start() + will fail. + This failure is indicated by the QAudioInput object setting + its error() value to \l{QAudio::OpenError} and then emitting a + \l{stateChanged()}{stateChanged}(\l{QAudio::StoppedState}) signal. + + Platform security capabilities are added via the + \l{qmake-variable-reference.html#target-capability}{TARGET.CAPABILITY} + qmake variable. +*/ + +/*! + Construct a new audio input and attach it to \a parent. + The default audio input device is used with the output + \a format parameters. +*/ + +QAudioInput::QAudioInput(const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createDefaultInputDevice(format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Construct a new audio input and attach it to \a parent. + The device referenced by \a audioDevice is used with the input + \a format parameters. +*/ + +QAudioInput::QAudioInput(const QAudioDeviceInfo &audioDevice, const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createInputDevice(audioDevice, format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Destroy this audio input. +*/ + +QAudioInput::~QAudioInput() +{ + delete d; +} + +/*! + Uses the \a device as the QIODevice to transfer data. + Passing a QIODevice allows the data to be transfered without any extra code. + All that is required is to open the QIODevice. + + If able to successfully get audio data from the systems audio device the + state() is set to either QAudio::ActiveState or QAudio::IdleState, + error() is set to QAudio::NoError and the stateChanged() signal is emitted. + + If a problem occurs during this process the error() is set to QAudio::OpenError, + state() is set to QAudio::StoppedState and stateChanged() signal is emitted. + + \sa {Symbian Platform Security Requirements} + + \sa QIODevice +*/ + +void QAudioInput::start(QIODevice* device) +{ + d->start(device); +} + +/*! + Returns a pointer to the QIODevice being used to handle the data + transfer. This QIODevice can be used to read() audio data + directly. + + If able to access the systems audio device the state() is set to + QAudio::IdleState, error() is set to QAudio::NoError + and the stateChanged() signal is emitted. + + If a problem occurs during this process the error() is set to QAudio::OpenError, + state() is set to QAudio::StoppedState and stateChanged() signal is emitted. + + \sa {Symbian Platform Security Requirements} + + \sa QIODevice +*/ + +QIODevice* QAudioInput::start() +{ + return d->start(0); +} + +/*! + Returns the QAudioFormat being used. +*/ + +QAudioFormat QAudioInput::format() const +{ + return d->format(); +} + +/*! + Stops the audio input, detaching from the system resource. + + Sets error() to QAudio::NoError, state() to QAudio::StoppedState and + emit stateChanged() signal. +*/ + +void QAudioInput::stop() +{ + d->stop(); +} + +/*! + Drops all audio data in the buffers, resets buffers to zero. +*/ + +void QAudioInput::reset() +{ + d->reset(); +} + +/*! + Stops processing audio data, preserving buffered audio data. + + Sets error() to QAudio::NoError, state() to QAudio::SuspendedState and + emit stateChanged() signal. +*/ + +void QAudioInput::suspend() +{ + d->suspend(); +} + +/*! + Resumes processing audio data after a suspend(). + + Sets error() to QAudio::NoError. + Sets state() to QAudio::ActiveState if you previously called start(QIODevice*). + Sets state() to QAudio::IdleState if you previously called start(). + emits stateChanged() signal. +*/ + +void QAudioInput::resume() +{ + d->resume(); +} + +/*! + Sets the audio buffer size to \a value milliseconds. + + Note: This function can be called anytime before start(), calls to this + are ignored after start(). It should not be assumed that the buffer size + set is the actual buffer size used, calling bufferSize() anytime after start() + will return the actual buffer size being used. + +*/ + +void QAudioInput::setBufferSize(int value) +{ + d->setBufferSize(value); +} + +/*! + Returns the audio buffer size in milliseconds. + + If called before start(), returns platform default value. + If called before start() but setBufferSize() was called prior, returns value set by setBufferSize(). + If called after start(), returns the actual buffer size being used. This may not be what was set previously + by setBufferSize(). + +*/ + +int QAudioInput::bufferSize() const +{ + return d->bufferSize(); +} + +/*! + Returns the amount of audio data available to read in bytes. + + NOTE: returned value is only valid while in QAudio::ActiveState or QAudio::IdleState + state, otherwise returns zero. +*/ + +int QAudioInput::bytesReady() const +{ + /* + -If not ActiveState|IdleState, return 0 + -return amount of audio data available to read + */ + return d->bytesReady(); +} + +/*! + Returns the period size in bytes. + + Note: This is the recommended read size in bytes. +*/ + +int QAudioInput::periodSize() const +{ + return d->periodSize(); +} + +/*! + Sets the interval for notify() signal to be emitted. + This is based on the \a ms of audio data processed + not on actual real-time. + The minimum resolution of the timer is platform specific and values + should be checked with notifyInterval() to confirm actual value + being used. +*/ + +void QAudioInput::setNotifyInterval(int ms) +{ + d->setNotifyInterval(ms); +} + +/*! + Returns the notify interval in milliseconds. +*/ + +int QAudioInput::notifyInterval() const +{ + return d->notifyInterval(); +} + +/*! + Returns the amount of audio data processed since start() + was called in microseconds. +*/ + +qint64 QAudioInput::processedUSecs() const +{ + return d->processedUSecs(); +} + +/*! + Returns the microseconds since start() was called, including time in Idle and + Suspend states. +*/ + +qint64 QAudioInput::elapsedUSecs() const +{ + return d->elapsedUSecs(); +} + +/*! + Returns the error state. +*/ + +QAudio::Error QAudioInput::error() const +{ + return d->error(); +} + +/*! + Returns the state of audio processing. +*/ + +QAudio::State QAudioInput::state() const +{ + return d->state(); +} + +/*! + \fn QAudioInput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. +*/ + +/*! + \fn QAudioInput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + +QT_END_NAMESPACE + diff --git a/src/multimedia/multimedia/audio/qaudioinput.h b/src/multimedia/multimedia/audio/qaudioinput.h new file mode 100644 index 0000000..5be9b5a --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioinput.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QAUDIOINPUT_H +#define QAUDIOINPUT_H + +#include <QtCore/qiodevice.h> +#include <QtCore/qglobal.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAbstractAudioInput; + +class Q_MULTIMEDIA_EXPORT QAudioInput : public QObject +{ + Q_OBJECT + +public: + explicit QAudioInput(const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + explicit QAudioInput(const QAudioDeviceInfo &audioDeviceInfo, const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + ~QAudioInput(); + + QAudioFormat format() const; + + void start(QIODevice *device); + QIODevice* start(); + + void stop(); + void reset(); + void suspend(); + void resume(); + + void setBufferSize(int bytes); + int bufferSize() const; + + int bytesReady() const; + int periodSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + + QAudio::Error error() const; + QAudio::State state() const; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); + +private: + Q_DISABLE_COPY(QAudioInput) + + QAbstractAudioInput* d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOINPUT_H diff --git a/src/multimedia/multimedia/audio/qaudioinput_alsa_p.cpp b/src/multimedia/multimedia/audio/qaudioinput_alsa_p.cpp new file mode 100644 index 0000000..6b15008 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioinput_alsa_p.cpp @@ -0,0 +1,706 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qcoreapplication.h> +#include "qaudioinput_alsa_p.h" +#include "qaudiodeviceinfo_alsa_p.h" + +QT_BEGIN_NAMESPACE + +//#define DEBUG_AUDIO 1 + +static const int minimumIntervalTime = 50; + +QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + handle = 0; + ahandler = 0; + access = SND_PCM_ACCESS_RW_INTERLEAVED; + pcmformat = SND_PCM_FORMAT_S16; + buffer_size = 0; + period_size = 0; + buffer_time = 100000; + period_time = 20000; + totalTimeValue = 0; + intervalTime = 1000; + audioBuffer = 0; + errorState = QAudio::NoError; + deviceState = QAudio::StoppedState; + audioSource = 0; + pullMode = true; + resuming = false; + + m_device = device; + + timer = new QTimer(this); + connect(timer,SIGNAL(timeout()),SLOT(userFeed())); +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + close(); + disconnect(timer, SIGNAL(timeout())); + QCoreApplication::processEvents(); + delete timer; +} + +QAudio::Error QAudioInputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioInputPrivate::state() const +{ + return deviceState; +} + + +QAudioFormat QAudioInputPrivate::format() const +{ + return settings; +} + +int QAudioInputPrivate::xrun_recovery(int err) +{ + int count = 0; + bool reset = false; + + if(err == -EPIPE) { + errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + else { + bytesAvailable = bytesReady(); + if (bytesAvailable <= 0) + reset = true; + } + + } else if((err == -ESTRPIPE)||(err == -EIO)) { + errorState = QAudio::IOError; + while((err = snd_pcm_resume(handle)) == -EAGAIN){ + usleep(100); + count++; + if(count > 5) { + reset = true; + break; + } + } + if(err < 0) { + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + } + } + if(reset) { + close(); + open(); + snd_pcm_prepare(handle); + return 0; + } + return err; +} + +int QAudioInputPrivate::setFormat() +{ + snd_pcm_format_t format = SND_PCM_FORMAT_S16; + + if(settings.sampleSize() == 8) { + format = SND_PCM_FORMAT_U8; + } else if(settings.sampleSize() == 16) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_S16_LE; + else + format = SND_PCM_FORMAT_S16_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_U16_LE; + else + format = SND_PCM_FORMAT_U16_BE; + } + } else if(settings.sampleSize() == 24) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_S24_LE; + else + format = SND_PCM_FORMAT_S24_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_U24_LE; + else + format = SND_PCM_FORMAT_U24_BE; + } + } else if(settings.sampleSize() == 32) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_S32_LE; + else + format = SND_PCM_FORMAT_S32_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_U32_LE; + else + format = SND_PCM_FORMAT_U32_BE; + } else if(settings.sampleType() == QAudioFormat::Float) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_FLOAT_LE; + else + format = SND_PCM_FORMAT_FLOAT_BE; + } + } else if(settings.sampleSize() == 64) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + format = SND_PCM_FORMAT_FLOAT64_LE; + else + format = SND_PCM_FORMAT_FLOAT64_BE; + } + + return snd_pcm_hw_params_set_format( handle, hwparams, format); +} + +QIODevice* QAudioInputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StoppedState) + close(); + + if(!pullMode && audioSource) { + delete audioSource; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + deviceState = QAudio::ActiveState; + } else { + //set to push mode + pullMode = false; + deviceState = QAudio::IdleState; + audioSource = new InputPrivate(this); + audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + } + + if( !open() ) + return 0; + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioInputPrivate::stop() +{ + if(deviceState == QAudio::StoppedState) + return; + + deviceState = QAudio::StoppedState; + + close(); + emit stateChanged(deviceState); +} + +bool QAudioInputPrivate::open() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + clockStamp.restart(); + timeStamp.restart(); + elapsedTimeOffset = 0; + + int dir; + int err=-1; + int count=0; + unsigned int freakuency=settings.frequency(); + + QString dev = QString(QLatin1String(m_device.constData())); + QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioInput); + if(dev.compare(QLatin1String("default")) == 0) { +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + dev = QLatin1String(devices.first()); +#else + dev = QLatin1String("hw:0,0"); +#endif + } else { +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + dev = QLatin1String(m_device); +#else + int idx = 0; + char *name; + + QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData()); + + while(snd_card_get_name(idx,&name) == 0) { + if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0) + break; + idx++; + } + dev = QString(QLatin1String("hw:%1,0")).arg(idx); +#endif + } + + // Step 1: try and open the device + while((count < 5) && (err < 0)) { + err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0); + if(err < 0) + count++; + } + if (( err < 0)||(handle == 0)) { + errorState = QAudio::OpenError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + return false; + } + snd_pcm_nonblock( handle, 0 ); + + // Step 2: Set the desired HW parameters. + snd_pcm_hw_params_alloca( &hwparams ); + + bool fatal = false; + QString errMessage; + unsigned int chunks = 8; + + err = snd_pcm_hw_params_any( handle, hwparams ); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_any: err = %1").arg(err); + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_access( handle, hwparams, access ); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_access: err = %1").arg(err); + } + } + if ( !fatal ) { + err = setFormat(); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_format: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() ); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_channels: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params(handle, hwparams); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params: err = %1").arg(err); + } + } + if( err < 0) { + qWarning()<<errMessage; + errorState = QAudio::OpenError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + return false; + } + snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); + buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); + snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); + period_size = snd_pcm_frames_to_bytes(handle,period_frames); + snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); + snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); + + // Step 3: Set the desired SW parameters. + snd_pcm_sw_params_t *swparams; + snd_pcm_sw_params_alloca(&swparams); + snd_pcm_sw_params_current(handle, swparams); + snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); + snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); + snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); + snd_pcm_sw_params(handle, swparams); + + // Step 4: Prepare audio + if(audioBuffer == 0) + audioBuffer = new char[buffer_size]; + snd_pcm_prepare( handle ); + snd_pcm_start(handle); + + // Step 5: Setup timer + bytesAvailable = bytesReady(); + + if(pullMode) + connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed())); + + // Step 6: Start audio processing + chunks = buffer_size/period_size; + timer->start(period_time*chunks/2000); + + errorState = QAudio::NoError; + + totalTimeValue = 0; + + return true; +} + +void QAudioInputPrivate::close() +{ + timer->stop(); + + if ( handle ) { + snd_pcm_drop( handle ); + snd_pcm_close( handle ); + handle = 0; + delete [] audioBuffer; + audioBuffer=0; + } +} + +int QAudioInputPrivate::bytesReady() const +{ + if(resuming) + return period_size; + + if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) + return 0; + int frames = snd_pcm_avail_update(handle); + if (frames < 0) return frames; + if((int)frames > (int)buffer_frames) + frames = buffer_frames; + + return snd_pcm_frames_to_bytes(handle, frames); +} + +qint64 QAudioInputPrivate::read(char* data, qint64 len) +{ + Q_UNUSED(len) + + // Read in some audio data and write it to QIODevice, pull mode + if ( !handle ) + return 0; + + bytesAvailable = bytesReady(); + + if (bytesAvailable < 0) { + // bytesAvailable as negative is error code, try to recover from it. + xrun_recovery(bytesAvailable); + bytesAvailable = bytesReady(); + if (bytesAvailable < 0) { + // recovery failed must stop and set error. + close(); + errorState = QAudio::IOError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + return 0; + } + } + + int count=0, err = 0; + while(count < 5) { + int chunks = bytesAvailable/period_size; + int frames = chunks*period_frames; + if(frames > (int)buffer_frames) + frames = buffer_frames; + int readFrames = snd_pcm_readi(handle, audioBuffer, frames); + if (readFrames >= 0) { + err = snd_pcm_frames_to_bytes(handle, readFrames); +#ifdef DEBUG_AUDIO + qDebug()<<QString::fromLatin1("read in bytes = %1 (frames=%2)").arg(err).arg(readFrames).toLatin1().constData(); +#endif + break; + } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) { + errorState = QAudio::IOError; + err = 0; + break; + } else { + if(readFrames == -EPIPE) { + errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(handle); + } else if(readFrames == -ESTRPIPE) { + err = snd_pcm_prepare(handle); + } + if(err != 0) break; + } + count++; + } + if(err > 0) { + // got some send it onward +#ifdef DEBUG_AUDIO + qDebug()<<"frames to write to QIODevice = "<< + snd_pcm_bytes_to_frames( handle, (int)err )<<" ("<<err<<") bytes"; +#endif + if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) + return 0; + if (pullMode) { + qint64 l = audioSource->write(audioBuffer,err); + if(l < 0) { + close(); + errorState = QAudio::IOError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + } else if(l == 0) { + if (deviceState != QAudio::IdleState) { + errorState = QAudio::NoError; + deviceState = QAudio::IdleState; + emit stateChanged(deviceState); + } + } else { + totalTimeValue += err; + resuming = false; + if (deviceState != QAudio::ActiveState) { + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + emit stateChanged(deviceState); + } + } + return l; + + } else { + memcpy(data,audioBuffer,err); + totalTimeValue += err; + resuming = false; + if (deviceState != QAudio::ActiveState) { + errorState = QAudio::NoError; + deviceState = QAudio::ActiveState; + emit stateChanged(deviceState); + } + return err; + } + } + return 0; +} + +void QAudioInputPrivate::resume() +{ + if(deviceState == QAudio::SuspendedState) { + int err = 0; + + if(handle) { + err = snd_pcm_prepare( handle ); + if(err < 0) + xrun_recovery(err); + + err = snd_pcm_start(handle); + if(err < 0) + xrun_recovery(err); + + bytesAvailable = buffer_size; + } + resuming = true; + deviceState = QAudio::ActiveState; + int chunks = buffer_size/period_size; + timer->start(period_time*chunks/2000); + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::setBufferSize(int value) +{ + buffer_size = value; +} + +int QAudioInputPrivate::bufferSize() const +{ + return buffer_size; +} + +int QAudioInputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioInputPrivate::setNotifyInterval(int ms) +{ + if(ms >= minimumIntervalTime) + intervalTime = ms; + else + intervalTime = minimumIntervalTime; +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioInputPrivate::processedUSecs() const +{ + qint64 result = qint64(1000000) * totalTimeValue / + (settings.channels()*(settings.sampleSize()/8)) / + settings.frequency(); + + return result; +} + +void QAudioInputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState||resuming) { + timer->stop(); + deviceState = QAudio::SuspendedState; + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::userFeed() +{ + if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState) + return; +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() IN"; +#endif + deviceReady(); +} + +bool QAudioInputPrivate::deviceReady() +{ + if(pullMode) { + // reads some audio data and writes it to QIODevice + read(0,0); + } else { + // emits readyRead() so user will call read() on QIODevice to get some audio data + InputPrivate* a = qobject_cast<InputPrivate*>(audioSource); + a->trigger(); + } + bytesAvailable = bytesReady(); + + if(deviceState != QAudio::ActiveState) + return true; + + if((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) { + emit notify(); + elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime; + timeStamp.restart(); + } + return true; +} + +qint64 QAudioInputPrivate::elapsedUSecs() const +{ + if (deviceState == QAudio::StoppedState) + return 0; + + return clockStamp.elapsed()*1000; +} + +void QAudioInputPrivate::reset() +{ + if(handle) + snd_pcm_reset(handle); +} + +void QAudioInputPrivate::drain() +{ + if(handle) + snd_pcm_drain(handle); +} + +InputPrivate::InputPrivate(QAudioInputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioInputPrivate*>(audio); +} + +InputPrivate::~InputPrivate() +{ +} + +qint64 InputPrivate::readData( char* data, qint64 len) +{ + return audioDevice->read(data,len); +} + +qint64 InputPrivate::writeData(const char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + return 0; +} + +void InputPrivate::trigger() +{ + emit readyRead(); +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudioinput_alsa_p.h b/src/multimedia/multimedia/audio/qaudioinput_alsa_p.h new file mode 100644 index 0000000..c907019 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioinput_alsa_p.h @@ -0,0 +1,158 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIOINPUTALSA_H +#define QAUDIOINPUTALSA_H + +#include <alsa/asoundlib.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qelapsedtimer.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_NAMESPACE + +class InputPrivate; + +class QAudioInputPrivate : public QAbstractAudioInput +{ + Q_OBJECT +public: + QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioInputPrivate(); + + qint64 read(char* data, qint64 len); + + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesReady() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + QAudioFormat format() const; + bool resuming; + snd_pcm_t* handle; + qint64 totalTimeValue; + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private slots: + void userFeed(); + bool deviceReady(); + +private: + int xrun_recovery(int err); + int setFormat(); + bool open(); + void close(); + void drain(); + + QTimer* timer; + QElapsedTimer timeStamp; + QElapsedTimer clockStamp; + qint64 elapsedTimeOffset; + int intervalTime; + char* audioBuffer; + int bytesAvailable; + QByteArray m_device; + bool pullMode; + int buffer_size; + int period_size; + unsigned int buffer_time; + unsigned int period_time; + snd_pcm_uframes_t buffer_frames; + snd_pcm_uframes_t period_frames; + snd_async_handler_t* ahandler; + snd_pcm_access_t access; + snd_pcm_format_t pcmformat; + snd_timestamp_t* timestamp; + snd_pcm_hw_params_t *hwparams; +}; + +class InputPrivate : public QIODevice +{ + Q_OBJECT +public: + InputPrivate(QAudioInputPrivate* audio); + ~InputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + + void trigger(); +private: + QAudioInputPrivate *audioDevice; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/audio/qaudioinput_mac_p.cpp b/src/multimedia/multimedia/audio/qaudioinput_mac_p.cpp new file mode 100644 index 0000000..cb65f6e --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioinput_mac_p.cpp @@ -0,0 +1,956 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qendian.h> +#include <QtCore/qtimer.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudioinput.h> + +#include "qaudio_mac_p.h" +#include "qaudioinput_mac_p.h" +#include "qaudiodeviceinfo_mac_p.h" + +QT_BEGIN_NAMESPACE + + +namespace QtMultimediaInternal +{ + +static const int default_buffer_size = 4 * 1024; + +class QAudioBufferList +{ +public: + QAudioBufferList(AudioStreamBasicDescription const& streamFormat): + owner(false), + sf(streamFormat) + { + const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; + const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame; + + dataSize = 0; + + bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) + + (sizeof(AudioBuffer) * numberOfBuffers))); + + bfs->mNumberBuffers = numberOfBuffers; + for (int i = 0; i < numberOfBuffers; ++i) { + bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1; + bfs->mBuffers[i].mDataByteSize = 0; + bfs->mBuffers[i].mData = 0; + } + } + + QAudioBufferList(AudioStreamBasicDescription const& streamFormat, char* buffer, int bufferSize): + owner(false), + sf(streamFormat), + bfs(0) + { + dataSize = bufferSize; + + bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) + sizeof(AudioBuffer))); + + bfs->mNumberBuffers = 1; + bfs->mBuffers[0].mNumberChannels = 1; + bfs->mBuffers[0].mDataByteSize = dataSize; + bfs->mBuffers[0].mData = buffer; + } + + QAudioBufferList(AudioStreamBasicDescription const& streamFormat, int framesToBuffer): + owner(true), + sf(streamFormat), + bfs(0) + { + const bool isInterleaved = (sf.mFormatFlags & kAudioFormatFlagIsNonInterleaved) == 0; + const int numberOfBuffers = isInterleaved ? 1 : sf.mChannelsPerFrame; + + dataSize = framesToBuffer * sf.mBytesPerFrame; + + bfs = reinterpret_cast<AudioBufferList*>(qMalloc(sizeof(AudioBufferList) + + (sizeof(AudioBuffer) * numberOfBuffers))); + bfs->mNumberBuffers = numberOfBuffers; + for (int i = 0; i < numberOfBuffers; ++i) { + bfs->mBuffers[i].mNumberChannels = isInterleaved ? numberOfBuffers : 1; + bfs->mBuffers[i].mDataByteSize = dataSize; + bfs->mBuffers[i].mData = qMalloc(dataSize); + } + } + + ~QAudioBufferList() + { + if (owner) { + for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i) + qFree(bfs->mBuffers[i].mData); + } + + qFree(bfs); + } + + AudioBufferList* audioBufferList() const + { + return bfs; + } + + char* data(int buffer = 0) const + { + return static_cast<char*>(bfs->mBuffers[buffer].mData); + } + + qint64 bufferSize(int buffer = 0) const + { + return bfs->mBuffers[buffer].mDataByteSize; + } + + int frameCount(int buffer = 0) const + { + return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerFrame; + } + + int packetCount(int buffer = 0) const + { + return bfs->mBuffers[buffer].mDataByteSize / sf.mBytesPerPacket; + } + + int packetSize() const + { + return sf.mBytesPerPacket; + } + + void reset() + { + for (UInt32 i = 0; i < bfs->mNumberBuffers; ++i) { + bfs->mBuffers[i].mDataByteSize = dataSize; + bfs->mBuffers[i].mData = 0; + } + } + +private: + bool owner; + int dataSize; + AudioStreamBasicDescription sf; + AudioBufferList* bfs; +}; + +class QAudioPacketFeeder +{ +public: + QAudioPacketFeeder(QAudioBufferList* abl): + audioBufferList(abl) + { + totalPackets = audioBufferList->packetCount(); + position = 0; + } + + bool feed(AudioBufferList& dst, UInt32& packetCount) + { + if (position == totalPackets) { + dst.mBuffers[0].mDataByteSize = 0; + packetCount = 0; + return false; + } + + if (totalPackets - position < packetCount) + packetCount = totalPackets - position; + + dst.mBuffers[0].mDataByteSize = packetCount * audioBufferList->packetSize(); + dst.mBuffers[0].mData = audioBufferList->data() + (position * audioBufferList->packetSize()); + + position += packetCount; + + return true; + } + +private: + UInt32 totalPackets; + UInt32 position; + QAudioBufferList* audioBufferList; +}; + +class QAudioInputBuffer : public QObject +{ + Q_OBJECT + +public: + QAudioInputBuffer(int bufferSize, + int maxPeriodSize, + AudioStreamBasicDescription const& inputFormat, + AudioStreamBasicDescription const& outputFormat, + QObject* parent): + QObject(parent), + m_deviceError(false), + m_audioConverter(0), + m_inputFormat(inputFormat), + m_outputFormat(outputFormat) + { + m_maxPeriodSize = maxPeriodSize; + m_periodTime = m_maxPeriodSize / m_outputFormat.mBytesPerFrame * 1000 / m_outputFormat.mSampleRate; + m_buffer = new QAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize))); + m_inputBufferList = new QAudioBufferList(m_inputFormat); + + m_flushTimer = new QTimer(this); + connect(m_flushTimer, SIGNAL(timeout()), SLOT(flushBuffer())); + + if (toQAudioFormat(inputFormat) != toQAudioFormat(outputFormat)) { + if (AudioConverterNew(&m_inputFormat, &m_outputFormat, &m_audioConverter) != noErr) { + qWarning() << "QAudioInput: Unable to create an Audio Converter"; + m_audioConverter = 0; + } + } + } + + ~QAudioInputBuffer() + { + delete m_buffer; + } + + qint64 renderFromDevice(AudioUnit audioUnit, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames) + { + const bool wasEmpty = m_buffer->used() == 0; + + OSStatus err; + qint64 framesRendered = 0; + + m_inputBufferList->reset(); + err = AudioUnitRender(audioUnit, + ioActionFlags, + inTimeStamp, + inBusNumber, + inNumberFrames, + m_inputBufferList->audioBufferList()); + + if (m_audioConverter != 0) { + QAudioPacketFeeder feeder(m_inputBufferList); + + bool wecan = true; + int copied = 0; + + const int available = m_buffer->free(); + + while (err == noErr && wecan) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available); + + if (region.second > 0) { + AudioBufferList output; + output.mNumberBuffers = 1; + output.mBuffers[0].mNumberChannels = 1; + output.mBuffers[0].mDataByteSize = region.second; + output.mBuffers[0].mData = region.first; + + UInt32 packetSize = region.second / m_outputFormat.mBytesPerPacket; + err = AudioConverterFillComplexBuffer(m_audioConverter, + converterCallback, + &feeder, + &packetSize, + &output, + 0); + + region.second = output.mBuffers[0].mDataByteSize; + copied += region.second; + + m_buffer->releaseWriteRegion(region); + } + else + wecan = false; + } + + framesRendered += copied / m_outputFormat.mBytesPerFrame; + } + else { + const int available = m_inputBufferList->bufferSize(); + bool wecan = true; + int copied = 0; + + while (wecan && copied < available) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available - copied); + + if (region.second > 0) { + memcpy(region.first, m_inputBufferList->data() + copied, region.second); + copied += region.second; + } + else + wecan = false; + + m_buffer->releaseWriteRegion(region); + } + + framesRendered = copied / m_outputFormat.mBytesPerFrame; + } + + if (wasEmpty && framesRendered > 0) + emit readyRead(); + + return framesRendered; + } + + qint64 readBytes(char* data, qint64 len) + { + bool wecan = true; + qint64 bytesCopied = 0; + + len -= len % m_maxPeriodSize; + while (wecan && bytesCopied < len) { + QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(len - bytesCopied); + + if (region.second > 0) { + memcpy(data + bytesCopied, region.first, region.second); + bytesCopied += region.second; + } + else + wecan = false; + + m_buffer->releaseReadRegion(region); + } + + return bytesCopied; + } + + void setFlushDevice(QIODevice* device) + { + if (m_device != device) + m_device = device; + } + + void startFlushTimer() + { + if (m_device != 0) { + m_flushTimer->start((m_buffer->size() - (m_maxPeriodSize * 2)) / m_maxPeriodSize * m_periodTime); + } + } + + void stopFlushTimer() + { + m_flushTimer->stop(); + } + + void flush(bool all = false) + { + if (m_device == 0) + return; + + const int used = m_buffer->used(); + const int readSize = all ? used : used - (used % m_maxPeriodSize); + + if (readSize > 0) { + bool wecan = true; + int flushed = 0; + + while (!m_deviceError && wecan && flushed < readSize) { + QAudioRingBuffer::Region region = m_buffer->acquireReadRegion(readSize - flushed); + + if (region.second > 0) { + int bytesWritten = m_device->write(region.first, region.second); + if (bytesWritten < 0) { + stopFlushTimer(); + m_deviceError = true; + } + else { + region.second = bytesWritten; + flushed += bytesWritten; + wecan = bytesWritten != 0; + } + } + else + wecan = false; + + m_buffer->releaseReadRegion(region); + } + } + } + + void reset() + { + m_buffer->reset(); + m_deviceError = false; + } + + int available() const + { + return m_buffer->free(); + } + + int used() const + { + return m_buffer->used(); + } + +signals: + void readyRead(); + +private slots: + void flushBuffer() + { + flush(); + } + +private: + bool m_deviceError; + int m_maxPeriodSize; + int m_periodTime; + QIODevice* m_device; + QTimer* m_flushTimer; + QAudioRingBuffer* m_buffer; + QAudioBufferList* m_inputBufferList; + AudioConverterRef m_audioConverter; + AudioStreamBasicDescription m_inputFormat; + AudioStreamBasicDescription m_outputFormat; + + const static OSStatus as_empty = 'qtem'; + + // Converter callback + static OSStatus converterCallback(AudioConverterRef inAudioConverter, + UInt32* ioNumberDataPackets, + AudioBufferList* ioData, + AudioStreamPacketDescription** outDataPacketDescription, + void* inUserData) + { + Q_UNUSED(inAudioConverter); + Q_UNUSED(outDataPacketDescription); + + QAudioPacketFeeder* feeder = static_cast<QAudioPacketFeeder*>(inUserData); + + if (!feeder->feed(*ioData, *ioNumberDataPackets)) + return as_empty; + + return noErr; + } +}; + + +class MacInputDevice : public QIODevice +{ + Q_OBJECT + +public: + MacInputDevice(QAudioInputBuffer* audioBuffer, QObject* parent): + QIODevice(parent), + m_audioBuffer(audioBuffer) + { + open(QIODevice::ReadOnly | QIODevice::Unbuffered); + connect(m_audioBuffer, SIGNAL(readyRead()), SIGNAL(readyRead())); + } + + qint64 readData(char* data, qint64 len) + { + return m_audioBuffer->readBytes(data, len); + } + + qint64 writeData(const char* data, qint64 len) + { + Q_UNUSED(data); + Q_UNUSED(len); + + return 0; + } + + bool isSequential() const + { + return true; + } + +private: + QAudioInputBuffer* m_audioBuffer; +}; + +} + + +QAudioInputPrivate::QAudioInputPrivate(const QByteArray& device, QAudioFormat const& format): + audioFormat(format) +{ + QDataStream ds(device); + quint32 did, mode; + + ds >> did >> mode; + + if (QAudio::Mode(mode) == QAudio::AudioOutput) + errorCode = QAudio::OpenError; + else { + audioDeviceInfo = new QAudioDeviceInfoInternal(device, QAudio::AudioInput); + isOpen = false; + audioDeviceId = AudioDeviceID(did); + audioUnit = 0; + startTime = 0; + totalFrames = 0; + audioBuffer = 0; + internalBufferSize = QtMultimediaInternal::default_buffer_size; + clockFrequency = AudioGetHostClockFrequency() / 1000; + errorCode = QAudio::NoError; + stateCode = QAudio::StoppedState; + + intervalTimer = new QTimer(this); + intervalTimer->setInterval(1000); + connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify())); + } +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + close(); + delete audioDeviceInfo; +} + +bool QAudioInputPrivate::open() +{ + UInt32 size = 0; + + if (isOpen) + return true; + + ComponentDescription cd; + cd.componentType = kAudioUnitType_Output; + cd.componentSubType = kAudioUnitSubType_HALOutput; + cd.componentManufacturer = kAudioUnitManufacturer_Apple; + cd.componentFlags = 0; + cd.componentFlagsMask = 0; + + // Open + Component cp = FindNextComponent(NULL, &cd); + if (cp == 0) { + qWarning() << "QAudioInput: Failed to find HAL Output component"; + return false; + } + + if (OpenAComponent(cp, &audioUnit) != noErr) { + qWarning() << "QAudioInput: Unable to Open Output Component"; + return false; + } + + // Set mode + // switch to input mode + UInt32 enable = 1; + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Input, + 1, + &enable, + sizeof(enable)) != noErr) { + qWarning() << "QAudioInput: Unable to switch to input mode (Enable Input)"; + return false; + } + + enable = 0; + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_EnableIO, + kAudioUnitScope_Output, + 0, + &enable, + sizeof(enable)) != noErr) { + qWarning() << "QAudioInput: Unable to switch to input mode (Disable output)"; + return false; + } + + // register callback + AURenderCallbackStruct cb; + cb.inputProc = inputCallback; + cb.inputProcRefCon = this; + + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_SetInputCallback, + kAudioUnitScope_Global, + 0, + &cb, + sizeof(cb)) != noErr) { + qWarning() << "QAudioInput: Failed to set AudioUnit callback"; + return false; + } + + // Set Audio Device + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 0, + &audioDeviceId, + sizeof(audioDeviceId)) != noErr) { + qWarning() << "QAudioInput: Unable to use configured device"; + return false; + } + + // Set format + // Wanted + streamFormat = toAudioStreamBasicDescription(audioFormat); + + // Required on unit + if (audioFormat == audioDeviceInfo->preferredFormat()) { + deviceFormat = streamFormat; + AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 1, + &deviceFormat, + sizeof(deviceFormat)); + } + else { + size = sizeof(deviceFormat); + if (AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 1, + &deviceFormat, + &size) != noErr) { + qWarning() << "QAudioInput: Unable to retrieve device format"; + return false; + } + + if (AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Output, + 1, + &deviceFormat, + sizeof(deviceFormat)) != noErr) { + qWarning() << "QAudioInput: Unable to set device format"; + return false; + } + } + + // Setup buffers + UInt32 numberOfFrames; + size = sizeof(UInt32); + if (AudioUnitGetProperty(audioUnit, + kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Global, + 0, + &numberOfFrames, + &size) != noErr) { + qWarning() << "QAudioInput: Failed to get audio period size"; + return false; + } + + // Allocate buffer + periodSizeBytes = numberOfFrames * streamFormat.mBytesPerFrame; + + if (internalBufferSize < periodSizeBytes * 2) + internalBufferSize = periodSizeBytes * 2; + else + internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame; + + audioBuffer = new QtMultimediaInternal::QAudioInputBuffer(internalBufferSize, + periodSizeBytes, + deviceFormat, + streamFormat, + this); + + audioIO = new QtMultimediaInternal::MacInputDevice(audioBuffer, this); + + // Init + if (AudioUnitInitialize(audioUnit) != noErr) { + qWarning() << "QAudioInput: Failed to initialize AudioUnit"; + return false; + } + + isOpen = true; + + return isOpen; +} + +void QAudioInputPrivate::close() +{ + if (audioUnit != 0) { + AudioOutputUnitStop(audioUnit); + AudioUnitUninitialize(audioUnit); + CloseComponent(audioUnit); + } + + delete audioBuffer; +} + +QAudioFormat QAudioInputPrivate::format() const +{ + return audioFormat; +} + +QIODevice* QAudioInputPrivate::start(QIODevice* device) +{ + QIODevice* op = device; + + if (!audioFormat.isValid() || !open()) { + stateCode = QAudio::StoppedState; + errorCode = QAudio::OpenError; + return audioIO; + } + + reset(); + audioBuffer->reset(); + audioBuffer->setFlushDevice(op); + + if (op == 0) + op = audioIO; + + // Start + startTime = AudioGetCurrentHostTime(); + totalFrames = 0; + + audioThreadStart(); + + stateCode = QAudio::ActiveState; + errorCode = QAudio::NoError; + emit stateChanged(stateCode); + + return op; +} + +void QAudioInputPrivate::stop() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StoppedState) { + audioThreadStop(); + audioBuffer->flush(true); + + errorCode = QAudio::NoError; + stateCode = QAudio::StoppedState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioInputPrivate::reset() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StoppedState) { + audioThreadStop(); + + errorCode = QAudio::NoError; + stateCode = QAudio::StoppedState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioInputPrivate::suspend() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) { + audioThreadStop(); + + errorCode = QAudio::NoError; + stateCode = QAudio::SuspendedState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioInputPrivate::resume() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::SuspendedState) { + audioThreadStart(); + + errorCode = QAudio::NoError; + stateCode = QAudio::ActiveState; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +int QAudioInputPrivate::bytesReady() const +{ + return audioBuffer->used(); +} + +int QAudioInputPrivate::periodSize() const +{ + return periodSizeBytes; +} + +void QAudioInputPrivate::setBufferSize(int bs) +{ + internalBufferSize = bs; +} + +int QAudioInputPrivate::bufferSize() const +{ + return internalBufferSize; +} + +void QAudioInputPrivate::setNotifyInterval(int milliSeconds) +{ + if (intervalTimer->interval() == milliSeconds) + return; + + if (milliSeconds <= 0) + milliSeconds = 0; + + intervalTimer->setInterval(milliSeconds); +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTimer->interval(); +} + +qint64 QAudioInputPrivate::processedUSecs() const +{ + return totalFrames * 1000000 / audioFormat.frequency(); +} + +qint64 QAudioInputPrivate::elapsedUSecs() const +{ + if (stateCode == QAudio::StoppedState) + return 0; + + return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000); +} + +QAudio::Error QAudioInputPrivate::error() const +{ + return errorCode; +} + +QAudio::State QAudioInputPrivate::state() const +{ + return stateCode; +} + +void QAudioInputPrivate::audioThreadStop() +{ + stopTimers(); + if (audioThreadState.testAndSetAcquire(Running, Stopped)) + threadFinished.wait(&mutex); +} + +void QAudioInputPrivate::audioThreadStart() +{ + startTimers(); + audioThreadState = Running; + AudioOutputUnitStart(audioUnit); +} + +void QAudioInputPrivate::audioDeviceStop() +{ + AudioOutputUnitStop(audioUnit); + audioThreadState = Stopped; + threadFinished.wakeOne(); +} + +void QAudioInputPrivate::audioDeviceFull() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::UnderrunError; + stateCode = QAudio::IdleState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioInputPrivate::audioDeviceError() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::IOError; + stateCode = QAudio::StoppedState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioInputPrivate::startTimers() +{ + audioBuffer->startFlushTimer(); + if (intervalTimer->interval() > 0) + intervalTimer->start(); +} + +void QAudioInputPrivate::stopTimers() +{ + audioBuffer->stopFlushTimer(); + intervalTimer->stop(); +} + +void QAudioInputPrivate::deviceStopped() +{ + stopTimers(); + emit stateChanged(stateCode); +} + +// Input callback +OSStatus QAudioInputPrivate::inputCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) +{ + Q_UNUSED(ioData); + + QAudioInputPrivate* d = static_cast<QAudioInputPrivate*>(inRefCon); + + const int threadState = d->audioThreadState.fetchAndAddAcquire(0); + if (threadState == Stopped) + d->audioDeviceStop(); + else { + qint64 framesWritten; + + framesWritten = d->audioBuffer->renderFromDevice(d->audioUnit, + ioActionFlags, + inTimeStamp, + inBusNumber, + inNumberFrames); + + if (framesWritten > 0) + d->totalFrames += framesWritten; + else if (framesWritten == 0) + d->audioDeviceFull(); + else if (framesWritten < 0) + d->audioDeviceError(); + } + + return noErr; +} + + +QT_END_NAMESPACE + +#include "qaudioinput_mac_p.moc" + diff --git a/src/multimedia/multimedia/audio/qaudioinput_mac_p.h b/src/multimedia/multimedia/audio/qaudioinput_mac_p.h new file mode 100644 index 0000000..7aa4168 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioinput_mac_p.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#ifndef QAUDIOINPUT_MAC_P_H +#define QAUDIOINPUT_MAC_P_H + +#include <CoreServices/CoreServices.h> +#include <CoreAudio/CoreAudio.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <QtCore/qobject.h> +#include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> +#include <QtCore/qatomic.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QTimer; +class QIODevice; +class QAbstractAudioDeviceInfo; + +namespace QtMultimediaInternal +{ +class QAudioInputBuffer; +} + +class QAudioInputPrivate : public QAbstractAudioInput +{ + Q_OBJECT + +public: + bool isOpen; + int periodSizeBytes; + int internalBufferSize; + qint64 totalFrames; + QAudioFormat audioFormat; + QIODevice* audioIO; + AudioUnit audioUnit; + AudioDeviceID audioDeviceId; + Float64 clockFrequency; + UInt64 startTime; + QAudio::Error errorCode; + QAudio::State stateCode; + QtMultimediaInternal::QAudioInputBuffer* audioBuffer; + QMutex mutex; + QWaitCondition threadFinished; + QAtomicInt audioThreadState; + QTimer* intervalTimer; + AudioStreamBasicDescription streamFormat; + AudioStreamBasicDescription deviceFormat; + QAbstractAudioDeviceInfo *audioDeviceInfo; + + QAudioInputPrivate(const QByteArray& device, QAudioFormat const& format); + ~QAudioInputPrivate(); + + bool open(); + void close(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice* device); + void stop(); + void reset(); + void suspend(); + void resume(); + void idle(); + + int bytesReady() const; + int periodSize() const; + + void setBufferSize(int value); + int bufferSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + + QAudio::Error error() const; + QAudio::State state() const; + + void audioThreadStart(); + void audioThreadStop(); + + void audioDeviceStop(); + void audioDeviceFull(); + void audioDeviceError(); + + void startTimers(); + void stopTimers(); + +private slots: + void deviceStopped(); + +private: + enum { Running, Stopped }; + + // Input callback + static OSStatus inputCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOINPUT_MAC_P_H diff --git a/src/multimedia/multimedia/audio/qaudioinput_symbian_p.cpp b/src/multimedia/multimedia/audio/qaudioinput_symbian_p.cpp new file mode 100644 index 0000000..52daa88 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioinput_symbian_p.cpp @@ -0,0 +1,594 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 "qaudioinput_symbian_p.h" + +QT_BEGIN_NAMESPACE + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- + +const int PushInterval = 50; // ms + + +//----------------------------------------------------------------------------- +// Private class +//----------------------------------------------------------------------------- + +SymbianAudioInputPrivate::SymbianAudioInputPrivate( + QAudioInputPrivate *audioDevice) + : m_audioDevice(audioDevice) +{ + +} + +SymbianAudioInputPrivate::~SymbianAudioInputPrivate() +{ + +} + +qint64 SymbianAudioInputPrivate::readData(char *data, qint64 len) +{ + qint64 totalRead = 0; + + if (m_audioDevice->state() == QAudio::ActiveState || + m_audioDevice->state() == QAudio::IdleState) { + + while (totalRead < len) { + const qint64 read = m_audioDevice->read(data + totalRead, + len - totalRead); + if (read > 0) + totalRead += read; + else + break; + } + } + + return totalRead; +} + +qint64 SymbianAudioInputPrivate::writeData(const char *data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + return 0; +} + +void SymbianAudioInputPrivate::dataReady() +{ + emit readyRead(); +} + + +//----------------------------------------------------------------------------- +// Public functions +//----------------------------------------------------------------------------- + +QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, + const QAudioFormat &format) + : m_device(device) + , m_format(format) + , m_clientBufferSize(SymbianAudio::DefaultBufferSize) + , m_notifyInterval(SymbianAudio::DefaultNotifyInterval) + , m_notifyTimer(new QTimer(this)) + , m_error(QAudio::NoError) + , m_internalState(SymbianAudio::ClosedState) + , m_externalState(QAudio::StoppedState) + , m_pullMode(false) + , m_sink(0) + , m_pullTimer(new QTimer(this)) + , m_devSoundBuffer(0) + , m_devSoundBufferSize(0) + , m_totalBytesReady(0) + , m_devSoundBufferPos(0) + , m_totalSamplesRecorded(0) +{ + connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify())); + + SymbianAudio::Utils::formatQtToNative(m_format, m_nativeFourCC, + m_nativeFormat); + + m_pullTimer->setInterval(PushInterval); + connect(m_pullTimer.data(), SIGNAL(timeout()), this, SLOT(pullData())); +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + close(); +} + +QIODevice* QAudioInputPrivate::start(QIODevice *device) +{ + stop(); + + open(); + if (SymbianAudio::ClosedState != m_internalState) { + if (device) { + m_pullMode = true; + m_sink = device; + } else { + m_sink = new SymbianAudioInputPrivate(this); + m_sink->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + } + + m_elapsed.restart(); + } + + return m_sink; +} + +void QAudioInputPrivate::stop() +{ + close(); +} + +void QAudioInputPrivate::reset() +{ + m_totalSamplesRecorded += getSamplesRecorded(); + m_devSound->Stop(); + startRecording(); +} + +void QAudioInputPrivate::suspend() +{ + if (SymbianAudio::ActiveState == m_internalState + || SymbianAudio::IdleState == m_internalState) { + m_notifyTimer->stop(); + m_pullTimer->stop(); + m_devSound->Pause(); + const qint64 samplesRecorded = getSamplesRecorded(); + m_totalSamplesRecorded += samplesRecorded; + + if (m_devSoundBuffer) { + m_devSoundBufferQ.append(m_devSoundBuffer); + m_devSoundBuffer = 0; + } + + setState(SymbianAudio::SuspendedState); + } +} + +void QAudioInputPrivate::resume() +{ + if (SymbianAudio::SuspendedState == m_internalState) + startDataTransfer(); +} + +int QAudioInputPrivate::bytesReady() const +{ + Q_ASSERT(m_devSoundBufferPos <= m_totalBytesReady); + return m_totalBytesReady - m_devSoundBufferPos; +} + +int QAudioInputPrivate::periodSize() const +{ + return bufferSize(); +} + +void QAudioInputPrivate::setBufferSize(int value) +{ + // Note that DevSound does not allow its client to specify the buffer size. + // This functionality is available via custom interfaces, but since these + // cannot be guaranteed to work across all DevSound implementations, we + // do not use them here. + // In order to comply with the expected bevahiour of QAudioInput, we store + // the value and return it from bufferSize(), but the underlying DevSound + // buffer size remains unchanged. + if (value > 0) + m_clientBufferSize = value; +} + +int QAudioInputPrivate::bufferSize() const +{ + return m_devSoundBufferSize ? m_devSoundBufferSize : m_clientBufferSize; +} + +void QAudioInputPrivate::setNotifyInterval(int ms) +{ + if (ms > 0) { + const int oldNotifyInterval = m_notifyInterval; + m_notifyInterval = ms; + if (m_notifyTimer->isActive() && ms != oldNotifyInterval) + m_notifyTimer->start(m_notifyInterval); + } +} + +int QAudioInputPrivate::notifyInterval() const +{ + return m_notifyInterval; +} + +qint64 QAudioInputPrivate::processedUSecs() const +{ + int samplesPlayed = 0; + if (m_devSound && SymbianAudio::SuspendedState != m_internalState) + samplesPlayed = getSamplesRecorded(); + + // Protect against division by zero + Q_ASSERT_X(m_format.frequency() > 0, Q_FUNC_INFO, "Invalid frequency"); + + const qint64 result = qint64(1000000) * + (samplesPlayed + m_totalSamplesRecorded) + / m_format.frequency(); + + return result; +} + +qint64 QAudioInputPrivate::elapsedUSecs() const +{ + const qint64 result = (QAudio::StoppedState == state()) ? + 0 : m_elapsed.elapsed() * 1000; + return result; +} + +QAudio::Error QAudioInputPrivate::error() const +{ + return m_error; +} + +QAudio::State QAudioInputPrivate::state() const +{ + return m_externalState; +} + +QAudioFormat QAudioInputPrivate::format() const +{ + return m_format; +} + +//----------------------------------------------------------------------------- +// MDevSoundObserver implementation +//----------------------------------------------------------------------------- + +void QAudioInputPrivate::InitializeComplete(TInt aError) +{ + Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState, + Q_FUNC_INFO, "Invalid state"); + + if (KErrNone == aError) + startRecording(); +} + +void QAudioInputPrivate::ToneFinished(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound's tone playback functions, so should + // never receive this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void QAudioInputPrivate::BufferToBeFilled(CMMFBuffer *aBuffer) +{ + Q_UNUSED(aBuffer) + // This class doesn't use DevSound in play mode, so should never receive + // this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void QAudioInputPrivate::PlayError(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound in play mode, so should never receive + // this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void QAudioInputPrivate::BufferToBeEmptied(CMMFBuffer *aBuffer) +{ + // Following receipt of this callback, DevSound should not provide another + // buffer until we have returned the current one. + Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held"); + + CMMFDataBuffer *const buffer = static_cast<CMMFDataBuffer*>(aBuffer); + + if (!m_devSoundBufferSize) + m_devSoundBufferSize = buffer->Data().MaxLength(); + + m_totalBytesReady += buffer->Data().Length(); + + if (SymbianAudio::SuspendedState == m_internalState) { + m_devSoundBufferQ.append(buffer); + } else { + // Will be returned to DevSound by bufferEmptied(). + m_devSoundBuffer = buffer; + m_devSoundBufferPos = 0; + + if (bytesReady() && !m_pullMode) + pushData(); + } +} + +void QAudioInputPrivate::RecordError(TInt aError) +{ + Q_UNUSED(aError) + setError(QAudio::IOError); +} + +void QAudioInputPrivate::ConvertError(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound's format conversion functions, so + // should never receive this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void QAudioInputPrivate::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg) +{ + Q_UNUSED(aMessageType) + Q_UNUSED(aMsg) + // Ignore this callback. +} + +//----------------------------------------------------------------------------- +// Private functions +//----------------------------------------------------------------------------- + +void QAudioInputPrivate::open() +{ + Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState, + Q_FUNC_INFO, "DevSound already opened"); + + QT_TRAP_THROWING( m_devSound.reset(CMMFDevSound::NewL()) ) + + QScopedPointer<SymbianAudio::DevSoundCapabilities> caps( + new SymbianAudio::DevSoundCapabilities(*m_devSound, QAudio::AudioInput)); + + int err = SymbianAudio::Utils::isFormatSupported(m_format, *caps) ? + KErrNone : KErrNotSupported; + + if (KErrNone == err) { + setState(SymbianAudio::InitializingState); + TRAP(err, m_devSound->InitializeL(*this, m_nativeFourCC, + EMMFStateRecording)); + } + + if (KErrNone != err) { + setError(QAudio::OpenError); + m_devSound.reset(); + } +} + +void QAudioInputPrivate::startRecording() +{ + const int samplesRecorded = m_devSound->SamplesRecorded(); + Q_ASSERT(samplesRecorded == 0); + + TRAPD(err, startDevSoundL()); + if (KErrNone == err) { + startDataTransfer(); + } else { + setError(QAudio::OpenError); + close(); + } +} + +void QAudioInputPrivate::startDevSoundL() +{ + TMMFCapabilities nativeFormat = m_devSound->Config(); + m_nativeFormat.iBufferSize = nativeFormat.iBufferSize; + m_devSound->SetConfigL(m_nativeFormat); + m_devSound->RecordInitL(); +} + +void QAudioInputPrivate::startDataTransfer() +{ + m_notifyTimer->start(m_notifyInterval); + + if (m_pullMode) + m_pullTimer->start(); + + if (bytesReady()) { + setState(SymbianAudio::ActiveState); + if (!m_pullMode) + pushData(); + } else { + if (SymbianAudio::SuspendedState == m_internalState) + setState(SymbianAudio::ActiveState); + else + setState(SymbianAudio::IdleState); + } +} + +CMMFDataBuffer* QAudioInputPrivate::currentBuffer() const +{ + CMMFDataBuffer *result = m_devSoundBuffer; + if (!result && !m_devSoundBufferQ.empty()) + result = m_devSoundBufferQ.front(); + return result; +} + +void QAudioInputPrivate::pushData() +{ + Q_ASSERT_X(bytesReady(), Q_FUNC_INFO, "No data available"); + Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO, "pushData called when in pull mode"); + qobject_cast<SymbianAudioInputPrivate *>(m_sink)->dataReady(); +} + +qint64 QAudioInputPrivate::read(char *data, qint64 len) +{ + // SymbianAudioInputPrivate is ready to read data + + Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO, + "read called when in pull mode"); + + qint64 bytesRead = 0; + + CMMFDataBuffer *buffer = 0; + while ((buffer = currentBuffer()) && (bytesRead < len)) { + if (SymbianAudio::IdleState == m_internalState) + setState(SymbianAudio::ActiveState); + + TDesC8 &inputBuffer = buffer->Data(); + + const qint64 inputBytes = bytesReady(); + const qint64 outputBytes = len - bytesRead; + const qint64 copyBytes = outputBytes < inputBytes ? + outputBytes : inputBytes; + + memcpy(data, inputBuffer.Ptr() + m_devSoundBufferPos, copyBytes); + + m_devSoundBufferPos += copyBytes; + data += copyBytes; + bytesRead += copyBytes; + + if (!bytesReady()) + bufferEmptied(); + } + + return bytesRead; +} + +void QAudioInputPrivate::pullData() +{ + Q_ASSERT_X(m_pullMode, Q_FUNC_INFO, + "pullData called when in push mode"); + + CMMFDataBuffer *buffer = 0; + while (buffer = currentBuffer()) { + if (SymbianAudio::IdleState == m_internalState) + setState(SymbianAudio::ActiveState); + + TDesC8 &inputBuffer = buffer->Data(); + + const qint64 inputBytes = bytesReady(); + const qint64 bytesPushed = m_sink->write( + (char*)inputBuffer.Ptr() + m_devSoundBufferPos, inputBytes); + + m_devSoundBufferPos += bytesPushed; + + if (!bytesReady()) + bufferEmptied(); + + if (!bytesPushed) + break; + } +} + +void QAudioInputPrivate::bufferEmptied() +{ + m_devSoundBufferPos = 0; + + if (m_devSoundBuffer) { + m_totalBytesReady -= m_devSoundBuffer->Data().Length(); + m_devSoundBuffer = 0; + m_devSound->RecordData(); + } else { + Q_ASSERT(!m_devSoundBufferQ.empty()); + m_totalBytesReady -= m_devSoundBufferQ.front()->Data().Length(); + m_devSoundBufferQ.erase(m_devSoundBufferQ.begin()); + + // If the queue has been emptied, resume transfer from the hardware + if (m_devSoundBufferQ.empty()) + m_devSound->RecordInitL(); + } + + Q_ASSERT(m_totalBytesReady >= 0); +} + +void QAudioInputPrivate::close() +{ + m_notifyTimer->stop(); + m_pullTimer->stop(); + + m_error = QAudio::NoError; + + if (m_devSound) + m_devSound->Stop(); + m_devSound.reset(); + m_devSoundBuffer = 0; + m_devSoundBufferSize = 0; + m_totalBytesReady = 0; + + if (!m_pullMode) // m_sink is owned + delete m_sink; + m_pullMode = false; + m_sink = 0; + + m_devSoundBufferQ.clear(); + m_devSoundBufferPos = 0; + m_totalSamplesRecorded = 0; + + setState(SymbianAudio::ClosedState); +} + +qint64 QAudioInputPrivate::getSamplesRecorded() const +{ + qint64 result = 0; + if (m_devSound) + result = qint64(m_devSound->SamplesRecorded()); + return result; +} + +void QAudioInputPrivate::setError(QAudio::Error error) +{ + m_error = error; + + // Although no state transition actually occurs here, a stateChanged event + // must be emitted to inform the client that the call to start() was + // unsuccessful. + if (QAudio::OpenError == error) + emit stateChanged(QAudio::StoppedState); + + // Close the DevSound instance. This causes a transition to StoppedState. + // This must be done asynchronously in case the current function was called + // from a DevSound event handler, in which case deleting the DevSound + // instance may cause an exception. + QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); +} + +void QAudioInputPrivate::setState(SymbianAudio::State newInternalState) +{ + const QAudio::State oldExternalState = m_externalState; + m_internalState = newInternalState; + m_externalState = SymbianAudio::Utils::stateNativeToQt( + m_internalState, initializingState()); + + if (m_externalState != oldExternalState) + emit stateChanged(m_externalState); +} + +QAudio::State QAudioInputPrivate::initializingState() const +{ + return QAudio::IdleState; +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudioinput_symbian_p.h b/src/multimedia/multimedia/audio/qaudioinput_symbian_p.h new file mode 100644 index 0000000..ca3ccf7 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioinput_symbian_p.h @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOINPUT_SYMBIAN_P_H +#define QAUDIOINPUT_SYMBIAN_P_H + +#include <QtMultimedia/qaudioengine.h> +#include <QTime> +#include <QTimer> +#include <sounddevice.h> +#include "qaudio_symbian_p.h" + +QT_BEGIN_NAMESPACE + +class QAudioInputPrivate; + +class SymbianAudioInputPrivate : public QIODevice +{ + friend class QAudioInputPrivate; + Q_OBJECT +public: + SymbianAudioInputPrivate(QAudioInputPrivate *audio); + ~SymbianAudioInputPrivate(); + + qint64 readData(char *data, qint64 len); + qint64 writeData(const char *data, qint64 len); + + void dataReady(); + +private: + QAudioInputPrivate *const m_audioDevice; +}; + +class QAudioInputPrivate + : public QAbstractAudioInput + , public MDevSoundObserver +{ + friend class SymbianAudioInputPrivate; + Q_OBJECT +public: + QAudioInputPrivate(const QByteArray &device, + const QAudioFormat &audioFormat); + ~QAudioInputPrivate(); + + // QAbstractAudioInput + QIODevice* start(QIODevice *device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesReady() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + QAudioFormat format() const; + + // MDevSoundObserver + void InitializeComplete(TInt aError); + void ToneFinished(TInt aError); + void BufferToBeFilled(CMMFBuffer *aBuffer); + void PlayError(TInt aError); + void BufferToBeEmptied(CMMFBuffer *aBuffer); + void RecordError(TInt aError); + void ConvertError(TInt aError); + void DeviceMessage(TUid aMessageType, const TDesC8 &aMsg); + +private slots: + void pullData(); + +private: + void open(); + void startRecording(); + void startDevSoundL(); + void startDataTransfer(); + CMMFDataBuffer* currentBuffer() const; + void pushData(); + qint64 read(char *data, qint64 len); + void bufferEmptied(); + Q_INVOKABLE void close(); + + qint64 getSamplesRecorded() const; + + void setError(QAudio::Error error); + void setState(SymbianAudio::State state); + + QAudio::State initializingState() const; + +private: + const QByteArray m_device; + const QAudioFormat m_format; + + int m_clientBufferSize; + int m_notifyInterval; + QScopedPointer<QTimer> m_notifyTimer; + QTime m_elapsed; + QAudio::Error m_error; + + SymbianAudio::State m_internalState; + QAudio::State m_externalState; + + bool m_pullMode; + QIODevice *m_sink; + + QScopedPointer<QTimer> m_pullTimer; + + QScopedPointer<CMMFDevSound> m_devSound; + TUint32 m_nativeFourCC; + TMMFCapabilities m_nativeFormat; + + // Latest buffer provided by DevSound, to be empied of data. + CMMFDataBuffer *m_devSoundBuffer; + + int m_devSoundBufferSize; + + // Total amount of data in buffers provided by DevSound + int m_totalBytesReady; + + // Queue of buffers returned after call to CMMFDevSound::Pause(). + QList<CMMFDataBuffer *> m_devSoundBufferQ; + + // Current read position within m_devSoundBuffer + qint64 m_devSoundBufferPos; + + // Samples recorded up to the last call to suspend(). It is necessary + // to cache this because suspend() is implemented using + // CMMFDevSound::Stop(), which resets DevSound's SamplesRecorded() counter. + quint32 m_totalSamplesRecorded; + +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/audio/qaudioinput_win32_p.cpp b/src/multimedia/multimedia/audio/qaudioinput_win32_p.cpp new file mode 100644 index 0000000..b5d673e --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioinput_win32_p.cpp @@ -0,0 +1,613 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + + +#include "qaudioinput_win32_p.h" + +QT_BEGIN_NAMESPACE + +//#define DEBUG_AUDIO 1 + +static const int minimumIntervalTime = 50; + +QAudioInputPrivate::QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + buffer_size = 0; + period_size = 0; + m_device = device; + totalTimeValue = 0; + intervalTime = 1000; + errorState = QAudio::NoError; + deviceState = QAudio::StoppedState; + audioSource = 0; + pullMode = true; + resuming = false; + finished = false; + + connect(this,SIGNAL(processMore()),SLOT(deviceReady())); + + InitializeCriticalSection(&waveInCriticalSection); +} + +QAudioInputPrivate::~QAudioInputPrivate() +{ + stop(); + DeleteCriticalSection(&waveInCriticalSection); +} + +void QT_WIN_CALLBACK QAudioInputPrivate::waveInProc( HWAVEIN hWaveIn, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) +{ + Q_UNUSED(dwParam1) + Q_UNUSED(dwParam2) + Q_UNUSED(hWaveIn) + + QAudioInputPrivate* qAudio; + qAudio = (QAudioInputPrivate*)(dwInstance); + if(!qAudio) + return; + + switch(uMsg) { + case WIM_OPEN: + break; + case WIM_DATA: + EnterCriticalSection(&qAudio->waveInCriticalSection); + if(qAudio->waveFreeBlockCount > 0) + qAudio->waveFreeBlockCount--; + qAudio->feedback(); + LeaveCriticalSection(&qAudio->waveInCriticalSection); + break; + case WIM_CLOSE: + EnterCriticalSection(&qAudio->waveInCriticalSection); + qAudio->finished = true; + LeaveCriticalSection(&qAudio->waveInCriticalSection); + break; + default: + return; + } +} + +WAVEHDR* QAudioInputPrivate::allocateBlocks(int size, int count) +{ + int i; + unsigned char* buffer; + WAVEHDR* blocks; + DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count; + + if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, + totalBufferSize)) == 0) { + qWarning("QAudioInput: Memory allocation error"); + return 0; + } + blocks = (WAVEHDR*)buffer; + buffer += sizeof(WAVEHDR)*count; + for(i = 0; i < count; i++) { + blocks[i].dwBufferLength = size; + blocks[i].lpData = (LPSTR)buffer; + blocks[i].dwBytesRecorded=0; + blocks[i].dwUser = 0L; + blocks[i].dwFlags = 0L; + blocks[i].dwLoops = 0L; + result = waveInPrepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: Can't prepare block %d",i); + return 0; + } + buffer += size; + } + return blocks; +} + +void QAudioInputPrivate::freeBlocks(WAVEHDR* blockArray) +{ + WAVEHDR* blocks = blockArray; + + int count = buffer_size/period_size; + + for(int i = 0; i < count; i++) { + waveInUnprepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR)); + blocks+=sizeof(WAVEHDR); + } + HeapFree(GetProcessHeap(), 0, blockArray); +} + +QAudio::Error QAudioInputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioInputPrivate::state() const +{ + return deviceState; +} + +QAudioFormat QAudioInputPrivate::format() const +{ + return settings; +} + +QIODevice* QAudioInputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StoppedState) + close(); + + if(!pullMode && audioSource) { + delete audioSource; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + deviceState = QAudio::ActiveState; + } else { + //set to push mode + pullMode = false; + deviceState = QAudio::IdleState; + audioSource = new InputPrivate(this); + audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + } + + if( !open() ) + return 0; + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioInputPrivate::stop() +{ + if(deviceState == QAudio::StoppedState) + return; + + close(); + emit stateChanged(deviceState); +} + +bool QAudioInputPrivate::open() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + header = 0; + if(buffer_size == 0) { + // Default buffer size, 200ms, default period size is 40ms + buffer_size = settings.frequency()*settings.channels()*(settings.sampleSize()/8)*0.2; + period_size = buffer_size/5; + } else { + period_size = buffer_size/5; + } +#ifdef Q_OS_WINCE + // For wince reduce size to 40ms for buffer size and 20ms period + buffer_size = settings.sampleRate()*settings.channelCount()*(settings.sampleSize()/8)*0.04; + period_size = buffer_size/2; +#endif + timeStamp.restart(); + elapsedTimeOffset = 0; + wfx.nSamplesPerSec = settings.frequency(); + wfx.wBitsPerSample = settings.sampleSize(); + wfx.nChannels = settings.channels(); + wfx.cbSize = 0; + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; + + UINT_PTR devId = WAVE_MAPPER; + + WAVEINCAPS wic; + unsigned long iNumDevs,ii; + iNumDevs = waveInGetNumDevs(); + for(ii=0;ii<iNumDevs;ii++) { + if(waveInGetDevCaps(ii, &wic, sizeof(WAVEINCAPS)) + == MMSYSERR_NOERROR) { + QString tmp; + tmp = QString((const QChar *)wic.szPname); + if(tmp.compare(QLatin1String(m_device)) == 0) { + devId = ii; + break; + } + } + } + + if(waveInOpen(&hWaveIn, devId, &wfx, + (DWORD_PTR)&waveInProc, + (DWORD_PTR) this, + CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { + errorState = QAudio::OpenError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + qWarning("QAudioInput: failed to open audio device"); + return false; + } + waveBlocks = allocateBlocks(period_size, buffer_size/period_size); + + if(waveBlocks == 0) { + errorState = QAudio::OpenError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + qWarning("QAudioInput: failed to allocate blocks. open failed"); + return false; + } + + EnterCriticalSection(&waveInCriticalSection); + waveFreeBlockCount = buffer_size/period_size; + LeaveCriticalSection(&waveInCriticalSection); + + waveCurrentBlock = 0; + + for(int i=0; i<buffer_size/period_size; i++) { + result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to setup block %d,err=%d",i,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + return false; + } + } + result = waveInStart(hWaveIn); + if(result) { + qWarning("QAudioInput: failed to start audio input"); + errorState = QAudio::OpenError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + return false; + } + timeStampOpened.restart(); + elapsedTimeOffset = 0; + totalTimeValue = 0; + errorState = QAudio::NoError; + return true; +} + +void QAudioInputPrivate::close() +{ + if(deviceState == QAudio::StoppedState) + return; + + deviceState = QAudio::StoppedState; + waveInReset(hWaveIn); + waveInClose(hWaveIn); + + int count = 0; + while(!finished && count < 500) { + count++; + Sleep(10); + } + + EnterCriticalSection(&waveInCriticalSection); + for(int i=0; i<waveFreeBlockCount; i++) { + if(waveBlocks[i].dwFlags & WHDR_PREPARED) + waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR)); + } + LeaveCriticalSection(&waveInCriticalSection); + freeBlocks(waveBlocks); +} + +int QAudioInputPrivate::bytesReady() const +{ + if(period_size == 0 || buffer_size == 0) + return 0; + + int buf = ((buffer_size/period_size)-waveFreeBlockCount)*period_size; + if(buf < 0) + buf = 0; + return buf; +} + +qint64 QAudioInputPrivate::read(char* data, qint64 len) +{ + bool done = false; + + char* p = data; + qint64 l = 0; + qint64 written = 0; + while(!done) { + // Read in some audio data + if(waveBlocks[header].dwBytesRecorded > 0 && waveBlocks[header].dwFlags & WHDR_DONE) { + if(pullMode) { + l = audioSource->write(waveBlocks[header].lpData, + waveBlocks[header].dwBytesRecorded); +#ifdef DEBUG_AUDIO + qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l; +#endif + if(l < 0) { + // error + qWarning("QAudioInput: IOError"); + errorState = QAudio::IOError; + + } else if(l == 0) { + // cant write to IODevice + qWarning("QAudioInput: IOError, can't write to QIODevice"); + errorState = QAudio::IOError; + + } else { + totalTimeValue += waveBlocks[header].dwBytesRecorded; + errorState = QAudio::NoError; + if (deviceState != QAudio::ActiveState) { + deviceState = QAudio::ActiveState; + emit stateChanged(deviceState); + } + resuming = false; + } + } else { + // push mode + memcpy(p,waveBlocks[header].lpData,waveBlocks[header].dwBytesRecorded); + l = waveBlocks[header].dwBytesRecorded; +#ifdef DEBUG_AUDIO + qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l; +#endif + totalTimeValue += waveBlocks[header].dwBytesRecorded; + errorState = QAudio::NoError; + if (deviceState != QAudio::ActiveState) { + deviceState = QAudio::ActiveState; + emit stateChanged(deviceState); + } + resuming = false; + } + } else { + //no data, not ready yet, next time + break; + } + + waveInUnprepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); + + EnterCriticalSection(&waveInCriticalSection); + waveFreeBlockCount++; + LeaveCriticalSection(&waveInCriticalSection); + waveBlocks[header].dwBytesRecorded=0; + waveBlocks[header].dwFlags = 0L; + result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR)); + qWarning("QAudioInput: failed to prepare block %d,err=%d",header,result); + errorState = QAudio::IOError; + EnterCriticalSection(&waveInCriticalSection); + waveFreeBlockCount--; + LeaveCriticalSection(&waveInCriticalSection); + return 0; + } + result = waveInAddBuffer(hWaveIn, &waveBlocks[header], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to setup block %d,err=%d",header,result); + errorState = QAudio::IOError; + EnterCriticalSection(&waveInCriticalSection); + waveFreeBlockCount--; + LeaveCriticalSection(&waveInCriticalSection); + return 0; + } + header++; + if(header >= buffer_size/period_size) + header = 0; + p+=l; + + EnterCriticalSection(&waveInCriticalSection); + if(!pullMode) { + if(l+period_size > len && waveFreeBlockCount == buffer_size/period_size) + done = true; + } else { + if(waveFreeBlockCount == buffer_size/period_size) + done = true; + } + LeaveCriticalSection(&waveInCriticalSection); + + written+=l; + } +#ifdef DEBUG_AUDIO + qDebug()<<"read in len="<<written; +#endif + return written; +} + +void QAudioInputPrivate::resume() +{ + if(deviceState == QAudio::SuspendedState) { + deviceState = QAudio::ActiveState; + for(int i=0; i<buffer_size/period_size; i++) { + result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR)); + if(result != MMSYSERR_NOERROR) { + qWarning("QAudioInput: failed to setup block %d,err=%d",i,result); + errorState = QAudio::OpenError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + return; + } + } + EnterCriticalSection(&waveInCriticalSection); + waveFreeBlockCount = buffer_size/period_size; + LeaveCriticalSection(&waveInCriticalSection); + + waveCurrentBlock = 0; + header = 0; + resuming = true; + waveInStart(hWaveIn); + QTimer::singleShot(20,this,SLOT(feedback())); + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::setBufferSize(int value) +{ + buffer_size = value; +} + +int QAudioInputPrivate::bufferSize() const +{ + return buffer_size; +} + +int QAudioInputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioInputPrivate::setNotifyInterval(int ms) +{ + if(ms >= minimumIntervalTime) + intervalTime = ms; + else + intervalTime = minimumIntervalTime; +} + +int QAudioInputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioInputPrivate::processedUSecs() const +{ + if (deviceState == QAudio::StoppedState) + return 0; + qint64 result = qint64(1000000) * totalTimeValue / + (settings.channels()*(settings.sampleSize()/8)) / + settings.frequency(); + + return result; +} + +void QAudioInputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState) { + waveInReset(hWaveIn); + deviceState = QAudio::SuspendedState; + emit stateChanged(deviceState); + } +} + +void QAudioInputPrivate::feedback() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback() INPUT "<<this; +#endif + bytesAvailable = bytesReady(); + + if(!(deviceState==QAudio::StoppedState||deviceState==QAudio::SuspendedState)) + emit processMore(); +} + +bool QAudioInputPrivate::deviceReady() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT"; +#endif + if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) + return true; + + if(pullMode) { + // reads some audio data and writes it to QIODevice + read(0,0); + } else { + // emits readyRead() so user will call read() on QIODevice to get some audio data + InputPrivate* a = qobject_cast<InputPrivate*>(audioSource); + a->trigger(); + } + + if((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) { + emit notify(); + elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime; + timeStamp.restart(); + } + return true; +} + +qint64 QAudioInputPrivate::elapsedUSecs() const +{ + if (deviceState == QAudio::StoppedState) + return 0; + + return timeStampOpened.elapsed()*1000; +} + +void QAudioInputPrivate::reset() +{ + close(); +} + +InputPrivate::InputPrivate(QAudioInputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioInputPrivate*>(audio); +} + +InputPrivate::~InputPrivate() {} + +qint64 InputPrivate::readData( char* data, qint64 len) +{ + // push mode, user read() called + if(audioDevice->deviceState != QAudio::ActiveState && + audioDevice->deviceState != QAudio::IdleState) + return 0; + // Read in some audio data + return audioDevice->read(data,len); +} + +qint64 InputPrivate::writeData(const char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + + emit readyRead(); + return 0; +} + +void InputPrivate::trigger() +{ + emit readyRead(); +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudioinput_win32_p.h b/src/multimedia/multimedia/audio/qaudioinput_win32_p.h new file mode 100644 index 0000000..66c2535 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudioinput_win32_p.h @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOINPUTWIN_H +#define QAUDIOINPUTWIN_H + +#include <windows.h> +#include <mmsystem.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +QT_BEGIN_NAMESPACE + +class QAudioInputPrivate : public QAbstractAudioInput +{ + Q_OBJECT +public: + QAudioInputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioInputPrivate(); + + qint64 read(char* data, qint64 len); + + QAudioFormat format() const; + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesReady() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private: + qint32 buffer_size; + qint32 period_size; + qint32 header; + QByteArray m_device; + int bytesAvailable; + int intervalTime; + QTime timeStamp; + qint64 elapsedTimeOffset; + QTime timeStampOpened; + qint64 totalTimeValue; + bool pullMode; + bool resuming; + WAVEFORMATEX wfx; + HWAVEIN hWaveIn; + MMRESULT result; + WAVEHDR* waveBlocks; + volatile bool finished; + volatile int waveFreeBlockCount; + int waveCurrentBlock; + + CRITICAL_SECTION waveInCriticalSection; + static void QT_WIN_CALLBACK waveInProc( HWAVEIN hWaveIn, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ); + + WAVEHDR* allocateBlocks(int size, int count); + void freeBlocks(WAVEHDR* blockArray); + bool open(); + void close(); + +private slots: + void feedback(); + bool deviceReady(); + +signals: + void processMore(); +}; + +class InputPrivate : public QIODevice +{ + Q_OBJECT +public: + InputPrivate(QAudioInputPrivate* audio); + ~InputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + + void trigger(); +private: + QAudioInputPrivate *audioDevice; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/audio/qaudiooutput.cpp b/src/multimedia/multimedia/audio/qaudiooutput.cpp new file mode 100644 index 0000000..b0b5244 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiooutput.cpp @@ -0,0 +1,411 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> +#include <QtMultimedia/qaudiooutput.h> + +#include "qaudiodevicefactory_p.h" + + +QT_BEGIN_NAMESPACE + +/*! + \class QAudioOutput + \brief The QAudioOutput class provides an interface for sending audio data to an audio output device. + + \inmodule QtMultimedia + \ingroup multimedia + \since 4.6 + + You can construct an audio output with the system's + \l{QAudioDeviceInfo::defaultOutputDevice()}{default audio output + device}. It is also possible to create QAudioOutput with a + specific QAudioDeviceInfo. When you create the audio output, you + should also send in the QAudioFormat to be used for the playback + (see the QAudioFormat class description for details). + + To play a file: + + Starting to play an audio stream is simply a matter of calling + start() with a QIODevice. QAudioOutput will then fetch the data it + needs from the io device. So playing back an audio file is as + simple as: + + \code + QFile inputFile; // class member. + QAudioOutput* audio; // class member. + \endcode + + \code + inputFile.setFileName("/tmp/test.raw"); + inputFile.open(QIODevice::ReadOnly); + + QAudioFormat format; + // Set up the format, eg. + format.setFrequency(8000); + format.setChannels(1); + format.setSampleSize(8); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::UnSignedInt); + + QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); + if (!info.isFormatSupported(format)) { + qWarning()<<"raw audio format not supported by backend, cannot play audio."; + return; + } + + audio = new QAudioOutput(format, this); + connect(audio,SIGNAL(stateChanged(QAudio::State)),SLOT(finishedPlaying(QAudio::State))); + audio->start(&inputFile); + + \endcode + + The file will start playing assuming that the audio system and + output device support it. If you run out of luck, check what's + up with the error() function. + + After the file has finished playing, we need to stop the device: + + \code + void finishedPlaying(QAudio::State state) + { + if(state == QAudio::IdleState) { + audio->stop(); + inputFile.close(); + delete audio; + } + } + \endcode + + At any given time, the QAudioOutput will be in one of four states: + active, suspended, stopped, or idle. These states are described + by the QAudio::State enum. + State changes are reported through the stateChanged() signal. You + can use this signal to, for instance, update the GUI of the + application; the mundane example here being changing the state of + a \c { play/pause } button. You request a state change directly + with suspend(), stop(), reset(), resume(), and start(). + + While the stream is playing, you can set a notify interval in + milliseconds with setNotifyInterval(). This interval specifies the + time between two emissions of the notify() signal. This is + relative to the position in the stream, i.e., if the QAudioOutput + is in the SuspendedState or the IdleState, the notify() signal is + not emitted. A typical use-case would be to update a + \l{QSlider}{slider} that allows seeking in the stream. + If you want the time since playback started regardless of which + states the audio output has been in, elapsedUSecs() is the function for you. + + If an error occurs, you can fetch the \l{QAudio::Error}{error + type} with the error() function. Please see the QAudio::Error enum + for a description of the possible errors that are reported. When + an error is encountered, the state changes to QAudio::StoppedState. + You can check for errors by connecting to the stateChanged() + signal: + + \snippet doc/src/snippets/audio/main.cpp 3 + + \sa QAudioInput, QAudioDeviceInfo +*/ + +/*! + Construct a new audio output and attach it to \a parent. + The default audio output device is used with the output + \a format parameters. +*/ + +QAudioOutput::QAudioOutput(const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createDefaultOutputDevice(format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Construct a new audio output and attach it to \a parent. + The device referenced by \a audioDevice is used with the output + \a format parameters. +*/ + +QAudioOutput::QAudioOutput(const QAudioDeviceInfo &audioDevice, const QAudioFormat &format, QObject *parent): + QObject(parent) +{ + d = QAudioDeviceFactory::createOutputDevice(audioDevice, format); + connect(d, SIGNAL(notify()), SIGNAL(notify())); + connect(d, SIGNAL(stateChanged(QAudio::State)), SIGNAL(stateChanged(QAudio::State))); +} + +/*! + Destroys this audio output. +*/ + +QAudioOutput::~QAudioOutput() +{ + delete d; +} + +/*! + Returns the QAudioFormat being used. + +*/ + +QAudioFormat QAudioOutput::format() const +{ + return d->format(); +} + +/*! + Uses the \a device as the QIODevice to transfer data. + Passing a QIODevice allows the data to be transfered without any extra code. + All that is required is to open the QIODevice. + + If able to successfully output audio data to the systems audio device the + state() is set to QAudio::ActiveState, error() is set to QAudio::NoError + and the stateChanged() signal is emitted. + + If a problem occurs during this process the error() is set to QAudio::OpenError, + state() is set to QAudio::StoppedState and stateChanged() signal is emitted. + + \sa QIODevice +*/ + +void QAudioOutput::start(QIODevice* device) +{ + d->start(device); +} + +/*! + Returns a pointer to the QIODevice being used to handle the data + transfer. This QIODevice can be used to write() audio data directly. + + If able to access the systems audio device the state() is set to + QAudio::IdleState, error() is set to QAudio::NoError + and the stateChanged() signal is emitted. + + If a problem occurs during this process the error() is set to QAudio::OpenError, + state() is set to QAudio::StoppedState and stateChanged() signal is emitted. + + \sa QIODevice +*/ + +QIODevice* QAudioOutput::start() +{ + return d->start(0); +} + +/*! + Stops the audio output, detaching from the system resource. + + Sets error() to QAudio::NoError, state() to QAudio::StoppedState and + emit stateChanged() signal. +*/ + +void QAudioOutput::stop() +{ + d->stop(); +} + +/*! + Drops all audio data in the buffers, resets buffers to zero. +*/ + +void QAudioOutput::reset() +{ + d->reset(); +} + +/*! + Stops processing audio data, preserving buffered audio data. + + Sets error() to QAudio::NoError, state() to QAudio::SuspendedState and + emit stateChanged() signal. +*/ + +void QAudioOutput::suspend() +{ + d->suspend(); +} + +/*! + Resumes processing audio data after a suspend(). + + Sets error() to QAudio::NoError. + Sets state() to QAudio::ActiveState if you previously called start(QIODevice*). + Sets state() to QAudio::IdleState if you previously called start(). + emits stateChanged() signal. +*/ + +void QAudioOutput::resume() +{ + d->resume(); +} + +/*! + Returns the free space available in bytes in the audio buffer. + + NOTE: returned value is only valid while in QAudio::ActiveState or QAudio::IdleState + state, otherwise returns zero. +*/ + +int QAudioOutput::bytesFree() const +{ + return d->bytesFree(); +} + +/*! + Returns the period size in bytes. + + Note: This is the recommended write size in bytes. +*/ + +int QAudioOutput::periodSize() const +{ + return d->periodSize(); +} + +/*! + Sets the audio buffer size to \a value in bytes. + + Note: This function can be called anytime before start(), calls to this + are ignored after start(). It should not be assumed that the buffer size + set is the actual buffer size used, calling bufferSize() anytime after start() + will return the actual buffer size being used. +*/ + +void QAudioOutput::setBufferSize(int value) +{ + d->setBufferSize(value); +} + +/*! + Returns the audio buffer size in bytes. + + If called before start(), returns platform default value. + If called before start() but setBufferSize() was called prior, returns value set by setBufferSize(). + If called after start(), returns the actual buffer size being used. This may not be what was set previously + by setBufferSize(). + +*/ + +int QAudioOutput::bufferSize() const +{ + return d->bufferSize(); +} + +/*! + Sets the interval for notify() signal to be emitted. + This is based on the \a ms of audio data processed + not on actual real-time. + The minimum resolution of the timer is platform specific and values + should be checked with notifyInterval() to confirm actual value + being used. +*/ + +void QAudioOutput::setNotifyInterval(int ms) +{ + d->setNotifyInterval(ms); +} + +/*! + Returns the notify interval in milliseconds. +*/ + +int QAudioOutput::notifyInterval() const +{ + return d->notifyInterval(); +} + +/*! + Returns the amount of audio data processed since start() + was called in microseconds. +*/ + +qint64 QAudioOutput::processedUSecs() const +{ + return d->processedUSecs(); +} + +/*! + Returns the microseconds since start() was called, including time in Idle and + Suspend states. +*/ + +qint64 QAudioOutput::elapsedUSecs() const +{ + return d->elapsedUSecs(); +} + +/*! + Returns the error state. +*/ + +QAudio::Error QAudioOutput::error() const +{ + return d->error(); +} + +/*! + Returns the state of audio processing. +*/ + +QAudio::State QAudioOutput::state() const +{ + return d->state(); +} + +/*! + \fn QAudioOutput::stateChanged(QAudio::State state) + This signal is emitted when the device \a state has changed. + This is the current state of the audio output. +*/ + +/*! + \fn QAudioOutput::notify() + This signal is emitted when x ms of audio data has been processed + the interval set by setNotifyInterval(x). +*/ + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudiooutput.h b/src/multimedia/multimedia/audio/qaudiooutput.h new file mode 100644 index 0000000..0f45b1b --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiooutput.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QAUDIOOUTPUT_H +#define QAUDIOOUTPUT_H + +#include <QtCore/qiodevice.h> +#include <QtCore/qglobal.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudiodeviceinfo.h> + + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + + +class QAbstractAudioOutput; + +class Q_MULTIMEDIA_EXPORT QAudioOutput : public QObject +{ + Q_OBJECT + +public: + explicit QAudioOutput(const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + explicit QAudioOutput(const QAudioDeviceInfo &audioDeviceInfo, const QAudioFormat &format = QAudioFormat(), QObject *parent = 0); + ~QAudioOutput(); + + QAudioFormat format() const; + + void start(QIODevice *device); + QIODevice* start(); + + void stop(); + void reset(); + void suspend(); + void resume(); + + void setBufferSize(int bytes); + int bufferSize() const; + + int bytesFree() const; + int periodSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + + QAudio::Error error() const; + QAudio::State state() const; + +Q_SIGNALS: + void stateChanged(QAudio::State); + void notify(); + +private: + Q_DISABLE_COPY(QAudioOutput) + + QAbstractAudioOutput* d; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // QAUDIOOUTPUT_H diff --git a/src/multimedia/multimedia/audio/qaudiooutput_alsa_p.cpp b/src/multimedia/multimedia/audio/qaudiooutput_alsa_p.cpp new file mode 100644 index 0000000..cf3726b --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiooutput_alsa_p.cpp @@ -0,0 +1,780 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qcoreapplication.h> +#include "qaudiooutput_alsa_p.h" +#include "qaudiodeviceinfo_alsa_p.h" + +QT_BEGIN_NAMESPACE + +//#define DEBUG_AUDIO 1 + +static const int minimumIntervalTime = 50; + +QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + handle = 0; + ahandler = 0; + access = SND_PCM_ACCESS_RW_INTERLEAVED; + pcmformat = SND_PCM_FORMAT_S16; + buffer_frames = 0; + period_frames = 0; + buffer_size = 0; + period_size = 0; + buffer_time = 100000; + period_time = 20000; + totalTimeValue = 0; + intervalTime = 1000; + audioBuffer = 0; + errorState = QAudio::NoError; + deviceState = QAudio::StoppedState; + audioSource = 0; + pullMode = true; + resuming = false; + opened = false; + + QStringList list1 = QString(QLatin1String(device)).split(QLatin1String(":")); + m_device = QByteArray(list1.at(0).toLocal8Bit().constData()); + + timer = new QTimer(this); + connect(timer,SIGNAL(timeout()),SLOT(userFeed())); +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + close(); + disconnect(timer, SIGNAL(timeout())); + QCoreApplication::processEvents(); + delete timer; +} + +QAudio::Error QAudioOutputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioOutputPrivate::state() const +{ + return deviceState; +} + +void QAudioOutputPrivate::async_callback(snd_async_handler_t *ahandler) +{ + QAudioOutputPrivate* audioOut; + + audioOut = static_cast<QAudioOutputPrivate*> + (snd_async_handler_get_callback_private(ahandler)); + + if((audioOut->deviceState==QAudio::ActiveState)||(audioOut->resuming)) + audioOut->feedback(); +} + +int QAudioOutputPrivate::xrun_recovery(int err) +{ + int count = 0; + bool reset = false; + + if(err == -EPIPE) { + errorState = QAudio::UnderrunError; + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + + } else if((err == -ESTRPIPE)||(err == -EIO)) { + errorState = QAudio::IOError; + while((err = snd_pcm_resume(handle)) == -EAGAIN){ + usleep(100); + count++; + if(count > 5) { + reset = true; + break; + } + } + if(err < 0) { + err = snd_pcm_prepare(handle); + if(err < 0) + reset = true; + } + } + if(reset) { + close(); + open(); + snd_pcm_prepare(handle); + return 0; + } + return err; +} + +int QAudioOutputPrivate::setFormat() +{ + snd_pcm_format_t pcmformat = SND_PCM_FORMAT_S16; + + if(settings.sampleSize() == 8) { + pcmformat = SND_PCM_FORMAT_U8; + + } else if(settings.sampleSize() == 16) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_S16_LE; + else + pcmformat = SND_PCM_FORMAT_S16_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_U16_LE; + else + pcmformat = SND_PCM_FORMAT_U16_BE; + } + } else if(settings.sampleSize() == 24) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_S24_LE; + else + pcmformat = SND_PCM_FORMAT_S24_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_U24_LE; + else + pcmformat = SND_PCM_FORMAT_U24_BE; + } + } else if(settings.sampleSize() == 32) { + if(settings.sampleType() == QAudioFormat::SignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_S32_LE; + else + pcmformat = SND_PCM_FORMAT_S32_BE; + } else if(settings.sampleType() == QAudioFormat::UnSignedInt) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_U32_LE; + else + pcmformat = SND_PCM_FORMAT_U32_BE; + } else if(settings.sampleType() == QAudioFormat::Float) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_FLOAT_LE; + else + pcmformat = SND_PCM_FORMAT_FLOAT_BE; + } + } else if(settings.sampleSize() == 64) { + if(settings.byteOrder() == QAudioFormat::LittleEndian) + pcmformat = SND_PCM_FORMAT_FLOAT64_LE; + else + pcmformat = SND_PCM_FORMAT_FLOAT64_BE; + } + + return snd_pcm_hw_params_set_format( handle, hwparams, pcmformat); +} + +QIODevice* QAudioOutputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StoppedState) + deviceState = QAudio::StoppedState; + + errorState = QAudio::NoError; + + // Handle change of mode + if(audioSource && pullMode && !device) { + // pull -> push + close(); + audioSource = 0; + } else if(audioSource && !pullMode && device) { + // push -> pull + close(); + delete audioSource; + audioSource = 0; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + deviceState = QAudio::ActiveState; + } else { + //set to push mode + if(!audioSource) { + audioSource = new OutputPrivate(this); + audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); + } + pullMode = false; + deviceState = QAudio::IdleState; + } + + open(); + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioOutputPrivate::stop() +{ + if(deviceState == QAudio::StoppedState) + return; + errorState = QAudio::NoError; + deviceState = QAudio::StoppedState; + close(); + emit stateChanged(deviceState); +} + +bool QAudioOutputPrivate::open() +{ + if(opened) + return true; + +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + timeStamp.restart(); + elapsedTimeOffset = 0; + + int dir; + int err=-1; + int count=0; + unsigned int freakuency=settings.frequency(); + + QString dev = QLatin1String(m_device.constData()); + QList<QByteArray> devices = QAudioDeviceInfoInternal::availableDevices(QAudio::AudioOutput); + if(dev.compare(QLatin1String("default")) == 0) { +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + dev = QLatin1String(devices.first().constData()); +#else + dev = QLatin1String("hw:0,0"); +#endif + } else { +#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14) + dev = QLatin1String(m_device); +#else + int idx = 0; + char *name; + + QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData()); + + while(snd_card_get_name(idx,&name) == 0) { + if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0) + break; + idx++; + } + dev = QString(QLatin1String("hw:%1,0")).arg(idx); +#endif + } + + // Step 1: try and open the device + while((count < 5) && (err < 0)) { + err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0); + if(err < 0) + count++; + } + if (( err < 0)||(handle == 0)) { + errorState = QAudio::OpenError; + deviceState = QAudio::StoppedState; + return false; + } + snd_pcm_nonblock( handle, 0 ); + + // Step 2: Set the desired HW parameters. + snd_pcm_hw_params_alloca( &hwparams ); + + bool fatal = false; + QString errMessage; + unsigned int chunks = 8; + + err = snd_pcm_hw_params_any( handle, hwparams ); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_any: err = %1").arg(err); + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_access( handle, hwparams, access ); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_access: err = %1").arg(err); + } + } + if ( !fatal ) { + err = setFormat(); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_format: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channels() ); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_channels: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &freakuency, 0 ); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err); + } + } + if ( !fatal ) { + unsigned int maxBufferTime = 0; + unsigned int minBufferTime = 0; + unsigned int maxPeriodTime = 0; + unsigned int minPeriodTime = 0; + + err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &maxBufferTime, &dir); + if ( err >= 0) + err = snd_pcm_hw_params_get_buffer_time_min(hwparams, &minBufferTime, &dir); + if ( err >= 0) + err = snd_pcm_hw_params_get_period_time_max(hwparams, &maxPeriodTime, &dir); + if ( err >= 0) + err = snd_pcm_hw_params_get_period_time_min(hwparams, &minPeriodTime, &dir); + + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioOutput: buffer/period min and max: err = %1").arg(err); + } else { + if (maxBufferTime < buffer_time || buffer_time < minBufferTime || maxPeriodTime < period_time || minPeriodTime > period_time) { +#ifdef DEBUG_AUDIO + qDebug()<<"defaults out of range"; + qDebug()<<"pmin="<<minPeriodTime<<", pmax="<<maxPeriodTime<<", bmin="<<minBufferTime<<", bmax="<<maxBufferTime; +#endif + period_time = minPeriodTime; + if (period_time*4 <= maxBufferTime) { + // Use 4 periods if possible + buffer_time = period_time*4; + chunks = 4; + } else if (period_time*2 <= maxBufferTime) { + // Use 2 periods if possible + buffer_time = period_time*2; + chunks = 2; + } else { + qWarning()<<"QAudioOutput: alsa only supports single period!"; + fatal = true; + } +#ifdef DEBUG_AUDIO + qDebug()<<"used: buffer_time="<<buffer_time<<", period_time="<<period_time; +#endif + } + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err); + } + } + if ( !fatal ) { + err = snd_pcm_hw_params(handle, hwparams); + if ( err < 0 ) { + fatal = true; + errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params: err = %1").arg(err); + } + } + if( err < 0) { + qWarning()<<errMessage; + errorState = QAudio::OpenError; + deviceState = QAudio::StoppedState; + return false; + } + snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames); + buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames); + snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir); + period_size = snd_pcm_frames_to_bytes(handle,period_frames); + snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir); + snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir); + + // Step 3: Set the desired SW parameters. + snd_pcm_sw_params_t *swparams; + snd_pcm_sw_params_alloca(&swparams); + snd_pcm_sw_params_current(handle, swparams); + snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames); + snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames); + snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames); + snd_pcm_sw_params(handle, swparams); + + // Step 4: Prepare audio + if(audioBuffer == 0) + audioBuffer = new char[snd_pcm_frames_to_bytes(handle,buffer_frames)]; + snd_pcm_prepare( handle ); + snd_pcm_start(handle); + + // Step 5: Setup callback and timer fallback + snd_async_add_pcm_handler(&ahandler, handle, async_callback, this); + bytesAvailable = bytesFree(); + + // Step 6: Start audio processing + timer->start(period_time/1000); + + clockStamp.restart(); + timeStamp.restart(); + elapsedTimeOffset = 0; + errorState = QAudio::NoError; + totalTimeValue = 0; + opened = true; + + return true; +} + +void QAudioOutputPrivate::close() +{ + timer->stop(); + + if ( handle ) { + snd_pcm_drain( handle ); + snd_pcm_close( handle ); + handle = 0; + delete [] audioBuffer; + audioBuffer=0; + } + if(!pullMode && audioSource) { + delete audioSource; + audioSource = 0; + } + opened = false; +} + +int QAudioOutputPrivate::bytesFree() const +{ + if(resuming) + return period_size; + + if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) + return 0; + int frames = snd_pcm_avail_update(handle); + if((int)frames > (int)buffer_frames) + frames = buffer_frames; + + return snd_pcm_frames_to_bytes(handle, frames); +} + +qint64 QAudioOutputPrivate::write( const char *data, qint64 len ) +{ + // Write out some audio data + if ( !handle ) + return 0; +#ifdef DEBUG_AUDIO + qDebug()<<"frames to write out = "<< + snd_pcm_bytes_to_frames( handle, (int)len )<<" ("<<len<<") bytes"; +#endif + int frames, err; + int space = bytesFree(); + if(len < space) { + // Just write it + frames = snd_pcm_bytes_to_frames( handle, (int)len ); + err = snd_pcm_writei( handle, data, frames ); + } else { + // Only write space worth + frames = snd_pcm_bytes_to_frames( handle, (int)space ); + err = snd_pcm_writei( handle, data, frames ); + } + if(err > 0) { + totalTimeValue += err; + resuming = false; + errorState = QAudio::NoError; + if (deviceState != QAudio::ActiveState) { + deviceState = QAudio::ActiveState; + emit stateChanged(deviceState); + } + return snd_pcm_frames_to_bytes( handle, err ); + } else + err = xrun_recovery(err); + + if(err < 0) { + close(); + errorState = QAudio::FatalError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + } + return 0; +} + +int QAudioOutputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioOutputPrivate::setBufferSize(int value) +{ + if(deviceState == QAudio::StoppedState) + buffer_size = value; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return buffer_size; +} + +void QAudioOutputPrivate::setNotifyInterval(int ms) +{ + if(ms >= minimumIntervalTime) + intervalTime = ms; + else + intervalTime = minimumIntervalTime; +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioOutputPrivate::processedUSecs() const +{ + return qint64(1000000) * totalTimeValue / settings.frequency(); +} + +void QAudioOutputPrivate::resume() +{ + if(deviceState == QAudio::SuspendedState) { + int err = 0; + + if(handle) { + err = snd_pcm_prepare( handle ); + if(err < 0) + xrun_recovery(err); + + err = snd_pcm_start(handle); + if(err < 0) + xrun_recovery(err); + + bytesAvailable = (int)snd_pcm_frames_to_bytes(handle, buffer_frames); + } + resuming = true; + + deviceState = QAudio::ActiveState; + + errorState = QAudio::NoError; + timer->start(period_time/1000); + emit stateChanged(deviceState); + } +} + +QAudioFormat QAudioOutputPrivate::format() const +{ + return settings; +} + +void QAudioOutputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState || resuming) { + timer->stop(); + deviceState = QAudio::SuspendedState; + errorState = QAudio::NoError; + emit stateChanged(deviceState); + } +} + +void QAudioOutputPrivate::userFeed() +{ + if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState) + return; +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() OUT"; +#endif + if(deviceState == QAudio::IdleState) + bytesAvailable = bytesFree(); + + deviceReady(); +} + +void QAudioOutputPrivate::feedback() +{ + updateAvailable(); +} + + +void QAudioOutputPrivate::updateAvailable() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :updateAvailable()"; +#endif + bytesAvailable = bytesFree(); +} + +bool QAudioOutputPrivate::deviceReady() +{ + if(pullMode) { + int l = 0; + int chunks = bytesAvailable/period_size; + if(chunks==0) { + bytesAvailable = bytesFree(); + return false; + } +#ifdef DEBUG_AUDIO + qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes"; + qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<period_size*chunks; +#endif + int input = period_frames*chunks; + if(input > (int)buffer_frames) + input = buffer_frames; + l = audioSource->read(audioBuffer,snd_pcm_frames_to_bytes(handle, input)); + if(l > 0) { + // Got some data to output + if(deviceState != QAudio::ActiveState) + return true; + qint64 bytesWritten = write(audioBuffer,l); + if (bytesWritten != l) + audioSource->seek(audioSource->pos()-(l-bytesWritten)); + bytesAvailable = bytesFree(); + + } else if(l == 0) { + // Did not get any data to output + bytesAvailable = bytesFree(); + if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) { + // Underrun + if (deviceState != QAudio::IdleState) { + errorState = QAudio::UnderrunError; + deviceState = QAudio::IdleState; + emit stateChanged(deviceState); + } + } + + } else if(l < 0) { + close(); + deviceState = QAudio::StoppedState; + errorState = QAudio::IOError; + emit stateChanged(deviceState); + } + } else { + bytesAvailable = bytesFree(); + if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) { + // Underrun + if (deviceState != QAudio::IdleState) { + errorState = QAudio::UnderrunError; + deviceState = QAudio::IdleState; + emit stateChanged(deviceState); + } + } + } + + if(deviceState != QAudio::ActiveState) + return true; + + if((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) { + emit notify(); + elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime; + timeStamp.restart(); + } + return true; +} + +qint64 QAudioOutputPrivate::elapsedUSecs() const +{ + if (deviceState == QAudio::StoppedState) + return 0; + + return clockStamp.elapsed()*1000; +} + +void QAudioOutputPrivate::reset() +{ + if(handle) + snd_pcm_reset(handle); + + stop(); +} + +OutputPrivate::OutputPrivate(QAudioOutputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioOutputPrivate*>(audio); +} + +OutputPrivate::~OutputPrivate() {} + +qint64 OutputPrivate::readData( char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + + return 0; +} + +qint64 OutputPrivate::writeData(const char* data, qint64 len) +{ + int retry = 0; + qint64 written = 0; + if((audioDevice->deviceState == QAudio::ActiveState) + ||(audioDevice->deviceState == QAudio::IdleState)) { + while(written < len) { + int chunk = audioDevice->write(data+written,(len-written)); + if(chunk <= 0) + retry++; + written+=chunk; + if(retry > 10) + return written; + } + } + return written; + +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudiooutput_alsa_p.h b/src/multimedia/multimedia/audio/qaudiooutput_alsa_p.h new file mode 100644 index 0000000..e6ac231 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiooutput_alsa_p.h @@ -0,0 +1,166 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOOUTPUTALSA_H +#define QAUDIOOUTPUTALSA_H + +#include <alsa/asoundlib.h> + +#include <QtCore/qfile.h> +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qelapsedtimer.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_NAMESPACE + +class OutputPrivate; + +class QAudioOutputPrivate : public QAbstractAudioOutput +{ + friend class OutputPrivate; + Q_OBJECT +public: + QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioOutputPrivate(); + + qint64 write( const char *data, qint64 len ); + + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesFree() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + QAudioFormat format() const; + + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private slots: + void userFeed(); + void feedback(); + void updateAvailable(); + bool deviceReady(); + +signals: + void processMore(); + +private: + bool opened; + bool pullMode; + bool resuming; + int buffer_size; + int period_size; + int intervalTime; + qint64 totalTimeValue; + unsigned int buffer_time; + unsigned int period_time; + snd_pcm_uframes_t buffer_frames; + snd_pcm_uframes_t period_frames; + static void async_callback(snd_async_handler_t *ahandler); + int xrun_recovery(int err); + + int setFormat(); + bool open(); + void close(); + + QTimer* timer; + QByteArray m_device; + int bytesAvailable; + QElapsedTimer timeStamp; + QElapsedTimer clockStamp; + qint64 elapsedTimeOffset; + char* audioBuffer; + snd_pcm_t* handle; + snd_async_handler_t* ahandler; + snd_pcm_access_t access; + snd_pcm_format_t pcmformat; + snd_timestamp_t* timestamp; + snd_pcm_hw_params_t *hwparams; +}; + +class OutputPrivate : public QIODevice +{ + friend class QAudioOutputPrivate; + Q_OBJECT +public: + OutputPrivate(QAudioOutputPrivate* audio); + ~OutputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + +private: + QAudioOutputPrivate *audioDevice; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/audio/qaudiooutput_mac_p.cpp b/src/multimedia/multimedia/audio/qaudiooutput_mac_p.cpp new file mode 100644 index 0000000..9689101 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiooutput_mac_p.cpp @@ -0,0 +1,712 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <CoreServices/CoreServices.h> +#include <CoreAudio/CoreAudio.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <QtCore/qendian.h> +#include <QtCore/qbuffer.h> +#include <QtCore/qtimer.h> +#include <QtCore/qdebug.h> + +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudiooutput.h> + +#include "qaudio_mac_p.h" +#include "qaudiooutput_mac_p.h" + + +QT_BEGIN_NAMESPACE + + +namespace QtMultimediaInternal +{ + +static const int default_buffer_size = 8 * 1024; + + +class QAudioOutputBuffer : public QObject +{ + Q_OBJECT + +public: + QAudioOutputBuffer(int bufferSize, int maxPeriodSize, QAudioFormat const& audioFormat): + m_deviceError(false), + m_maxPeriodSize(maxPeriodSize), + m_device(0) + { + m_buffer = new QAudioRingBuffer(bufferSize + (bufferSize % maxPeriodSize == 0 ? 0 : maxPeriodSize - (bufferSize % maxPeriodSize))); + m_bytesPerFrame = (audioFormat.sampleSize() / 8) * audioFormat.channels(); + m_periodTime = m_maxPeriodSize / m_bytesPerFrame * 1000 / audioFormat.frequency(); + + m_fillTimer = new QTimer(this); + connect(m_fillTimer, SIGNAL(timeout()), SLOT(fillBuffer())); + } + + ~QAudioOutputBuffer() + { + delete m_buffer; + } + + qint64 readFrames(char* data, qint64 maxFrames) + { + bool wecan = true; + qint64 framesRead = 0; + + while (wecan && framesRead < maxFrames) { + QAudioRingBuffer::Region region = m_buffer->acquireReadRegion((maxFrames - framesRead) * m_bytesPerFrame); + + if (region.second > 0) { + region.second -= region.second % m_bytesPerFrame; + memcpy(data + (framesRead * m_bytesPerFrame), region.first, region.second); + framesRead += region.second / m_bytesPerFrame; + } + else + wecan = false; + + m_buffer->releaseReadRegion(region); + } + + if (framesRead == 0 && m_deviceError) + framesRead = -1; + + return framesRead; + } + + qint64 writeBytes(const char* data, qint64 maxSize) + { + bool wecan = true; + qint64 bytesWritten = 0; + + maxSize -= maxSize % m_bytesPerFrame; + while (wecan && bytesWritten < maxSize) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(maxSize - bytesWritten); + + if (region.second > 0) { + memcpy(region.first, data + bytesWritten, region.second); + bytesWritten += region.second; + } + else + wecan = false; + + m_buffer->releaseWriteRegion(region); + } + + if (bytesWritten > 0) + emit readyRead(); + + return bytesWritten; + } + + int available() const + { + return m_buffer->free(); + } + + void reset() + { + m_buffer->reset(); + m_deviceError = false; + } + + void setPrefetchDevice(QIODevice* device) + { + if (m_device != device) { + m_device = device; + if (m_device != 0) + fillBuffer(); + } + } + + void startFillTimer() + { + if (m_device != 0) + m_fillTimer->start(m_buffer->size() / 2 / m_maxPeriodSize * m_periodTime); + } + + void stopFillTimer() + { + m_fillTimer->stop(); + } + +signals: + void readyRead(); + +private slots: + void fillBuffer() + { + const int free = m_buffer->free(); + const int writeSize = free - (free % m_maxPeriodSize); + + if (writeSize > 0) { + bool wecan = true; + int filled = 0; + + while (!m_deviceError && wecan && filled < writeSize) { + QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(writeSize - filled); + + if (region.second > 0) { + region.second = m_device->read(region.first, region.second); + if (region.second > 0) + filled += region.second; + else if (region.second == 0) + wecan = false; + else if (region.second < 0) { + m_fillTimer->stop(); + region.second = 0; + m_deviceError = true; + } + } + else + wecan = false; + + m_buffer->releaseWriteRegion(region); + } + + if (filled > 0) + emit readyRead(); + } + } + +private: + bool m_deviceError; + int m_maxPeriodSize; + int m_bytesPerFrame; + int m_periodTime; + QIODevice* m_device; + QTimer* m_fillTimer; + QAudioRingBuffer* m_buffer; +}; + + +} + +class MacOutputDevice : public QIODevice +{ + Q_OBJECT + +public: + MacOutputDevice(QtMultimediaInternal::QAudioOutputBuffer* audioBuffer, QObject* parent): + QIODevice(parent), + m_audioBuffer(audioBuffer) + { + open(QIODevice::WriteOnly | QIODevice::Unbuffered); + } + + qint64 readData(char* data, qint64 len) + { + Q_UNUSED(data); + Q_UNUSED(len); + + return 0; + } + + qint64 writeData(const char* data, qint64 len) + { + return m_audioBuffer->writeBytes(data, len); + } + + bool isSequential() const + { + return true; + } + +private: + QtMultimediaInternal::QAudioOutputBuffer* m_audioBuffer; +}; + + +QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray& device, const QAudioFormat& format): + audioFormat(format) +{ + QDataStream ds(device); + quint32 did, mode; + + ds >> did >> mode; + + if (QAudio::Mode(mode) == QAudio::AudioInput) + errorCode = QAudio::OpenError; + else { + isOpen = false; + audioDeviceId = AudioDeviceID(did); + audioUnit = 0; + audioIO = 0; + startTime = 0; + totalFrames = 0; + audioBuffer = 0; + internalBufferSize = QtMultimediaInternal::default_buffer_size; + clockFrequency = AudioGetHostClockFrequency() / 1000; + errorCode = QAudio::NoError; + stateCode = QAudio::StoppedState; + audioThreadState = Stopped; + + intervalTimer = new QTimer(this); + intervalTimer->setInterval(1000); + connect(intervalTimer, SIGNAL(timeout()), SIGNAL(notify())); + } +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + close(); +} + +bool QAudioOutputPrivate::open() +{ + if (errorCode != QAudio::NoError) + return false; + + if (isOpen) + return true; + + ComponentDescription cd; + cd.componentType = kAudioUnitType_Output; + cd.componentSubType = kAudioUnitSubType_HALOutput; + cd.componentManufacturer = kAudioUnitManufacturer_Apple; + cd.componentFlags = 0; + cd.componentFlagsMask = 0; + + // Open + Component cp = FindNextComponent(NULL, &cd); + if (cp == 0) { + qWarning() << "QAudioOutput: Failed to find HAL Output component"; + return false; + } + + if (OpenAComponent(cp, &audioUnit) != noErr) { + qWarning() << "QAudioOutput: Unable to Open Output Component"; + return false; + } + + // register callback + AURenderCallbackStruct cb; + cb.inputProc = renderCallback; + cb.inputProcRefCon = this; + + if (AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_SetRenderCallback, + kAudioUnitScope_Global, + 0, + &cb, + sizeof(cb)) != noErr) { + qWarning() << "QAudioOutput: Failed to set AudioUnit callback"; + return false; + } + + // Set Audio Device + if (AudioUnitSetProperty(audioUnit, + kAudioOutputUnitProperty_CurrentDevice, + kAudioUnitScope_Global, + 0, + &audioDeviceId, + sizeof(audioDeviceId)) != noErr) { + qWarning() << "QAudioOutput: Unable to use configured device"; + return false; + } + + // Set stream format + streamFormat = toAudioStreamBasicDescription(audioFormat); + + UInt32 size = sizeof(deviceFormat); + if (AudioUnitGetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &deviceFormat, + &size) != noErr) { + qWarning() << "QAudioOutput: Unable to retrieve device format"; + return false; + } + + if (AudioUnitSetProperty(audioUnit, + kAudioUnitProperty_StreamFormat, + kAudioUnitScope_Input, + 0, + &streamFormat, + sizeof(streamFormat)) != noErr) { + qWarning() << "QAudioOutput: Unable to Set Stream information"; + return false; + } + + // Allocate buffer + UInt32 numberOfFrames = 0; + size = sizeof(UInt32); + if (AudioUnitGetProperty(audioUnit, + kAudioDevicePropertyBufferFrameSize, + kAudioUnitScope_Global, + 0, + &numberOfFrames, + &size) != noErr) { + qWarning() << "QAudioInput: Failed to get audio period size"; + return false; + } + + periodSizeBytes = (numberOfFrames * streamFormat.mSampleRate / deviceFormat.mSampleRate) * + streamFormat.mBytesPerFrame; + if (internalBufferSize < periodSizeBytes * 2) + internalBufferSize = periodSizeBytes * 2; + else + internalBufferSize -= internalBufferSize % streamFormat.mBytesPerFrame; + + audioBuffer = new QtMultimediaInternal::QAudioOutputBuffer(internalBufferSize, periodSizeBytes, audioFormat); + connect(audioBuffer, SIGNAL(readyRead()), SLOT(inputReady())); // Pull + + audioIO = new MacOutputDevice(audioBuffer, this); + + // Init + if (AudioUnitInitialize(audioUnit)) { + qWarning() << "QAudioOutput: Failed to initialize AudioUnit"; + return false; + } + + isOpen = true; + + return true; +} + +void QAudioOutputPrivate::close() +{ + if (audioUnit != 0) { + AudioOutputUnitStop(audioUnit); + AudioUnitUninitialize(audioUnit); + CloseComponent(audioUnit); + } + + delete audioBuffer; +} + +QAudioFormat QAudioOutputPrivate::format() const +{ + return audioFormat; +} + +QIODevice* QAudioOutputPrivate::start(QIODevice* device) +{ + QIODevice* op = device; + + if (!audioFormat.isValid() || !open()) { + stateCode = QAudio::StoppedState; + errorCode = QAudio::OpenError; + return audioIO; + } + + reset(); + audioBuffer->reset(); + audioBuffer->setPrefetchDevice(op); + + if (op == 0) { + op = audioIO; + stateCode = QAudio::IdleState; + } + else + stateCode = QAudio::ActiveState; + + // Start + errorCode = QAudio::NoError; + totalFrames = 0; + startTime = AudioGetCurrentHostTime(); + + if (stateCode == QAudio::ActiveState) + audioThreadStart(); + + emit stateChanged(stateCode); + + return op; +} + +void QAudioOutputPrivate::stop() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StoppedState) { + audioThreadDrain(); + + stateCode = QAudio::StoppedState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioOutputPrivate::reset() +{ + QMutexLocker lock(&mutex); + if (stateCode != QAudio::StoppedState) { + audioThreadStop(); + + stateCode = QAudio::StoppedState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioOutputPrivate::suspend() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState || stateCode == QAudio::IdleState) { + audioThreadStop(); + + stateCode = QAudio::SuspendedState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +void QAudioOutputPrivate::resume() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::SuspendedState) { + audioThreadStart(); + + stateCode = QAudio::ActiveState; + errorCode = QAudio::NoError; + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + +int QAudioOutputPrivate::bytesFree() const +{ + return audioBuffer->available(); +} + +int QAudioOutputPrivate::periodSize() const +{ + return periodSizeBytes; +} + +void QAudioOutputPrivate::setBufferSize(int bs) +{ + if (stateCode == QAudio::StoppedState) + internalBufferSize = bs; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return internalBufferSize; +} + +void QAudioOutputPrivate::setNotifyInterval(int milliSeconds) +{ + if (intervalTimer->interval() == milliSeconds) + return; + + if (milliSeconds <= 0) + milliSeconds = 0; + + intervalTimer->setInterval(milliSeconds); +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTimer->interval(); +} + +qint64 QAudioOutputPrivate::processedUSecs() const +{ + return totalFrames * 1000000 / audioFormat.frequency(); +} + +qint64 QAudioOutputPrivate::elapsedUSecs() const +{ + if (stateCode == QAudio::StoppedState) + return 0; + + return (AudioGetCurrentHostTime() - startTime) / (clockFrequency / 1000); +} + +QAudio::Error QAudioOutputPrivate::error() const +{ + return errorCode; +} + +QAudio::State QAudioOutputPrivate::state() const +{ + return stateCode; +} + +void QAudioOutputPrivate::audioThreadStart() +{ + startTimers(); + audioThreadState = Running; + AudioOutputUnitStart(audioUnit); +} + +void QAudioOutputPrivate::audioThreadStop() +{ + stopTimers(); + if (audioThreadState.testAndSetAcquire(Running, Stopped)) + threadFinished.wait(&mutex); +} + +void QAudioOutputPrivate::audioThreadDrain() +{ + stopTimers(); + if (audioThreadState.testAndSetAcquire(Running, Draining)) + threadFinished.wait(&mutex); +} + +void QAudioOutputPrivate::audioDeviceStop() +{ + AudioOutputUnitStop(audioUnit); + audioThreadState = Stopped; + threadFinished.wakeOne(); +} + +void QAudioOutputPrivate::audioDeviceIdle() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::UnderrunError; + stateCode = QAudio::IdleState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioOutputPrivate::audioDeviceError() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::ActiveState) { + audioDeviceStop(); + + errorCode = QAudio::IOError; + stateCode = QAudio::StoppedState; + QMetaObject::invokeMethod(this, "deviceStopped", Qt::QueuedConnection); + } +} + +void QAudioOutputPrivate::startTimers() +{ + audioBuffer->startFillTimer(); + if (intervalTimer->interval() > 0) + intervalTimer->start(); +} + +void QAudioOutputPrivate::stopTimers() +{ + audioBuffer->stopFillTimer(); + intervalTimer->stop(); +} + + +void QAudioOutputPrivate::deviceStopped() +{ + intervalTimer->stop(); + emit stateChanged(stateCode); +} + +void QAudioOutputPrivate::inputReady() +{ + QMutexLocker lock(&mutex); + if (stateCode == QAudio::IdleState) { + audioThreadStart(); + + stateCode = QAudio::ActiveState; + errorCode = QAudio::NoError; + + QMetaObject::invokeMethod(this, "stateChanged", Qt::QueuedConnection, Q_ARG(QAudio::State, stateCode)); + } +} + + +OSStatus QAudioOutputPrivate::renderCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData) +{ + Q_UNUSED(ioActionFlags) + Q_UNUSED(inTimeStamp) + Q_UNUSED(inBusNumber) + Q_UNUSED(inNumberFrames) + + QAudioOutputPrivate* d = static_cast<QAudioOutputPrivate*>(inRefCon); + + const int threadState = d->audioThreadState.fetchAndAddAcquire(0); + if (threadState == Stopped) { + ioData->mBuffers[0].mDataByteSize = 0; + d->audioDeviceStop(); + } + else { + const UInt32 bytesPerFrame = d->streamFormat.mBytesPerFrame; + qint64 framesRead; + + framesRead = d->audioBuffer->readFrames((char*)ioData->mBuffers[0].mData, + ioData->mBuffers[0].mDataByteSize / bytesPerFrame); + + if (framesRead > 0) { + ioData->mBuffers[0].mDataByteSize = framesRead * bytesPerFrame; + d->totalFrames += framesRead; + } + else { + ioData->mBuffers[0].mDataByteSize = 0; + if (framesRead == 0) { + if (threadState == Draining) + d->audioDeviceStop(); + else + d->audioDeviceIdle(); + } + else + d->audioDeviceError(); + } + } + + return noErr; +} + + +QT_END_NAMESPACE + +#include "qaudiooutput_mac_p.moc" + diff --git a/src/multimedia/multimedia/audio/qaudiooutput_mac_p.h b/src/multimedia/multimedia/audio/qaudiooutput_mac_p.h new file mode 100644 index 0000000..752905c --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiooutput_mac_p.h @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOOUTPUT_MAC_P_H +#define QAUDIOOUTPUT_MAC_P_H + +#include <CoreServices/CoreServices.h> +#include <CoreAudio/CoreAudio.h> +#include <AudioUnit/AudioUnit.h> +#include <AudioToolbox/AudioToolbox.h> + +#include <QtCore/qobject.h> +#include <QtCore/qmutex.h> +#include <QtCore/qwaitcondition.h> +#include <QtCore/qtimer.h> +#include <QtCore/qatomic.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudioformat.h> +#include <QtMultimedia/qaudioengine.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class QIODevice; + +namespace QtMultimediaInternal +{ +class QAudioOutputBuffer; +} + +class QAudioOutputPrivate : public QAbstractAudioOutput +{ + Q_OBJECT + +public: + bool isOpen; + int internalBufferSize; + int periodSizeBytes; + qint64 totalFrames; + QAudioFormat audioFormat; + QIODevice* audioIO; + AudioDeviceID audioDeviceId; + AudioUnit audioUnit; + Float64 clockFrequency; + UInt64 startTime; + AudioStreamBasicDescription deviceFormat; + AudioStreamBasicDescription streamFormat; + QtMultimediaInternal::QAudioOutputBuffer* audioBuffer; + QAtomicInt audioThreadState; + QWaitCondition threadFinished; + QMutex mutex; + QTimer* intervalTimer; + + QAudio::Error errorCode; + QAudio::State stateCode; + + QAudioOutputPrivate(const QByteArray& device, const QAudioFormat& format); + ~QAudioOutputPrivate(); + + bool open(); + void close(); + + QAudioFormat format() const; + + QIODevice* start(QIODevice* device); + void stop(); + void reset(); + void suspend(); + void resume(); + + int bytesFree() const; + int periodSize() const; + + void setBufferSize(int value); + int bufferSize() const; + + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + + QAudio::Error error() const; + QAudio::State state() const; + + void audioThreadStart(); + void audioThreadStop(); + void audioThreadDrain(); + + void audioDeviceStop(); + void audioDeviceIdle(); + void audioDeviceError(); + + void startTimers(); + void stopTimers(); + +private slots: + void deviceStopped(); + void inputReady(); + +private: + enum { Running, Draining, Stopped }; + + static OSStatus renderCallback(void* inRefCon, + AudioUnitRenderActionFlags* ioActionFlags, + const AudioTimeStamp* inTimeStamp, + UInt32 inBusNumber, + UInt32 inNumberFrames, + AudioBufferList* ioData); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/multimedia/multimedia/audio/qaudiooutput_symbian_p.cpp b/src/multimedia/multimedia/audio/qaudiooutput_symbian_p.cpp new file mode 100644 index 0000000..3f8e933 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiooutput_symbian_p.cpp @@ -0,0 +1,697 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 "qaudiooutput_symbian_p.h" + +QT_BEGIN_NAMESPACE + +//----------------------------------------------------------------------------- +// Constants +//----------------------------------------------------------------------------- + +const int UnderflowTimerInterval = 50; // ms + + +//----------------------------------------------------------------------------- +// Private class +//----------------------------------------------------------------------------- + +SymbianAudioOutputPrivate::SymbianAudioOutputPrivate( + QAudioOutputPrivate *audioDevice) + : m_audioDevice(audioDevice) +{ + +} + +SymbianAudioOutputPrivate::~SymbianAudioOutputPrivate() +{ + +} + +qint64 SymbianAudioOutputPrivate::readData(char *data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + return 0; +} + +qint64 SymbianAudioOutputPrivate::writeData(const char *data, qint64 len) +{ + qint64 totalWritten = 0; + + if (m_audioDevice->state() == QAudio::ActiveState || + m_audioDevice->state() == QAudio::IdleState) { + + while (totalWritten < len) { + const qint64 written = m_audioDevice->pushData(data + totalWritten, + len - totalWritten); + if (written > 0) + totalWritten += written; + else + break; + } + } + + return totalWritten; +} + + +//----------------------------------------------------------------------------- +// Public functions +//----------------------------------------------------------------------------- + +QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, + const QAudioFormat &format) + : m_device(device) + , m_format(format) + , m_clientBufferSize(SymbianAudio::DefaultBufferSize) + , m_notifyInterval(SymbianAudio::DefaultNotifyInterval) + , m_notifyTimer(new QTimer(this)) + , m_error(QAudio::NoError) + , m_internalState(SymbianAudio::ClosedState) + , m_externalState(QAudio::StoppedState) + , m_pullMode(false) + , m_source(0) + , m_devSoundBuffer(0) + , m_devSoundBufferSize(0) + , m_bytesWritten(0) + , m_pushDataReady(false) + , m_bytesPadding(0) + , m_underflow(false) + , m_lastBuffer(false) + , m_underflowTimer(new QTimer(this)) + , m_samplesPlayed(0) + , m_totalSamplesPlayed(0) +{ + connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify())); + + SymbianAudio::Utils::formatQtToNative(m_format, m_nativeFourCC, + m_nativeFormat); + + m_underflowTimer->setInterval(UnderflowTimerInterval); + connect(m_underflowTimer.data(), SIGNAL(timeout()), this, + SLOT(underflowTimerExpired())); +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + close(); +} + +QIODevice* QAudioOutputPrivate::start(QIODevice *device) +{ + stop(); + + // We have to set these before the call to open() because of the + // logic in initializingState() + if (device) { + m_pullMode = true; + m_source = device; + } + + open(); + + if (SymbianAudio::ClosedState != m_internalState) { + if (device) { + connect(m_source, SIGNAL(readyRead()), this, SLOT(dataReady())); + } else { + m_source = new SymbianAudioOutputPrivate(this); + m_source->open(QIODevice::WriteOnly | QIODevice::Unbuffered); + } + + m_elapsed.restart(); + } + + return m_source; +} + +void QAudioOutputPrivate::stop() +{ + close(); +} + +void QAudioOutputPrivate::reset() +{ + m_totalSamplesPlayed += getSamplesPlayed(); + m_devSound->Stop(); + m_bytesPadding = 0; + startPlayback(); +} + +void QAudioOutputPrivate::suspend() +{ + if (SymbianAudio::ActiveState == m_internalState + || SymbianAudio::IdleState == m_internalState) { + m_notifyTimer->stop(); + m_underflowTimer->stop(); + + const qint64 samplesWritten = SymbianAudio::Utils::bytesToSamples( + m_format, m_bytesWritten); + + const qint64 samplesPlayed = getSamplesPlayed(); + + m_bytesWritten = 0; + + // CMMFDevSound::Pause() is not guaranteed to work correctly in all + // implementations, for play-mode DevSound sessions. We therefore + // have to implement suspend() by calling CMMFDevSound::Stop(). + // Because this causes buffered data to be dropped, we replace the + // lost data with silence following a call to resume(), in order to + // ensure that processedUSecs() returns the correct value. + m_devSound->Stop(); + m_totalSamplesPlayed += samplesPlayed; + + // Calculate the amount of data dropped + const qint64 paddingSamples = samplesWritten - samplesPlayed; + m_bytesPadding = SymbianAudio::Utils::samplesToBytes(m_format, + paddingSamples); + + setState(SymbianAudio::SuspendedState); + } +} + +void QAudioOutputPrivate::resume() +{ + if (SymbianAudio::SuspendedState == m_internalState) + startPlayback(); +} + +int QAudioOutputPrivate::bytesFree() const +{ + int result = 0; + if (m_devSoundBuffer) { + const TDes8 &outputBuffer = m_devSoundBuffer->Data(); + result = outputBuffer.MaxLength() - outputBuffer.Length(); + } + return result; +} + +int QAudioOutputPrivate::periodSize() const +{ + return bufferSize(); +} + +void QAudioOutputPrivate::setBufferSize(int value) +{ + // Note that DevSound does not allow its client to specify the buffer size. + // This functionality is available via custom interfaces, but since these + // cannot be guaranteed to work across all DevSound implementations, we + // do not use them here. + // In order to comply with the expected bevahiour of QAudioOutput, we store + // the value and return it from bufferSize(), but the underlying DevSound + // buffer size remains unchanged. + if (value > 0) + m_clientBufferSize = value; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return m_devSoundBufferSize ? m_devSoundBufferSize : m_clientBufferSize; +} + +void QAudioOutputPrivate::setNotifyInterval(int ms) +{ + if (ms > 0) { + const int oldNotifyInterval = m_notifyInterval; + m_notifyInterval = ms; + if (m_notifyTimer->isActive() && ms != oldNotifyInterval) + m_notifyTimer->start(m_notifyInterval); + } +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return m_notifyInterval; +} + +qint64 QAudioOutputPrivate::processedUSecs() const +{ + int samplesPlayed = 0; + if (m_devSound && SymbianAudio::SuspendedState != m_internalState) + samplesPlayed = getSamplesPlayed(); + + // Protect against division by zero + Q_ASSERT_X(m_format.frequency() > 0, Q_FUNC_INFO, "Invalid frequency"); + + const qint64 result = qint64(1000000) * + (samplesPlayed + m_totalSamplesPlayed) + / m_format.frequency(); + + return result; +} + +qint64 QAudioOutputPrivate::elapsedUSecs() const +{ + const qint64 result = (QAudio::StoppedState == state()) ? + 0 : m_elapsed.elapsed() * 1000; + return result; +} + +QAudio::Error QAudioOutputPrivate::error() const +{ + return m_error; +} + +QAudio::State QAudioOutputPrivate::state() const +{ + return m_externalState; +} + +QAudioFormat QAudioOutputPrivate::format() const +{ + return m_format; +} + +//----------------------------------------------------------------------------- +// MDevSoundObserver implementation +//----------------------------------------------------------------------------- + +void QAudioOutputPrivate::InitializeComplete(TInt aError) +{ + Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState, + Q_FUNC_INFO, "Invalid state"); + + if (KErrNone == aError) + startPlayback(); +} + +void QAudioOutputPrivate::ToneFinished(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound's tone playback functions, so should + // never receive this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void QAudioOutputPrivate::BufferToBeFilled(CMMFBuffer *aBuffer) +{ + // Following receipt of this callback, DevSound should not provide another + // buffer until we have returned the current one. + Q_ASSERT_X(!m_devSoundBuffer, Q_FUNC_INFO, "Buffer already held"); + + // Will be returned to DevSound by bufferFilled(). + m_devSoundBuffer = static_cast<CMMFDataBuffer*>(aBuffer); + + if (!m_devSoundBufferSize) + m_devSoundBufferSize = m_devSoundBuffer->Data().MaxLength(); + + writePaddingData(); + + if (m_pullMode && isDataReady() && !m_bytesPadding) + pullData(); +} + +void QAudioOutputPrivate::PlayError(TInt aError) +{ + switch (aError) { + case KErrUnderflow: + m_underflow = true; + if (m_pullMode && !m_lastBuffer) + setError(QAudio::UnderrunError); + else + setState(SymbianAudio::IdleState); + break; + default: + setError(QAudio::IOError); + break; + } +} + +void QAudioOutputPrivate::BufferToBeEmptied(CMMFBuffer *aBuffer) +{ + Q_UNUSED(aBuffer) + // This class doesn't use DevSound in record mode, so should never receive + // this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void QAudioOutputPrivate::RecordError(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound in record mode, so should never receive + // this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void QAudioOutputPrivate::ConvertError(TInt aError) +{ + Q_UNUSED(aError) + // This class doesn't use DevSound's format conversion functions, so + // should never receive this callback. + Q_ASSERT_X(false, Q_FUNC_INFO, "Unexpected callback"); +} + +void QAudioOutputPrivate::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg) +{ + Q_UNUSED(aMessageType) + Q_UNUSED(aMsg) + // Ignore this callback. +} + +//----------------------------------------------------------------------------- +// Private functions +//----------------------------------------------------------------------------- + +void QAudioOutputPrivate::dataReady() +{ + // Client-provided QIODevice has data ready to read. + + Q_ASSERT_X(m_source->bytesAvailable(), Q_FUNC_INFO, + "readyRead signal received, but no data available"); + + if (!m_bytesPadding) + pullData(); +} + +void QAudioOutputPrivate::underflowTimerExpired() +{ + const TInt samplesPlayed = getSamplesPlayed(); + if (m_samplesPlayed && (samplesPlayed == m_samplesPlayed)) { + setError(QAudio::UnderrunError); + } else { + m_samplesPlayed = samplesPlayed; + m_underflowTimer->start(); + } +} + +void QAudioOutputPrivate::open() +{ + Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState, + Q_FUNC_INFO, "DevSound already opened"); + + QT_TRAP_THROWING( m_devSound.reset(CMMFDevSound::NewL()) ) + + QScopedPointer<SymbianAudio::DevSoundCapabilities> caps( + new SymbianAudio::DevSoundCapabilities(*m_devSound, + QAudio::AudioOutput)); + + int err = SymbianAudio::Utils::isFormatSupported(m_format, *caps) ? + KErrNone : KErrNotSupported; + + if (KErrNone == err) { + setState(SymbianAudio::InitializingState); + TRAP(err, m_devSound->InitializeL(*this, m_nativeFourCC, + EMMFStatePlaying)); + } + + if (KErrNone != err) { + setError(QAudio::OpenError); + m_devSound.reset(); + } +} + +void QAudioOutputPrivate::startPlayback() +{ + TRAPD(err, startDevSoundL()); + if (KErrNone == err) { + if (isDataReady()) + setState(SymbianAudio::ActiveState); + else + setState(SymbianAudio::IdleState); + + m_notifyTimer->start(m_notifyInterval); + m_underflow = false; + + Q_ASSERT(m_devSound->SamplesPlayed() == 0); + + writePaddingData(); + + if (m_pullMode && m_source->bytesAvailable() && !m_bytesPadding) + dataReady(); + } else { + setError(QAudio::OpenError); + close(); + } +} + +void QAudioOutputPrivate::startDevSoundL() +{ + TMMFCapabilities nativeFormat = m_devSound->Config(); + m_nativeFormat.iBufferSize = nativeFormat.iBufferSize; + m_devSound->SetConfigL(m_nativeFormat); + m_devSound->PlayInitL(); +} + +void QAudioOutputPrivate::writePaddingData() +{ + // See comments in suspend() + + while (m_devSoundBuffer && m_bytesPadding) { + if (SymbianAudio::IdleState == m_internalState) + setState(SymbianAudio::ActiveState); + + TDes8 &outputBuffer = m_devSoundBuffer->Data(); + const qint64 outputBytes = bytesFree(); + const qint64 paddingBytes = outputBytes < m_bytesPadding ? + outputBytes : m_bytesPadding; + unsigned char *ptr = const_cast<unsigned char*>(outputBuffer.Ptr()); + Mem::FillZ(ptr, paddingBytes); + outputBuffer.SetLength(outputBuffer.Length() + paddingBytes); + m_bytesPadding -= paddingBytes; + + if (m_pullMode && m_source->atEnd()) + lastBufferFilled(); + if (paddingBytes == outputBytes) + bufferFilled(); + } +} + +qint64 QAudioOutputPrivate::pushData(const char *data, qint64 len) +{ + // Data has been written to SymbianAudioOutputPrivate + + Q_ASSERT_X(!m_pullMode, Q_FUNC_INFO, + "pushData called when in pull mode"); + + const unsigned char *const inputPtr = + reinterpret_cast<const unsigned char*>(data); + qint64 bytesWritten = 0; + + if (SymbianAudio::IdleState == m_internalState) + setState(SymbianAudio::ActiveState); + + while (m_devSoundBuffer && (bytesWritten < len)) { + // writePaddingData() is called from BufferToBeFilled(), so we should + // never have any padding data left at this point. + Q_ASSERT_X(0 == m_bytesPadding, Q_FUNC_INFO, + "Padding bytes remaining in pushData"); + + TDes8 &outputBuffer = m_devSoundBuffer->Data(); + + const qint64 outputBytes = bytesFree(); + const qint64 inputBytes = len - bytesWritten; + const qint64 copyBytes = outputBytes < inputBytes ? + outputBytes : inputBytes; + + outputBuffer.Append(inputPtr + bytesWritten, copyBytes); + bytesWritten += copyBytes; + + bufferFilled(); + } + + m_pushDataReady = (bytesWritten < len); + + // If DevSound is still initializing (m_internalState == InitializingState), + // we cannot transition m_internalState to ActiveState, but we must emit + // an (external) state change from IdleState to ActiveState. The following + // call triggers this signal. + setState(m_internalState); + + return bytesWritten; +} + +void QAudioOutputPrivate::pullData() +{ + Q_ASSERT_X(m_pullMode, Q_FUNC_INFO, + "pullData called when in push mode"); + + if (m_bytesPadding) + m_bytesPadding = 1; + + // writePaddingData() is called by BufferToBeFilled() before pullData(), + // so we should never have any padding data left at this point. + Q_ASSERT_X(0 == m_bytesPadding, Q_FUNC_INFO, + "Padding bytes remaining in pullData"); + + qint64 inputBytes = m_source->bytesAvailable(); + while (m_devSoundBuffer && inputBytes) { + if (SymbianAudio::IdleState == m_internalState) + setState(SymbianAudio::ActiveState); + + TDes8 &outputBuffer = m_devSoundBuffer->Data(); + + const qint64 outputBytes = bytesFree(); + const qint64 copyBytes = outputBytes < inputBytes ? + outputBytes : inputBytes; + + char *outputPtr = (char*)(outputBuffer.Ptr() + outputBuffer.Length()); + const qint64 bytesCopied = m_source->read(outputPtr, copyBytes); + Q_ASSERT(bytesCopied == copyBytes); + outputBuffer.SetLength(outputBuffer.Length() + bytesCopied); + inputBytes -= bytesCopied; + + if (m_source->atEnd()) + lastBufferFilled(); + else if (copyBytes == outputBytes) + bufferFilled(); + } +} + +void QAudioOutputPrivate::bufferFilled() +{ + Q_ASSERT_X(m_devSoundBuffer, Q_FUNC_INFO, "No buffer to return"); + + const TDes8 &outputBuffer = m_devSoundBuffer->Data(); + m_bytesWritten += outputBuffer.Length(); + + m_devSoundBuffer = 0; + + m_samplesPlayed = getSamplesPlayed(); + m_underflowTimer->start(); + + if (QAudio::UnderrunError == m_error) + m_error = QAudio::NoError; + + m_devSound->PlayData(); +} + +void QAudioOutputPrivate::lastBufferFilled() +{ + Q_ASSERT_X(m_devSoundBuffer, Q_FUNC_INFO, "No buffer to fill"); + Q_ASSERT_X(!m_lastBuffer, Q_FUNC_INFO, "Last buffer already sent"); + m_lastBuffer = true; + m_devSoundBuffer->SetLastBuffer(ETrue); + bufferFilled(); +} + +void QAudioOutputPrivate::close() +{ + m_notifyTimer->stop(); + m_underflowTimer->stop(); + + m_error = QAudio::NoError; + + if (m_devSound) + m_devSound->Stop(); + m_devSound.reset(); + m_devSoundBuffer = 0; + m_devSoundBufferSize = 0; + + if (!m_pullMode) // m_source is owned + delete m_source; + m_pullMode = false; + m_source = 0; + + m_bytesWritten = 0; + m_pushDataReady = false; + m_bytesPadding = 0; + m_underflow = false; + m_lastBuffer = false; + m_samplesPlayed = 0; + m_totalSamplesPlayed = 0; + + setState(SymbianAudio::ClosedState); +} + +qint64 QAudioOutputPrivate::getSamplesPlayed() const +{ + qint64 result = 0; + if (m_devSound) { + const qint64 samplesWritten = SymbianAudio::Utils::bytesToSamples( + m_format, m_bytesWritten); + + if (m_underflow) { + result = samplesWritten; + } else { + // This is necessary because some DevSound implementations report + // that they have played more data than has actually been provided to them + // by the client. + const qint64 devSoundSamplesPlayed(m_devSound->SamplesPlayed()); + result = qMin(devSoundSamplesPlayed, samplesWritten); + } + } + return result; +} + +void QAudioOutputPrivate::setError(QAudio::Error error) +{ + m_error = error; + + // Although no state transition actually occurs here, a stateChanged event + // must be emitted to inform the client that the call to start() was + // unsuccessful. + if (QAudio::OpenError == error) + emit stateChanged(QAudio::StoppedState); + + if (QAudio::UnderrunError == error) + setState(SymbianAudio::IdleState); + else + // Close the DevSound instance. This causes a transition to + // StoppedState. This must be done asynchronously in case the + // current function was called from a DevSound event handler, in which + // case deleting the DevSound instance may cause an exception. + QMetaObject::invokeMethod(this, "close", Qt::QueuedConnection); +} + +void QAudioOutputPrivate::setState(SymbianAudio::State newInternalState) +{ + const QAudio::State oldExternalState = m_externalState; + m_internalState = newInternalState; + m_externalState = SymbianAudio::Utils::stateNativeToQt( + m_internalState, initializingState()); + + if (m_externalState != oldExternalState) + emit stateChanged(m_externalState); +} + +bool QAudioOutputPrivate::isDataReady() const +{ + return (m_source && m_source->bytesAvailable()) + || m_bytesPadding + || m_pushDataReady; +} + +QAudio::State QAudioOutputPrivate::initializingState() const +{ + return isDataReady() ? QAudio::ActiveState : QAudio::IdleState; +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudiooutput_symbian_p.h b/src/multimedia/multimedia/audio/qaudiooutput_symbian_p.h new file mode 100644 index 0000000..00ccb24 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiooutput_symbian_p.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOOUTPUT_SYMBIAN_P_H +#define QAUDIOOUTPUT_SYMBIAN_P_H + +#include <QtMultimedia/qaudioengine.h> +#include <QTime> +#include <QTimer> +#include <sounddevice.h> +#include "qaudio_symbian_p.h" + +QT_BEGIN_NAMESPACE + +class QAudioOutputPrivate; + +class SymbianAudioOutputPrivate : public QIODevice +{ + friend class QAudioOutputPrivate; + Q_OBJECT +public: + SymbianAudioOutputPrivate(QAudioOutputPrivate *audio); + ~SymbianAudioOutputPrivate(); + + qint64 readData(char *data, qint64 len); + qint64 writeData(const char *data, qint64 len); + +private: + QAudioOutputPrivate *const m_audioDevice; +}; + +class QAudioOutputPrivate + : public QAbstractAudioOutput + , public MDevSoundObserver +{ + friend class SymbianAudioOutputPrivate; + Q_OBJECT +public: + QAudioOutputPrivate(const QByteArray &device, + const QAudioFormat &audioFormat); + ~QAudioOutputPrivate(); + + // QAbstractAudioOutput + QIODevice* start(QIODevice *device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesFree() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + QAudioFormat format() const; + + // MDevSoundObserver + void InitializeComplete(TInt aError); + void ToneFinished(TInt aError); + void BufferToBeFilled(CMMFBuffer *aBuffer); + void PlayError(TInt aError); + void BufferToBeEmptied(CMMFBuffer *aBuffer); + void RecordError(TInt aError); + void ConvertError(TInt aError); + void DeviceMessage(TUid aMessageType, const TDesC8 &aMsg); + +private slots: + void dataReady(); + void underflowTimerExpired(); + +private: + void open(); + void startPlayback(); + void startDevSoundL(); + void writePaddingData(); + qint64 pushData(const char *data, qint64 len); + void pullData(); + void bufferFilled(); + void lastBufferFilled(); + Q_INVOKABLE void close(); + + qint64 getSamplesPlayed() const; + + void setError(QAudio::Error error); + void setState(SymbianAudio::State state); + + bool isDataReady() const; + QAudio::State initializingState() const; + +private: + const QByteArray m_device; + const QAudioFormat m_format; + + int m_clientBufferSize; + int m_notifyInterval; + QScopedPointer<QTimer> m_notifyTimer; + QTime m_elapsed; + QAudio::Error m_error; + + SymbianAudio::State m_internalState; + QAudio::State m_externalState; + + bool m_pullMode; + QIODevice *m_source; + + QScopedPointer<CMMFDevSound> m_devSound; + TUint32 m_nativeFourCC; + TMMFCapabilities m_nativeFormat; + + // Buffer provided by DevSound, to be filled with data. + CMMFDataBuffer *m_devSoundBuffer; + + int m_devSoundBufferSize; + + // Number of bytes transferred from QIODevice to QAudioOutput. It is + // necessary to count this because data is dropped when suspend() is + // called. The difference between the position reported by DevSound and + // this value allows us to calculate m_bytesPadding; + quint32 m_bytesWritten; + + // True if client has provided data while the audio subsystem was not + // ready to consume it. + bool m_pushDataReady; + + // Number of zero bytes which will be written when client calls resume(). + quint32 m_bytesPadding; + + // True if PlayError(KErrUnderflow) has been called. + bool m_underflow; + + // True if a buffer marked with the "last buffer" flag has been provided + // to DevSound. + bool m_lastBuffer; + + // Some DevSound implementations ignore all underflow errors raised by the + // audio driver, unless the last buffer flag has been set by the client. + // In push-mode playback, this flag will never be set, so the underflow + // error will never be reported. In order to work around this, a timer + // is used, which gets reset every time the client provides more data. If + // the timer expires, an underflow error is raised by this object. + QScopedPointer<QTimer> m_underflowTimer; + + // Result of previous call to CMMFDevSound::SamplesPlayed(). This value is + // used to determine whether, when m_underflowTimer expires, an + // underflow error has actually occurred. + quint32 m_samplesPlayed; + + // Samples played up to the last call to suspend(). It is necessary + // to cache this because suspend() is implemented using + // CMMFDevSound::Stop(), which resets DevSound's SamplesPlayed() counter. + quint32 m_totalSamplesPlayed; + +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/audio/qaudiooutput_win32_p.cpp b/src/multimedia/multimedia/audio/qaudiooutput_win32_p.cpp new file mode 100644 index 0000000..13bce58 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiooutput_win32_p.cpp @@ -0,0 +1,606 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qaudiooutput_win32_p.h" + +//#define DEBUG_AUDIO 1 + +QT_BEGIN_NAMESPACE + +static const int minimumIntervalTime = 50; + +QAudioOutputPrivate::QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat): + settings(audioFormat) +{ + bytesAvailable = 0; + buffer_size = 0; + period_size = 0; + m_device = device; + totalTimeValue = 0; + intervalTime = 1000; + audioBuffer = 0; + errorState = QAudio::NoError; + deviceState = QAudio::StoppedState; + audioSource = 0; + pullMode = true; + finished = false; + InitializeCriticalSection(&waveOutCriticalSection); +} + +QAudioOutputPrivate::~QAudioOutputPrivate() +{ + EnterCriticalSection(&waveOutCriticalSection); + finished = true; + LeaveCriticalSection(&waveOutCriticalSection); + + close(); + DeleteCriticalSection(&waveOutCriticalSection); +} + +void CALLBACK QAudioOutputPrivate::waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ) +{ + Q_UNUSED(dwParam1) + Q_UNUSED(dwParam2) + Q_UNUSED(hWaveOut) + + QAudioOutputPrivate* qAudio; + qAudio = (QAudioOutputPrivate*)(dwInstance); + if(!qAudio) + return; + + switch(uMsg) { + case WOM_OPEN: + qAudio->feedback(); + break; + case WOM_CLOSE: + return; + case WOM_DONE: + EnterCriticalSection(&qAudio->waveOutCriticalSection); + if(qAudio->finished || qAudio->buffer_size == 0 || qAudio->period_size == 0) { + LeaveCriticalSection(&qAudio->waveOutCriticalSection); + return; + } + qAudio->waveFreeBlockCount++; + if(qAudio->waveFreeBlockCount >= qAudio->buffer_size/qAudio->period_size) + qAudio->waveFreeBlockCount = qAudio->buffer_size/qAudio->period_size; + qAudio->feedback(); + LeaveCriticalSection(&qAudio->waveOutCriticalSection); + break; + default: + return; + } +} + +WAVEHDR* QAudioOutputPrivate::allocateBlocks(int size, int count) +{ + int i; + unsigned char* buffer; + WAVEHDR* blocks; + DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count; + + if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY, + totalBufferSize)) == 0) { + qWarning("QAudioOutput: Memory allocation error"); + return 0; + } + blocks = (WAVEHDR*)buffer; + buffer += sizeof(WAVEHDR)*count; + for(i = 0; i < count; i++) { + blocks[i].dwBufferLength = size; + blocks[i].lpData = (LPSTR)buffer; + buffer += size; + } + return blocks; +} + +void QAudioOutputPrivate::freeBlocks(WAVEHDR* blockArray) +{ + WAVEHDR* blocks = blockArray; + + int count = buffer_size/period_size; + + for(int i = 0; i < count; i++) { + waveOutUnprepareHeader(hWaveOut,&blocks[i], sizeof(WAVEHDR)); + blocks+=sizeof(WAVEHDR); + } + HeapFree(GetProcessHeap(), 0, blockArray); +} + +QAudioFormat QAudioOutputPrivate::format() const +{ + return settings; +} + +QIODevice* QAudioOutputPrivate::start(QIODevice* device) +{ + if(deviceState != QAudio::StoppedState) + close(); + + if(!pullMode && audioSource) { + delete audioSource; + } + + if(device) { + //set to pull mode + pullMode = true; + audioSource = device; + deviceState = QAudio::ActiveState; + } else { + //set to push mode + pullMode = false; + audioSource = new OutputPrivate(this); + audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); + deviceState = QAudio::IdleState; + } + + if( !open() ) + return 0; + + emit stateChanged(deviceState); + + return audioSource; +} + +void QAudioOutputPrivate::stop() +{ + if(deviceState == QAudio::StoppedState) + return; + close(); + if(!pullMode && audioSource) { + delete audioSource; + audioSource = 0; + } + emit stateChanged(deviceState); +} + +bool QAudioOutputPrivate::open() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()"; +#endif + if (!(settings.frequency() >= 8000 && settings.frequency() <= 48000)) { + errorState = QAudio::OpenError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + qWarning("QAudioOutput: open error, frequency out of range."); + return false; + } + if(buffer_size == 0) { + // Default buffer size, 200ms, default period size is 40ms + buffer_size = settings.frequency()*settings.channels()*(settings.sampleSize()/8)*0.2; + period_size = buffer_size/5; + } else { + period_size = buffer_size/5; + } + waveBlocks = allocateBlocks(period_size, buffer_size/period_size); + + EnterCriticalSection(&waveOutCriticalSection); + waveFreeBlockCount = buffer_size/period_size; + LeaveCriticalSection(&waveOutCriticalSection); + + waveCurrentBlock = 0; + + if(audioBuffer == 0) + audioBuffer = new char[buffer_size]; + + timeStamp.restart(); + elapsedTimeOffset = 0; + + wfx.nSamplesPerSec = settings.frequency(); + wfx.wBitsPerSample = settings.sampleSize(); + wfx.nChannels = settings.channels(); + wfx.cbSize = 0; + + wfx.wFormatTag = WAVE_FORMAT_PCM; + wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels; + wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec; + + UINT_PTR devId = WAVE_MAPPER; + + WAVEOUTCAPS woc; + unsigned long iNumDevs,ii; + iNumDevs = waveOutGetNumDevs(); + for(ii=0;ii<iNumDevs;ii++) { + if(waveOutGetDevCaps(ii, &woc, sizeof(WAVEOUTCAPS)) + == MMSYSERR_NOERROR) { + QString tmp; + tmp = QString((const QChar *)woc.szPname); + if(tmp.compare(QLatin1String(m_device)) == 0) { + devId = ii; + break; + } + } + } + + if(waveOutOpen(&hWaveOut, devId, &wfx, + (DWORD_PTR)&waveOutProc, + (DWORD_PTR) this, + CALLBACK_FUNCTION) != MMSYSERR_NOERROR) { + errorState = QAudio::OpenError; + deviceState = QAudio::StoppedState; + emit stateChanged(deviceState); + qWarning("QAudioOutput: open error"); + return false; + } + + totalTimeValue = 0; + timeStampOpened.restart(); + elapsedTimeOffset = 0; + + errorState = QAudio::NoError; + if(pullMode) { + deviceState = QAudio::ActiveState; + QTimer::singleShot(10, this, SLOT(feedback())); + } else + deviceState = QAudio::IdleState; + + return true; +} + +void QAudioOutputPrivate::close() +{ + if(deviceState == QAudio::StoppedState) + return; + + deviceState = QAudio::StoppedState; + errorState = QAudio::NoError; + int delay = (buffer_size-bytesFree())*1000/(settings.frequency() + *settings.channels()*(settings.sampleSize()/8)); + waveOutReset(hWaveOut); + Sleep(delay+10); + + freeBlocks(waveBlocks); + waveOutClose(hWaveOut); + delete [] audioBuffer; + audioBuffer = 0; + buffer_size = 0; +} + +int QAudioOutputPrivate::bytesFree() const +{ + int buf; + buf = waveFreeBlockCount*period_size; + + return buf; +} + +int QAudioOutputPrivate::periodSize() const +{ + return period_size; +} + +void QAudioOutputPrivate::setBufferSize(int value) +{ + if(deviceState == QAudio::StoppedState) + buffer_size = value; +} + +int QAudioOutputPrivate::bufferSize() const +{ + return buffer_size; +} + +void QAudioOutputPrivate::setNotifyInterval(int ms) +{ + if(ms >= minimumIntervalTime) + intervalTime = ms; + else + intervalTime = minimumIntervalTime; +} + +int QAudioOutputPrivate::notifyInterval() const +{ + return intervalTime; +} + +qint64 QAudioOutputPrivate::processedUSecs() const +{ + if (deviceState == QAudio::StoppedState) + return 0; + qint64 result = qint64(1000000) * totalTimeValue / + (settings.channels()*(settings.sampleSize()/8)) / + settings.frequency(); + + return result; +} + +qint64 QAudioOutputPrivate::write( const char *data, qint64 len ) +{ + // Write out some audio data + if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) + return 0; + + char* p = (char*)data; + int l = (int)len; + + WAVEHDR* current; + int remain; + current = &waveBlocks[waveCurrentBlock]; + while(l > 0) { + EnterCriticalSection(&waveOutCriticalSection); + if(waveFreeBlockCount==0) { + LeaveCriticalSection(&waveOutCriticalSection); + break; + } + LeaveCriticalSection(&waveOutCriticalSection); + + if(current->dwFlags & WHDR_PREPARED) + waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR)); + + if(l < period_size) + remain = l; + else + remain = period_size; + memcpy(current->lpData, p, remain); + + l -= remain; + p += remain; + current->dwBufferLength = remain; + waveOutPrepareHeader(hWaveOut, current, sizeof(WAVEHDR)); + waveOutWrite(hWaveOut, current, sizeof(WAVEHDR)); + + EnterCriticalSection(&waveOutCriticalSection); + waveFreeBlockCount--; + LeaveCriticalSection(&waveOutCriticalSection); +#ifdef DEBUG_AUDIO + EnterCriticalSection(&waveOutCriticalSection); + qDebug("write out l=%d, waveFreeBlockCount=%d", + current->dwBufferLength,waveFreeBlockCount); + LeaveCriticalSection(&waveOutCriticalSection); +#endif + totalTimeValue += current->dwBufferLength; + waveCurrentBlock++; + waveCurrentBlock %= buffer_size/period_size; + current = &waveBlocks[waveCurrentBlock]; + current->dwUser = 0; + errorState = QAudio::NoError; + if (deviceState != QAudio::ActiveState) { + deviceState = QAudio::ActiveState; + emit stateChanged(deviceState); + } + } + return (len-l); +} + +void QAudioOutputPrivate::resume() +{ + if(deviceState == QAudio::SuspendedState) { + deviceState = QAudio::ActiveState; + errorState = QAudio::NoError; + waveOutRestart(hWaveOut); + QTimer::singleShot(10, this, SLOT(feedback())); + emit stateChanged(deviceState); + } +} + +void QAudioOutputPrivate::suspend() +{ + if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState) { + int delay = (buffer_size-bytesFree())*1000/(settings.frequency() + *settings.channels()*(settings.sampleSize()/8)); + waveOutPause(hWaveOut); + Sleep(delay+10); + deviceState = QAudio::SuspendedState; + errorState = QAudio::NoError; + emit stateChanged(deviceState); + } +} + +void QAudioOutputPrivate::feedback() +{ +#ifdef DEBUG_AUDIO + QTime now(QTime::currentTime()); + qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback()"; +#endif + bytesAvailable = bytesFree(); + + if(!(deviceState==QAudio::StoppedState||deviceState==QAudio::SuspendedState)) { + if(bytesAvailable >= period_size) + QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection); + } +} + +bool QAudioOutputPrivate::deviceReady() +{ + if(deviceState == QAudio::StoppedState) + return false; + + if(pullMode) { + int chunks = bytesAvailable/period_size; +#ifdef DEBUG_AUDIO + qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes"; + qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<chunks*period_size; +#endif + bool startup = false; + if(totalTimeValue == 0) + startup = true; + + bool full=false; + EnterCriticalSection(&waveOutCriticalSection); + if(waveFreeBlockCount==0) full = true; + LeaveCriticalSection(&waveOutCriticalSection); + if (full){ +#ifdef DEBUG_AUDIO + qDebug() << "Skipping data as unable to write"; +#endif + if((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime ) { + emit notify(); + elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime; + timeStamp.restart(); + } + return true; + } + + if(startup) + waveOutPause(hWaveOut); + int input = period_size*chunks; + int l = audioSource->read(audioBuffer,input); + if(l > 0) { + int out= write(audioBuffer,l); + if(out > 0) { + if (deviceState != QAudio::ActiveState) { + deviceState = QAudio::ActiveState; + emit stateChanged(deviceState); + } + } + if ( out < l) { + // Didnt write all data + audioSource->seek(audioSource->pos()-(l-out)); + } + if(startup) + waveOutRestart(hWaveOut); + } else if(l == 0) { + bytesAvailable = bytesFree(); + + int check = 0; + EnterCriticalSection(&waveOutCriticalSection); + check = waveFreeBlockCount; + LeaveCriticalSection(&waveOutCriticalSection); + if(check == buffer_size/period_size) { + errorState = QAudio::UnderrunError; + if (deviceState != QAudio::IdleState) { + deviceState = QAudio::IdleState; + emit stateChanged(deviceState); + } + } + + } else if(l < 0) { + bytesAvailable = bytesFree(); + errorState = QAudio::IOError; + } + } else { + int buffered; + EnterCriticalSection(&waveOutCriticalSection); + buffered = waveFreeBlockCount; + LeaveCriticalSection(&waveOutCriticalSection); + errorState = QAudio::UnderrunError; + if (buffered >= buffer_size/period_size && deviceState == QAudio::ActiveState) { + deviceState = QAudio::IdleState; + emit stateChanged(deviceState); + } + } + if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState) + return true; + + if((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) { + emit notify(); + elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime; + timeStamp.restart(); + } + + return true; +} + +qint64 QAudioOutputPrivate::elapsedUSecs() const +{ + if (deviceState == QAudio::StoppedState) + return 0; + + return timeStampOpened.elapsed()*1000; +} + +QAudio::Error QAudioOutputPrivate::error() const +{ + return errorState; +} + +QAudio::State QAudioOutputPrivate::state() const +{ + return deviceState; +} + +void QAudioOutputPrivate::reset() +{ + close(); +} + +OutputPrivate::OutputPrivate(QAudioOutputPrivate* audio) +{ + audioDevice = qobject_cast<QAudioOutputPrivate*>(audio); +} + +OutputPrivate::~OutputPrivate() {} + +qint64 OutputPrivate::readData( char* data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + + return 0; +} + +qint64 OutputPrivate::writeData(const char* data, qint64 len) +{ + int retry = 0; + qint64 written = 0; + + if((audioDevice->deviceState == QAudio::ActiveState) + ||(audioDevice->deviceState == QAudio::IdleState)) { + qint64 l = len; + while(written < l) { + int chunk = audioDevice->write(data+written,(l-written)); + if(chunk <= 0) + retry++; + else + written+=chunk; + + if(retry > 10) + return written; + } + audioDevice->deviceState = QAudio::ActiveState; + } + return written; +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/audio/qaudiooutput_win32_p.h b/src/multimedia/multimedia/audio/qaudiooutput_win32_p.h new file mode 100644 index 0000000..68a40f7 --- /dev/null +++ b/src/multimedia/multimedia/audio/qaudiooutput_win32_p.h @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOOUTPUTWIN_H +#define QAUDIOOUTPUTWIN_H + +#include <windows.h> +#include <mmsystem.h> + +#include <QtCore/qdebug.h> +#include <QtCore/qtimer.h> +#include <QtCore/qstring.h> +#include <QtCore/qstringlist.h> +#include <QtCore/qdatetime.h> + +#include <QtMultimedia/qaudio.h> +#include <QtMultimedia/qaudiodeviceinfo.h> +#include <QtMultimedia/qaudioengine.h> + + +QT_BEGIN_NAMESPACE + +class QAudioOutputPrivate : public QAbstractAudioOutput +{ + Q_OBJECT +public: + QAudioOutputPrivate(const QByteArray &device, const QAudioFormat& audioFormat); + ~QAudioOutputPrivate(); + + qint64 write( const char *data, qint64 len ); + + QAudioFormat format() const; + QIODevice* start(QIODevice* device = 0); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesFree() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + + QIODevice* audioSource; + QAudioFormat settings; + QAudio::Error errorState; + QAudio::State deviceState; + +private slots: + void feedback(); + bool deviceReady(); + +private: + QByteArray m_device; + bool resuming; + int bytesAvailable; + QTime timeStamp; + qint64 elapsedTimeOffset; + QTime timeStampOpened; + qint32 buffer_size; + qint32 period_size; + qint64 totalTimeValue; + bool pullMode; + int intervalTime; + static void QT_WIN_CALLBACK waveOutProc( HWAVEOUT hWaveOut, UINT uMsg, + DWORD dwInstance, DWORD dwParam1, DWORD dwParam2 ); + + CRITICAL_SECTION waveOutCriticalSection; + + WAVEHDR* allocateBlocks(int size, int count); + void freeBlocks(WAVEHDR* blockArray); + bool open(); + void close(); + + WAVEFORMATEX wfx; + HWAVEOUT hWaveOut; + MMRESULT result; + WAVEHDR header; + WAVEHDR* waveBlocks; + volatile bool finished; + volatile int waveFreeBlockCount; + int waveCurrentBlock; + char* audioBuffer; +}; + +class OutputPrivate : public QIODevice +{ + Q_OBJECT +public: + OutputPrivate(QAudioOutputPrivate* audio); + ~OutputPrivate(); + + qint64 readData( char* data, qint64 len); + qint64 writeData(const char* data, qint64 len); + +private: + QAudioOutputPrivate *audioDevice; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/multimedia.pro b/src/multimedia/multimedia/multimedia.pro new file mode 100644 index 0000000..8d130bd --- /dev/null +++ b/src/multimedia/multimedia/multimedia.pro @@ -0,0 +1,14 @@ +TARGET = QtMultimedia +QPRO_PWD = $$PWD +QT = core gui + +DEFINES += QT_BUILD_MULTIMEDIA_LIB QT_NO_USING_NAMESPACE + +unix:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui + +include(../../qbase.pri) + +include(audio/audio.pri) +include(video/video.pri) + +symbian: TARGET.UID3 = 0x2001E627 diff --git a/src/multimedia/multimedia/video/qabstractvideobuffer.cpp b/src/multimedia/multimedia/video/qabstractvideobuffer.cpp new file mode 100644 index 0000000..e9d30d0 --- /dev/null +++ b/src/multimedia/multimedia/video/qabstractvideobuffer.cpp @@ -0,0 +1,200 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 "qabstractvideobuffer_p.h" + +#include <qvariant.h> + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractVideoBuffer + \brief The QAbstractVideoBuffer class is an abstraction for video data. + \since 4.6 + + The QVideoFrame class makes use of a QAbstractVideoBuffer internally to reference a buffer of + video data. Creating a subclass of QAbstractVideoBuffer will allow you to construct video + frames from preallocated or static buffers. + + The contents of a buffer can be accessed by mapping the buffer to memory using the map() + function which returns a pointer to memory containing the contents of the the video buffer. + The memory returned by map() is released by calling the unmap() function. + + The handle() of a buffer may also be used to manipulate it's contents using type specific APIs. + The type of a buffer's handle is given by the handleType() function. + + \sa QVideoFrame +*/ + +/*! + \enum QAbstractVideoBuffer::HandleType + + Identifies the type of a video buffers handle. + + \value NoHandle The buffer has no handle, its data can only be accessed by mapping the buffer. + \value GLTextureHandle The handle of the buffer is an OpenGL texture ID. + \value XvShmImageHandle The handle contains pointer to shared memory XVideo image. + \value CoreImageHandle The handle contains pointer to Mac OS X CIImage. + \value UserHandle Start value for user defined handle types. + + \sa handleType() +*/ + +/*! + \enum QAbstractVideoBuffer::MapMode + + Enumerates how a video buffer's data is mapped to memory. + + \value NotMapped The video buffer has is not mapped to memory. + \value ReadOnly The mapped memory is populated with data from the video buffer when mapped, but + the content of the mapped memory may be discarded when unmapped. + \value WriteOnly The mapped memory in unitialized when mapped, and the content will be used to + populate the video buffer when unmapped. + \value ReadWrite The mapped memory is populated with data from the video buffer, and the + video buffer is repopulated with the content of the mapped memory. + + \sa mapMode(), map() +*/ + +/*! + Constructs an abstract video buffer of the given \a type. +*/ + +QAbstractVideoBuffer::QAbstractVideoBuffer(HandleType type) + : d_ptr(new QAbstractVideoBufferPrivate) +{ + Q_D(QAbstractVideoBuffer); + + d->handleType = type; +} + +/*! + \internal +*/ + +QAbstractVideoBuffer::QAbstractVideoBuffer(QAbstractVideoBufferPrivate &dd, HandleType type) + : d_ptr(&dd) +{ + Q_D(QAbstractVideoBuffer); + + d->handleType = type; +} + +/*! + Destroys an abstract video buffer. +*/ + +QAbstractVideoBuffer::~QAbstractVideoBuffer() +{ + delete d_ptr; +} + +/*! + Returns the type of a video buffer's handle. + + \sa handle() +*/ + +QAbstractVideoBuffer::HandleType QAbstractVideoBuffer::handleType() const +{ + return d_func()->handleType; +} + +/*! + \fn QAbstractVideoBuffer::mapMode() const + + Returns the mode a video buffer is mapped in. + + \sa map() +*/ + +/*! + \fn QAbstractVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) + + Maps the contents of a video buffer to memory. + + The map \a mode indicates whether the contents of the mapped memory should be read from and/or + written to the buffer. If the map mode includes the QAbstractVideoBuffer::ReadOnly flag the + mapped memory will be populated with the content of the video buffer when mapped. If the map + mode includes the QAbstractVideoBuffer::WriteOnly flag the content of the mapped memory will be + persisted in the buffer when unmapped. + + When access to the data is no longer needed be sure to call the unmap() function to release the + mapped memory. + + Returns a pointer to the mapped memory region, or a null pointer if the mapping failed. The + size in bytes of the mapped memory region is returned in \a numBytes, and the line stride in \a + bytesPerLine. + + When access to the data is no longer needed be sure to unmap() the buffer. + + \note Writing to memory that is mapped as read-only is undefined, and may result in changes + to shared data. + + \sa unmap(), mapMode() +*/ + +/*! + \fn QAbstractVideoBuffer::unmap() + + Releases the memory mapped by the map() function + + If the \l {QAbstractVideoBuffer::MapMode}{MapMode} included the QAbstractVideoBuffer::WriteOnly + flag this will persist the current content of the mapped memory to the video frame. + + \sa map() +*/ + +/*! + Returns a type specific handle to the data buffer. + + The type of the handle is given by handleType() function. + + \sa handleType() +*/ + +QVariant QAbstractVideoBuffer::handle() const +{ + return QVariant(); +} + + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/video/qabstractvideobuffer.h b/src/multimedia/multimedia/video/qabstractvideobuffer.h new file mode 100644 index 0000000..a8389db --- /dev/null +++ b/src/multimedia/multimedia/video/qabstractvideobuffer.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QABSTRACTVIDEOBUFFER_H +#define QABSTRACTVIDEOBUFFER_H + +#include <QtCore/qmetatype.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class QVariant; + +class QAbstractVideoBufferPrivate; + +class Q_MULTIMEDIA_EXPORT QAbstractVideoBuffer +{ +public: + enum HandleType + { + NoHandle, + GLTextureHandle, + XvShmImageHandle, + CoreImageHandle, + UserHandle = 1000 + }; + + enum MapMode + { + NotMapped = 0x00, + ReadOnly = 0x01, + WriteOnly = 0x02, + ReadWrite = ReadOnly | WriteOnly + }; + + QAbstractVideoBuffer(HandleType type); + virtual ~QAbstractVideoBuffer(); + + HandleType handleType() const; + + virtual MapMode mapMode() const = 0; + + virtual uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) = 0; + virtual void unmap() = 0; + + virtual QVariant handle() const; + +protected: + QAbstractVideoBuffer(QAbstractVideoBufferPrivate &dd, HandleType type); + + QAbstractVideoBufferPrivate *d_ptr; + +private: + Q_DECLARE_PRIVATE(QAbstractVideoBuffer) + Q_DISABLE_COPY(QAbstractVideoBuffer) +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QAbstractVideoBuffer::HandleType) +Q_DECLARE_METATYPE(QAbstractVideoBuffer::MapMode) + +QT_END_HEADER + +#endif diff --git a/src/multimedia/multimedia/video/qabstractvideobuffer_p.h b/src/multimedia/multimedia/video/qabstractvideobuffer_p.h new file mode 100644 index 0000000..c72f303 --- /dev/null +++ b/src/multimedia/multimedia/video/qabstractvideobuffer_p.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QABSTRACTVIDEOBUFFER_P_H +#define QABSTRACTVIDEOBUFFER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtCore/qshareddata.h> +#include <QtMultimedia/qabstractvideobuffer.h> + +QT_BEGIN_NAMESPACE + +class QAbstractVideoBufferPrivate +{ +public: + QAbstractVideoBufferPrivate() + : handleType(QAbstractVideoBuffer::NoHandle) + {} + + QAbstractVideoBuffer::HandleType handleType; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/video/qabstractvideosurface.cpp b/src/multimedia/multimedia/video/qabstractvideosurface.cpp new file mode 100644 index 0000000..3dabb6b --- /dev/null +++ b/src/multimedia/multimedia/video/qabstractvideosurface.cpp @@ -0,0 +1,283 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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_p.h" + +QT_BEGIN_NAMESPACE + +/*! + \class QAbstractVideoSurface + \brief The QAbstractVideoSurface class is a base class for video presentation surfaces. + \since 4.6 + + The QAbstractVideoSurface class defines the standard interface that video producers use to + inter-operate with video presentation surfaces. It is not supposed to be instantiated directly. + Instead, you should subclass it to create new video surfaces. + + A video surface presents a continuous stream of identically formatted frames, where the format + of each frame is compatible with a stream format supplied when starting a presentation. + + A list of pixel formats a surface can present is given by the supportedPixelFormats() function, + and the isFormatSupported() function will test if a video surface format is supported. If a + format is not supported the nearestFormat() function may be able to suggest a similar format. + For example if a surface supports fixed set of resolutions it may suggest the smallest + supported resolution that contains the proposed resolution. + + The start() function takes a supported format and enables a video surface. Once started a + surface will begin displaying the frames it receives in the present() function. Surfaces may + hold a reference to the buffer of a presented video frame until a new frame is presented or + streaming is stopped. The stop() function will disable a surface and a release any video + buffers it holds references to. +*/ + +/*! + \enum QAbstractVideoSurface::Error + This enum describes the errors that may be returned by the error() function. + + \value NoError No error occurred. + \value UnsupportedFormatError A video format was not supported. + \value IncorrectFormatError A video frame was not compatible with the format of the surface. + \value StoppedError The surface has not been started. + \value ResourceError The surface could not allocate some resource. +*/ + +/*! + Constructs a video surface with the given \a parent. +*/ + +QAbstractVideoSurface::QAbstractVideoSurface(QObject *parent) + : QObject(*new QAbstractVideoSurfacePrivate, parent) +{ +} + +/*! + \internal +*/ + +QAbstractVideoSurface::QAbstractVideoSurface(QAbstractVideoSurfacePrivate &dd, QObject *parent) + : QObject(dd, parent) +{ +} + +/*! + Destroys a video surface. +*/ + +QAbstractVideoSurface::~QAbstractVideoSurface() +{ +} + +/*! + \fn QAbstractVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType type) const + + Returns a list of pixel formats a video surface can present for a given handle \a type. + + The pixel formats returned for the QAbstractVideoBuffer::NoHandle type are valid for any buffer + that can be mapped in read-only mode. + + Types that are first in the list can be assumed to be faster to render. +*/ + +/*! + Tests a video surface \a format to determine if a surface can accept it. + + Returns true if the format is supported by the surface, and false otherwise. +*/ + +bool QAbstractVideoSurface::isFormatSupported(const QVideoSurfaceFormat &format) const +{ + return supportedPixelFormats(format.handleType()).contains(format.pixelFormat()); +} + +/*! + Returns a supported video surface format that is similar to \a format. + + A similar surface format is one that has the same \l {QVideoSurfaceFormat::pixelFormat()}{pixel + format} and \l {QVideoSurfaceFormat::handleType()}{handle type} but differs in some of the other + properties. For example if there are restrictions on the \l {QVideoSurfaceFormat::frameSize()} + {frame sizes} a video surface can accept it may suggest a format with a larger frame size and + a \l {QVideoSurfaceFormat::viewport()}{viewport} the size of the original frame size. + + If the format is already supported it will be returned unchanged, or if there is no similar + supported format an invalid format will be returned. +*/ + +QVideoSurfaceFormat QAbstractVideoSurface::nearestFormat(const QVideoSurfaceFormat &format) const +{ + return isFormatSupported(format) + ? format + : QVideoSurfaceFormat(); +} + +/*! + \fn QAbstractVideoSurface::supportedFormatsChanged() + + Signals that the set of formats supported by a video surface has changed. + + \sa supportedPixelFormats(), isFormatSupported() +*/ + +/*! + Returns the format of a video surface. +*/ + +QVideoSurfaceFormat QAbstractVideoSurface::surfaceFormat() const +{ + return d_func()->format; +} + +/*! + \fn QAbstractVideoSurface::surfaceFormatChanged(const QVideoSurfaceFormat &format) + + Signals that the configured \a format of a video surface has changed. + + \sa surfaceFormat(), start() +*/ + +/*! + Starts a video surface presenting \a format frames. + + Returns true if the surface was started, and false if an error occurred. + + \sa isActive(), stop() +*/ + +bool QAbstractVideoSurface::start(const QVideoSurfaceFormat &format) +{ + Q_D(QAbstractVideoSurface); + + bool wasActive = d->active; + + d->active = true; + d->format = format; + d->error = NoError; + + emit surfaceFormatChanged(d->format); + + if (!wasActive) + emit activeChanged(true); + + return true; +} + +/*! + Stops a video surface presenting frames and releases any resources acquired in start(). + + \sa isActive(), start() +*/ + +void QAbstractVideoSurface::stop() +{ + Q_D(QAbstractVideoSurface); + + if (d->active) { + d->format = QVideoSurfaceFormat(); + d->active = false; + + emit activeChanged(false); + emit surfaceFormatChanged(d->format); + } +} + +/*! + Indicates whether a video surface has been started. + + Returns true if the surface has been started, and false otherwise. +*/ + +bool QAbstractVideoSurface::isActive() const +{ + return d_func()->active; +} + +/*! + \fn QAbstractVideoSurface::activeChanged(bool active) + + Signals that the \a active state of a video surface has changed. + + \sa isActive(), start(), stop() +*/ + +/*! + \fn QAbstractVideoSurface::present(const QVideoFrame &frame) + + Presents a video \a frame. + + Returns true if the frame was presented, and false if an error occurred. + + Not all surfaces will block until the presentation of a frame has completed. Calling present() + on a non-blocking surface may fail if called before the presentation of a previous frame has + completed. In such cases the surface may not return to a ready state until it's had an + opportunity to process events. + + If present() fails for any other reason the surface will immediately enter the stopped state + and an error() value will be set. + + A video surface must be in the started state for present() to succeed, and the format of the + video frame must be compatible with the current video surface format. + + \sa error() +*/ + +/*! + Returns the last error that occurred. + + If a surface fails to start(), or stops unexpectedly this function can be called to discover + what error occurred. +*/ + +QAbstractVideoSurface::Error QAbstractVideoSurface::error() const +{ + return d_func()->error; +} + +/*! + Sets the value of error() to \a error. +*/ + +void QAbstractVideoSurface::setError(Error error) +{ + Q_D(QAbstractVideoSurface); + + d->error = error; +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/video/qabstractvideosurface.h b/src/multimedia/multimedia/video/qabstractvideosurface.h new file mode 100644 index 0000000..f2cae17 --- /dev/null +++ b/src/multimedia/multimedia/video/qabstractvideosurface.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QABSTRACTVIDEOSURFACE_H +#define QABSTRACTVIDEOSURFACE_H + +#include <QtCore/qobject.h> +#include <QtMultimedia/qvideoframe.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class QRectF; +class QVideoSurfaceFormat; + +class QAbstractVideoSurfacePrivate; + +class Q_MULTIMEDIA_EXPORT QAbstractVideoSurface : public QObject +{ + Q_OBJECT + +public: + enum Error + { + NoError, + UnsupportedFormatError, + IncorrectFormatError, + StoppedError, + ResourceError + }; + + explicit QAbstractVideoSurface(QObject *parent = 0); + ~QAbstractVideoSurface(); + + virtual QList<QVideoFrame::PixelFormat> supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const = 0; + virtual bool isFormatSupported(const QVideoSurfaceFormat &format) const; + virtual QVideoSurfaceFormat nearestFormat(const QVideoSurfaceFormat &format) const; + + QVideoSurfaceFormat surfaceFormat() const; + + virtual bool start(const QVideoSurfaceFormat &format); + virtual void stop(); + + bool isActive() const; + + virtual bool present(const QVideoFrame &frame) = 0; + + Error error() const; + +Q_SIGNALS: + void activeChanged(bool active); + void surfaceFormatChanged(const QVideoSurfaceFormat &format); + void supportedFormatsChanged(); + +protected: + QAbstractVideoSurface(QAbstractVideoSurfacePrivate &dd, QObject *parent); + + void setError(Error error); + +private: + Q_DECLARE_PRIVATE(QAbstractVideoSurface) +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/multimedia/multimedia/video/qabstractvideosurface_p.h b/src/multimedia/multimedia/video/qabstractvideosurface_p.h new file mode 100644 index 0000000..42df112 --- /dev/null +++ b/src/multimedia/multimedia/video/qabstractvideosurface_p.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QABSTRACTVIDEOSURFACE_P_H +#define QABSTRACTVIDEOSURFACE_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtMultimedia/qabstractvideosurface.h> +#include <QtMultimedia/qvideosurfaceformat.h> +#include <QtCore/private/qobject_p.h> + +QT_BEGIN_NAMESPACE + +class QAbstractVideoSurfacePrivate : public QObjectPrivate +{ +public: + QAbstractVideoSurfacePrivate() + : error(QAbstractVideoSurface::NoError) + , active(false) + { + } + + mutable QAbstractVideoSurface::Error error; + QVideoSurfaceFormat format; + bool active; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/video/qimagevideobuffer.cpp b/src/multimedia/multimedia/video/qimagevideobuffer.cpp new file mode 100644 index 0000000..e3e1585 --- /dev/null +++ b/src/multimedia/multimedia/video/qimagevideobuffer.cpp @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 "qimagevideobuffer_p.h" + +#include <private/qabstractvideobuffer_p.h> + +#include <qimage.h> +#include <qvariant.h> + +QT_BEGIN_NAMESPACE + +class QImageVideoBufferPrivate : public QAbstractVideoBufferPrivate +{ +public: + QImageVideoBufferPrivate() + : mapMode(QAbstractVideoBuffer::NotMapped) + { + } + + QAbstractVideoBuffer::MapMode mapMode; + QImage image; +}; + +QImageVideoBuffer::QImageVideoBuffer(const QImage &image) + : QAbstractVideoBuffer(*new QImageVideoBufferPrivate, NoHandle) +{ + Q_D(QImageVideoBuffer); + + d->image = image; +} + +QImageVideoBuffer::~QImageVideoBuffer() +{ +} + +QAbstractVideoBuffer::MapMode QImageVideoBuffer::mapMode() const +{ + return d_func()->mapMode; +} + +uchar *QImageVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) +{ + Q_D(QImageVideoBuffer); + + if (d->mapMode == NotMapped && d->image.bits() && mode != NotMapped) { + d->mapMode = mode; + + if (numBytes) + *numBytes = d->image.byteCount(); + + if (bytesPerLine) + *bytesPerLine = d->image.bytesPerLine(); + + return d->image.bits(); + } else { + return 0; + } +} + +void QImageVideoBuffer::unmap() +{ + Q_D(QImageVideoBuffer); + + d->mapMode = NotMapped; +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/video/qimagevideobuffer_p.h b/src/multimedia/multimedia/video/qimagevideobuffer_p.h new file mode 100644 index 0000000..82075d7 --- /dev/null +++ b/src/multimedia/multimedia/video/qimagevideobuffer_p.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QIMAGEVIDEOBUFFER_P_H +#define QIMAGEVIDEOBUFFER_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include <QtMultimedia/qabstractvideobuffer.h> + +QT_BEGIN_NAMESPACE + +class QImage; + +class QImageVideoBufferPrivate; + +class Q_MULTIMEDIA_EXPORT QImageVideoBuffer : public QAbstractVideoBuffer +{ + Q_DECLARE_PRIVATE(QImageVideoBuffer) +public: + QImageVideoBuffer(const QImage &image); + ~QImageVideoBuffer(); + + MapMode mapMode() const; + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); + void unmap(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/multimedia/multimedia/video/qmemoryvideobuffer.cpp b/src/multimedia/multimedia/video/qmemoryvideobuffer.cpp new file mode 100644 index 0000000..2e892b7 --- /dev/null +++ b/src/multimedia/multimedia/video/qmemoryvideobuffer.cpp @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 "qmemoryvideobuffer_p.h" + +#include <private/qabstractvideobuffer_p.h> +#include <qbytearray.h> + +QT_BEGIN_NAMESPACE + +class QMemoryVideoBufferPrivate : public QAbstractVideoBufferPrivate +{ +public: + QMemoryVideoBufferPrivate() + : bytesPerLine(0) + , mapMode(QAbstractVideoBuffer::NotMapped) + { + } + + int bytesPerLine; + QAbstractVideoBuffer::MapMode mapMode; + QByteArray data; +}; + +/*! + \class QMemoryVideoBuffer + \brief The QMemoryVideoBuffer class provides a system memory allocated video data buffer. + \internal + + QMemoryVideoBuffer is the default video buffer for allocating system memory. It may be used to + allocate memory for a QVideoFrame without implementing your own QAbstractVideoBuffer. +*/ + +/*! + Constructs a video buffer with an image stride of \a bytesPerLine from a byte \a array. +*/ +QMemoryVideoBuffer::QMemoryVideoBuffer(const QByteArray &array, int bytesPerLine) + : QAbstractVideoBuffer(*new QMemoryVideoBufferPrivate, NoHandle) +{ + Q_D(QMemoryVideoBuffer); + + d->data = array; + d->bytesPerLine = bytesPerLine; +} + +/*! + Destroys a system memory allocated video buffer. +*/ +QMemoryVideoBuffer::~QMemoryVideoBuffer() +{ +} + +/*! + \reimp +*/ +QAbstractVideoBuffer::MapMode QMemoryVideoBuffer::mapMode() const +{ + return d_func()->mapMode; +} + +/*! + \reimp +*/ +uchar *QMemoryVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) +{ + Q_D(QMemoryVideoBuffer); + + if (d->mapMode == NotMapped && d->data.data() && mode != NotMapped) { + d->mapMode = mode; + + if (numBytes) + *numBytes = d->data.size(); + + if (bytesPerLine) + *bytesPerLine = d->bytesPerLine; + + return reinterpret_cast<uchar *>(d->data.data()); + } else { + return 0; + } +} + +/*! + \reimp +*/ +void QMemoryVideoBuffer::unmap() +{ + d_func()->mapMode = NotMapped; +} + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/video/qmemoryvideobuffer_p.h b/src/multimedia/multimedia/video/qmemoryvideobuffer_p.h new file mode 100644 index 0000000..c66cf93 --- /dev/null +++ b/src/multimedia/multimedia/video/qmemoryvideobuffer_p.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QMEMORYVIDEOBUFFER_P_H +#define QMEMORYVIDEOBUFFER_P_H + +#include <QtMultimedia/qabstractvideobuffer.h> + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class QMemoryVideoBufferPrivate; + +class Q_MULTIMEDIA_EXPORT QMemoryVideoBuffer : public QAbstractVideoBuffer +{ + Q_DECLARE_PRIVATE(QMemoryVideoBuffer) +public: + QMemoryVideoBuffer(const QByteArray &data, int bytesPerLine); + ~QMemoryVideoBuffer(); + + MapMode mapMode() const; + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); + void unmap(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/multimedia/multimedia/video/qvideoframe.cpp b/src/multimedia/multimedia/video/qvideoframe.cpp new file mode 100644 index 0000000..2d66d9e --- /dev/null +++ b/src/multimedia/multimedia/video/qvideoframe.cpp @@ -0,0 +1,741 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 "qvideoframe.h" + +#include <private/qimagevideobuffer_p.h> +#include <private/qmemoryvideobuffer_p.h> + +#include <qimage.h> +#include <qpair.h> +#include <qsize.h> +#include <qvariant.h> +#include <qvector.h> + +QT_BEGIN_NAMESPACE + +class QVideoFramePrivate : public QSharedData +{ +public: + QVideoFramePrivate() + : startTime(-1) + , endTime(-1) + , data(0) + , mappedBytes(0) + , bytesPerLine(0) + , pixelFormat(QVideoFrame::Format_Invalid) + , fieldType(QVideoFrame::ProgressiveFrame) + , buffer(0) + { + } + + QVideoFramePrivate(const QSize &size, QVideoFrame::PixelFormat format) + : size(size) + , startTime(-1) + , endTime(-1) + , data(0) + , mappedBytes(0) + , bytesPerLine(0) + , pixelFormat(format) + , fieldType(QVideoFrame::ProgressiveFrame) + , buffer(0) + { + } + + ~QVideoFramePrivate() + { + delete buffer; + } + + QSize size; + qint64 startTime; + qint64 endTime; + uchar *data; + int mappedBytes; + int bytesPerLine; + QVideoFrame::PixelFormat pixelFormat; + QVideoFrame::FieldType fieldType; + QAbstractVideoBuffer *buffer; + +private: + Q_DISABLE_COPY(QVideoFramePrivate) +}; + +/*! + \class QVideoFrame + \brief The QVideoFrame class provides a representation of a frame of video data. + \since 4.6 + + A QVideoFrame encapsulates the data of a video frame, and information about the frame. + + The contents of a video frame can be mapped to memory using the map() function. While + mapped the video data can accessed using the bits() function which returns a pointer to a + buffer, the total size of which is given by the mappedBytes(), and the size of each line is given + by bytesPerLine(). The return value of the handle() function may be used to access frame data + using the internal buffer's native APIs. + + The video data in a QVideoFrame is encapsulated in a QAbstractVideoBuffer. A QVideoFrame + may be constructed from any buffer type by subclassing the QAbstractVideoBuffer class. + + \note QVideoFrame is explicitly shared, any change made to video frame will also apply to any + copies. +*/ + +/*! + \enum QVideoFrame::PixelFormat + + Enumerates video data types. + + \value Format_Invalid + The frame is invalid. + + \value Format_ARGB32 + The frame is stored using a 32-bit ARGB format (0xAARRGGBB). This is equivalent to + QImage::Format_ARGB32. + + \value Format_ARGB32_Premultiplied + The frame stored using a premultiplied 32-bit ARGB format (0xAARRGGBB). This is equivalent + to QImage::Format_ARGB32_Premultiplied. + + \value Format_RGB32 + The frame stored using a 32-bit RGB format (0xffRRGGBB). This is equivalent to + QImage::Format_RGB32 + + \value Format_RGB24 + The frame is stored using a 24-bit RGB format (8-8-8). This is equivalent to + QImage::Format_RGB888 + + \value Format_RGB565 + The frame is stored using a 16-bit RGB format (5-6-5). This is equivalent to + QImage::Format_RGB16. + + \value Format_RGB555 + The frame is stored using a 16-bit RGB format (5-5-5). This is equivalent to + QImage::Format_RGB555. + + \value Format_ARGB8565_Premultiplied + The frame is stored using a 24-bit premultiplied ARGB format (8-6-6-5). + + \value Format_BGRA32 + The frame is stored using a 32-bit ARGB format (0xBBGGRRAA). + + \value Format_BGRA32_Premultiplied + The frame is stored using a premultiplied 32bit BGRA format. + + \value Format_BGR32 + The frame is stored using a 32-bit BGR format (0xBBGGRRff). + + \value Format_BGR24 + The frame is stored using a 24-bit BGR format (0xBBGGRR). + + \value Format_BGR565 + The frame is stored using a 16-bit BGR format (5-6-5). + + \value Format_BGR555 + The frame is stored using a 16-bit BGR format (5-5-5). + + \value Format_BGRA5658_Premultiplied + The frame is stored using a 24-bit premultiplied BGRA format (5-6-5-8). + + \value Format_AYUV444 + The frame is stored using a packed 32-bit AYUV format (0xAAYYUUVV). + + \value Format_AYUV444_Premultiplied + The frame is stored using a packed premultiplied 32-bit AYUV format (0xAAYYUUVV). + + \value Format_YUV444 + The frame is stored using a 24-bit packed YUV format (8-8-8). + + \value Format_YUV420P + The frame is stored using an 8-bit per component planar YUV format with the U and V planes + horizontally and vertically sub-sampled, i.e. the height and width of the U and V planes are + half that of the Y plane. + + \value Format_YV12 + The frame is stored using an 8-bit per component planar YVU format with the V and U planes + horizontally and vertically sub-sampled, i.e. the height and width of the V and U planes are + half that of the Y plane. + + \value Format_UYVY + The frame is stored using an 8-bit per component packed YUV format with the U and V planes + horizontally sub-sampled (U-Y-V-Y), i.e. two horizontally adjacent pixels are stored as a 32-bit + macropixel which has a Y value for each pixel and common U and V values. + + \value Format_YUYV + The frame is stored using an 8-bit per component packed YUV format with the U and V planes + horizontally sub-sampled (Y-U-Y-V), i.e. two horizontally adjacent pixels are stored as a 32-bit + macropixel which has a Y value for each pixel and common U and V values. + + \value Format_NV12 + The frame is stored using an 8-bit per component semi-planar YUV format with a Y plane (Y) + followed by a horizontally and vertically sub-sampled, packed UV plane (U-V). + + \value Format_NV21 + The frame is stored using an 8-bit per component semi-planar YUV format with a Y plane (Y) + followed by a horizontally and vertically sub-sampled, packed VU plane (V-U). + + \value Format_IMC1 + The frame is stored using an 8-bit per component planar YUV format with the U and V planes + horizontally and vertically sub-sampled. This is similar to the Format_YUV420P type, except + that the bytes per line of the U and V planes are padded out to the same stride as the Y plane. + + \value Format_IMC2 + The frame is stored using an 8-bit per component planar YUV format with the U and V planes + horizontally and vertically sub-sampled. This is similar to the Format_YUV420P type, except + that the lines of the U and V planes are interleaved, i.e. each line of U data is followed by a + line of V data creating a single line of the same stride as the Y data. + + \value Format_IMC3 + The frame is stored using an 8-bit per component planar YVU format with the V and U planes + horizontally and vertically sub-sampled. This is similar to the Format_YV12 type, except that + the bytes per line of the V and U planes are padded out to the same stride as the Y plane. + + \value Format_IMC4 + The frame is stored using an 8-bit per component planar YVU format with the V and U planes + horizontally and vertically sub-sampled. This is similar to the Format_YV12 type, except that + the lines of the V and U planes are interleaved, i.e. each line of V data is followed by a line + of U data creating a single line of the same stride as the Y data. + + \value Format_Y8 + The frame is stored using an 8-bit greyscale format. + + \value Format_Y16 + The frame is stored using a 16-bit linear greyscale format. Little endian. + + \value Format_User + Start value for user defined pixel formats. +*/ + +/*! + \enum QVideoFrame::FieldType + + Specifies the field an interlaced video frame belongs to. + + \value ProgressiveFrame The frame is not interlaced. + \value TopField The frame contains a top field. + \value BottomField The frame contains a bottom field. + \value InterlacedFrame The frame contains a merged top and bottom field. +*/ + +/*! + Constructs a null video frame. +*/ + +QVideoFrame::QVideoFrame() + : d(new QVideoFramePrivate) +{ +} + +/*! + Constructs a video frame from a \a buffer of the given pixel \a format and \a size in pixels. + + \note This doesn't increment the reference count of the video buffer. +*/ + +QVideoFrame::QVideoFrame( + QAbstractVideoBuffer *buffer, const QSize &size, PixelFormat format) + : d(new QVideoFramePrivate(size, format)) +{ + d->buffer = buffer; +} + +/*! + Constructs a video frame of the given pixel \a format and \a size in pixels. + + The \a bytesPerLine (stride) is the length of each scan line in bytes, and \a bytes is the total + number of bytes that must be allocated for the frame. +*/ + +QVideoFrame::QVideoFrame(int bytes, const QSize &size, int bytesPerLine, PixelFormat format) + : d(new QVideoFramePrivate(size, format)) +{ + if (bytes > 0) { + QByteArray data; + data.resize(bytes); + + // Check the memory was successfully allocated. + if (!data.isEmpty()) + d->buffer = new QMemoryVideoBuffer(data, bytesPerLine); + } +} + +/*! + Constructs a video frame from an \a image. + + \note This will construct an invalid video frame if there is no frame type equivalent to the + image format. + + \sa pixelFormatFromImageFormat() +*/ + +QVideoFrame::QVideoFrame(const QImage &image) + : d(new QVideoFramePrivate( + image.size(), pixelFormatFromImageFormat(image.format()))) +{ + if (d->pixelFormat != Format_Invalid) + d->buffer = new QImageVideoBuffer(image); +} + +/*! + Constructs a copy of \a other. +*/ + +QVideoFrame::QVideoFrame(const QVideoFrame &other) + : d(other.d) +{ +} + +/*! + Assigns the contents of \a other to a video frame. +*/ + +QVideoFrame &QVideoFrame::operator =(const QVideoFrame &other) +{ + d = other.d; + + return *this; +} + +/*! + Destroys a video frame. +*/ + +QVideoFrame::~QVideoFrame() +{ +} + +/*! + Identifies whether a video frame is valid. + + An invalid frame has no video buffer associated with it. + + Returns true if the frame is valid, and false if it is not. +*/ + +bool QVideoFrame::isValid() const +{ + return d->buffer != 0; +} + +/*! + Returns the color format of a video frame. +*/ + +QVideoFrame::PixelFormat QVideoFrame::pixelFormat() const +{ + return d->pixelFormat; +} + +/*! + Returns the type of a video frame's handle. +*/ + +QAbstractVideoBuffer::HandleType QVideoFrame::handleType() const +{ + return d->buffer ? d->buffer->handleType() : QAbstractVideoBuffer::NoHandle; +} + +/*! + Returns the size of a video frame. +*/ + +QSize QVideoFrame::size() const +{ + return d->size; +} + +/*! + Returns the width of a video frame. +*/ + +int QVideoFrame::width() const +{ + return d->size.width(); +} + +/*! + Returns the height of a video frame. +*/ + +int QVideoFrame::height() const +{ + return d->size.height(); +} + +/*! + Returns the field an interlaced video frame belongs to. + + If the video is not interlaced this will return WholeFrame. +*/ + +QVideoFrame::FieldType QVideoFrame::fieldType() const +{ + return d->fieldType; +} + +/*! + Sets the \a field an interlaced video frame belongs to. +*/ + +void QVideoFrame::setFieldType(QVideoFrame::FieldType field) +{ + d->fieldType = field; +} + +/*! + Identifies if a video frame's contents are currently mapped to system memory. + + This is a convenience function which checks that the \l {QAbstractVideoBuffer::MapMode}{MapMode} + of the frame is not equal to QAbstractVideoBuffer::NotMapped. + + Returns true if the contents of the video frame are mapped to system memory, and false + otherwise. + + \sa mapMode() QAbstractVideoBuffer::MapMode +*/ + +bool QVideoFrame::isMapped() const +{ + return d->buffer != 0 && d->buffer->mapMode() != QAbstractVideoBuffer::NotMapped; +} + +/*! + Identifies if the mapped contents of a video frame will be persisted when the frame is unmapped. + + This is a convenience function which checks if the \l {QAbstractVideoBuffer::MapMode}{MapMode} + contains the QAbstractVideoBuffer::WriteOnly flag. + + Returns true if the video frame will be updated when unmapped, and false otherwise. + + \note The result of altering the data of a frame that is mapped in read-only mode is undefined. + Depending on the buffer implementation the changes may be persisted, or worse alter a shared + buffer. + + \sa mapMode(), QAbstractVideoBuffer::MapMode +*/ + +bool QVideoFrame::isWritable() const +{ + return d->buffer != 0 && (d->buffer->mapMode() & QAbstractVideoBuffer::WriteOnly); +} + +/*! + Identifies if the mapped contents of a video frame were read from the frame when it was mapped. + + This is a convenience function which checks if the \l {QAbstractVideoBuffer::MapMode}{MapMode} + contains the QAbstractVideoBuffer::WriteOnly flag. + + Returns true if the contents of the mapped memory were read from the video frame, and false + otherwise. + + \sa mapMode(), QAbstractVideoBuffer::MapMode +*/ + +bool QVideoFrame::isReadable() const +{ + return d->buffer != 0 && (d->buffer->mapMode() & QAbstractVideoBuffer::ReadOnly); +} + +/*! + Returns the mode a video frame was mapped to system memory in. + + \sa map(), QAbstractVideoBuffer::MapMode +*/ + +QAbstractVideoBuffer::MapMode QVideoFrame::mapMode() const +{ + return d->buffer != 0 ? d->buffer->mapMode() : QAbstractVideoBuffer::NotMapped; +} + +/*! + Maps the contents of a video frame to memory. + + The map \a mode indicates whether the contents of the mapped memory should be read from and/or + written to the frame. If the map mode includes the QAbstractVideoBuffer::ReadOnly flag the + mapped memory will be populated with the content of the video frame when mapped. If the map + mode inclues the QAbstractVideoBuffer::WriteOnly flag the content of the mapped memory will be + persisted in the frame when unmapped. + + While mapped the contents of a video frame can be accessed directly through the pointer returned + by the bits() function. + + When access to the data is no longer needed be sure to call the unmap() function to release the + mapped memory. + + Returns true if the buffer was mapped to memory in the given \a mode and false otherwise. + + \sa unmap(), mapMode(), bits() +*/ + +bool QVideoFrame::map(QAbstractVideoBuffer::MapMode mode) +{ + if (d->buffer != 0 && d->data == 0) { + Q_ASSERT(d->bytesPerLine == 0); + Q_ASSERT(d->mappedBytes == 0); + + d->data = d->buffer->map(mode, &d->mappedBytes, &d->bytesPerLine); + + return d->data != 0; + } + + return false; +} + +/*! + Releases the memory mapped by the map() function. + + If the \l {QAbstractVideoBuffer::MapMode}{MapMode} included the QAbstractVideoBuffer::WriteOnly + flag this will persist the current content of the mapped memory to the video frame. + + \sa map() +*/ + +void QVideoFrame::unmap() +{ + if (d->data != 0) { + d->mappedBytes = 0; + d->bytesPerLine = 0; + d->data = 0; + + d->buffer->unmap(); + } +} + +/*! + Returns the number of bytes in a scan line. + + \note This is the bytes per line of the first plane only. The bytes per line of subsequent + planes should be calculated as per the frame type. + + This value is only valid while the frame data is \l {map()}{mapped}. + + \sa bits(), map(), mappedBytes() +*/ + +int QVideoFrame::bytesPerLine() const +{ + return d->bytesPerLine; +} + +/*! + Returns a pointer to the start of the frame data buffer. + + This value is only valid while the frame data is \l {map()}{mapped}. + + \sa map(), mappedBytes(), bytesPerLine() +*/ + +uchar *QVideoFrame::bits() +{ + return d->data; +} + +/*! + Returns a pointer to the start of the frame data buffer. + + This value is only valid while the frame data is \l {map()}{mapped}. + + \sa map(), mappedBytes(), bytesPerLine() +*/ + +const uchar *QVideoFrame::bits() const +{ + return d->data; +} + +/*! + Returns the number of bytes occupied by the mapped frame data. + + This value is only valid while the frame data is \l {map()}{mapped}. + + \sa map() +*/ + +int QVideoFrame::mappedBytes() const +{ + return d->mappedBytes; +} + +/*! + Returns a type specific handle to a video frame's buffer. + + For an OpenGL texture this would be the texture ID. + + \sa QAbstractVideoBuffer::handle() +*/ + +QVariant QVideoFrame::handle() const +{ + return d->buffer != 0 ? d->buffer->handle() : QVariant(); +} + +/*! + Returns the presentation time when the frame should be displayed. +*/ + +qint64 QVideoFrame::startTime() const +{ + return d->startTime; +} + +/*! + Sets the presentation \a time when the frame should be displayed. +*/ + +void QVideoFrame::setStartTime(qint64 time) +{ + d->startTime = time; +} + +/*! + Returns the presentation time when a frame should stop being displayed. +*/ + +qint64 QVideoFrame::endTime() const +{ + return d->endTime; +} + +/*! + Sets the presentation \a time when a frame should stop being displayed. +*/ + +void QVideoFrame::setEndTime(qint64 time) +{ + d->endTime = time; +} + +/*! + Returns an video pixel format equivalent to an image \a format. If there is no equivalent + format QVideoFrame::InvalidType is returned instead. +*/ + +QVideoFrame::PixelFormat QVideoFrame::pixelFormatFromImageFormat(QImage::Format format) +{ + switch (format) { + case QImage::Format_Invalid: + case QImage::Format_Mono: + case QImage::Format_MonoLSB: + case QImage::Format_Indexed8: + return Format_Invalid; + case QImage::Format_RGB32: + return Format_RGB32; + case QImage::Format_ARGB32: + return Format_ARGB32; + case QImage::Format_ARGB32_Premultiplied: + return Format_ARGB32_Premultiplied; + case QImage::Format_RGB16: + return Format_RGB565; + case QImage::Format_ARGB8565_Premultiplied: + case QImage::Format_RGB666: + case QImage::Format_ARGB6666_Premultiplied: + return Format_Invalid; + case QImage::Format_RGB555: + return Format_RGB555; + case QImage::Format_ARGB8555_Premultiplied: + return Format_Invalid; + case QImage::Format_RGB888: + return Format_RGB24; + case QImage::Format_RGB444: + case QImage::Format_ARGB4444_Premultiplied: + return Format_Invalid; + case QImage::NImageFormats: + return Format_Invalid; + } + return Format_Invalid; +} + +/*! + Returns an image format equivalent to a video frame pixel \a format. If there is no equivalent + format QImage::Format_Invalid is returned instead. +*/ + +QImage::Format QVideoFrame::imageFormatFromPixelFormat(PixelFormat format) +{ + switch (format) { + case Format_Invalid: + return QImage::Format_Invalid; + case Format_ARGB32: + return QImage::Format_ARGB32; + case Format_ARGB32_Premultiplied: + return QImage::Format_ARGB32_Premultiplied; + case Format_RGB32: + return QImage::Format_RGB32; + case Format_RGB24: + return QImage::Format_RGB888; + case Format_RGB565: + return QImage::Format_RGB16; + case Format_RGB555: + return QImage::Format_RGB555; + case Format_ARGB8565_Premultiplied: + return QImage::Format_ARGB8565_Premultiplied; + case Format_BGRA32: + case Format_BGRA32_Premultiplied: + case Format_BGR32: + case Format_BGR24: + return QImage::Format_Invalid; + case Format_BGR565: + case Format_BGR555: + case Format_BGRA5658_Premultiplied: + case Format_AYUV444: + case Format_AYUV444_Premultiplied: + case Format_YUV444: + case Format_YUV420P: + case Format_YV12: + case Format_UYVY: + case Format_YUYV: + case Format_NV12: + case Format_NV21: + case Format_IMC1: + case Format_IMC2: + case Format_IMC3: + case Format_IMC4: + case Format_Y8: + case Format_Y16: + return QImage::Format_Invalid; + case Format_User: + return QImage::Format_Invalid; + } + return QImage::Format_Invalid; +} + +QT_END_NAMESPACE + diff --git a/src/multimedia/multimedia/video/qvideoframe.h b/src/multimedia/multimedia/video/qvideoframe.h new file mode 100644 index 0000000..668a738 --- /dev/null +++ b/src/multimedia/multimedia/video/qvideoframe.h @@ -0,0 +1,169 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QVIDEOFRAME_H +#define QVIDEOFRAME_H + +#include <QtCore/qmetatype.h> +#include <QtCore/qshareddata.h> +#include <QtGui/qimage.h> +#include <QtMultimedia/qabstractvideobuffer.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class QSize; +class QVariant; + +class QVideoFramePrivate; + +class Q_MULTIMEDIA_EXPORT QVideoFrame +{ +public: + enum FieldType + { + ProgressiveFrame, + TopField, + BottomField, + InterlacedFrame + }; + + enum PixelFormat + { + Format_Invalid, + Format_ARGB32, + Format_ARGB32_Premultiplied, + Format_RGB32, + Format_RGB24, + Format_RGB565, + Format_RGB555, + Format_ARGB8565_Premultiplied, + Format_BGRA32, + Format_BGRA32_Premultiplied, + Format_BGR32, + Format_BGR24, + Format_BGR565, + Format_BGR555, + Format_BGRA5658_Premultiplied, + + Format_AYUV444, + Format_AYUV444_Premultiplied, + Format_YUV444, + Format_YUV420P, + Format_YV12, + Format_UYVY, + Format_YUYV, + Format_NV12, + Format_NV21, + Format_IMC1, + Format_IMC2, + Format_IMC3, + Format_IMC4, + Format_Y8, + Format_Y16, + + Format_User = 1000 + }; + + QVideoFrame(); + QVideoFrame(QAbstractVideoBuffer *buffer, const QSize &size, PixelFormat format); + QVideoFrame(int bytes, const QSize &size, int bytesPerLine, PixelFormat format); + QVideoFrame(const QImage &image); + QVideoFrame(const QVideoFrame &other); + ~QVideoFrame(); + + QVideoFrame &operator =(const QVideoFrame &other); + + bool isValid() const; + + PixelFormat pixelFormat() const; + + QAbstractVideoBuffer::HandleType handleType() const; + + QSize size() const; + int width() const; + int height() const; + + FieldType fieldType() const; + void setFieldType(FieldType); + + bool isMapped() const; + bool isReadable() const; + bool isWritable() const; + + QAbstractVideoBuffer::MapMode mapMode() const; + + bool map(QAbstractVideoBuffer::MapMode mode); + void unmap(); + + int bytesPerLine() const; + + uchar *bits(); + const uchar *bits() const; + int mappedBytes() const; + + QVariant handle() const; + + qint64 startTime() const; + void setStartTime(qint64 time); + + qint64 endTime() const; + void setEndTime(qint64 time); + + static PixelFormat pixelFormatFromImageFormat(QImage::Format format); + static QImage::Format imageFormatFromPixelFormat(PixelFormat format); + +private: + QExplicitlySharedDataPointer<QVideoFramePrivate> d; +}; + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QVideoFrame::FieldType) +Q_DECLARE_METATYPE(QVideoFrame::PixelFormat) + +QT_END_HEADER + +#endif + diff --git a/src/multimedia/multimedia/video/qvideosurfaceformat.cpp b/src/multimedia/multimedia/video/qvideosurfaceformat.cpp new file mode 100644 index 0000000..1fc13a6 --- /dev/null +++ b/src/multimedia/multimedia/video/qvideosurfaceformat.cpp @@ -0,0 +1,704 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 "qvideosurfaceformat.h" + +#include <qdebug.h> +#include <qmetatype.h> +#include <qpair.h> +#include <qvariant.h> +#include <qvector.h> + +QT_BEGIN_NAMESPACE + +class QVideoSurfaceFormatPrivate : public QSharedData +{ +public: + QVideoSurfaceFormatPrivate() + : pixelFormat(QVideoFrame::Format_Invalid) + , handleType(QAbstractVideoBuffer::NoHandle) + , scanLineDirection(QVideoSurfaceFormat::TopToBottom) + , pixelAspectRatio(1, 1) + , ycbcrColorSpace(QVideoSurfaceFormat::YCbCr_Undefined) + , frameRate(0.0) + { + } + + QVideoSurfaceFormatPrivate( + const QSize &size, + QVideoFrame::PixelFormat format, + QAbstractVideoBuffer::HandleType type) + : pixelFormat(format) + , handleType(type) + , scanLineDirection(QVideoSurfaceFormat::TopToBottom) + , frameSize(size) + , pixelAspectRatio(1, 1) + , ycbcrColorSpace(QVideoSurfaceFormat::YCbCr_Undefined) + , viewport(QPoint(0, 0), size) + , frameRate(0.0) + { + } + + QVideoSurfaceFormatPrivate(const QVideoSurfaceFormatPrivate &other) + : QSharedData(other) + , pixelFormat(other.pixelFormat) + , handleType(other.handleType) + , scanLineDirection(other.scanLineDirection) + , frameSize(other.frameSize) + , pixelAspectRatio(other.pixelAspectRatio) + , ycbcrColorSpace(other.ycbcrColorSpace) + , viewport(other.viewport) + , frameRate(other.frameRate) + , propertyNames(other.propertyNames) + , propertyValues(other.propertyValues) + { + } + + bool operator ==(const QVideoSurfaceFormatPrivate &other) const + { + if (pixelFormat == other.pixelFormat + && handleType == other.handleType + && scanLineDirection == other.scanLineDirection + && frameSize == other.frameSize + && pixelAspectRatio == other.pixelAspectRatio + && viewport == other.viewport + && frameRatesEqual(frameRate, other.frameRate) + && ycbcrColorSpace == other.ycbcrColorSpace + && propertyNames.count() == other.propertyNames.count()) { + for (int i = 0; i < propertyNames.count(); ++i) { + int j = other.propertyNames.indexOf(propertyNames.at(i)); + + if (j == -1 || propertyValues.at(i) != other.propertyValues.at(j)) + return false; + } + return true; + } else { + return false; + } + } + + inline static bool frameRatesEqual(qreal r1, qreal r2) + { + return qAbs(r1 - r2) <= 0.00001 * qMin(qAbs(r1), qAbs(r2)); + } + + QVideoFrame::PixelFormat pixelFormat; + QAbstractVideoBuffer::HandleType handleType; + QVideoSurfaceFormat::Direction scanLineDirection; + QSize frameSize; + QSize pixelAspectRatio; + QVideoSurfaceFormat::YCbCrColorSpace ycbcrColorSpace; + QRect viewport; + qreal frameRate; + QList<QByteArray> propertyNames; + QList<QVariant> propertyValues; +}; + +/*! + \class QVideoSurfaceFormat + \brief The QVideoSurfaceFormat class specifies the stream format of a video presentation + surface. + \since 4.6 + + A video surface presents a stream of video frames. The surface's format describes the type of + the frames and determines how they should be presented. + + The core properties of a video stream required to setup a video surface are the pixel format + given by pixelFormat(), and the frame dimensions given by frameSize(). + + If the surface is to present frames using a frame's handle a surface format will also include + a handle type which is given by the handleType() function. + + The region of a frame that is actually displayed on a video surface is given by the viewport(). + A stream may have a viewport less than the entire region of a frame to allow for videos smaller + than the nearest optimal size of a video frame. For example the width of a frame may be + extended so that the start of each scan line is eight byte aligned. + + Other common properties are the pixelAspectRatio(), scanLineDirection(), and frameRate(). + Additionally a stream may have some additional type specific properties which are listed by the + dynamicPropertyNames() function and can be accessed using the property(), and setProperty() + functions. +*/ + +/*! + \enum QVideoSurfaceFormat::Direction + + Enumerates the layout direction of video scan lines. + + \value TopToBottom Scan lines are arranged from the top of the frame to the bottom. + \value BottomToTop Scan lines are arranged from the bottom of the frame to the top. +*/ + +/*! + \enum QVideoSurfaceFormat::YCbCrColorSpace + + Enumerates the Y'CbCr color space of video frames. + + \value YCbCr_Undefined + No color space is specified. + + \value YCbCr_BT601 + A Y'CbCr color space defined by ITU-R recommendation BT.601 + with Y value range from 16 to 235, and Cb/Cr range from 16 to 240. + Used in standard definition video. + + \value YCbCr_BT709 + A Y'CbCr color space defined by ITU-R BT.709 with the same values range as YCbCr_BT601. Used + for HDTV. + + \value YCbCr_xvYCC601 + The BT.601 color space with the value range extended to 0 to 255. + It is backward compatibile with BT.601 and uses values outside BT.601 range to represent + wider colors range. + + \value YCbCr_xvYCC709 + The BT.709 color space with the value range extended to 0 to 255. + + \value YCbCr_JPEG + The full range Y'CbCr color space used in JPEG files. +*/ + +/*! + Constructs a null video stream format. +*/ + +QVideoSurfaceFormat::QVideoSurfaceFormat() + : d(new QVideoSurfaceFormatPrivate) +{ +} + +/*! + Contructs a description of stream which receives stream of \a type buffers with given frame + \a size and pixel \a format. +*/ + +QVideoSurfaceFormat::QVideoSurfaceFormat( + const QSize& size, QVideoFrame::PixelFormat format, QAbstractVideoBuffer::HandleType type) + : d(new QVideoSurfaceFormatPrivate(size, format, type)) +{ +} + +/*! + Constructs a copy of \a other. +*/ + +QVideoSurfaceFormat::QVideoSurfaceFormat(const QVideoSurfaceFormat &other) + : d(other.d) +{ +} + +/*! + Assigns the values of \a other to a video stream description. +*/ + +QVideoSurfaceFormat &QVideoSurfaceFormat::operator =(const QVideoSurfaceFormat &other) +{ + d = other.d; + + return *this; +} + +/*! + Destroys a video stream description. +*/ + +QVideoSurfaceFormat::~QVideoSurfaceFormat() +{ +} + +/*! + Identifies if a video surface format has a valid pixel format and frame size. + + Returns true if the format is valid, and false otherwise. +*/ + +bool QVideoSurfaceFormat::isValid() const +{ + return d->pixelFormat == QVideoFrame::Format_Invalid && d->frameSize.isValid(); +} + +/*! + Returns true if \a other is the same as a video format, and false if they are the different. +*/ + +bool QVideoSurfaceFormat::operator ==(const QVideoSurfaceFormat &other) const +{ + return d == other.d || *d == *other.d; +} + +/*! + Returns true if \a other is different to a video format, and false if they are the same. +*/ + +bool QVideoSurfaceFormat::operator !=(const QVideoSurfaceFormat &other) const +{ + return d != other.d && !(*d == *other.d); +} + +/*! + Returns the pixel format of frames in a video stream. +*/ + +QVideoFrame::PixelFormat QVideoSurfaceFormat::pixelFormat() const +{ + return d->pixelFormat; +} + +/*! + Returns the type of handle the surface uses to present the frame data. + + If the handle type is QAbstractVideoBuffer::NoHandle buffers with any handle type are valid + provided they can be \l {QAbstractVideoBuffer::map()}{mapped} with the + QAbstractVideoBuffer::ReadOnly flag. If the handleType() is not QAbstractVideoBuffer::NoHandle + then the handle type of the buffer be the same as that of the surface format. +*/ + +QAbstractVideoBuffer::HandleType QVideoSurfaceFormat::handleType() const +{ + return d->handleType; +} + +/*! + Returns the size of frames in a video stream. + + \sa frameWidth(), frameHeight() +*/ + +QSize QVideoSurfaceFormat::frameSize() const +{ + return d->frameSize; +} + +/*! + Returns the width of frames in a video stream. + + \sa frameSize(), frameHeight() +*/ + +int QVideoSurfaceFormat::frameWidth() const +{ + return d->frameSize.width(); +} + +/*! + Returns the height of frame in a video stream. +*/ + +int QVideoSurfaceFormat::frameHeight() const +{ + return d->frameSize.height(); +} + +/*! + Sets the size of frames in a video stream to \a size. + + This will reset the viewport() to fill the entire frame. +*/ + +void QVideoSurfaceFormat::setFrameSize(const QSize &size) +{ + d->frameSize = size; + d->viewport = QRect(QPoint(0, 0), size); +} + +/*! + \overload + + Sets the \a width and \a height of frames in a video stream. + + This will reset the viewport() to fill the entire frame. +*/ + +void QVideoSurfaceFormat::setFrameSize(int width, int height) +{ + d->frameSize = QSize(width, height); + d->viewport = QRect(0, 0, width, height); +} + +/*! + Returns the viewport of a video stream. + + The viewport is the region of a video frame that is actually displayed. + + By default the viewport covers an entire frame. +*/ + +QRect QVideoSurfaceFormat::viewport() const +{ + return d->viewport; +} + +/*! + Sets the viewport of a video stream to \a viewport. +*/ + +void QVideoSurfaceFormat::setViewport(const QRect &viewport) +{ + d->viewport = viewport; +} + +/*! + Returns the direction of scan lines. +*/ + +QVideoSurfaceFormat::Direction QVideoSurfaceFormat::scanLineDirection() const +{ + return d->scanLineDirection; +} + +/*! + Sets the \a direction of scan lines. +*/ + +void QVideoSurfaceFormat::setScanLineDirection(Direction direction) +{ + d->scanLineDirection = direction; +} + +/*! + Returns the frame rate of a video stream in frames per second. +*/ + +qreal QVideoSurfaceFormat::frameRate() const +{ + return d->frameRate; +} + +/*! + Sets the frame \a rate of a video stream in frames per second. +*/ + +void QVideoSurfaceFormat::setFrameRate(qreal rate) +{ + d->frameRate = rate; +} + +/*! + Returns a video stream's pixel aspect ratio. +*/ + +QSize QVideoSurfaceFormat::pixelAspectRatio() const +{ + return d->pixelAspectRatio; +} + +/*! + Sets a video stream's pixel aspect \a ratio. +*/ + +void QVideoSurfaceFormat::setPixelAspectRatio(const QSize &ratio) +{ + d->pixelAspectRatio = ratio; +} + +/*! + \overload + + Sets the \a horizontal and \a vertical elements of a video stream's pixel aspect ratio. +*/ + +void QVideoSurfaceFormat::setPixelAspectRatio(int horizontal, int vertical) +{ + d->pixelAspectRatio = QSize(horizontal, vertical); +} + +/*! + Returns the Y'CbCr color space of a video stream. +*/ + +QVideoSurfaceFormat::YCbCrColorSpace QVideoSurfaceFormat::yCbCrColorSpace() const +{ + return d->ycbcrColorSpace; +} + +/*! + Sets the Y'CbCr color \a space of a video stream. + It is only used with raw YUV frame types. +*/ + +void QVideoSurfaceFormat::setYCbCrColorSpace(QVideoSurfaceFormat::YCbCrColorSpace space) +{ + d->ycbcrColorSpace = space; +} + +/*! + Returns a suggested size in pixels for the video stream. + + This is the size of the viewport scaled according to the pixel aspect ratio. +*/ + +QSize QVideoSurfaceFormat::sizeHint() const +{ + QSize size = d->viewport.size(); + + if (d->pixelAspectRatio.height() != 0) + size.setWidth(size.width() * d->pixelAspectRatio.width() / d->pixelAspectRatio.height()); + + return size; +} + +/*! + Returns a list of video format dynamic property names. +*/ + +QList<QByteArray> QVideoSurfaceFormat::propertyNames() const +{ + return (QList<QByteArray>() + << "handleType" + << "pixelFormat" + << "frameSize" + << "frameWidth" + << "viewport" + << "scanLineDirection" + << "frameRate" + << "pixelAspectRatio" + << "sizeHint" + << "yCbCrColorSpace") + + d->propertyNames; +} + +/*! + Returns the value of the video format's \a name property. +*/ + +QVariant QVideoSurfaceFormat::property(const char *name) const +{ + if (qstrcmp(name, "handleType") == 0) { + return qVariantFromValue(d->handleType); + } else if (qstrcmp(name, "pixelFormat") == 0) { + return qVariantFromValue(d->pixelFormat); + } else if (qstrcmp(name, "handleType") == 0) { + return qVariantFromValue(d->handleType); + } else if (qstrcmp(name, "frameSize") == 0) { + return d->frameSize; + } else if (qstrcmp(name, "frameWidth") == 0) { + return d->frameSize.width(); + } else if (qstrcmp(name, "frameHeight") == 0) { + return d->frameSize.height(); + } else if (qstrcmp(name, "viewport") == 0) { + return d->viewport; + } else if (qstrcmp(name, "scanLineDirection") == 0) { + return qVariantFromValue(d->scanLineDirection); + } else if (qstrcmp(name, "frameRate") == 0) { + return qVariantFromValue(d->frameRate); + } else if (qstrcmp(name, "pixelAspectRatio") == 0) { + return qVariantFromValue(d->pixelAspectRatio); + } else if (qstrcmp(name, "sizeHint") == 0) { + return sizeHint(); + } else if (qstrcmp(name, "yCbCrColorSpace") == 0) { + return qVariantFromValue(d->ycbcrColorSpace); + } else { + int id = 0; + for (; id < d->propertyNames.count() && d->propertyNames.at(id) != name; ++id) {} + + return id < d->propertyValues.count() + ? d->propertyValues.at(id) + : QVariant(); + } +} + +/*! + Sets the video format's \a name property to \a value. +*/ + +void QVideoSurfaceFormat::setProperty(const char *name, const QVariant &value) +{ + if (qstrcmp(name, "handleType") == 0) { + // read only. + } else if (qstrcmp(name, "pixelFormat") == 0) { + // read only. + } else if (qstrcmp(name, "frameSize") == 0) { + if (qVariantCanConvert<QSize>(value)) { + d->frameSize = qvariant_cast<QSize>(value); + d->viewport = QRect(QPoint(0, 0), d->frameSize); + } + } else if (qstrcmp(name, "frameWidth") == 0) { + // read only. + } else if (qstrcmp(name, "frameHeight") == 0) { + // read only. + } else if (qstrcmp(name, "viewport") == 0) { + if (qVariantCanConvert<QRect>(value)) + d->viewport = qvariant_cast<QRect>(value); + } else if (qstrcmp(name, "scanLineDirection") == 0) { + if (qVariantCanConvert<Direction>(value)) + d->scanLineDirection = qvariant_cast<Direction>(value); + } else if (qstrcmp(name, "frameRate") == 0) { + if (qVariantCanConvert<qreal>(value)) + d->frameRate = qvariant_cast<qreal>(value); + } else if (qstrcmp(name, "pixelAspectRatio") == 0) { + if (qVariantCanConvert<QSize>(value)) + d->pixelAspectRatio = qvariant_cast<QSize>(value); + } else if (qstrcmp(name, "sizeHint") == 0) { + // read only. + } else if (qstrcmp(name, "yCbCrColorSpace") == 0) { + if (qVariantCanConvert<YCbCrColorSpace>(value)) + d->ycbcrColorSpace = qvariant_cast<YCbCrColorSpace>(value); + } else { + int id = 0; + for (; id < d->propertyNames.count() && d->propertyNames.at(id) != name; ++id) {} + + if (id < d->propertyValues.count()) { + if (value.isNull()) { + d->propertyNames.removeAt(id); + d->propertyValues.removeAt(id); + } else { + d->propertyValues[id] = value; + } + } else if (!value.isNull()) { + d->propertyNames.append(QByteArray(name)); + d->propertyValues.append(value); + } + } +} + + +#ifndef QT_NO_DEBUG_STREAM +QDebug operator<<(QDebug dbg, const QVideoSurfaceFormat &f) +{ + QString typeName; + switch (f.pixelFormat()) { + case QVideoFrame::Format_Invalid: + typeName = QLatin1String("Format_Invalid"); + break; + case QVideoFrame::Format_ARGB32: + typeName = QLatin1String("Format_ARGB32"); + break; + case QVideoFrame::Format_ARGB32_Premultiplied: + typeName = QLatin1String("Format_ARGB32_Premultiplied"); + break; + case QVideoFrame::Format_RGB32: + typeName = QLatin1String("Format_RGB32"); + break; + case QVideoFrame::Format_RGB24: + typeName = QLatin1String("Format_RGB24"); + break; + case QVideoFrame::Format_RGB565: + typeName = QLatin1String("Format_RGB565"); + break; + case QVideoFrame::Format_RGB555: + typeName = QLatin1String("Format_RGB555"); + break; + case QVideoFrame::Format_ARGB8565_Premultiplied: + typeName = QLatin1String("Format_ARGB8565_Premultiplied"); + break; + case QVideoFrame::Format_BGRA32: + typeName = QLatin1String("Format_BGRA32"); + break; + case QVideoFrame::Format_BGRA32_Premultiplied: + typeName = QLatin1String("Format_BGRA32_Premultiplied"); + break; + case QVideoFrame::Format_BGR32: + typeName = QLatin1String("Format_BGR32"); + break; + case QVideoFrame::Format_BGR24: + typeName = QLatin1String("Format_BGR24"); + break; + case QVideoFrame::Format_BGR565: + typeName = QLatin1String("Format_BGR565"); + break; + case QVideoFrame::Format_BGR555: + typeName = QLatin1String("Format_BGR555"); + break; + case QVideoFrame::Format_BGRA5658_Premultiplied: + typeName = QLatin1String("Format_BGRA5658_Premultiplied"); + break; + case QVideoFrame::Format_AYUV444: + typeName = QLatin1String("Format_AYUV444"); + break; + case QVideoFrame::Format_AYUV444_Premultiplied: + typeName = QLatin1String("Format_AYUV444_Premultiplied"); + break; + case QVideoFrame::Format_YUV444: + typeName = QLatin1String("Format_YUV444"); + break; + case QVideoFrame::Format_YUV420P: + typeName = QLatin1String("Format_YUV420P"); + break; + case QVideoFrame::Format_YV12: + typeName = QLatin1String("Format_YV12"); + break; + case QVideoFrame::Format_UYVY: + typeName = QLatin1String("Format_UYVY"); + break; + case QVideoFrame::Format_YUYV: + typeName = QLatin1String("Format_YUYV"); + break; + case QVideoFrame::Format_NV12: + typeName = QLatin1String("Format_NV12"); + break; + case QVideoFrame::Format_NV21: + typeName = QLatin1String("Format_NV21"); + break; + case QVideoFrame::Format_IMC1: + typeName = QLatin1String("Format_IMC1"); + break; + case QVideoFrame::Format_IMC2: + typeName = QLatin1String("Format_IMC2"); + break; + case QVideoFrame::Format_IMC3: + typeName = QLatin1String("Format_IMC3"); + break; + case QVideoFrame::Format_IMC4: + typeName = QLatin1String("Format_IMC4"); + break; + case QVideoFrame::Format_Y8: + typeName = QLatin1String("Format_Y8"); + break; + case QVideoFrame::Format_Y16: + typeName = QLatin1String("Format_Y16"); + default: + typeName = QString(QLatin1String("UserType(%1)" )).arg(int(f.pixelFormat())); + } + + dbg.nospace() << "QVideoSurfaceFormat(" << typeName; + dbg.nospace() << ", " << f.frameSize(); + dbg.nospace() << ", viewport=" << f.viewport(); + dbg.nospace() << ", pixelAspectRatio=" << f.pixelAspectRatio(); + dbg.nospace() << ")"; + + foreach(const QByteArray& propertyName, f.propertyNames()) + dbg << "\n " << propertyName.data() << " = " << f.property(propertyName.data()); + + return dbg.space(); +} +#endif + +QT_END_NAMESPACE diff --git a/src/multimedia/multimedia/video/qvideosurfaceformat.h b/src/multimedia/multimedia/video/qvideosurfaceformat.h new file mode 100644 index 0000000..9c73f5f --- /dev/null +++ b/src/multimedia/multimedia/video/qvideosurfaceformat.h @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** 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 QtMultimedia module of the Qt Toolkit. +** +** $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 QVIDEOSURFACEFORMAT_H +#define QVIDEOSURFACEFORMAT_H + +#include <QtCore/qlist.h> +#include <QtCore/qpair.h> +#include <QtCore/qshareddata.h> +#include <QtCore/qsize.h> +#include <QtGui/qimage.h> +#include <QtMultimedia/qvideoframe.h> + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +QT_MODULE(Multimedia) + +class QDebug; + +class QVideoSurfaceFormatPrivate; + +class Q_MULTIMEDIA_EXPORT QVideoSurfaceFormat +{ +public: + enum Direction + { + TopToBottom, + BottomToTop + }; + + enum YCbCrColorSpace + { + YCbCr_Undefined, + YCbCr_BT601, + YCbCr_BT709, + YCbCr_xvYCC601, + YCbCr_xvYCC709, + YCbCr_JPEG, +#ifndef qdoc + YCbCr_CustomMatrix +#endif + }; + + QVideoSurfaceFormat(); + QVideoSurfaceFormat( + const QSize &size, + QVideoFrame::PixelFormat pixelFormat, + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle); + QVideoSurfaceFormat(const QVideoSurfaceFormat &format); + ~QVideoSurfaceFormat(); + + QVideoSurfaceFormat &operator =(const QVideoSurfaceFormat &format); + + bool operator ==(const QVideoSurfaceFormat &format) const; + bool operator !=(const QVideoSurfaceFormat &format) const; + + bool isValid() const; + + QVideoFrame::PixelFormat pixelFormat() const; + QAbstractVideoBuffer::HandleType handleType() const; + + QSize frameSize() const; + void setFrameSize(const QSize &size); + void setFrameSize(int width, int height); + + int frameWidth() const; + int frameHeight() const; + + QRect viewport() const; + void setViewport(const QRect &viewport); + + Direction scanLineDirection() const; + void setScanLineDirection(Direction direction); + + qreal frameRate() const; + void setFrameRate(qreal rate); + + QSize pixelAspectRatio() const; + void setPixelAspectRatio(const QSize &ratio); + void setPixelAspectRatio(int width, int height); + + YCbCrColorSpace yCbCrColorSpace() const; + void setYCbCrColorSpace(YCbCrColorSpace colorSpace); + + QSize sizeHint() const; + + QList<QByteArray> propertyNames() const; + QVariant property(const char *name) const; + void setProperty(const char *name, const QVariant &value); + +private: + QSharedDataPointer<QVideoSurfaceFormatPrivate> d; +}; + +#ifndef QT_NO_DEBUG_STREAM +Q_MULTIMEDIA_EXPORT QDebug operator<<(QDebug, const QVideoSurfaceFormat &); +#endif + +QT_END_NAMESPACE + +Q_DECLARE_METATYPE(QVideoSurfaceFormat::Direction) +Q_DECLARE_METATYPE(QVideoSurfaceFormat::YCbCrColorSpace) + +QT_END_HEADER + +#endif + diff --git a/src/multimedia/multimedia/video/video.pri b/src/multimedia/multimedia/video/video.pri new file mode 100644 index 0000000..0547a4c --- /dev/null +++ b/src/multimedia/multimedia/video/video.pri @@ -0,0 +1,21 @@ + +INCLUDEPATH += $$PWD + +HEADERS += \ + $$PWD/qabstractvideobuffer.h \ + $$PWD/qabstractvideobuffer_p.h \ + $$PWD/qabstractvideosurface.h \ + $$PWD/qabstractvideosurface_p.h \ + $$PWD/qimagevideobuffer_p.h \ + $$PWD/qmemoryvideobuffer_p.h \ + $$PWD/qvideoframe.h \ + $$PWD/qvideosurfaceformat.h + +SOURCES += \ + $$PWD/qabstractvideobuffer.cpp \ + $$PWD/qabstractvideosurface.cpp \ + $$PWD/qimagevideobuffer.cpp \ + $$PWD/qmemoryvideobuffer.cpp \ + $$PWD/qvideoframe.cpp \ + $$PWD/qvideosurfaceformat.cpp + |