Initial import from the monolithic Qt.

This is the beginning of revision history for this module. If you
want to look at revision history older than this, please refer to the
Qt Git wiki for how to use Git history grafting. At the time of
writing, this wiki is located here:

http://qt.gitorious.org/qt/pages/GitIntroductionWithQt

If you have already performed the grafting and you don't see any
history beyond this commit, try running "git log" with the "--follow"
argument.

Branched from the monolithic repo, Qt master branch, at commit
896db169ea224deb96c59ce8af800d019de63f12
This commit is contained in:
Qt by Nokia
2011-04-27 12:05:43 +02:00
committed by axis
commit 60941c2741
211 changed files with 87979 additions and 0 deletions

View File

@@ -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 += $${EPOCROOT}epoc32/include/mmf/common
INCLUDEPATH += $${EPOCROOT}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
}

View File

@@ -0,0 +1,104 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,71 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,145 @@
/****************************************************************************
**
** Copyright (C) 2011 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 & kAudioFormatFlagIsBigEndian) != 0 ? QAudioFormat::BigEndian : QAudioFormat::LittleEndian);
QAudioFormat::SampleType type = QAudioFormat::UnSignedInt;
if ((sf.mFormatFlags & kAudioFormatFlagIsSignedInteger) != 0)
type = QAudioFormat::SignedInt;
else if ((sf.mFormatFlags & kAudioFormatFlagIsFloat) != 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;
}
if (audioFormat.byteOrder() == QAudioFormat::BigEndian)
sf.mFormatFlags |= kAudioFormatFlagIsBigEndian;
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

View File

@@ -0,0 +1,144 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,644 @@
/****************************************************************************
**
** Copyright (C) 2011 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 {
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;
}
void capabilitiesNativeToQt(const TMMFCapabilities &caps,
const TFourCC &fourcc,
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.iRate & SampleRateListNative[i])
frequencies += SampleRateListQt[i];
for (int i=0; i<ChannelsCount; ++i)
if (caps.iChannels & ChannelsListNative[i])
channels += ChannelsListQt[i];
for (int i=0; i<EncodingCount; ++i) {
if (fourcc == EncodingFourCC[i]) {
sampleSizes += EncodingSampleSize[i];
byteOrders += EncodingByteOrder[i];
sampleTypes += EncodingSampleType[i];
}
}
}
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() == QLatin1String("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)
{
switch (nativeState) {
case ClosedState:
return QAudio::StoppedState;
case InitializingState:
return QAudio::StoppedState;
case ActiveState:
return QAudio::ActiveState;
case IdleState:
return QAudio::IdleState;
case SuspendedPausedState:
case SuspendedStoppedState:
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
//-----------------------------------------------------------------------------
// DevSoundWrapper
//-----------------------------------------------------------------------------
DevSoundWrapper::DevSoundWrapper(QAudio::Mode mode, QObject *parent)
: QObject(parent)
, m_mode(mode)
, m_state(StateIdle)
, m_devsound(0)
, m_fourcc(0)
{
QT_TRAP_THROWING(m_devsound = CMMFDevSound::NewL());
switch (mode) {
case QAudio::AudioOutput:
m_nativeMode = EMMFStatePlaying;
break;
case QAudio::AudioInput:
m_nativeMode = EMMFStateRecording;
break;
default:
Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode");
}
getSupportedCodecs();
}
DevSoundWrapper::~DevSoundWrapper()
{
delete m_devsound;
}
const QList<QString>& DevSoundWrapper::supportedCodecs() const
{
return m_supportedCodecs;
}
void DevSoundWrapper::initialize(const QString& codec)
{
Q_ASSERT(StateInitializing != m_state);
m_state = StateInitializing;
if (QLatin1String("audio/pcm") == codec) {
m_fourcc = KMMFFourCCCodePCM16;
TRAPD(err, m_devsound->InitializeL(*this, m_fourcc, m_nativeMode));
if (KErrNone != err) {
m_state = StateIdle;
emit initializeComplete(err);
}
} else {
emit initializeComplete(KErrNotSupported);
}
}
const QList<int>& DevSoundWrapper::supportedFrequencies() const
{
Q_ASSERT(StateInitialized == m_state);
return m_supportedFrequencies;
}
const QList<int>& DevSoundWrapper::supportedChannels() const
{
Q_ASSERT(StateInitialized == m_state);
return m_supportedChannels;
}
const QList<int>& DevSoundWrapper::supportedSampleSizes() const
{
Q_ASSERT(StateInitialized == m_state);
return m_supportedSampleSizes;
}
const QList<QAudioFormat::Endian>& DevSoundWrapper::supportedByteOrders() const
{
Q_ASSERT(StateInitialized == m_state);
return m_supportedByteOrders;
}
const QList<QAudioFormat::SampleType>& DevSoundWrapper::supportedSampleTypes() const
{
Q_ASSERT(StateInitialized == m_state);
return m_supportedSampleTypes;
}
bool DevSoundWrapper::isFormatSupported(const QAudioFormat &format) const
{
Q_ASSERT(StateInitialized == m_state);
return m_supportedCodecs.contains(format.codec())
&& m_supportedFrequencies.contains(format.frequency())
&& m_supportedChannels.contains(format.channels())
&& m_supportedSampleSizes.contains(format.sampleSize())
&& m_supportedSampleTypes.contains(format.sampleType())
&& m_supportedByteOrders.contains(format.byteOrder());
}
int DevSoundWrapper::samplesProcessed() const
{
int result = 0;
if (StateInitialized == m_state) {
switch (m_mode) {
case QAudio::AudioInput:
result = m_devsound->SamplesRecorded();
break;
case QAudio::AudioOutput:
result = m_devsound->SamplesPlayed();
break;
}
}
return result;
}
bool DevSoundWrapper::setFormat(const QAudioFormat &format)
{
Q_ASSERT(StateInitialized == m_state);
bool result = false;
TUint32 fourcc;
TMMFCapabilities nativeFormat;
if (Utils::formatQtToNative(format, fourcc, nativeFormat)) {
TMMFCapabilities currentNativeFormat = m_devsound->Config();
nativeFormat.iBufferSize = currentNativeFormat.iBufferSize;
TRAPD(err, m_devsound->SetConfigL(nativeFormat));
result = (KErrNone == err);
}
return result;
}
bool DevSoundWrapper::start()
{
Q_ASSERT(StateInitialized == m_state);
int err = KErrArgument;
switch (m_mode) {
case QAudio::AudioInput:
TRAP(err, m_devsound->RecordInitL());
break;
case QAudio::AudioOutput:
TRAP(err, m_devsound->PlayInitL());
break;
}
return (KErrNone == err);
}
bool DevSoundWrapper::pause()
{
Q_ASSERT(StateInitialized == m_state);
const bool canPause = isResumeSupported();
if (canPause)
m_devsound->Pause();
else
stop();
return canPause;
}
void DevSoundWrapper::resume()
{
Q_ASSERT(StateInitialized == m_state);
Q_ASSERT(isResumeSupported());
// TODO: QTBUG-13625
}
void DevSoundWrapper::stop()
{
m_devsound->Stop();
}
void DevSoundWrapper::bufferProcessed()
{
Q_ASSERT(StateInitialized == m_state);
switch (m_mode) {
case QAudio::AudioInput:
m_devsound->RecordData();
break;
case QAudio::AudioOutput:
m_devsound->PlayData();
break;
}
}
void DevSoundWrapper::getSupportedCodecs()
{
/*
* TODO: once we support formats other than PCM, this function should
* convert the array of FourCC codes into MIME types for each codec.
*
RArray<TFourCC> fourcc;
QT_TRAP_THROWING(CleanupClosePushL(&fourcc));
TMMFPrioritySettings settings;
switch (mode) {
case QAudio::AudioOutput:
settings.iState = EMMFStatePlaying;
m_devsound->GetSupportedInputDataTypesL(fourcc, settings);
break;
case QAudio::AudioInput:
settings.iState = EMMFStateRecording;
m_devsound->GetSupportedInputDataTypesL(fourcc, settings);
break;
default:
Q_ASSERT_X(false, Q_FUNC_INFO, "Invalid mode");
}
CleanupStack::PopAndDestroy(); // fourcc
*/
m_supportedCodecs.append(QLatin1String("audio/pcm"));
}
void DevSoundWrapper::populateCapabilities()
{
m_supportedFrequencies.clear();
m_supportedChannels.clear();
m_supportedSampleSizes.clear();
m_supportedByteOrders.clear();
m_supportedSampleTypes.clear();
const TMMFCapabilities caps = m_devsound->Capabilities();
for (int i=0; i<Utils::SampleRateCount; ++i)
if (caps.iRate & Utils::SampleRateListNative[i])
m_supportedFrequencies += Utils::SampleRateListQt[i];
for (int i=0; i<Utils::ChannelsCount; ++i)
if (caps.iChannels & Utils::ChannelsListNative[i])
m_supportedChannels += Utils::ChannelsListQt[i];
for (int i=0; i<Utils::EncodingCount; ++i) {
if (m_fourcc == Utils::EncodingFourCC[i]) {
m_supportedSampleSizes += Utils::EncodingSampleSize[i];
m_supportedByteOrders += Utils::EncodingByteOrder[i];
m_supportedSampleTypes += Utils::EncodingSampleType[i];
}
}
}
bool DevSoundWrapper::isResumeSupported() const
{
// TODO: QTBUG-13625
return false;
}
void DevSoundWrapper::InitializeComplete(TInt aError)
{
Q_ASSERT(StateInitializing == m_state);
if (KErrNone == aError) {
m_state = StateInitialized;
populateCapabilities();
} else {
m_state = StateIdle;
}
emit initializeComplete(aError);
}
void DevSoundWrapper::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 DevSoundWrapper::BufferToBeFilled(CMMFBuffer *aBuffer)
{
Q_ASSERT(QAudio::AudioOutput == m_mode);
emit bufferToBeProcessed(aBuffer);
}
void DevSoundWrapper::PlayError(TInt aError)
{
Q_ASSERT(QAudio::AudioOutput == m_mode);
emit processingError(aError);
}
void DevSoundWrapper::BufferToBeEmptied(CMMFBuffer *aBuffer)
{
Q_ASSERT(QAudio::AudioInput == m_mode);
emit bufferToBeProcessed(aBuffer);
}
void DevSoundWrapper::RecordError(TInt aError)
{
Q_ASSERT(QAudio::AudioInput == m_mode);
emit processingError(aError);
}
void DevSoundWrapper::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 DevSoundWrapper::DeviceMessage(TUid aMessageType, const TDesC8 &aMsg)
{
Q_UNUSED(aMessageType)
Q_UNUSED(aMsg)
// Ignore this callback.
}
} // namespace SymbianAudio
QT_END_NAMESPACE

View File

@@ -0,0 +1,200 @@
/****************************************************************************
**
** Copyright (C) 2011 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/QList>
#include <QtCore/QString>
#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
// QAudio is suspended; DevSound is paused
, SuspendedPausedState
// QAudio is suspended; DevSound is stopped
, SuspendedStoppedState
};
/**
* Wrapper around DevSound instance
*/
class DevSoundWrapper
: public QObject
, public MDevSoundObserver
{
Q_OBJECT
public:
DevSoundWrapper(QAudio::Mode mode, QObject *parent = 0);
~DevSoundWrapper();
public:
// List of supported codecs; can be called once object is constructed
const QList<QString>& supportedCodecs() const;
// Asynchronous initialization function; emits devsoundInitializeComplete
void initialize(const QString& codec);
// Capabilities, for selected codec. Can be called once initialize has returned
// successfully.
const QList<int>& supportedFrequencies() const;
const QList<int>& supportedChannels() const;
const QList<int>& supportedSampleSizes() const;
const QList<QAudioFormat::Endian>& supportedByteOrders() const;
const QList<QAudioFormat::SampleType>& supportedSampleTypes() const;
bool isFormatSupported(const QAudioFormat &format) const;
int samplesProcessed() const;
bool setFormat(const QAudioFormat &format);
bool start();
// If DevSound implementation supports pause, calls pause and returns true.
// Otherwise calls stop and returns false. In this case, all DevSound buffers
// currently held by the backend must be discarded.
bool pause();
void resume();
void stop();
void bufferProcessed();
public:
// 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);
signals:
void initializeComplete(int error);
void bufferToBeProcessed(CMMFBuffer *buffer);
void processingError(int error);
private:
void getSupportedCodecs();
void populateCapabilities();
bool isResumeSupported() const;
private:
const QAudio::Mode m_mode;
TMMFState m_nativeMode;
enum State {
StateIdle,
StateInitializing,
StateInitialized
} m_state;
CMMFDevSound* m_devsound;
TFourCC m_fourcc;
QList<QString> m_supportedCodecs;
QList<int> m_supportedFrequencies;
QList<int> m_supportedChannels;
QList<int> m_supportedSampleSizes;
QList<QAudioFormat::Endian> m_supportedByteOrders;
QList<QAudioFormat::SampleType> m_supportedSampleTypes;
};
namespace Utils {
/**
* Convert internal states to QAudio states.
*/
QAudio::State stateNativeToQt(State nativeState);
/**
* 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

View File

@@ -0,0 +1,269 @@
/****************************************************************************
**
** Copyright (C) 2011 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
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
(QAudioEngineFactoryInterface_iid, QLatin1String("/audio"), Qt::CaseInsensitive))
#endif
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
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
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;
}
#endif
return devices;
}
QAudioDeviceInfo QAudioDeviceFactory::defaultInputDevice()
{
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
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);
}
#endif
#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()
{
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
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);
}
#endif
#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
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
QAudioEngineFactoryInterface* plugin =
qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(realm));
if (plugin)
rc = plugin->createDeviceInfo(handle, mode);
#endif
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
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
QAudioEngineFactoryInterface* plugin =
qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(deviceInfo.realm()));
if (plugin)
return plugin->createInput(deviceInfo.handle(), format);
#endif
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
#if !defined(QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
QAudioEngineFactoryInterface* plugin =
qobject_cast<QAudioEngineFactoryInterface*>(loader()->instance(deviceInfo.realm()));
if (plugin)
return plugin->createOutput(deviceInfo.handle(), format);
#endif
return new QNullOutputDevice();
}
QT_END_NAMESPACE

View File

@@ -0,0 +1,97 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,460 @@
/****************************************************************************
**
** Copyright (C) 2011 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>
#include <QtCore/qmap.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
{
if (isFormatSupported(settings))
return settings;
QAudioFormat nearest = settings;
nearest.setCodec(QLatin1String("audio/pcm"));
if (nearest.sampleType() == QAudioFormat::Unknown) {
QAudioFormat preferred = preferredFormat();
nearest.setSampleType(preferred.sampleType());
}
QMap<int,int> testFrequencies;
QList<int> frequenciesAvailable = supportedFrequencies();
QMap<int,int> testSampleSizes;
QList<int> sampleSizesAvailable = supportedSampleSizes();
// Get sorted sampleSizes (equal to and ascending values only)
if (sampleSizesAvailable.contains(settings.sampleSize()))
testSampleSizes.insert(0,settings.sampleSize());
sampleSizesAvailable.removeAll(settings.sampleSize());
foreach (int size, sampleSizesAvailable) {
int larger = (size > settings.sampleSize()) ? size : settings.sampleSize();
int smaller = (size > settings.sampleSize()) ? settings.sampleSize() : size;
if (size >= settings.sampleSize()) {
int diff = larger - smaller;
testSampleSizes.insert(diff, size);
}
}
// Get sorted frequencies (equal to and ascending values only)
if (frequenciesAvailable.contains(settings.frequency()))
testFrequencies.insert(0,settings.frequency());
frequenciesAvailable.removeAll(settings.frequency());
foreach (int frequency, frequenciesAvailable) {
int larger = (frequency > settings.frequency()) ? frequency : settings.frequency();
int smaller = (frequency > settings.frequency()) ? settings.frequency() : frequency;
if (frequency >= settings.frequency()) {
int diff = larger - smaller;
testFrequencies.insert(diff, frequency);
}
}
// Try to find nearest
// Check ascending frequencies, ascending sampleSizes
QMapIterator<int, int> sz(testSampleSizes);
while (sz.hasNext()) {
sz.next();
nearest.setSampleSize(sz.value());
QMapIterator<int, int> i(testFrequencies);
while (i.hasNext()) {
i.next();
nearest.setFrequency(i.value());
if (isFormatSupported(nearest))
return nearest;
}
}
//Fallback
return preferredFormat();
}
/*!
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

View File

@@ -0,0 +1,114 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,544 @@
/****************************************************************************
**
** Copyright (C) 2011 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;
#if (SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
checkSurround();
#endif
}
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( &params );
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);
err = -1;
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);
}
}
// 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);
#if (SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
if (surround40) channelz.append(4);
if (surround51) channelz.append(6);
if (surround71) channelz.append(8);
#endif
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");
if (name != 0 && qstrcmp(name, "null") != 0) {
descr = snd_device_name_get_hint(*n, "DESC");
io = snd_device_name_get_hint(*n, "IOID");
if ((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());
}
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();
}
#if (SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
void QAudioDeviceInfoInternal::checkSurround()
{
QList<QByteArray> devices;
surround40 = false;
surround51 = false;
surround71 = false;
void **hints, **n;
char *name, *descr, *io;
if(snd_device_name_hint(-1, "pcm", &hints) < 0)
return;
n = hints;
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)) {
QString deviceName = QLatin1String(name);
if (mode == QAudio::AudioOutput) {
if(deviceName.contains(QLatin1String("surround40")))
surround40 = true;
if(deviceName.contains(QLatin1String("surround51")))
surround51 = true;
if(deviceName.contains(QLatin1String("surround71")))
surround71 = true;
}
}
if(name != NULL)
free(name);
if(descr != NULL)
free(descr);
if(io != NULL)
free(io);
++n;
}
snd_device_name_free_hint(hints);
}
#endif
QT_END_NAMESPACE

View File

@@ -0,0 +1,124 @@
/****************************************************************************
**
** Copyright (C) 2011 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();
#if (SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
void checkSurround();
bool surround40;
bool surround51;
bool surround71;
#endif
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

View File

@@ -0,0 +1,363 @@
/****************************************************************************
**
** Copyright (C) 2011 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
{
QAudioDeviceInfoInternal *self = const_cast<QAudioDeviceInfoInternal*>(this);
return format.isValid()
&& format.codec() == QString::fromLatin1("audio/pcm")
&& self->frequencyList().contains(format.frequency())
&& self->channelsList().contains(format.channels())
&& self->sampleSizeList().contains(format.sampleSize());
}
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

View File

@@ -0,0 +1,97 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,240 @@
/****************************************************************************
**
** Copyright (C) 2011 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/QCoreApplication>
#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)
{
}
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)) {
format = QAudioFormat();
format.setCodec(QLatin1String("audio/pcm"));
if (m_capabilities.contains(format.codec())) {
const Capabilities &codecCaps = m_capabilities[format.codec()];
if (codecCaps.m_frequencies.size())
format.setFrequency(codecCaps.m_frequencies[0]);
if (codecCaps.m_channels.size())
format.setChannels(codecCaps.m_channels[0]);
if (codecCaps.m_sampleSizes.size())
format.setSampleSize(codecCaps.m_sampleSizes[0]);
if (codecCaps.m_byteOrders.size())
format.setByteOrder(codecCaps.m_byteOrders[0]);
if (codecCaps.m_sampleTypes.size())
format.setSampleType(codecCaps.m_sampleTypes[0]);
}
}
return format;
}
bool QAudioDeviceInfoInternal::isFormatSupported(
const QAudioFormat &format) const
{
getSupportedFormats();
bool supported = false;
if (m_capabilities.contains(format.codec())) {
const Capabilities &codecCaps = m_capabilities[format.codec()];
supported = codecCaps.m_frequencies.contains(format.frequency())
&& codecCaps.m_channels.contains(format.channels())
&& codecCaps.m_sampleSizes.contains(format.sampleSize())
&& codecCaps.m_byteOrders.contains(format.byteOrder())
&& codecCaps.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_capabilities.keys();
}
QList<int> QAudioDeviceInfoInternal::frequencyList()
{
getSupportedFormats();
return m_unionCapabilities.m_frequencies;
}
QList<int> QAudioDeviceInfoInternal::channelsList()
{
getSupportedFormats();
return m_unionCapabilities.m_channels;
}
QList<int> QAudioDeviceInfoInternal::sampleSizeList()
{
getSupportedFormats();
return m_unionCapabilities.m_sampleSizes;
}
QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::byteOrderList()
{
getSupportedFormats();
return m_unionCapabilities.m_byteOrders;
}
QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::sampleTypeList()
{
getSupportedFormats();
return m_unionCapabilities.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::devsoundInitializeComplete(int err)
{
m_intializationResult = err;
m_initializing = false;
}
// Helper function
template<typename T>
void appendUnique(QList<T> &left, const QList<T> &right)
{
foreach (const T &value, right)
if (!left.contains(value))
left += value;
}
void QAudioDeviceInfoInternal::getSupportedFormats() const
{
if (!m_updated) {
QScopedPointer<SymbianAudio::DevSoundWrapper> devsound(new SymbianAudio::DevSoundWrapper(m_mode));
connect(devsound.data(), SIGNAL(initializeComplete(int)),
this, SLOT(devsoundInitializeComplete(int)));
foreach (const QString& codec, devsound->supportedCodecs()) {
m_initializing = true;
devsound->initialize(codec);
while (m_initializing)
QCoreApplication::instance()->processEvents(QEventLoop::WaitForMoreEvents);
if (KErrNone == m_intializationResult) {
m_capabilities[codec].m_frequencies = devsound->supportedFrequencies();
appendUnique(m_unionCapabilities.m_frequencies, devsound->supportedFrequencies());
m_capabilities[codec].m_channels = devsound->supportedChannels();
appendUnique(m_unionCapabilities.m_channels, devsound->supportedChannels());
m_capabilities[codec].m_sampleSizes = devsound->supportedSampleSizes();
appendUnique(m_unionCapabilities.m_sampleSizes, devsound->supportedSampleSizes());
m_capabilities[codec].m_byteOrders = devsound->supportedByteOrders();
appendUnique(m_unionCapabilities.m_byteOrders, devsound->supportedByteOrders());
m_capabilities[codec].m_sampleTypes = devsound->supportedSampleTypes();
appendUnique(m_unionCapabilities.m_sampleTypes, devsound->supportedSampleTypes());
}
}
m_updated = true;
}
}
QT_END_NAMESPACE

View File

@@ -0,0 +1,121 @@
/****************************************************************************
**
** Copyright (C) 2011 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 <QtCore/QMap>
#include <QtMultimedia/qaudioengine.h>
#include <sounddevice.h>
QT_BEGIN_NAMESPACE
namespace SymbianAudio {
class DevSoundWrapper;
}
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 slots:
void devsoundInitializeComplete(int err);
private:
void getSupportedFormats() const;
private:
mutable bool m_initializing;
int m_intializationResult;
QString m_deviceName;
QAudio::Mode m_mode;
struct Capabilities
{
QList<int> m_frequencies;
QList<int> m_channels;
QList<int> m_sampleSizes;
QList<QAudioFormat::Endian> m_byteOrders;
QList<QAudioFormat::SampleType> m_sampleTypes;
};
// Mutable to allow lazy initialization when called from const-qualified
// public functions (isFormatSupported, nearestFormat)
mutable bool m_updated;
mutable QMap<QString, Capabilities> m_capabilities;
mutable Capabilities m_unionCapabilities;
};
QT_END_NAMESPACE
#endif

View File

@@ -0,0 +1,447 @@
/****************************************************************************
**
** Copyright (C) 2011 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;
}
}
if (!match)
failed = true;
}
// check sample size
match = false;
if (!failed) {
for( int i = 0; i < sizez.count(); i++) {
if (format.sampleSize() == sizez.at(i)) {
match = true;
break;
}
}
if (!match)
failed = true;
}
// check byte order
match = false;
if (!failed) {
for( int i = 0; i < byteOrderz.count(); i++) {
if (format.byteOrder() == byteOrderz.at(i)) {
match = true;
break;
}
}
if (!match)
failed = true;
}
// 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 (!match)
failed = true;
}
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);
if (mode == QAudio::AudioOutput) {
channelz.append(4);
channelz.append(6);
channelz.append(8);
}
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

View File

@@ -0,0 +1,112 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,343 @@
/****************************************************************************
**
** Copyright (C) 2011 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 transferred 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 transferred
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

View File

@@ -0,0 +1,131 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,54 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,93 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,407 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,107 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,441 @@
/****************************************************************************
**
** Copyright (C) 2011 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 transferred without any extra code.
All that is required is to open the QIODevice. QAudioInput does not take
ownership of \a device.
The QAudioInput will write to the device when new data is available. You can
subclass QIODevice and reimplement \l{QIODevice::}{writeData()} if you wish to
access the data. If you simply want to save data to a file, you can pass a
QFile to this function.
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.
\l{QAudioInput#Symbian Platform Security Requirements}
\sa QIODevice
*/
void QAudioInput::start(QIODevice* device)
{
d->start(device);
}
/*!
Returns a pointer to a new QIODevice that will be used to handle the data transfer.
This QIODevice can be used to \l{QIODevice::}{read()} audio data directly.
You will typically connect to the \l{QIODevice::}{readyRead()} signal, and
read from the device in the slot you connect to. QAudioInput keeps ownership
of the device.
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.
\l{QAudioInput#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

View File

@@ -0,0 +1,111 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,746 @@
/****************************************************************************
**
** Copyright (C) 2011 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
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 = checkBytesReady();
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_UNKNOWN;
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 format != SND_PCM_FORMAT_UNKNOWN
? snd_pcm_hw_params_set_format( handle, hwparams, format)
: -1;
}
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 = 0;
int count=0;
unsigned int freakuency=settings.frequency();
if (!settings.isValid()) {
qWarning("QAudioOutput: open error, invalid format.");
} else if (settings.frequency() <= 0) {
qWarning("QAudioOutput: open error, invalid sample rate (%d).",
settings.frequency());
} else {
err = -1;
}
if (err == 0) {
errorState = QAudio::OpenError;
deviceState = QAudio::StoppedState;
return false;
}
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 = checkBytesReady();
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::checkBytesReady()
{
if(resuming)
bytesAvailable = period_size;
else if(deviceState != QAudio::ActiveState
&& deviceState != QAudio::IdleState)
bytesAvailable = 0;
else {
int frames = snd_pcm_avail_update(handle);
if (frames < 0) {
bytesAvailable = frames;
} else {
if((int)frames > (int)buffer_frames)
frames = buffer_frames;
bytesAvailable = snd_pcm_frames_to_bytes(handle, frames);
}
}
return bytesAvailable;
}
int QAudioInputPrivate::bytesReady() const
{
return qMax(bytesAvailable, 0);
}
qint64 QAudioInputPrivate::read(char* data, qint64 len)
{
// Read in some audio data and write it to QIODevice, pull mode
if ( !handle )
return 0;
// bytesAvaiable is saved as a side effect of checkBytesReady().
int bytesToRead = checkBytesReady();
if (bytesToRead < 0) {
// bytesAvailable as negative is error code, try to recover from it.
xrun_recovery(bytesToRead);
bytesToRead = checkBytesReady();
if (bytesToRead < 0) {
// recovery failed must stop and set error.
close();
errorState = QAudio::IOError;
deviceState = QAudio::StoppedState;
emit stateChanged(deviceState);
return 0;
}
}
bytesToRead = qMin<qint64>(len, bytesToRead);
bytesToRead -= bytesToRead % period_size;
int count=0, err = 0;
while(count < 5) {
int chunks = bytesToRead/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 {
bytesAvailable -= err;
totalTimeValue += err;
resuming = false;
if (deviceState != QAudio::ActiveState) {
errorState = QAudio::NoError;
deviceState = QAudio::ActiveState;
emit stateChanged(deviceState);
}
}
return l;
} else {
memcpy(data,audioBuffer,err);
bytesAvailable -= 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)
{
intervalTime = qMax(0, ms);
}
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, buffer_size);
} else {
// emits readyRead() so user will call read() on QIODevice to get some audio data
InputPrivate* a = qobject_cast<InputPrivate*>(audioSource);
a->trigger();
}
bytesAvailable = checkBytesReady();
if(deviceState != QAudio::ActiveState)
return true;
if (bytesAvailable < 0) {
// bytesAvailable as negative is error code, try to recover from it.
xrun_recovery(bytesAvailable);
bytesAvailable = checkBytesReady();
if (bytesAvailable < 0) {
// recovery failed must stop and set error.
close();
errorState = QAudio::IOError;
deviceState = QAudio::StoppedState;
emit stateChanged(deviceState);
return 0;
}
}
if(intervalTime && (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

View File

@@ -0,0 +1,159 @@
/****************************************************************************
**
** Copyright (C) 2011 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 checkBytesReady();
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

View File

@@ -0,0 +1,957 @@
/****************************************************************************
**
** Copyright (C) 2011 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;
}
bool empty() const
{
return position == totalPackets;
}
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 pullMode = m_device == 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);
int copied = 0;
const int available = m_buffer->free();
while (err == noErr && !feeder.empty()) {
QAudioRingBuffer::Region region = m_buffer->acquireWriteRegion(available);
if (region.second == 0)
break;
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);
}
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 (pullMode && 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 (!audioDeviceInfo->isFormatSupported(audioFormat) || !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"

View File

@@ -0,0 +1,169 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,560 @@
/****************************************************************************
**
** Copyright (C) 2011 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_devSound(0)
, m_devSoundBuffer(0)
, m_devSoundBufferSize(0)
, m_totalBytesReady(0)
, m_devSoundBufferPos(0)
, m_totalSamplesRecorded(0)
{
qRegisterMetaType<CMMFBuffer *>("CMMFBuffer *");
connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify()));
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();
const qint64 samplesRecorded = getSamplesRecorded();
m_totalSamplesRecorded += samplesRecorded;
const bool paused = m_devSound->pause();
if (paused) {
if (m_devSoundBuffer)
m_devSoundBufferQ.append(m_devSoundBuffer);
m_devSoundBuffer = 0;
setState(SymbianAudio::SuspendedPausedState);
} else {
m_devSoundBuffer = 0;
m_devSoundBufferQ.clear();
m_devSoundBufferPos = 0;
setState(SymbianAudio::SuspendedStoppedState);
}
}
}
void QAudioInputPrivate::resume()
{
if (QAudio::SuspendedState == m_externalState) {
if (SymbianAudio::SuspendedPausedState == m_internalState)
m_devSound->resume();
else
m_devSound->start();
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_notifyInterval && (SymbianAudio::ActiveState == m_internalState ||
SymbianAudio::IdleState == m_internalState))
m_notifyTimer->start(m_notifyInterval);
else
m_notifyTimer->stop();
}
}
int QAudioInputPrivate::notifyInterval() const
{
return m_notifyInterval;
}
qint64 QAudioInputPrivate::processedUSecs() const
{
int samplesPlayed = 0;
if (m_devSound && QAudio::SuspendedState != m_externalState)
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;
}
//-----------------------------------------------------------------------------
// Private functions
//-----------------------------------------------------------------------------
void QAudioInputPrivate::open()
{
Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState,
Q_FUNC_INFO, "DevSound already opened");
Q_ASSERT(!m_devSound);
m_devSound = new SymbianAudio::DevSoundWrapper(QAudio::AudioInput, this);
connect(m_devSound, SIGNAL(initializeComplete(int)),
this, SLOT(devsoundInitializeComplete(int)));
connect(m_devSound, SIGNAL(bufferToBeProcessed(CMMFBuffer *)),
this, SLOT(devsoundBufferToBeEmptied(CMMFBuffer *)));
connect(m_devSound, SIGNAL(processingError(int)),
this, SLOT(devsoundRecordError(int)));
setState(SymbianAudio::InitializingState);
m_devSound->initialize(m_format.codec());
}
void QAudioInputPrivate::startRecording()
{
const int samplesRecorded = m_devSound->samplesProcessed();
Q_ASSERT(samplesRecorded == 0);
bool ok = m_devSound->setFormat(m_format);
if (ok)
ok = m_devSound->start();
if (ok) {
startDataTransfer();
} else {
setError(QAudio::OpenError);
close();
}
}
void QAudioInputPrivate::startDataTransfer()
{
if (m_notifyInterval)
m_notifyTimer->start(m_notifyInterval);
if (m_pullMode)
m_pullTimer->start();
if (bytesReady()) {
setState(SymbianAudio::ActiveState);
if (!m_pullMode)
pushData();
} else {
if (QAudio::SuspendedState == m_externalState)
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();
Q_ASSERT(inputBuffer.Length() >= m_devSoundBufferPos);
const qint64 inputBytes = inputBuffer.Length() - m_devSoundBufferPos;
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 (inputBytes == copyBytes)
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();
Q_ASSERT(inputBuffer.Length() >= m_devSoundBufferPos);
const qint64 inputBytes = inputBuffer.Length() - m_devSoundBufferPos;
const qint64 bytesPushed = m_sink->write(
(char*)inputBuffer.Ptr() + m_devSoundBufferPos, inputBytes);
m_devSoundBufferPos += bytesPushed;
if (inputBytes == bytesPushed)
bufferEmptied();
if (!bytesPushed)
break;
}
}
void QAudioInputPrivate::devsoundInitializeComplete(int err)
{
Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState,
Q_FUNC_INFO, "Invalid state");
if (!err && m_devSound->isFormatSupported(m_format))
startRecording();
else
setError(QAudio::OpenError);
}
void QAudioInputPrivate::devsoundBufferToBeEmptied(CMMFBuffer *baseBuffer)
{
// Following receipt of this signal, 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*>(baseBuffer);
if (!m_devSoundBufferSize)
m_devSoundBufferSize = buffer->Data().MaxLength();
m_totalBytesReady += buffer->Data().Length();
if (SymbianAudio::SuspendedPausedState == m_internalState) {
m_devSoundBufferQ.append(buffer);
} else {
// Will be returned to DevSoundWrapper by bufferProcessed().
m_devSoundBuffer = buffer;
m_devSoundBufferPos = 0;
if (bytesReady() && !m_pullMode)
pushData();
}
}
void QAudioInputPrivate::devsoundRecordError(int err)
{
Q_UNUSED(err)
setError(QAudio::IOError);
}
void QAudioInputPrivate::bufferEmptied()
{
m_devSoundBufferPos = 0;
if (m_devSoundBuffer) {
m_totalBytesReady -= m_devSoundBuffer->Data().Length();
m_devSoundBuffer = 0;
m_devSound->bufferProcessed();
} 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())
if (!m_devSound->start())
setError(QAudio::IOError);
}
Q_ASSERT(m_totalBytesReady >= 0);
}
void QAudioInputPrivate::close()
{
m_notifyTimer->stop();
m_pullTimer->stop();
m_error = QAudio::NoError;
if (m_devSound)
m_devSound->stop();
delete m_devSound;
m_devSound = 0;
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->samplesProcessed());
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);
} else {
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 QAudioInputPrivate::setState(SymbianAudio::State newInternalState)
{
const QAudio::State oldExternalState = m_externalState;
m_internalState = newInternalState;
m_externalState = SymbianAudio::Utils::stateNativeToQt(m_internalState);
if (m_externalState != oldExternalState)
emit stateChanged(m_externalState);
}
QT_END_NAMESPACE

View File

@@ -0,0 +1,174 @@
/****************************************************************************
**
** Copyright (C) 2011 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 "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
{
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;
private slots:
void pullData();
void devsoundInitializeComplete(int err);
void devsoundBufferToBeEmptied(CMMFBuffer *);
void devsoundRecordError(int err);
private:
void open();
void startRecording();
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);
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;
SymbianAudio::DevSoundWrapper* m_devSound;
// 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

View File

@@ -0,0 +1,633 @@
/****************************************************************************
**
** Copyright (C) 2011 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
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;
}
QAudioInputPrivate::~QAudioInputPrivate()
{
stop();
}
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;
QMutexLocker(&qAudio->mutex);
switch(uMsg) {
case WIM_OPEN:
break;
case WIM_DATA:
if(qAudio->waveFreeBlockCount > 0)
qAudio->waveFreeBlockCount--;
qAudio->feedback();
break;
case WIM_CLOSE:
qAudio->finished = true;
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, sizeof(WAVEHDR));
blocks++;
}
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;
period_size = 0;
if (!settings.isValid()) {
qWarning("QAudioInput: open error, invalid format.");
} else if (settings.channels() <= 0) {
qWarning("QAudioInput: open error, invalid number of channels (%d).",
settings.channels());
} else if (settings.sampleSize() <= 0) {
qWarning("QAudioInput: open error, invalid sample size (%d).",
settings.sampleSize());
} else if (settings.frequency() < 8000 || settings.frequency() > 48000) {
qWarning("QAudioInput: open error, frequency out of range (%d).", settings.frequency());
} else if (buffer_size == 0) {
buffer_size
= (settings.frequency()
* settings.channels()
* settings.sampleSize()
#ifndef Q_OS_WINCE // Default buffer size, 200ms, default period size is 40ms
+ 39) / 40;
period_size = buffer_size / 5;
} else {
period_size = buffer_size / 5;
#else // For wince reduce size to 40ms for buffer size and 20ms period
+ 199) / 200;
period_size = buffer_size / 2;
} else {
period_size = buffer_size / 2;
#endif
}
if (period_size == 0) {
errorState = QAudio::OpenError;
deviceState = QAudio::StoppedState;
emit stateChanged(deviceState);
return false;
}
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;
}
mutex.lock();
waveFreeBlockCount = buffer_size/period_size;
mutex.unlock();
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);
}
mutex.lock();
for(int i=0; i<waveFreeBlockCount; i++)
waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR));
freeBlocks(waveBlocks);
mutex.unlock();
}
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 {
l = qMin<qint64>(len, waveBlocks[header].dwBytesRecorded);
// push mode
memcpy(p, waveBlocks[header].lpData, l);
len -= l;
#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));
mutex.lock();
waveFreeBlockCount++;
mutex.unlock();
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;
mutex.lock();
waveFreeBlockCount--;
mutex.unlock();
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;
mutex.lock();
waveFreeBlockCount--;
mutex.unlock();
return 0;
}
header++;
if(header >= buffer_size/period_size)
header = 0;
p+=l;
mutex.lock();
if(!pullMode) {
if(len < period_size || waveFreeBlockCount == buffer_size/period_size)
done = true;
} else {
if(waveFreeBlockCount == buffer_size/period_size)
done = true;
}
mutex.unlock();
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;
}
}
mutex.lock();
waveFreeBlockCount = buffer_size/period_size;
mutex.unlock();
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)
{
intervalTime = qMax(0, ms);
}
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
if(!(deviceState==QAudio::StoppedState||deviceState==QAudio::SuspendedState))
QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection);
}
bool QAudioInputPrivate::deviceReady()
{
bytesAvailable = bytesReady();
#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, buffer_size);
} else {
// emits readyRead() so user will call read() on QIODevice to get some audio data
InputPrivate* a = qobject_cast<InputPrivate*>(audioSource);
a->trigger();
}
if(intervalTime && (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

View File

@@ -0,0 +1,160 @@
/****************************************************************************
**
** Copyright (C) 2011 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 <QtCore/qmutex.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;
QMutex mutex;
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

View File

@@ -0,0 +1,430 @@
/****************************************************************************
**
** Copyright (C) 2011 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 transferred 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.
In either case, the stateChanged() signal may be emitted either synchronously
during execution of the start() function or asynchronously after start() has
returned to the caller.
\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.
In either case, the stateChanged() signal may be emitted either synchronously
during execution of the start() function or asynchronously after start() has
returned to the caller.
\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.
Note: signal will always be emitted during execution of the resume() function.
*/
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 by the class since start()
was called in microseconds.
Note: The amount of audio data played can be determined by subtracting
the microseconds of audio data still in the systems audio buffer.
\code
qint64 bytesInBuffer = bufferSize() - bytesFree();
qint64 usInBuffer = (qint64)(1000000) * bytesInBuffer / ( channels() * sampleSize() / 8 ) / frequency();
qint64 usPlayed = processedUSecs() - usInBuffer;
\endcode
*/
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

View File

@@ -0,0 +1,111 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,791 @@
/****************************************************************************
**
** Copyright (C) 2011 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
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;
m_device = device;
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_UNKNOWN;
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 pcmformat != SND_PCM_FORMAT_UNKNOWN
? snd_pcm_hw_params_set_format( handle, hwparams, pcmformat)
: -1;
}
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 = 0;
int count=0;
unsigned int freakuency=settings.frequency();
if (!settings.isValid()) {
qWarning("QAudioOutput: open error, invalid format.");
} else if (settings.frequency() <= 0) {
qWarning("QAudioOutput: open error, invalid sample rate (%d).",
settings.frequency());
} else {
err = -1;
}
if (err == 0) {
errorState = QAudio::OpenError;
deviceState = QAudio::StoppedState;
return false;
}
QString dev = QString(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());
#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)
{
intervalTime = qMax(0, ms);
}
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(intervalTime && (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

View File

@@ -0,0 +1,166 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,703 @@
/****************************************************************************
**
** Copyright (C) 2011 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/qaudiooutput.h>
#include "qaudio_mac_p.h"
#include "qaudiooutput_mac_p.h"
#include "qaudiodeviceinfo_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 {
audioDeviceInfo = new QAudioDeviceInfoInternal(device, QAudio::AudioOutput);
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()
{
delete audioDeviceInfo;
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(streamFormat);
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.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 (!audioDeviceInfo->isFormatSupported(audioFormat) || !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"

View File

@@ -0,0 +1,169 @@
/****************************************************************************
**
** Copyright (C) 2011 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;
class QAbstractAudioDeviceInfo;
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;
QAbstractAudioDeviceInfo *audioDeviceInfo;
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

View File

@@ -0,0 +1,643 @@
/****************************************************************************
**
** Copyright (C) 2011 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_devSound(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)
{
qRegisterMetaType<CMMFBuffer *>("CMMFBuffer *");
connect(m_notifyTimer.data(), SIGNAL(timeout()), this, SIGNAL(notify()));
m_underflowTimer->setInterval(UnderflowTimerInterval);
connect(m_underflowTimer.data(), SIGNAL(timeout()), this,
SLOT(underflowTimerExpired()));
}
QAudioOutputPrivate::~QAudioOutputPrivate()
{
close();
}
QIODevice* QAudioOutputPrivate::start(QIODevice *device)
{
stop();
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_totalSamplesPlayed += samplesPlayed;
m_bytesWritten = 0;
const bool paused = m_devSound->pause();
if (paused) {
setState(SymbianAudio::SuspendedPausedState);
} else {
m_devSoundBuffer = 0;
// Calculate the amount of data dropped
const qint64 paddingSamples = samplesWritten - samplesPlayed;
Q_ASSERT(paddingSamples >= 0);
m_bytesPadding = SymbianAudio::Utils::samplesToBytes(m_format,
paddingSamples);
setState(SymbianAudio::SuspendedStoppedState);
}
}
}
void QAudioOutputPrivate::resume()
{
if (QAudio::SuspendedState == m_externalState) {
if (SymbianAudio::SuspendedPausedState == m_internalState)
m_devSound->resume();
else
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_notifyInterval && (SymbianAudio::ActiveState == m_internalState ||
SymbianAudio::IdleState == m_internalState))
m_notifyTimer->start(m_notifyInterval);
else
m_notifyTimer->stop();
}
}
int QAudioOutputPrivate::notifyInterval() const
{
return m_notifyInterval;
}
qint64 QAudioOutputPrivate::processedUSecs() const
{
int samplesPlayed = 0;
if (m_devSound && QAudio::SuspendedState != m_externalState)
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;
}
//-----------------------------------------------------------------------------
// 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::devsoundInitializeComplete(int err)
{
Q_ASSERT_X(SymbianAudio::InitializingState == m_internalState,
Q_FUNC_INFO, "Invalid state");
if (!err && m_devSound->isFormatSupported(m_format))
startPlayback();
else
setError(QAudio::OpenError);
}
void QAudioOutputPrivate::devsoundBufferToBeFilled(CMMFBuffer *bufferBase)
{
// Following receipt of this signal, 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 DevSoundWrapper by bufferProcessed().
m_devSoundBuffer = static_cast<CMMFDataBuffer*>(bufferBase);
if (!m_devSoundBufferSize)
m_devSoundBufferSize = m_devSoundBuffer->Data().MaxLength();
writePaddingData();
if (m_pullMode && isDataReady() && !m_bytesPadding)
pullData();
}
void QAudioOutputPrivate::devsoundPlayError(int err)
{
switch (err) {
case KErrUnderflow:
m_underflow = true;
if (m_pullMode && !m_lastBuffer)
setError(QAudio::UnderrunError);
else
setState(SymbianAudio::IdleState);
break;
case KErrOverflow:
// Silently consume this error when in playback mode
break;
default:
setError(QAudio::IOError);
break;
}
}
void QAudioOutputPrivate::open()
{
Q_ASSERT_X(SymbianAudio::ClosedState == m_internalState,
Q_FUNC_INFO, "DevSound already opened");
Q_ASSERT(!m_devSound);
m_devSound = new SymbianAudio::DevSoundWrapper(QAudio::AudioOutput, this);
connect(m_devSound, SIGNAL(initializeComplete(int)),
this, SLOT(devsoundInitializeComplete(int)));
connect(m_devSound, SIGNAL(bufferToBeProcessed(CMMFBuffer *)),
this, SLOT(devsoundBufferToBeFilled(CMMFBuffer *)));
connect(m_devSound, SIGNAL(processingError(int)),
this, SLOT(devsoundPlayError(int)));
setState(SymbianAudio::InitializingState);
m_devSound->initialize(m_format.codec());
}
void QAudioOutputPrivate::startPlayback()
{
bool ok = m_devSound->setFormat(m_format);
if (ok)
ok = m_devSound->start();
if (ok) {
if (isDataReady())
setState(SymbianAudio::ActiveState);
else
setState(SymbianAudio::IdleState);
if (m_notifyInterval)
m_notifyTimer->start(m_notifyInterval);
m_underflow = false;
Q_ASSERT(m_devSound->samplesProcessed() == 0);
writePaddingData();
if (m_pullMode && m_source->bytesAvailable() && !m_bytesPadding)
dataReady();
} else {
setError(QAudio::OpenError);
close();
}
}
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);
Q_ASSERT(m_bytesPadding >= paddingBytes);
m_bytesPadding -= paddingBytes;
if (m_pullMode && m_source->atEnd())
lastBufferFilled();
if ((paddingBytes == outputBytes) || !m_bytesPadding)
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");
// 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->bufferProcessed();
}
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();
delete m_devSound;
m_devSound = 0;
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->samplesProcessed());
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);
} else {
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);
if (m_externalState != oldExternalState)
emit stateChanged(m_externalState);
}
bool QAudioOutputPrivate::isDataReady() const
{
return (m_source && m_source->bytesAvailable())
|| m_bytesPadding
|| m_pushDataReady;
}
QT_END_NAMESPACE

View File

@@ -0,0 +1,198 @@
/****************************************************************************
**
** Copyright (C) 2011 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
{
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;
private slots:
void dataReady();
void underflowTimerExpired();
void devsoundInitializeComplete(int err);
void devsoundBufferToBeFilled(CMMFBuffer *);
void devsoundPlayError(int err);
private:
void open();
void startPlayback();
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;
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;
SymbianAudio::DevSoundWrapper* m_devSound;
// 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

View File

@@ -0,0 +1,703 @@
/****************************************************************************
**
** Copyright (C) 2011 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"
#ifndef SPEAKER_FRONT_LEFT
#define SPEAKER_FRONT_LEFT 0x00000001
#define SPEAKER_FRONT_RIGHT 0x00000002
#define SPEAKER_FRONT_CENTER 0x00000004
#define SPEAKER_LOW_FREQUENCY 0x00000008
#define SPEAKER_BACK_LEFT 0x00000010
#define SPEAKER_BACK_RIGHT 0x00000020
#define SPEAKER_FRONT_LEFT_OF_CENTER 0x00000040
#define SPEAKER_FRONT_RIGHT_OF_CENTER 0x00000080
#define SPEAKER_BACK_CENTER 0x00000100
#define SPEAKER_SIDE_LEFT 0x00000200
#define SPEAKER_SIDE_RIGHT 0x00000400
#define SPEAKER_TOP_CENTER 0x00000800
#define SPEAKER_TOP_FRONT_LEFT 0x00001000
#define SPEAKER_TOP_FRONT_CENTER 0x00002000
#define SPEAKER_TOP_FRONT_RIGHT 0x00004000
#define SPEAKER_TOP_BACK_LEFT 0x00008000
#define SPEAKER_TOP_BACK_CENTER 0x00010000
#define SPEAKER_TOP_BACK_RIGHT 0x00020000
#define SPEAKER_RESERVED 0x7FFC0000
#define SPEAKER_ALL 0x80000000
#endif
#ifndef _WAVEFORMATEXTENSIBLE_
#define _WAVEFORMATEXTENSIBLE_
typedef struct
{
WAVEFORMATEX Format; // Base WAVEFORMATEX data
union
{
WORD wValidBitsPerSample; // Valid bits in each sample container
WORD wSamplesPerBlock; // Samples per block of audio data; valid
// if wBitsPerSample=0 (but rarely used).
WORD wReserved; // Zero if neither case above applies.
} Samples;
DWORD dwChannelMask; // Positions of the audio channels
GUID SubFormat; // Format identifier GUID
} WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE, *LPPWAVEFORMATEXTENSIBLE;
typedef const WAVEFORMATEXTENSIBLE* LPCWAVEFORMATEXTENSIBLE;
#endif
#if !defined(WAVE_FORMAT_EXTENSIBLE)
#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
#endif
//#define DEBUG_AUDIO 1
QT_BEGIN_NAMESPACE
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;
}
QAudioOutputPrivate::~QAudioOutputPrivate()
{
mutex.lock();
finished = true;
mutex.unlock();
close();
}
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;
QMutexLocker(&qAudio->mutex);
switch(uMsg) {
case WOM_OPEN:
qAudio->feedback();
break;
case WOM_CLOSE:
return;
case WOM_DONE:
if(qAudio->finished || qAudio->buffer_size == 0 || qAudio->period_size == 0) {
return;
}
qAudio->waveFreeBlockCount++;
if(qAudio->waveFreeBlockCount >= qAudio->buffer_size/qAudio->period_size)
qAudio->waveFreeBlockCount = qAudio->buffer_size/qAudio->period_size;
qAudio->feedback();
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, sizeof(WAVEHDR));
blocks++;
}
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
period_size = 0;
if (!settings.isValid()) {
qWarning("QAudioOutput: open error, invalid format.");
} else if (settings.channels() <= 0) {
qWarning("QAudioOutput: open error, invalid number of channels (%d).",
settings.channels());
} else if (settings.sampleSize() <= 0) {
qWarning("QAudioOutput: open error, invalid sample size (%d).",
settings.sampleSize());
} else if (settings.frequency() < 8000 || settings.frequency() > 48000) {
qWarning("QAudioOutput: open error, frequency out of range (%d).", settings.frequency());
} else if (buffer_size == 0) {
// Default buffer size, 200ms, default period size is 40ms
buffer_size
= (settings.frequency()
* settings.channels()
* settings.sampleSize()
+ 39) / 40;
period_size = buffer_size / 5;
} else {
period_size = buffer_size / 5;
}
if (period_size == 0) {
errorState = QAudio::OpenError;
deviceState = QAudio::StoppedState;
emit stateChanged(deviceState);
return false;
}
waveBlocks = allocateBlocks(period_size, buffer_size/period_size);
mutex.lock();
waveFreeBlockCount = buffer_size/period_size;
mutex.unlock();
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 ( settings.channels() <= 2) {
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;
}
} else {
WAVEFORMATEXTENSIBLE wfex;
wfex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
wfex.Format.nChannels = settings.channels();
wfex.Format.wBitsPerSample = settings.sampleSize();
wfex.Format.nSamplesPerSec = settings.frequency();
wfex.Format.nBlockAlign = wfex.Format.nChannels*wfex.Format.wBitsPerSample/8;
wfex.Format.nAvgBytesPerSec=wfex.Format.nSamplesPerSec*wfex.Format.nBlockAlign;
wfex.Samples.wValidBitsPerSample=wfex.Format.wBitsPerSample;
static const GUID _KSDATAFORMAT_SUBTYPE_PCM = {
0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
wfex.SubFormat=_KSDATAFORMAT_SUBTYPE_PCM;
wfex.Format.cbSize=22;
wfex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
if (settings.channels() >= 4)
wfex.dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
if (settings.channels() >= 6)
wfex.dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
if (settings.channels() == 8)
wfex.dwChannelMask |= SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
if(waveOutOpen(&hWaveOut, devId, &wfex.Format,
(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)
{
intervalTime = qMax(0, ms);
}
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) {
mutex.lock();
if(waveFreeBlockCount==0) {
mutex.unlock();
break;
}
mutex.unlock();
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));
mutex.lock();
waveFreeBlockCount--;
#ifdef DEBUG_AUDIO
qDebug("write out l=%d, waveFreeBlockCount=%d",
current->dwBufferLength,waveFreeBlockCount);
#endif
mutex.unlock();
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 || deviceState == QAudio::SuspendedState)
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;
mutex.lock();
if(waveFreeBlockCount==0) full = true;
mutex.unlock();
if (full){
#ifdef DEBUG_AUDIO
qDebug() << "Skipping data as unable to write";
#endif
if(intervalTime && (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) {
// Didn't write all data
audioSource->seek(audioSource->pos()-(l-out));
}
if(startup)
waveOutRestart(hWaveOut);
} else if(l == 0) {
bytesAvailable = bytesFree();
int check = 0;
mutex.lock();
check = waveFreeBlockCount;
mutex.unlock();
if(check == buffer_size/period_size) {
if (deviceState != QAudio::IdleState) {
errorState = QAudio::UnderrunError;
deviceState = QAudio::IdleState;
emit stateChanged(deviceState);
}
}
} else if(l < 0) {
bytesAvailable = bytesFree();
errorState = QAudio::IOError;
}
} else {
int buffered;
mutex.lock();
buffered = waveFreeBlockCount;
mutex.unlock();
if (buffered >= buffer_size/period_size && deviceState == QAudio::ActiveState) {
if (deviceState != QAudio::IdleState) {
errorState = QAudio::UnderrunError;
deviceState = QAudio::IdleState;
emit stateChanged(deviceState);
}
}
}
if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
return true;
if(intervalTime && (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

View File

@@ -0,0 +1,157 @@
/****************************************************************************
**
** Copyright (C) 2011 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 <QtCore/qmutex.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 );
QMutex mutex;
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

View File

@@ -0,0 +1,16 @@
TARGET = QtMultimedia
QPRO_PWD = $$PWD
QT = core gui
DEFINES += QT_BUILD_MULTIMEDIA_LIB QT_NO_USING_NAMESPACE
unix|win32-g++*:QMAKE_PKGCONFIG_REQUIRES = QtCore QtGui
include(../qbase.pri)
include(audio/audio.pri)
include(video/video.pri)
symbian: {
TARGET.UID3 = 0x2001E627
}

View File

@@ -0,0 +1,201 @@
/****************************************************************************
**
** Copyright (C) 2011 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 QPixmapHandle The handle of the buffer is a QPixmap.
\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 is uninitialized 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

View File

@@ -0,0 +1,107 @@
/****************************************************************************
**
** Copyright (C) 2011 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,
QPixmapHandle,
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

View File

@@ -0,0 +1,76 @@
/****************************************************************************
**
** Copyright (C) 2011 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)
{}
virtual ~QAbstractVideoBufferPrivate()
{}
QAbstractVideoBuffer::HandleType handleType;
};
QT_END_NAMESPACE
#endif

View File

@@ -0,0 +1,283 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,110 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,78 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,106 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,79 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,129 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,83 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,742 @@
/****************************************************************************
**
** Copyright (C) 2011 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:
return 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

View File

@@ -0,0 +1,169 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -0,0 +1,704 @@
/****************************************************************************
**
** Copyright (C) 2011 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 QVariant::fromValue(d->handleType);
} else if (qstrcmp(name, "pixelFormat") == 0) {
return QVariant::fromValue(d->pixelFormat);
} else if (qstrcmp(name, "handleType") == 0) {
return QVariant::fromValue(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 QVariant::fromValue(d->scanLineDirection);
} else if (qstrcmp(name, "frameRate") == 0) {
return QVariant::fromValue(d->frameRate);
} else if (qstrcmp(name, "pixelAspectRatio") == 0) {
return QVariant::fromValue(d->pixelAspectRatio);
} else if (qstrcmp(name, "sizeHint") == 0) {
return sizeHint();
} else if (qstrcmp(name, "yCbCrColorSpace") == 0) {
return QVariant::fromValue(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 (value.canConvert<QSize>()) {
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 (value.canConvert<QRect>())
d->viewport = qvariant_cast<QRect>(value);
} else if (qstrcmp(name, "scanLineDirection") == 0) {
if (value.canConvert<Direction>())
d->scanLineDirection = qvariant_cast<Direction>(value);
} else if (qstrcmp(name, "frameRate") == 0) {
if (value.canConvert<qreal>())
d->frameRate = qvariant_cast<qreal>(value);
} else if (qstrcmp(name, "pixelAspectRatio") == 0) {
if (value.canConvert<QSize>())
d->pixelAspectRatio = qvariant_cast<QSize>(value);
} else if (qstrcmp(name, "sizeHint") == 0) {
// read only.
} else if (qstrcmp(name, "yCbCrColorSpace") == 0) {
if (value.canConvert<YCbCrColorSpace>())
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

View File

@@ -0,0 +1,147 @@
/****************************************************************************
**
** Copyright (C) 2011 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

View File

@@ -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

2
src/src.pro Normal file
View File

@@ -0,0 +1,2 @@
TEMPLATE = subdirs
SUBDIRS += multimedia