Remove some obsolete bits.
They can be updated again later when things are more stable, if needed. Change-Id: I73bdacdd3d1fd43a60cd3a0c14b925fa9c32ee27 Reviewed-on: http://codereview.qt.nokia.com/1724 Reviewed-by: Qt Sanity Bot <qt_sanity_bot@ovi.com> Reviewed-by: Jonas Rabbe <jonas.rabbe@nokia.com>
This commit is contained in:
committed by
Qt by Nokia
parent
7085775a11
commit
1403a1c7be
@@ -40,29 +40,10 @@ win32 {
|
||||
SOURCES += audio/qaudiodeviceinfo_win32_p.cpp \
|
||||
audio/qaudiooutput_win32_p.cpp \
|
||||
audio/qaudioinput_win32_p.cpp
|
||||
!wince*:LIBS += -lwinmm
|
||||
wince*:LIBS += -lcoredll
|
||||
LIBS += -lstrmiids -lole32 -loleaut32
|
||||
LIBS += -lwinmm -lstrmiids -lole32 -loleaut32
|
||||
}
|
||||
|
||||
symbian {
|
||||
INCLUDEPATH += $${EPOCROOT}epoc32/include/mmf/common
|
||||
INCLUDEPATH += $${EPOCROOT}epoc32/include/mmf/server
|
||||
|
||||
PRIVATE_HEADERS += audio/qaudio_symbian_p.h \
|
||||
audio/qaudiodeviceinfo_symbian_p.h \
|
||||
audio/qaudioinput_symbian_p.h \
|
||||
audio/qaudiooutput_symbian_p.h
|
||||
|
||||
SOURCES += audio/qaudio_symbian_p.cpp \
|
||||
audio/qaudiodeviceinfo_symbian_p.cpp \
|
||||
audio/qaudioinput_symbian_p.cpp \
|
||||
audio/qaudiooutput_symbian_p.cpp
|
||||
|
||||
LIBS += -lmmfdevsound
|
||||
}
|
||||
|
||||
unix:!mac:!symbian {
|
||||
unix:!mac {
|
||||
contains(pulseaudio_enabled, yes) {
|
||||
DEFINES += QT_NO_AUDIO_BACKEND
|
||||
}
|
||||
|
||||
@@ -1,663 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "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);
|
||||
#ifndef PRE_S60_52_PLATFORM
|
||||
if (m_mode == QAudio::AudioOutput ) {
|
||||
m_devsound->Pause();
|
||||
return true;
|
||||
} else {
|
||||
const bool canPause = isResumeSupported();
|
||||
if (canPause)
|
||||
m_devsound->Pause();
|
||||
else
|
||||
stop();
|
||||
return canPause;
|
||||
}
|
||||
#else
|
||||
const bool canPause = isResumeSupported();
|
||||
if (canPause)
|
||||
m_devsound->Pause();
|
||||
else
|
||||
stop();
|
||||
return canPause;
|
||||
#endif
|
||||
}
|
||||
|
||||
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.
|
||||
}
|
||||
|
||||
#ifndef PRE_S60_52_PLATFORM
|
||||
int DevSoundWrapper::flush()
|
||||
{
|
||||
return m_devsound->EmptyBuffers();
|
||||
}
|
||||
#endif
|
||||
} // namespace SymbianAudio
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
|
||||
@@ -1,204 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//
|
||||
// W A R N I N G
|
||||
// -------------
|
||||
//
|
||||
// This file is not part of the Qt API. It exists for the convenience
|
||||
// of other Qt classes. This header file may change from version to
|
||||
// version without notice, or even be removed.
|
||||
//
|
||||
// We mean it.
|
||||
//
|
||||
|
||||
#ifndef QAUDIO_SYMBIAN_P_H
|
||||
#define QAUDIO_SYMBIAN_P_H
|
||||
|
||||
#include <QtCore/qnamespace.h>
|
||||
#include <QtCore/QList>
|
||||
#include <QtCore/QString>
|
||||
#include <qaudioformat.h>
|
||||
#include <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();
|
||||
#ifndef PRE_S60_52_PLATFORM
|
||||
int flush();
|
||||
#endif
|
||||
|
||||
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
|
||||
@@ -60,10 +60,6 @@
|
||||
#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
|
||||
|
||||
@@ -139,7 +135,7 @@ 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))
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA))
|
||||
foreach (const QByteArray &handle, QAudioDeviceInfoInternal::availableDevices(mode))
|
||||
devices << QAudioDeviceInfo(QLatin1String("builtin"), handle, mode);
|
||||
#endif
|
||||
@@ -174,7 +170,7 @@ QAudioDeviceInfo QAudioDeviceFactory::defaultInputDevice()
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_AUDIO_BACKEND
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN))
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA))
|
||||
return QAudioDeviceInfo(QLatin1String("builtin"), QAudioDeviceInfoInternal::defaultInputDevice(), QAudio::AudioInput);
|
||||
#endif
|
||||
#endif
|
||||
@@ -194,7 +190,7 @@ QAudioDeviceInfo QAudioDeviceFactory::defaultOutputDevice()
|
||||
#endif
|
||||
|
||||
#ifndef QT_NO_AUDIO_BACKEND
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA) || defined(Q_OS_SYMBIAN))
|
||||
#if (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA))
|
||||
return QAudioDeviceInfo(QLatin1String("builtin"), QAudioDeviceInfoInternal::defaultOutputDevice(), QAudio::AudioOutput);
|
||||
#endif
|
||||
#endif
|
||||
@@ -206,7 +202,7 @@ QAbstractAudioDeviceInfo* QAudioDeviceFactory::audioDeviceInfo(const QString &re
|
||||
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 (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA))
|
||||
if (realm == QLatin1String("builtin"))
|
||||
return new QAudioDeviceInfoInternal(handle, mode);
|
||||
#endif
|
||||
@@ -238,7 +234,7 @@ QAbstractAudioInput* QAudioDeviceFactory::createInputDevice(QAudioDeviceInfo con
|
||||
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 (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA))
|
||||
if (deviceInfo.realm() == QLatin1String("builtin")) {
|
||||
QAbstractAudioInput* p = new QAudioInputPrivate(deviceInfo.handle());
|
||||
if (p) p->setFormat(format);
|
||||
@@ -265,7 +261,7 @@ QAbstractAudioOutput* QAudioDeviceFactory::createOutputDevice(QAudioDeviceInfo c
|
||||
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 (defined(Q_OS_WIN) || defined(Q_OS_MAC) || defined(HAS_ALSA))
|
||||
if (deviceInfo.realm() == QLatin1String("builtin")) {
|
||||
QAbstractAudioOutput* p = new QAudioOutputPrivate(deviceInfo.handle());
|
||||
if (p) p->setFormat(format);
|
||||
|
||||
@@ -1,235 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <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;
|
||||
}
|
||||
|
||||
QString QAudioDeviceInfoInternal::deviceName() const
|
||||
{
|
||||
return m_deviceName;
|
||||
}
|
||||
|
||||
QStringList QAudioDeviceInfoInternal::supportedCodecs()
|
||||
{
|
||||
getSupportedFormats();
|
||||
return m_capabilities.keys();
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::supportedSampleRates()
|
||||
{
|
||||
getSupportedFormats();
|
||||
return m_unionCapabilities.m_frequencies;
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::supportedChannelCounts()
|
||||
{
|
||||
getSupportedFormats();
|
||||
return m_unionCapabilities.m_channels;
|
||||
}
|
||||
|
||||
QList<int> QAudioDeviceInfoInternal::supportedSampleSizes()
|
||||
{
|
||||
getSupportedFormats();
|
||||
return m_unionCapabilities.m_sampleSizes;
|
||||
}
|
||||
|
||||
QList<QAudioFormat::Endian> QAudioDeviceInfoInternal::supportedByteOrders()
|
||||
{
|
||||
getSupportedFormats();
|
||||
return m_unionCapabilities.m_byteOrders;
|
||||
}
|
||||
|
||||
QList<QAudioFormat::SampleType> QAudioDeviceInfoInternal::supportedSampleTypes()
|
||||
{
|
||||
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
|
||||
|
||||
#include "moc_qaudiodeviceinfo_symbian_p.cpp"
|
||||
|
||||
@@ -1,116 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//
|
||||
// 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 <qaudiosystem.h>
|
||||
#include <sounddevice.h>
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QAudioDeviceInfoInternal
|
||||
: public QAbstractAudioDeviceInfo
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QAudioDeviceInfoInternal(QByteArray device, QAudio::Mode mode);
|
||||
~QAudioDeviceInfoInternal();
|
||||
|
||||
// QAbstractAudioDeviceInfo
|
||||
QAudioFormat preferredFormat() const;
|
||||
bool isFormatSupported(const QAudioFormat &format) const;
|
||||
QString deviceName() const;
|
||||
QStringList supportedCodecs();
|
||||
QList<int> supportedSampleRates();
|
||||
QList<int> supportedChannelCounts();
|
||||
QList<int> supportedSampleSizes();
|
||||
QList<QAudioFormat::Endian> supportedByteOrders();
|
||||
QList<QAudioFormat::SampleType> supportedSampleTypes();
|
||||
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
|
||||
@@ -315,12 +315,10 @@ void QAudioDeviceInfoInternal::updateLists()
|
||||
|| (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);
|
||||
}
|
||||
@@ -330,12 +328,10 @@ void QAudioDeviceInfoInternal::updateLists()
|
||||
|| (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);
|
||||
}
|
||||
@@ -357,7 +353,6 @@ void QAudioDeviceInfoInternal::updateLists()
|
||||
|| (fmt && WAVE_FORMAT_4S16)) {
|
||||
freqz.append(44100);
|
||||
}
|
||||
#ifndef Q_OS_WINCE
|
||||
if((fmt && WAVE_FORMAT_48M08)
|
||||
|| (fmt && WAVE_FORMAT_48S08)
|
||||
|| (fmt && WAVE_FORMAT_48M16)
|
||||
@@ -370,7 +365,6 @@ void QAudioDeviceInfoInternal::updateLists()
|
||||
|| (fmt && WAVE_FORMAT_96S16)) {
|
||||
freqz.append(96000);
|
||||
}
|
||||
#endif
|
||||
channelz.append(1);
|
||||
channelz.append(2);
|
||||
if (mode == QAudio::AudioOutput) {
|
||||
|
||||
@@ -117,20 +117,6 @@ QT_BEGIN_NAMESPACE
|
||||
\snippet doc/src/snippets/multimedia-snippets/audio.cpp Audio input state changed
|
||||
|
||||
\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.
|
||||
*/
|
||||
|
||||
/*!
|
||||
@@ -184,8 +170,6 @@ QAudioInput::~QAudioInput()
|
||||
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}
|
||||
|
||||
\since 1.0
|
||||
\sa QIODevice
|
||||
*/
|
||||
@@ -207,8 +191,6 @@ void QAudioInput::start(QIODevice* device)
|
||||
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}
|
||||
|
||||
\since 1.0
|
||||
\sa QIODevice
|
||||
*/
|
||||
|
||||
@@ -1,594 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "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)
|
||||
: m_device(device)
|
||||
, m_clientBufferSize(SymbianAudio::DefaultBufferSize)
|
||||
, m_notifyInterval(SymbianAudio::DefaultNotifyInterval)
|
||||
, m_notifyTimer(new QTimer(this))
|
||||
, m_lastNotifyPosition(0)
|
||||
, 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(notifyTimerExpired()));
|
||||
|
||||
m_pullTimer->setInterval(PushInterval);
|
||||
connect(m_pullTimer.data(), SIGNAL(timeout()), this, SLOT(pullData()));
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::setFormat(const QAudioFormat& fmt)
|
||||
{
|
||||
m_format = fmt;
|
||||
}
|
||||
|
||||
QAudioInputPrivate::~QAudioInputPrivate()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::start(QIODevice *device)
|
||||
{
|
||||
stop();
|
||||
|
||||
open();
|
||||
if (SymbianAudio::ClosedState != m_internalState) {
|
||||
m_pullMode = true;
|
||||
m_sink = device;
|
||||
m_elapsed.restart();
|
||||
}
|
||||
}
|
||||
|
||||
QIODevice* QAudioInputPrivate::start()
|
||||
{
|
||||
stop();
|
||||
|
||||
open();
|
||||
if (SymbianAudio::ClosedState != m_internalState) {
|
||||
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_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;
|
||||
buffer = currentBuffer();
|
||||
while (buffer && (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();
|
||||
|
||||
buffer = currentBuffer();
|
||||
}
|
||||
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::notifyTimerExpired()
|
||||
{
|
||||
const qint64 pos = processedUSecs();
|
||||
if (pos > m_lastNotifyPosition) {
|
||||
int count = (pos - m_lastNotifyPosition) / (m_notifyInterval * 1000);
|
||||
while (count--) {
|
||||
emit notify();
|
||||
m_lastNotifyPosition += m_notifyInterval * 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QAudioInputPrivate::pullData()
|
||||
{
|
||||
Q_ASSERT_X(m_pullMode, Q_FUNC_INFO,
|
||||
"pullData called when in push mode");
|
||||
|
||||
CMMFDataBuffer *buffer = 0;
|
||||
buffer = currentBuffer();
|
||||
while (buffer) {
|
||||
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;
|
||||
|
||||
buffer = currentBuffer();
|
||||
}
|
||||
}
|
||||
|
||||
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_lastNotifyPosition = 0;
|
||||
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 != QAudio::ActiveState &&
|
||||
m_externalState != QAudio::IdleState)
|
||||
m_notifyTimer->stop();
|
||||
|
||||
if (m_externalState != oldExternalState)
|
||||
emit stateChanged(m_externalState);
|
||||
}
|
||||
|
||||
QT_END_NAMESPACE
|
||||
|
||||
#include "moc_qaudioinput_symbian_p.cpp"
|
||||
@@ -1,178 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//
|
||||
// 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 <qaudiosystem.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);
|
||||
~QAudioInputPrivate();
|
||||
|
||||
// QAbstractAudioInput
|
||||
void start(QIODevice *device);
|
||||
QIODevice* start();
|
||||
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;
|
||||
void setFormat(const QAudioFormat& fmt);
|
||||
|
||||
private slots:
|
||||
void notifyTimerExpired();
|
||||
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;
|
||||
QAudioFormat m_format;
|
||||
|
||||
int m_clientBufferSize;
|
||||
int m_notifyInterval;
|
||||
QScopedPointer<QTimer> m_notifyTimer;
|
||||
qint64 m_lastNotifyPosition;
|
||||
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
|
||||
@@ -250,17 +250,10 @@ bool QAudioInputPrivate::open()
|
||||
= (settings.frequency()
|
||||
* settings.channelCount()
|
||||
* 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) {
|
||||
|
||||
@@ -1,713 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "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)
|
||||
: m_device(device)
|
||||
, m_clientBufferSize(SymbianAudio::DefaultBufferSize)
|
||||
, m_notifyInterval(SymbianAudio::DefaultNotifyInterval)
|
||||
, m_notifyTimer(new QTimer(this))
|
||||
, m_lastNotifyPosition(0)
|
||||
, 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)
|
||||
{
|
||||
connect(m_notifyTimer.data(), SIGNAL(timeout()),
|
||||
this, SLOT(notifyTimerExpired()));
|
||||
|
||||
m_underflowTimer->setInterval(UnderflowTimerInterval);
|
||||
|
||||
connect(m_underflowTimer.data(), SIGNAL(timeout()), this,
|
||||
SLOT(underflowTimerExpired()));
|
||||
}
|
||||
|
||||
QAudioOutputPrivate::~QAudioOutputPrivate()
|
||||
{
|
||||
close();
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::setFormat(const QAudioFormat& fmt)
|
||||
{
|
||||
m_format = fmt;
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::start(QIODevice *device)
|
||||
{
|
||||
stop();
|
||||
|
||||
// We have to set these before the call to open() because of the
|
||||
// logic in initializingState()
|
||||
m_pullMode = true;
|
||||
m_source = device;
|
||||
|
||||
open();
|
||||
|
||||
if (SymbianAudio::ClosedState != m_internalState) {
|
||||
connect(m_source, SIGNAL(readyRead()), this, SLOT(dataReady()));
|
||||
m_elapsed.restart();
|
||||
}
|
||||
}
|
||||
|
||||
QIODevice* QAudioOutputPrivate::start()
|
||||
{
|
||||
stop();
|
||||
|
||||
open();
|
||||
|
||||
if (SymbianAudio::ClosedState != m_internalState) {
|
||||
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()
|
||||
{
|
||||
#ifndef PRE_S60_52_PLATFORM
|
||||
int err = m_devSound->flush();
|
||||
if (err != 0)
|
||||
setError(QAudio::FatalError);
|
||||
#else
|
||||
m_totalSamplesPlayed += getSamplesPlayed();
|
||||
m_devSound->stop();
|
||||
m_bytesPadding = 0;
|
||||
startPlayback();
|
||||
#endif
|
||||
}
|
||||
|
||||
void QAudioOutputPrivate::suspend()
|
||||
{
|
||||
if (SymbianAudio::ActiveState == m_internalState
|
||||
|| SymbianAudio::IdleState == m_internalState) {
|
||||
m_underflowTimer->stop();
|
||||
const qint64 samplesWritten = SymbianAudio::Utils::bytesToSamples(
|
||||
m_format, m_bytesWritten);
|
||||
const qint64 samplesPlayed = getSamplesPlayed();
|
||||
#ifdef PRE_S60_52_PLATFORM
|
||||
m_totalSamplesPlayed += samplesPlayed;
|
||||
m_bytesWritten = 0;
|
||||
#endif
|
||||
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) {
|
||||
#ifndef PRE_S60_52_PLATFORM
|
||||
setState(SymbianAudio::ActiveState);
|
||||
if (m_devSoundBuffer != 0)
|
||||
devsoundBufferToBeFilled(m_devSoundBuffer);
|
||||
m_devSound->start();
|
||||
#else
|
||||
//defined in else part of macro to enable compatibility of previous code
|
||||
m_devSound->resume();
|
||||
#endif
|
||||
} 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)
|
||||
return samplesPlayed;
|
||||
|
||||
if (QAudio::SuspendedState != m_externalState)
|
||||
samplesPlayed = getSamplesPlayed();
|
||||
|
||||
// Protect against division by zero
|
||||
Q_ASSERT_X(m_format.frequency() > 0, Q_FUNC_INFO, "Invalid frequency");
|
||||
|
||||
#ifndef PRE_S60_52_PLATFORM
|
||||
const qint64 devSoundSamplesPlayed(m_devSound->samplesProcessed());
|
||||
const qint64 result = qint64(1000000) *
|
||||
(devSoundSamplesPlayed)
|
||||
/ m_format.frequency();
|
||||
#else
|
||||
const qint64 result = qint64(1000000) *
|
||||
(samplesPlayed + m_totalSamplesPlayed)
|
||||
/ m_format.frequency();
|
||||
#endif
|
||||
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::notifyTimerExpired()
|
||||
{
|
||||
const qint64 pos = processedUSecs();
|
||||
if (pos > m_lastNotifyPosition) {
|
||||
int count = (pos - m_lastNotifyPosition) / (m_notifyInterval * 1000);
|
||||
while (count--) {
|
||||
emit notify();
|
||||
m_lastNotifyPosition += m_notifyInterval * 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
#ifndef PRE_S60_52_PLATFORM
|
||||
m_underflowTimer->stop();
|
||||
#endif
|
||||
} 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);
|
||||
#ifndef PRE_S60_52_PLATFORM
|
||||
if (m_externalState == QAudio::SuspendedState) {
|
||||
// This condition occurs when buffertobefilled callback is received after
|
||||
// pause command is processed.
|
||||
return;
|
||||
}
|
||||
#endif
|
||||
|
||||
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);
|
||||
m_bytesPadding -= paddingBytes;
|
||||
Q_ASSERT(m_bytesPadding >= 0);
|
||||
|
||||
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);
|
||||
|
||||
//Partial buffers can be sent to DevSound. This assert not required.
|
||||
//Q_ASSERT(bytesCopied == copyBytes);
|
||||
outputBuffer.SetLength(outputBuffer.Length() + bytesCopied);
|
||||
inputBytes -= bytesCopied;
|
||||
|
||||
if(bytesCopied == 0)
|
||||
return;
|
||||
|
||||
if (m_source->atEnd())
|
||||
lastBufferFilled();
|
||||
else
|
||||
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_lastNotifyPosition = 0;
|
||||
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 != QAudio::ActiveState &&
|
||||
m_externalState != QAudio::IdleState)
|
||||
m_notifyTimer->stop();
|
||||
|
||||
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
|
||||
|
||||
#include "moc_qaudiooutput_symbian_p.cpp"
|
||||
@@ -1,201 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//
|
||||
// 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 <qaudiosystem.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);
|
||||
~QAudioOutputPrivate();
|
||||
|
||||
// QAbstractAudioOutput
|
||||
void start(QIODevice *device);
|
||||
QIODevice* start();
|
||||
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;
|
||||
void setFormat(const QAudioFormat& fmt);
|
||||
|
||||
private slots:
|
||||
void dataReady();
|
||||
void notifyTimerExpired();
|
||||
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;
|
||||
QAudioFormat m_format;
|
||||
|
||||
int m_clientBufferSize;
|
||||
int m_notifyInterval;
|
||||
QScopedPointer<QTimer> m_notifyTimer;
|
||||
qint64 m_lastNotifyPosition;
|
||||
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
|
||||
@@ -1,6 +1,6 @@
|
||||
INCLUDEPATH += effects
|
||||
|
||||
unix:!mac:!symbian {
|
||||
unix:!mac {
|
||||
contains(pulseaudio_enabled, yes) {
|
||||
CONFIG += link_pkgconfig
|
||||
PKGCONFIG += libpulse
|
||||
|
||||
@@ -86,7 +86,7 @@ QT_BEGIN_NAMESPACE
|
||||
\since 1.0
|
||||
|
||||
This property holds the volume of the playback, from 0.0 (silent) to 1.0 (maximum volume).
|
||||
Note: Currently this has no effect on Mac OS X and Symbian.
|
||||
Note: Currently this has no effect on Mac OS X.
|
||||
*/
|
||||
|
||||
/*!
|
||||
|
||||
@@ -8,7 +8,7 @@ QT = core network gui
|
||||
CONFIG += module
|
||||
MODULE_PRI += ../../modules/qt_multimediakit.pri
|
||||
|
||||
contains(QT_CONFIG, opengl) | contains(QT_CONFIG, opengles2): !symbian {
|
||||
contains(QT_CONFIG, opengl) | contains(QT_CONFIG, opengles2) {
|
||||
QT += opengl
|
||||
} else {
|
||||
DEFINES += QT_NO_OPENGL
|
||||
@@ -167,14 +167,6 @@ mac {
|
||||
LIBS += -framework AppKit -framework QuartzCore -framework QTKit
|
||||
}
|
||||
|
||||
maemo5 {
|
||||
isEqual(QT_ARCH,armv6):QMAKE_CXXFLAGS += -march=armv7a -mcpu=cortex-a8 -mfloat-abi=softfp -mfpu=neon
|
||||
HEADERS += qxvideosurface_maemo5_p.h
|
||||
SOURCES += qxvideosurface_maemo5.cpp
|
||||
SOURCES += qgraphicsvideoitem_maemo5.cpp
|
||||
LIBS += -lXv -lX11 -lXext
|
||||
}
|
||||
|
||||
maemo6 {
|
||||
isEqual(QT_ARCH,armv6) {
|
||||
HEADERS += qeglimagetexturesurface_p.h
|
||||
@@ -188,30 +180,9 @@ maemo6 {
|
||||
}
|
||||
}
|
||||
|
||||
symbian {
|
||||
contains(surfaces_s60_enabled, yes) {
|
||||
SOURCES += qgraphicsvideoitem_symbian.cpp
|
||||
} else {
|
||||
SOURCES += qgraphicsvideoitem_overlay.cpp
|
||||
}
|
||||
}
|
||||
|
||||
!maemo*:!symbian {
|
||||
!maemo* {
|
||||
SOURCES += qgraphicsvideoitem.cpp
|
||||
}
|
||||
|
||||
HEADERS += $$PUBLIC_HEADERS $$PRIVATE_HEADERS
|
||||
|
||||
symbian {
|
||||
contains(S60_VERSION, 5.1) |contains (S60_VERSION, 3.2) | contains(S60_VERSION, 3.1): DEFINES += PRE_S60_52_PLATFORM
|
||||
load(data_caging_paths)
|
||||
QtMediaDeployment.sources = QtMultimediaKit.dll
|
||||
QtMediaDeployment.path = /sys/bin
|
||||
DEPLOYMENT += QtMediaDeployment
|
||||
TARGET.UID3=0x2002AC77
|
||||
TARGET.CAPABILITY = ALL -TCB
|
||||
LIBS += -lefsrv
|
||||
}
|
||||
|
||||
# CONFIG += middleware
|
||||
# include(../../features/deploy.pri)
|
||||
|
||||
@@ -1,647 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtCore/qpointer.h>
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qbasictimer.h>
|
||||
#include <QtCore/qcoreevent.h>
|
||||
#include <QtGui/qgraphicsscene.h>
|
||||
#include <QtGui/qgraphicsview.h>
|
||||
#include <QtGui/qscrollbar.h>
|
||||
#include <QtGui/qx11info_x11.h>
|
||||
|
||||
#include "qgraphicsvideoitem.h"
|
||||
|
||||
#include <qmediaobject.h>
|
||||
#include <qmediaservice.h>
|
||||
#include <qpaintervideosurface_p.h>
|
||||
#include <qvideorenderercontrol.h>
|
||||
|
||||
#include <qvideosurfaceformat.h>
|
||||
|
||||
#include "qxvideosurface_maemo5_p.h"
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
//#define DEBUG_GFX_VIDEO_ITEM
|
||||
|
||||
//update overlay geometry slightly later,
|
||||
//to ensure color key is alredy replaced with static frame
|
||||
#define GEOMETRY_UPDATE_DELAY 20
|
||||
//this is necessary to prevent flickering, see maemo bug 8798
|
||||
//on geometry changes, the color key is replaced with static image frame
|
||||
//until the overlay is re-initialized
|
||||
#define SOFTWARE_RENDERING_DURATION 150
|
||||
|
||||
#ifdef __ARM_NEON__
|
||||
|
||||
/*
|
||||
* ARM NEON optimized implementation of UYVY -> RGB16 convertor
|
||||
*/
|
||||
static void uyvy422_to_rgb16_line_neon (uint8_t * dst, const uint8_t * src, int n)
|
||||
{
|
||||
/* and this is the NEON code itself */
|
||||
static __attribute__ ((aligned (16))) uint16_t acc_r[8] = {
|
||||
22840, 22840, 22840, 22840, 22840, 22840, 22840, 22840,
|
||||
};
|
||||
static __attribute__ ((aligned (16))) uint16_t acc_g[8] = {
|
||||
17312, 17312, 17312, 17312, 17312, 17312, 17312, 17312,
|
||||
};
|
||||
static __attribute__ ((aligned (16))) uint16_t acc_b[8] = {
|
||||
28832, 28832, 28832, 28832, 28832, 28832, 28832, 28832,
|
||||
};
|
||||
/*
|
||||
* Registers:
|
||||
* q0, q1 : d0, d1, d2, d3 - are used for initial loading of YUV data
|
||||
* q2 : d4, d5 - are used for storing converted RGB data
|
||||
* q3 : d6, d7 - are used for temporary storage
|
||||
*
|
||||
* q6 : d12, d13 - are used for converting to RGB16
|
||||
* q7 : d14, d15 - are used for storing RGB16 data
|
||||
* q4-q5 - reserved
|
||||
*
|
||||
* q8, q9 : d16, d17, d18, d19 - are used for expanded Y data
|
||||
* q10 : d20, d21
|
||||
* q11 : d22, d23
|
||||
* q12 : d24, d25
|
||||
* q13 : d26, d27
|
||||
* q13, q14, q15 - various constants (#16, #149, #204, #50, #104, #154)
|
||||
*/
|
||||
asm volatile (".macro convert_macroblock size\n"
|
||||
/* load up to 16 source pixels in UYVY format */
|
||||
".if \\size == 16\n"
|
||||
"pld [%[src], #128]\n"
|
||||
"vld1.32 {d0, d1, d2, d3}, [%[src]]!\n"
|
||||
".elseif \\size == 8\n"
|
||||
"vld1.32 {d0, d1}, [%[src]]!\n"
|
||||
".elseif \\size == 4\n"
|
||||
"vld1.32 {d0}, [%[src]]!\n"
|
||||
".elseif \\size == 2\n"
|
||||
"vld1.32 {d0[0]}, [%[src]]!\n"
|
||||
".else\n" ".error \"unsupported macroblock size\"\n" ".endif\n"
|
||||
/* convert from 'packed' to 'planar' representation */
|
||||
"vuzp.8 d0, d1\n" /* d1 - separated Y data (first 8 bytes) */
|
||||
"vuzp.8 d2, d3\n" /* d3 - separated Y data (next 8 bytes) */
|
||||
"vuzp.8 d0, d2\n" /* d0 - separated U data, d2 - separated V data */
|
||||
/* split even and odd Y color components */
|
||||
"vuzp.8 d1, d3\n" /* d1 - evenY, d3 - oddY */
|
||||
/* clip upper and lower boundaries */
|
||||
"vqadd.u8 q0, q0, q4\n"
|
||||
"vqadd.u8 q1, q1, q4\n"
|
||||
"vqsub.u8 q0, q0, q5\n"
|
||||
"vqsub.u8 q1, q1, q5\n"
|
||||
"vshr.u8 d4, d2, #1\n" /* d4 = V >> 1 */
|
||||
"vmull.u8 q8, d1, d27\n" /* q8 = evenY * 149 */
|
||||
"vmull.u8 q9, d3, d27\n" /* q9 = oddY * 149 */
|
||||
"vld1.16 {d20, d21}, [%[acc_r], :128]\n" /* q10 - initialize accumulator for red */
|
||||
"vsubw.u8 q10, q10, d4\n" /* red acc -= (V >> 1) */
|
||||
"vmlsl.u8 q10, d2, d28\n" /* red acc -= V * 204 */
|
||||
"vld1.16 {d22, d23}, [%[acc_g], :128]\n" /* q11 - initialize accumulator for green */
|
||||
"vmlsl.u8 q11, d2, d30\n" /* green acc -= V * 104 */
|
||||
"vmlsl.u8 q11, d0, d29\n" /* green acc -= U * 50 */
|
||||
"vld1.16 {d24, d25}, [%[acc_b], :128]\n" /* q12 - initialize accumulator for blue */
|
||||
"vmlsl.u8 q12, d0, d30\n" /* blue acc -= U * 104 */
|
||||
"vmlsl.u8 q12, d0, d31\n" /* blue acc -= U * 154 */
|
||||
"vhsub.s16 q3, q8, q10\n" /* calculate even red components */
|
||||
"vhsub.s16 q10, q9, q10\n" /* calculate odd red components */
|
||||
"vqshrun.s16 d0, q3, #6\n" /* right shift, narrow and saturate even red components */
|
||||
"vqshrun.s16 d3, q10, #6\n" /* right shift, narrow and saturate odd red components */
|
||||
"vhadd.s16 q3, q8, q11\n" /* calculate even green components */
|
||||
"vhadd.s16 q11, q9, q11\n" /* calculate odd green components */
|
||||
"vqshrun.s16 d1, q3, #6\n" /* right shift, narrow and saturate even green components */
|
||||
"vqshrun.s16 d4, q11, #6\n" /* right shift, narrow and saturate odd green components */
|
||||
"vhsub.s16 q3, q8, q12\n" /* calculate even blue components */
|
||||
"vhsub.s16 q12, q9, q12\n" /* calculate odd blue components */
|
||||
"vqshrun.s16 d2, q3, #6\n" /* right shift, narrow and saturate even blue components */
|
||||
"vqshrun.s16 d5, q12, #6\n" /* right shift, narrow and saturate odd blue components */
|
||||
"vzip.8 d0, d3\n" /* join even and odd red components */
|
||||
"vzip.8 d1, d4\n" /* join even and odd green components */
|
||||
"vzip.8 d2, d5\n" /* join even and odd blue components */
|
||||
"vshll.u8 q7, d0, #8\n" //red
|
||||
"vshll.u8 q6, d1, #8\n" //greed
|
||||
"vsri.u16 q7, q6, #5\n"
|
||||
"vshll.u8 q6, d2, #8\n" //blue
|
||||
"vsri.u16 q7, q6, #11\n" //now there is rgb16 in q7
|
||||
".if \\size == 16\n"
|
||||
"vst1.16 {d14, d15}, [%[dst]]!\n"
|
||||
//"vst3.8 {d0, d1, d2}, [%[dst]]!\n"
|
||||
"vshll.u8 q7, d3, #8\n" //red
|
||||
"vshll.u8 q6, d4, #8\n" //greed
|
||||
"vsri.u16 q7, q6, #5\n"
|
||||
"vshll.u8 q6, d5, #8\n" //blue
|
||||
"vsri.u16 q7, q6, #11\n" //now there is rgb16 in q7
|
||||
//"vst3.8 {d3, d4, d5}, [%[dst]]!\n"
|
||||
"vst1.16 {d14, d15}, [%[dst]]!\n"
|
||||
".elseif \\size == 8\n"
|
||||
"vst1.16 {d14, d15}, [%[dst]]!\n"
|
||||
//"vst3.8 {d0, d1, d2}, [%[dst]]!\n"
|
||||
".elseif \\size == 4\n"
|
||||
"vst1.8 {d14}, [%[dst]]!\n"
|
||||
".elseif \\size == 2\n"
|
||||
"vst1.8 {d14[0]}, [%[dst]]!\n"
|
||||
"vst1.8 {d14[1]}, [%[dst]]!\n"
|
||||
".else\n"
|
||||
".error \"unsupported macroblock size\"\n"
|
||||
".endif\n"
|
||||
".endm\n"
|
||||
"vmov.u8 d8, #15\n" /* add this to U/V to saturate upper boundary */
|
||||
"vmov.u8 d9, #20\n" /* add this to Y to saturate upper boundary */
|
||||
"vmov.u8 d10, #31\n" /* sub this from U/V to saturate lower boundary */
|
||||
"vmov.u8 d11, #36\n" /* sub this from Y to saturate lower boundary */
|
||||
"vmov.u8 d26, #16\n"
|
||||
"vmov.u8 d27, #149\n"
|
||||
"vmov.u8 d28, #204\n"
|
||||
"vmov.u8 d29, #50\n"
|
||||
"vmov.u8 d30, #104\n"
|
||||
"vmov.u8 d31, #154\n"
|
||||
"subs %[n], %[n], #16\n"
|
||||
"blt 2f\n"
|
||||
"1:\n"
|
||||
"convert_macroblock 16\n"
|
||||
"subs %[n], %[n], #16\n"
|
||||
"bge 1b\n"
|
||||
"2:\n"
|
||||
"tst %[n], #8\n"
|
||||
"beq 3f\n"
|
||||
"convert_macroblock 8\n"
|
||||
"3:\n"
|
||||
"tst %[n], #4\n"
|
||||
"beq 4f\n"
|
||||
"convert_macroblock 4\n"
|
||||
"4:\n"
|
||||
"tst %[n], #2\n"
|
||||
"beq 5f\n"
|
||||
"convert_macroblock 2\n"
|
||||
"5:\n"
|
||||
".purgem convert_macroblock\n":[src] "+&r" (src),[dst] "+&r" (dst),
|
||||
[n] "+&r" (n)
|
||||
:[acc_r] "r" (&acc_r[0]),[acc_g] "r" (&acc_g[0]),[acc_b] "r" (&acc_b[0])
|
||||
:"cc", "memory", "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10", "d11", "d12", "d13", "d14", "d15",
|
||||
"d16", "d17", "d18", "d19", "d20", "d21", "d22", "d23",
|
||||
"d24", "d25", "d26", "d27", "d28", "d29", "d30", "d31");
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
class QGraphicsVideoItemPrivate
|
||||
{
|
||||
public:
|
||||
QGraphicsVideoItemPrivate()
|
||||
: q_ptr(0)
|
||||
, surface(0)
|
||||
, mediaObject(0)
|
||||
, service(0)
|
||||
, rendererControl(0)
|
||||
, savedViewportUpdateMode(QGraphicsView::FullViewportUpdate)
|
||||
, aspectRatioMode(Qt::KeepAspectRatio)
|
||||
, rect(0.0, 0.0, 320, 240)
|
||||
, softwareRenderingEnabled(false)
|
||||
{
|
||||
}
|
||||
|
||||
QGraphicsVideoItem *q_ptr;
|
||||
|
||||
QXVideoSurface *surface;
|
||||
QMediaObject *mediaObject;
|
||||
QMediaService *service;
|
||||
QVideoRendererControl *rendererControl;
|
||||
QPointer<QGraphicsView> currentView;
|
||||
QGraphicsView::ViewportUpdateMode savedViewportUpdateMode;
|
||||
|
||||
Qt::AspectRatioMode aspectRatioMode;
|
||||
QRectF rect;
|
||||
QRectF boundingRect;
|
||||
QRectF sourceRect;
|
||||
QSizeF nativeSize;
|
||||
|
||||
QPixmap lastFrame;
|
||||
QBasicTimer softwareRenderingTimer;
|
||||
QBasicTimer geometryUpdateTimer;
|
||||
bool softwareRenderingEnabled;
|
||||
QRect overlayRect;
|
||||
|
||||
void clearService();
|
||||
void updateRects();
|
||||
void updateLastFrame();
|
||||
|
||||
void _q_present();
|
||||
void _q_updateNativeSize();
|
||||
void _q_serviceDestroyed();
|
||||
void _q_mediaObjectDestroyed();
|
||||
};
|
||||
|
||||
void QGraphicsVideoItemPrivate::clearService()
|
||||
{
|
||||
if (rendererControl) {
|
||||
surface->stop();
|
||||
rendererControl->setSurface(0);
|
||||
service->releaseControl(rendererControl);
|
||||
rendererControl = 0;
|
||||
}
|
||||
|
||||
if (service) {
|
||||
QObject::disconnect(service, SIGNAL(destroyed()), q_ptr, SLOT(_q_serviceDestroyed()));
|
||||
service = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::updateRects()
|
||||
{
|
||||
q_ptr->prepareGeometryChange();
|
||||
|
||||
if (nativeSize.isEmpty()) {
|
||||
boundingRect = QRectF();
|
||||
} else if (aspectRatioMode == Qt::IgnoreAspectRatio) {
|
||||
boundingRect = rect;
|
||||
sourceRect = QRectF(0, 0, 1, 1);
|
||||
} else if (aspectRatioMode == Qt::KeepAspectRatio) {
|
||||
QSizeF size = nativeSize;
|
||||
size.scale(rect.size(), Qt::KeepAspectRatio);
|
||||
|
||||
boundingRect = QRectF(0, 0, size.width(), size.height());
|
||||
boundingRect.moveCenter(rect.center());
|
||||
|
||||
sourceRect = QRectF(0, 0, 1, 1);
|
||||
} else if (aspectRatioMode == Qt::KeepAspectRatioByExpanding) {
|
||||
boundingRect = rect;
|
||||
|
||||
QSizeF size = rect.size();
|
||||
size.scale(nativeSize, Qt::KeepAspectRatio);
|
||||
|
||||
sourceRect = QRectF(
|
||||
0, 0, size.width() / nativeSize.width(), size.height() / nativeSize.height());
|
||||
sourceRect.moveCenter(QPointF(0.5, 0.5));
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::updateLastFrame()
|
||||
{
|
||||
lastFrame = QPixmap();
|
||||
|
||||
if (!softwareRenderingEnabled)
|
||||
return;
|
||||
|
||||
QVideoFrame lastVideoFrame = surface->lastFrame();
|
||||
|
||||
if (!lastVideoFrame.isValid())
|
||||
return;
|
||||
|
||||
if (lastVideoFrame.map(QAbstractVideoBuffer::ReadOnly)) {
|
||||
|
||||
#ifdef __ARM_NEON__
|
||||
if (lastVideoFrame.pixelFormat() == QVideoFrame::Format_UYVY) {
|
||||
QImage lastImage(lastVideoFrame.size(), QImage::Format_RGB16);
|
||||
|
||||
const uchar *src = lastVideoFrame.bits();
|
||||
uchar *dst = lastImage.bits();
|
||||
const int srcLineStep = lastVideoFrame.bytesPerLine();
|
||||
const int dstLineStep = lastImage.bytesPerLine();
|
||||
const int h = lastVideoFrame.height();
|
||||
const int w = lastVideoFrame.width();
|
||||
|
||||
for (int y=0; y<h; y++) {
|
||||
uyvy422_to_rgb16_line_neon(dst, src, w);
|
||||
src += srcLineStep;
|
||||
dst += dstLineStep;
|
||||
}
|
||||
lastFrame = QPixmap::fromImage(
|
||||
lastImage.scaled(boundingRect.size().toSize(), Qt::IgnoreAspectRatio, Qt::FastTransformation));
|
||||
} else
|
||||
#endif
|
||||
{
|
||||
QImage::Format imgFormat = QVideoFrame::imageFormatFromPixelFormat(lastVideoFrame.pixelFormat());
|
||||
|
||||
if (imgFormat != QImage::Format_Invalid) {
|
||||
QImage lastImage(lastVideoFrame.bits(),
|
||||
lastVideoFrame.width(),
|
||||
lastVideoFrame.height(),
|
||||
lastVideoFrame.bytesPerLine(),
|
||||
imgFormat);
|
||||
|
||||
lastFrame = QPixmap::fromImage(
|
||||
lastImage.scaled(boundingRect.size().toSize(), Qt::IgnoreAspectRatio, Qt::FastTransformation));
|
||||
}
|
||||
}
|
||||
|
||||
lastVideoFrame.unmap();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_present()
|
||||
{
|
||||
q_ptr->update(boundingRect);
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_updateNativeSize()
|
||||
{
|
||||
const QSize &size = surface->surfaceFormat().sizeHint();
|
||||
if (nativeSize != size) {
|
||||
lastFrame = QPixmap();
|
||||
nativeSize = size;
|
||||
|
||||
updateRects();
|
||||
emit q_ptr->nativeSizeChanged(nativeSize);
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_serviceDestroyed()
|
||||
{
|
||||
rendererControl = 0;
|
||||
service = 0;
|
||||
|
||||
surface->stop();
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_mediaObjectDestroyed()
|
||||
{
|
||||
mediaObject = 0;
|
||||
|
||||
clearService();
|
||||
}
|
||||
|
||||
QGraphicsVideoItem::QGraphicsVideoItem(QGraphicsItem *parent)
|
||||
: QGraphicsObject(parent)
|
||||
, d_ptr(new QGraphicsVideoItemPrivate)
|
||||
{
|
||||
d_ptr->q_ptr = this;
|
||||
d_ptr->surface = new QXVideoSurface;
|
||||
|
||||
setCacheMode(NoCache);
|
||||
setFlag(QGraphicsItem::ItemIgnoresParentOpacity);
|
||||
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
|
||||
setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
|
||||
|
||||
connect(d_ptr->surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)),
|
||||
this, SLOT(_q_updateNativeSize()));
|
||||
|
||||
connect(d_ptr->surface, SIGNAL(activeChanged(bool)), this, SLOT(_q_present()));
|
||||
}
|
||||
|
||||
QGraphicsVideoItem::~QGraphicsVideoItem()
|
||||
{
|
||||
if (d_ptr->rendererControl) {
|
||||
d_ptr->rendererControl->setSurface(0);
|
||||
d_ptr->service->releaseControl(d_ptr->rendererControl);
|
||||
}
|
||||
|
||||
if (d_ptr->currentView)
|
||||
d_ptr->currentView->setViewportUpdateMode(d_ptr->savedViewportUpdateMode);
|
||||
|
||||
delete d_ptr->surface;
|
||||
delete d_ptr;
|
||||
}
|
||||
|
||||
QMediaObject *QGraphicsVideoItem::mediaObject() const
|
||||
{
|
||||
return d_func()->mediaObject;
|
||||
}
|
||||
|
||||
bool QGraphicsVideoItem::setMediaObject(QMediaObject *object)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
if (object == d->mediaObject)
|
||||
return true;
|
||||
|
||||
d->clearService();
|
||||
|
||||
d->mediaObject = object;
|
||||
|
||||
if (d->mediaObject) {
|
||||
d->service = d->mediaObject->service();
|
||||
|
||||
if (d->service) {
|
||||
d->rendererControl = qobject_cast<QVideoRendererControl *>(
|
||||
d->service->requestControl(QVideoRendererControl_iid));
|
||||
|
||||
if (d->rendererControl != 0) {
|
||||
connect(d->service, SIGNAL(destroyed()), this, SLOT(_q_serviceDestroyed()));
|
||||
d->rendererControl->setSurface(d->surface);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::AspectRatioMode QGraphicsVideoItem::aspectRatioMode() const
|
||||
{
|
||||
return d_func()->aspectRatioMode;
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::setAspectRatioMode(Qt::AspectRatioMode mode)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
d->aspectRatioMode = mode;
|
||||
d->updateRects();
|
||||
}
|
||||
|
||||
QPointF QGraphicsVideoItem::offset() const
|
||||
{
|
||||
return d_func()->rect.topLeft();
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::setOffset(const QPointF &offset)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
d->rect.moveTo(offset);
|
||||
d->updateRects();
|
||||
}
|
||||
|
||||
QSizeF QGraphicsVideoItem::size() const
|
||||
{
|
||||
return d_func()->rect.size();
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::setSize(const QSizeF &size)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
d->rect.setSize(size.isValid() ? size : QSizeF(0, 0));
|
||||
d->updateRects();
|
||||
}
|
||||
|
||||
QSizeF QGraphicsVideoItem::nativeSize() const
|
||||
{
|
||||
return d_func()->nativeSize;
|
||||
}
|
||||
|
||||
QRectF QGraphicsVideoItem::boundingRect() const
|
||||
{
|
||||
return d_func()->boundingRect;
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::paint(
|
||||
QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
#ifdef DEBUG_GFX_VIDEO_ITEM
|
||||
qDebug() << "QGraphicsVideoItem::paint";
|
||||
#endif
|
||||
|
||||
Q_UNUSED(option);
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
QGraphicsView *view = 0;
|
||||
if (scene() && !scene()->views().isEmpty())
|
||||
view = scene()->views().first();
|
||||
|
||||
//it's necessary to switch vieport update mode to FullViewportUpdate
|
||||
//otherwise the video item area can be just scrolled without notifying overlay
|
||||
//about geometry changes
|
||||
if (view != d->currentView) {
|
||||
if (d->currentView) {
|
||||
d->currentView->setViewportUpdateMode(d->savedViewportUpdateMode);
|
||||
}
|
||||
|
||||
d->currentView = view;
|
||||
if (view) {
|
||||
d->savedViewportUpdateMode = view->viewportUpdateMode();
|
||||
view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
|
||||
}
|
||||
}
|
||||
|
||||
QColor colorKey = Qt::black;
|
||||
bool geometryChanged = false;
|
||||
|
||||
if (d->surface) {
|
||||
if (widget)
|
||||
d->surface->setWinId(widget->winId());
|
||||
|
||||
QTransform transform = painter->combinedTransform();
|
||||
QRect overlayRect = transform.mapRect(boundingRect()).toRect();
|
||||
QRect currentSurfaceRect = d->surface->displayRect();
|
||||
|
||||
if (widget) {
|
||||
//workaround for xvideo issue with U/V planes swapped
|
||||
QPoint topLeft = widget->mapToGlobal(overlayRect.topLeft());
|
||||
if ((topLeft.x() & 1) == 0 && topLeft.x() != 0)
|
||||
overlayRect.moveLeft(overlayRect.left()-1);
|
||||
}
|
||||
|
||||
d->overlayRect = overlayRect;
|
||||
|
||||
if (currentSurfaceRect != overlayRect) {
|
||||
if (!d->surface->displayRect().isEmpty()) {
|
||||
if (d->softwareRenderingEnabled) {
|
||||
//recalculate scaled frame pixmap if area is resized
|
||||
if (currentSurfaceRect.size() != overlayRect.size()) {
|
||||
d->updateLastFrame();
|
||||
d->surface->setDisplayRect( overlayRect );
|
||||
}
|
||||
} else {
|
||||
d->softwareRenderingEnabled = true;
|
||||
d->updateLastFrame();
|
||||
|
||||
//don't set new geometry right now,
|
||||
//but with small delay, to ensure the frame is already
|
||||
//rendered on top of color key
|
||||
if (!d->geometryUpdateTimer.isActive())
|
||||
d->geometryUpdateTimer.start(GEOMETRY_UPDATE_DELAY, this);
|
||||
}
|
||||
} else
|
||||
d->surface->setDisplayRect( overlayRect );
|
||||
|
||||
geometryChanged = true;
|
||||
d->softwareRenderingTimer.start(SOFTWARE_RENDERING_DURATION, this);
|
||||
|
||||
#ifdef DEBUG_GFX_VIDEO_ITEM
|
||||
qDebug() << "set video display rect:" << overlayRect;
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
colorKey = d->surface->colorKey();
|
||||
}
|
||||
|
||||
|
||||
if (!d->softwareRenderingEnabled) {
|
||||
painter->fillRect(d->boundingRect, colorKey);
|
||||
} else {
|
||||
if (!d->lastFrame.isNull()) {
|
||||
painter->drawPixmap(d->boundingRect.topLeft(), d->lastFrame );
|
||||
|
||||
} else
|
||||
painter->fillRect(d->boundingRect, Qt::black);
|
||||
}
|
||||
}
|
||||
|
||||
QVariant QGraphicsVideoItem::itemChange(GraphicsItemChange change, const QVariant &value)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
if (change == ItemScenePositionHasChanged) {
|
||||
update(boundingRect());
|
||||
} else {
|
||||
return QGraphicsItem::itemChange(change, value);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
if (event->timerId() == d->softwareRenderingTimer.timerId() && d->softwareRenderingEnabled) {
|
||||
d->softwareRenderingTimer.stop();
|
||||
d->softwareRenderingEnabled = false;
|
||||
d->updateLastFrame();
|
||||
// repaint last frame, to ensure geometry change is applyed in paused state
|
||||
d->surface->repaintLastFrame();
|
||||
d->_q_present();
|
||||
} else if ((event->timerId() == d->geometryUpdateTimer.timerId())) {
|
||||
d->geometryUpdateTimer.stop();
|
||||
//slightly delayed geometry update,
|
||||
//to avoid flicker at the first geometry change
|
||||
d->surface->setDisplayRect( d->overlayRect );
|
||||
}
|
||||
|
||||
QGraphicsObject::timerEvent(event);
|
||||
}
|
||||
|
||||
#include "moc_qgraphicsvideoitem.cpp"
|
||||
QT_END_NAMESPACE
|
||||
@@ -1,436 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtCore/qpointer.h>
|
||||
#include <QtCore/qdatetime.h>
|
||||
#include <QtCore/qbasictimer.h>
|
||||
#include <QtCore/qcoreevent.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtGui/qgraphicsscene.h>
|
||||
#include <QtGui/qgraphicsview.h>
|
||||
#include <QtGui/qscrollbar.h>
|
||||
#include <QtGui/qx11info_x11.h>
|
||||
|
||||
#include "qgraphicsvideoitem.h"
|
||||
|
||||
#ifdef Q_OS_SYMBIAN
|
||||
#define QGRAPHICSVIDEOITEM_ROTATION_SUPPORT
|
||||
#endif
|
||||
|
||||
#include <qmediaobject.h>
|
||||
#include <qmediaservice.h>
|
||||
#include <qvideowindowcontrol.h>
|
||||
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
#define DEBUG_GFX_VIDEO_ITEM
|
||||
|
||||
class QGraphicsVideoItemPrivate : public QObject
|
||||
{
|
||||
public:
|
||||
QGraphicsVideoItemPrivate()
|
||||
: q_ptr(0)
|
||||
, mediaObject(0)
|
||||
, service(0)
|
||||
, windowControl(0)
|
||||
, savedViewportUpdateMode(QGraphicsView::FullViewportUpdate)
|
||||
, aspectRatioMode(Qt::KeepAspectRatio)
|
||||
, rect(0.0, 0.0, 320, 240)
|
||||
, videoWidget(0)
|
||||
{
|
||||
}
|
||||
|
||||
QGraphicsVideoItem *q_ptr;
|
||||
|
||||
QMediaObject *mediaObject;
|
||||
QMediaService *service;
|
||||
QVideoWindowControl *windowControl;
|
||||
QPointer<QGraphicsView> currentView;
|
||||
QList<QPointer<QObject> > eventFilterTargets;
|
||||
QGraphicsView::ViewportUpdateMode savedViewportUpdateMode;
|
||||
|
||||
Qt::AspectRatioMode aspectRatioMode;
|
||||
QRectF rect;
|
||||
QRectF boundingRect;
|
||||
QRectF displayRect;
|
||||
QSizeF nativeSize;
|
||||
|
||||
QWidget *videoWidget;
|
||||
|
||||
bool eventFilter(QObject *object, QEvent *event);
|
||||
void updateEventFilters();
|
||||
|
||||
void setWidget(QWidget *widget);
|
||||
void clearService();
|
||||
void updateRects();
|
||||
void updateLastFrame();
|
||||
|
||||
void _q_present();
|
||||
void _q_updateNativeSize();
|
||||
void _q_serviceDestroyed();
|
||||
void _q_mediaObjectDestroyed();
|
||||
};
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_present()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool QGraphicsVideoItemPrivate::eventFilter(QObject *object, QEvent *event)
|
||||
{
|
||||
if (windowControl && object == videoWidget && QEvent::WinIdChange == event->type()) {
|
||||
windowControl->setWinId(videoWidget->effectiveWinId());
|
||||
} else {
|
||||
bool updateEventFiltersRequired = false;
|
||||
bool refreshDisplayRequired = false;
|
||||
foreach (QPointer<QObject> target, eventFilterTargets) {
|
||||
if (object == target.data()) {
|
||||
switch (event->type()) {
|
||||
case QEvent::ParentChange:
|
||||
updateEventFiltersRequired = true;
|
||||
refreshDisplayRequired = true;
|
||||
break;
|
||||
case QEvent::Move:
|
||||
case QEvent::Resize:
|
||||
refreshDisplayRequired = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updateEventFiltersRequired)
|
||||
updateEventFilters();
|
||||
#ifdef Q_OS_SYMBIAN
|
||||
if (refreshDisplayRequired && windowControl)
|
||||
QMetaObject::invokeMethod(windowControl, "refreshDisplay");
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::setWidget(QWidget *widget)
|
||||
{
|
||||
if (videoWidget != widget) {
|
||||
videoWidget = widget;
|
||||
if (widget) {
|
||||
windowControl->setWinId(widget->winId());
|
||||
widget->installEventFilter(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::clearService()
|
||||
{
|
||||
if (windowControl) {
|
||||
QObject::disconnect(windowControl, SIGNAL(nativeSizeChanged()), q_ptr, SLOT(_q_updateNativeSize()));
|
||||
service->releaseControl(windowControl);
|
||||
windowControl = 0;
|
||||
}
|
||||
|
||||
if (service) {
|
||||
QObject::disconnect(service, SIGNAL(destroyed()), q_ptr, SLOT(_q_serviceDestroyed()));
|
||||
service = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::updateRects()
|
||||
{
|
||||
q_ptr->prepareGeometryChange();
|
||||
QSizeF videoSize;
|
||||
if (nativeSize.isEmpty()) {
|
||||
videoSize = rect.size();
|
||||
} else if (aspectRatioMode == Qt::IgnoreAspectRatio) {
|
||||
videoSize = rect.size();
|
||||
} else {
|
||||
// KeepAspectRatio or KeepAspectRatioByExpanding
|
||||
videoSize = nativeSize;
|
||||
videoSize.scale(rect.size(), aspectRatioMode);
|
||||
}
|
||||
displayRect = QRectF(QPointF(0, 0), videoSize);
|
||||
displayRect.moveCenter(rect.center());
|
||||
boundingRect = displayRect.intersected(rect);
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::updateLastFrame()
|
||||
{
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::updateEventFilters()
|
||||
{
|
||||
// In order to determine when the absolute screen position of the item
|
||||
// changes, we need to receive move events sent to m_currentView
|
||||
// or any of its ancestors.
|
||||
foreach (QPointer<QObject> target, eventFilterTargets)
|
||||
if (target)
|
||||
target->removeEventFilter(this);
|
||||
eventFilterTargets.clear();
|
||||
QObject *target = currentView;
|
||||
while (target) {
|
||||
target->installEventFilter(this);
|
||||
eventFilterTargets.append(target);
|
||||
target = target->parent();
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_updateNativeSize()
|
||||
{
|
||||
const QSize size = windowControl->nativeSize();
|
||||
if (nativeSize != size) {
|
||||
nativeSize = size;
|
||||
|
||||
updateRects();
|
||||
emit q_ptr->nativeSizeChanged(nativeSize);
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_serviceDestroyed()
|
||||
{
|
||||
windowControl = 0;
|
||||
service = 0;
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_mediaObjectDestroyed()
|
||||
{
|
||||
mediaObject = 0;
|
||||
|
||||
clearService();
|
||||
}
|
||||
|
||||
QGraphicsVideoItem::QGraphicsVideoItem(QGraphicsItem *parent)
|
||||
: QGraphicsObject(parent)
|
||||
, d_ptr(new QGraphicsVideoItemPrivate)
|
||||
{
|
||||
d_ptr->q_ptr = this;
|
||||
|
||||
setCacheMode(NoCache);
|
||||
setFlag(QGraphicsItem::ItemIgnoresParentOpacity);
|
||||
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
|
||||
setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
|
||||
}
|
||||
|
||||
QGraphicsVideoItem::~QGraphicsVideoItem()
|
||||
{
|
||||
if (d_ptr->windowControl) {
|
||||
d_ptr->service->releaseControl(d_ptr->windowControl);
|
||||
}
|
||||
|
||||
if (d_ptr->currentView)
|
||||
d_ptr->currentView->setViewportUpdateMode(d_ptr->savedViewportUpdateMode);
|
||||
|
||||
delete d_ptr;
|
||||
}
|
||||
|
||||
QMediaObject *QGraphicsVideoItem::mediaObject() const
|
||||
{
|
||||
return d_func()->mediaObject;
|
||||
}
|
||||
|
||||
bool QGraphicsVideoItem::setMediaObject(QMediaObject *object)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
if (object == d->mediaObject)
|
||||
return true;
|
||||
|
||||
d->clearService();
|
||||
|
||||
d->mediaObject = object;
|
||||
|
||||
if (d->mediaObject) {
|
||||
d->service = d->mediaObject->service();
|
||||
|
||||
if (d->service) {
|
||||
d->windowControl = qobject_cast<QVideoWindowControl *>(
|
||||
d->service->requestControl(QVideoWindowControl_iid));
|
||||
|
||||
if (d->windowControl != 0) {
|
||||
connect(d->service, SIGNAL(destroyed()), SLOT(_q_serviceDestroyed()));
|
||||
connect(d->windowControl, SIGNAL(nativeSizeChanged()), SLOT(_q_updateNativeSize()));
|
||||
d->windowControl->setAspectRatioMode(Qt::IgnoreAspectRatio);
|
||||
//d->windowControl->setProperty("colorKey", QVariant(QColor(16,7,2)));
|
||||
d->windowControl->setProperty("autopaintColorKey", QVariant(false));
|
||||
|
||||
d->updateRects();
|
||||
return true;
|
||||
} else {
|
||||
qWarning() << "Service doesn't support QVideoWindowControl, overlay item failed";
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
d->mediaObject = 0;
|
||||
return false;
|
||||
}
|
||||
|
||||
Qt::AspectRatioMode QGraphicsVideoItem::aspectRatioMode() const
|
||||
{
|
||||
return d_func()->aspectRatioMode;
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::setAspectRatioMode(Qt::AspectRatioMode mode)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
d->aspectRatioMode = mode;
|
||||
d->updateRects();
|
||||
}
|
||||
|
||||
QPointF QGraphicsVideoItem::offset() const
|
||||
{
|
||||
return d_func()->rect.topLeft();
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::setOffset(const QPointF &offset)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
d->rect.moveTo(offset);
|
||||
d->updateRects();
|
||||
}
|
||||
|
||||
QSizeF QGraphicsVideoItem::size() const
|
||||
{
|
||||
return d_func()->rect.size();
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::setSize(const QSizeF &size)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
d->rect.setSize(size.isValid() ? size : QSizeF(0, 0));
|
||||
d->updateRects();
|
||||
}
|
||||
|
||||
QSizeF QGraphicsVideoItem::nativeSize() const
|
||||
{
|
||||
return d_func()->nativeSize;
|
||||
}
|
||||
|
||||
QRectF QGraphicsVideoItem::boundingRect() const
|
||||
{
|
||||
return d_func()->boundingRect;
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::paint(
|
||||
QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
#ifdef DEBUG_GFX_VIDEO_ITEM
|
||||
qDebug() << "QGraphicsVideoItem::paint";
|
||||
#endif
|
||||
|
||||
Q_UNUSED(option);
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
QGraphicsView *view = 0;
|
||||
if (scene() && !scene()->views().isEmpty())
|
||||
view = scene()->views().first();
|
||||
|
||||
//it's necessary to switch vieport update mode to FullViewportUpdate
|
||||
//otherwise the video item area can be just scrolled without notifying overlay
|
||||
//about geometry changes
|
||||
if (view != d->currentView) {
|
||||
if (d->currentView) {
|
||||
d->currentView->setViewportUpdateMode(d->savedViewportUpdateMode);
|
||||
}
|
||||
|
||||
d->currentView = view;
|
||||
if (view) {
|
||||
d->savedViewportUpdateMode = view->viewportUpdateMode();
|
||||
view->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
|
||||
}
|
||||
d->updateEventFilters();
|
||||
}
|
||||
|
||||
QColor colorKey = Qt::black;
|
||||
|
||||
if (d->windowControl != 0 && widget != 0) {
|
||||
d->setWidget(widget);
|
||||
|
||||
QTransform transform = painter->combinedTransform();
|
||||
QRect overlayRect = transform.mapRect(d->displayRect).toRect();
|
||||
QRect currentSurfaceRect = d->windowControl->displayRect();
|
||||
|
||||
if (currentSurfaceRect != overlayRect) {
|
||||
#ifdef DEBUG_GFX_VIDEO_ITEM
|
||||
qDebug() << "set video display rect:" << overlayRect;
|
||||
#endif
|
||||
d->windowControl->setDisplayRect(overlayRect);
|
||||
}
|
||||
|
||||
colorKey = d->windowControl->property("colorKey").value<QColor>();
|
||||
#ifdef QGRAPHICSVIDEOITEM_ROTATION_SUPPORT
|
||||
const qreal angle = transform.map(QLineF(0, 0, 1, 0)).angle();
|
||||
d->windowControl->setProperty("rotation", QVariant::fromValue<qreal>(angle));
|
||||
#endif
|
||||
}
|
||||
|
||||
if (colorKey.alpha() != 255)
|
||||
painter->setCompositionMode(QPainter::CompositionMode_Source);
|
||||
painter->fillRect(d->boundingRect, colorKey);
|
||||
}
|
||||
|
||||
QVariant QGraphicsVideoItem::itemChange(GraphicsItemChange change, const QVariant &value)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
|
||||
switch (change) {
|
||||
case ItemScenePositionHasChanged:
|
||||
update(boundingRect());
|
||||
break;
|
||||
case ItemVisibleChange:
|
||||
//move overlay out of the screen if video item becomes invisible
|
||||
if (d->windowControl != 0 && !value.toBool())
|
||||
d->windowControl->setDisplayRect(QRect(-1,-1,1,1));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return QGraphicsItem::itemChange(change, value);
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
QGraphicsObject::timerEvent(event);
|
||||
}
|
||||
|
||||
#include "moc_qgraphicsvideoitem.cpp"
|
||||
QT_END_NAMESPACE
|
||||
@@ -1,604 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
|
||||
#include <QtCore/QDebug>
|
||||
#include <QtCore/QEvent>
|
||||
#include <QtCore/QPointer>
|
||||
#include <QtGui/QApplication>
|
||||
#include <QtGui/QGraphicsScene>
|
||||
#include <QtGui/QGraphicsView>
|
||||
|
||||
#include "qgraphicsvideoitem.h"
|
||||
|
||||
#include <qmediaobject.h>
|
||||
#include <qmediaservice.h>
|
||||
#include <qvideowidgetcontrol.h>
|
||||
|
||||
Q_DECLARE_METATYPE(WId)
|
||||
|
||||
static const QEvent::Type UpdateViewportTransparencyEvent =
|
||||
static_cast<QEvent::Type>(QEvent::registerEventType());
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
|
||||
class QGraphicsVideoItemPrivate : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
QGraphicsVideoItemPrivate(QGraphicsVideoItem *parent);
|
||||
~QGraphicsVideoItemPrivate();
|
||||
QMediaObject *mediaObject() const;
|
||||
bool setMediaObject(QMediaObject *mediaObject);
|
||||
Qt::AspectRatioMode aspectRatioMode() const;
|
||||
void setAspectRatioMode(Qt::AspectRatioMode mode);
|
||||
QPointF offset() const;
|
||||
void setOffset(const QPointF &offset);
|
||||
QSizeF size() const;
|
||||
void setSize(const QSizeF &size);
|
||||
QRectF rect() const;
|
||||
QRectF boundingRect() const;
|
||||
QSize nativeSize() const;
|
||||
void setCurrentView(QGraphicsView *view);
|
||||
void setVisible(bool visible);
|
||||
void setZValue(int zValue);
|
||||
void setTransform(const QTransform &transform);
|
||||
void setWithinViewBounds(bool within);
|
||||
|
||||
bool eventFilter(QObject *watched, QEvent *event);
|
||||
void customEvent(QEvent *event);
|
||||
|
||||
void _q_present();
|
||||
void _q_updateNativeSize();
|
||||
void _q_serviceDestroyed();
|
||||
void _q_mediaObjectDestroyed();
|
||||
|
||||
public slots:
|
||||
void updateWidgetOrdinalPosition();
|
||||
void updateItemAncestors();
|
||||
|
||||
private:
|
||||
void clearService();
|
||||
QWidget *videoWidget() const;
|
||||
void updateGeometry();
|
||||
void updateViewportAncestorEventFilters();
|
||||
void updateWidgetVisibility();
|
||||
void updateTopWinId();
|
||||
|
||||
private:
|
||||
QGraphicsVideoItem *q_ptr;
|
||||
QMediaService *m_service;
|
||||
QMediaObject *m_mediaObject;
|
||||
QVideoWidgetControl *m_widgetControl;
|
||||
QPointer<QGraphicsView> m_currentView;
|
||||
QList<QPointer<QObject> > m_viewportAncestors;
|
||||
QList<QPointer<QObject> > m_itemAncestors;
|
||||
QGraphicsView::ViewportUpdateMode m_savedViewportUpdateMode;
|
||||
Qt::AspectRatioMode m_aspectRatioMode;
|
||||
QRectF m_rect;
|
||||
QRectF m_boundingRect;
|
||||
QSize m_nativeSize;
|
||||
QPointF m_offset;
|
||||
QTransform m_transform;
|
||||
bool m_visible;
|
||||
bool m_withinViewBounds;
|
||||
};
|
||||
|
||||
|
||||
QGraphicsVideoItemPrivate::QGraphicsVideoItemPrivate(QGraphicsVideoItem *parent)
|
||||
: q_ptr(parent)
|
||||
, m_service(0)
|
||||
, m_mediaObject(0)
|
||||
, m_widgetControl(0)
|
||||
, m_savedViewportUpdateMode(QGraphicsView::FullViewportUpdate)
|
||||
, m_aspectRatioMode(Qt::KeepAspectRatio)
|
||||
, m_rect(0.0, 0.0, 320.0, 240.0)
|
||||
, m_visible(false)
|
||||
, m_withinViewBounds(false)
|
||||
{
|
||||
qRegisterMetaType<WId>("WId");
|
||||
updateItemAncestors();
|
||||
}
|
||||
|
||||
QGraphicsVideoItemPrivate::~QGraphicsVideoItemPrivate()
|
||||
{
|
||||
if (m_widgetControl)
|
||||
m_service->releaseControl(m_widgetControl);
|
||||
setCurrentView(0);
|
||||
}
|
||||
|
||||
QMediaObject *QGraphicsVideoItemPrivate::mediaObject() const
|
||||
{
|
||||
return m_mediaObject;
|
||||
}
|
||||
|
||||
bool QGraphicsVideoItemPrivate::setMediaObject(QMediaObject *mediaObject)
|
||||
{
|
||||
bool bound = false;
|
||||
if (m_mediaObject != mediaObject) {
|
||||
clearService();
|
||||
m_mediaObject = mediaObject;
|
||||
if (m_mediaObject) {
|
||||
m_service = m_mediaObject->service();
|
||||
if (m_service) {
|
||||
connect(m_service, SIGNAL(destroyed()), q_ptr, SLOT(_q_serviceDestroyed()));
|
||||
m_widgetControl = qobject_cast<QVideoWidgetControl *>(
|
||||
m_service->requestControl(QVideoWidgetControl_iid));
|
||||
if (m_widgetControl) {
|
||||
connect(m_widgetControl, SIGNAL(nativeSizeChanged()), q_ptr, SLOT(_q_updateNativeSize()));
|
||||
m_widgetControl->setAspectRatioMode(Qt::IgnoreAspectRatio);
|
||||
updateGeometry();
|
||||
updateTopWinId();
|
||||
updateWidgetOrdinalPosition();
|
||||
updateWidgetVisibility();
|
||||
bound = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return bound;
|
||||
}
|
||||
|
||||
Qt::AspectRatioMode QGraphicsVideoItemPrivate::aspectRatioMode() const
|
||||
{
|
||||
return m_aspectRatioMode;
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::setAspectRatioMode(Qt::AspectRatioMode mode)
|
||||
{
|
||||
if (mode != m_aspectRatioMode) {
|
||||
m_aspectRatioMode = mode;
|
||||
updateGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
QPointF QGraphicsVideoItemPrivate::offset() const
|
||||
{
|
||||
return m_rect.topLeft();
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::setOffset(const QPointF &offset)
|
||||
{
|
||||
if (m_offset != offset) {
|
||||
m_offset = offset;
|
||||
updateGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
QSizeF QGraphicsVideoItemPrivate::size() const
|
||||
{
|
||||
return m_rect.size();
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::setSize(const QSizeF &size)
|
||||
{
|
||||
if (m_rect.size() != size) {
|
||||
m_rect.setSize(size.isValid() ? size : QSizeF(0, 0));
|
||||
updateGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
QRectF QGraphicsVideoItemPrivate::rect() const
|
||||
{
|
||||
return m_rect;
|
||||
}
|
||||
|
||||
QRectF QGraphicsVideoItemPrivate::boundingRect() const
|
||||
{
|
||||
return m_boundingRect;
|
||||
}
|
||||
|
||||
QSize QGraphicsVideoItemPrivate::nativeSize() const
|
||||
{
|
||||
return m_nativeSize;
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::setCurrentView(QGraphicsView *view)
|
||||
{
|
||||
if (m_currentView != view) {
|
||||
if (m_currentView)
|
||||
m_currentView->setViewportUpdateMode(m_savedViewportUpdateMode);
|
||||
m_currentView = view;
|
||||
updateTopWinId();
|
||||
if (m_currentView) {
|
||||
m_savedViewportUpdateMode = m_currentView->viewportUpdateMode();
|
||||
m_currentView->setViewportUpdateMode(QGraphicsView::FullViewportUpdate);
|
||||
updateWidgetOrdinalPosition();
|
||||
updateGeometry();
|
||||
}
|
||||
updateViewportAncestorEventFilters();
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::setVisible(bool visible)
|
||||
{
|
||||
if (m_visible != visible) {
|
||||
m_visible = visible;
|
||||
updateWidgetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::setTransform(const QTransform &transform)
|
||||
{
|
||||
if (m_transform != transform) {
|
||||
m_transform = transform;
|
||||
updateGeometry();
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::setWithinViewBounds(bool within)
|
||||
{
|
||||
if (m_withinViewBounds != within) {
|
||||
m_withinViewBounds = within;
|
||||
updateWidgetVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
bool QGraphicsVideoItemPrivate::eventFilter(QObject *watched, QEvent *event)
|
||||
{
|
||||
bool updateViewportAncestorEventFiltersRequired = false;
|
||||
bool updateGeometryRequired = false;
|
||||
foreach (QPointer<QObject> target, m_viewportAncestors) {
|
||||
if (watched == target.data()) {
|
||||
switch (event->type()) {
|
||||
case QEvent::ParentChange:
|
||||
updateViewportAncestorEventFiltersRequired = true;
|
||||
break;
|
||||
case QEvent::WinIdChange:
|
||||
updateViewportAncestorEventFiltersRequired = true;
|
||||
updateTopWinId();
|
||||
break;
|
||||
case QEvent::Move:
|
||||
case QEvent::Resize:
|
||||
updateGeometryRequired = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (updateViewportAncestorEventFiltersRequired)
|
||||
updateViewportAncestorEventFilters();
|
||||
if (updateGeometryRequired)
|
||||
updateGeometry();
|
||||
if (watched == m_currentView) {
|
||||
switch (event->type()) {
|
||||
case QEvent::Show:
|
||||
setVisible(true);
|
||||
break;
|
||||
case QEvent::Hide:
|
||||
setVisible(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
return QObject::eventFilter(watched, event);
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::customEvent(QEvent *event)
|
||||
{
|
||||
if (event->type() == UpdateViewportTransparencyEvent && m_currentView) {
|
||||
m_currentView->window()->setAttribute(Qt::WA_TranslucentBackground);
|
||||
m_currentView->window()->update();
|
||||
}
|
||||
QObject::customEvent(event);
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::clearService()
|
||||
{
|
||||
if (m_widgetControl) {
|
||||
m_service->releaseControl(m_widgetControl);
|
||||
m_widgetControl = 0;
|
||||
}
|
||||
if (m_service) {
|
||||
m_service->disconnect(q_ptr);
|
||||
m_service = 0;
|
||||
}
|
||||
}
|
||||
|
||||
QWidget *QGraphicsVideoItemPrivate::videoWidget() const
|
||||
{
|
||||
return m_widgetControl ? m_widgetControl->videoWidget() : 0;
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::updateViewportAncestorEventFilters()
|
||||
{
|
||||
// In order to determine when the absolute screen position of the item
|
||||
// changes, we need to receive move events sent to m_currentView
|
||||
// or any of its ancestors.
|
||||
foreach (QPointer<QObject> target, m_viewportAncestors)
|
||||
if (target)
|
||||
target->removeEventFilter(this);
|
||||
m_viewportAncestors.clear();
|
||||
QObject *target = m_currentView;
|
||||
while (target) {
|
||||
target->installEventFilter(this);
|
||||
m_viewportAncestors.append(target);
|
||||
target = target->parent();
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::updateItemAncestors()
|
||||
{
|
||||
// We need to monitor the ancestors of this item to check for zOrder
|
||||
// changes and reparenting, both of which influence the stacking order
|
||||
// of this item and so require changes to the backend window ordinal position.
|
||||
foreach (QPointer<QObject> target, m_itemAncestors) {
|
||||
if (target) {
|
||||
disconnect(target, SIGNAL(zChanged()), this, SLOT(updateWidgetOrdinalPosition()));
|
||||
disconnect(target, SIGNAL(parentChanged()), this, SLOT(updateItemAncestors()));
|
||||
disconnect(target, SIGNAL(parentChanged()), this, SLOT(updateWidgetOrdinalPosition()));
|
||||
}
|
||||
}
|
||||
m_itemAncestors.clear();
|
||||
QGraphicsItem *item = q_ptr;
|
||||
while (item) {
|
||||
if (QGraphicsObject *object = item->toGraphicsObject()) {
|
||||
connect(object, SIGNAL(zChanged()), this, SLOT(updateWidgetOrdinalPosition()));
|
||||
connect(object, SIGNAL(parentChanged()), this, SLOT(updateItemAncestors()));
|
||||
connect(object, SIGNAL(parentChanged()), this, SLOT(updateWidgetOrdinalPosition()));
|
||||
m_itemAncestors.append(object);
|
||||
}
|
||||
item = item->parentItem();
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::updateGeometry()
|
||||
{
|
||||
q_ptr->prepareGeometryChange();
|
||||
QSizeF videoSize;
|
||||
if (m_nativeSize.isEmpty()) {
|
||||
videoSize = m_rect.size();
|
||||
} else if (m_aspectRatioMode == Qt::IgnoreAspectRatio) {
|
||||
videoSize = m_rect.size();
|
||||
} else {
|
||||
// KeepAspectRatio or KeepAspectRatioByExpanding
|
||||
videoSize = m_nativeSize;
|
||||
videoSize.scale(m_rect.size(), m_aspectRatioMode);
|
||||
}
|
||||
QRectF displayRect(QPointF(0, 0), videoSize);
|
||||
displayRect.moveCenter(m_rect.center());
|
||||
m_boundingRect = displayRect.intersected(m_rect);
|
||||
if (QWidget *widget = videoWidget()) {
|
||||
QRect widgetGeometry;
|
||||
QRect extent;
|
||||
if (m_currentView) {
|
||||
const QRectF viewRectF = m_transform.mapRect(displayRect);
|
||||
const QRect viewRect(viewRectF.topLeft().toPoint(), viewRectF.size().toSize());
|
||||
// Without this, a line of transparent pixels is visible round the edge of the
|
||||
// item. This is probably down to an error in conversion between scene and
|
||||
// screen coordinates, but the root cause has not yet been tracked down.
|
||||
static const QPoint positionFudgeFactor(-1, -1);
|
||||
static const QSize sizeFudgeFactor(4, 4);
|
||||
const QRect videoGeometry(m_currentView->mapToGlobal(viewRect.topLeft()) + positionFudgeFactor,
|
||||
viewRect.size() + sizeFudgeFactor);
|
||||
QRect viewportGeometry = QRect(m_currentView->viewport()->mapToGlobal(QPoint(0, 0)),
|
||||
m_currentView->viewport()->size());
|
||||
widgetGeometry = videoGeometry.intersected(viewportGeometry);
|
||||
extent = QRect(videoGeometry.topLeft() - widgetGeometry.topLeft(),
|
||||
videoGeometry.size());
|
||||
}
|
||||
setWithinViewBounds(!widgetGeometry.size().isEmpty());
|
||||
widget->setGeometry(widgetGeometry);
|
||||
m_widgetControl->setProperty("extentRect", QVariant::fromValue<QRect>(extent));
|
||||
const qreal angle = m_transform.map(QLineF(0, 0, 1, 0)).angle();
|
||||
m_widgetControl->setProperty("rotation", QVariant::fromValue<qreal>(angle));
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::updateWidgetVisibility()
|
||||
{
|
||||
if (QWidget *widget = videoWidget())
|
||||
widget->setVisible(m_visible && m_withinViewBounds);
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::updateTopWinId()
|
||||
{
|
||||
if (m_widgetControl) {
|
||||
WId topWinId = m_currentView ? m_currentView->effectiveWinId() : 0;
|
||||
// Set custom property
|
||||
m_widgetControl->setProperty("topWinId", QVariant::fromValue<WId>(topWinId));
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::updateWidgetOrdinalPosition()
|
||||
{
|
||||
if (m_currentView) {
|
||||
QGraphicsScene *scene = m_currentView->scene();
|
||||
const QGraphicsScene::ItemIndexMethod indexMethod = scene->itemIndexMethod();
|
||||
scene->setItemIndexMethod(QGraphicsScene::BspTreeIndex);
|
||||
const QList<QGraphicsItem*> items = m_currentView->items();
|
||||
QList<QGraphicsVideoItem*> graphicsVideoItems;
|
||||
foreach (QGraphicsItem *item, items)
|
||||
if (QGraphicsVideoItem *x = qobject_cast<QGraphicsVideoItem *>(item->toGraphicsObject()))
|
||||
graphicsVideoItems.append(x);
|
||||
int ordinalPosition = 1;
|
||||
foreach (QGraphicsVideoItem *item, graphicsVideoItems)
|
||||
if (QVideoWidgetControl *widgetControl = item->d_ptr->m_widgetControl)
|
||||
widgetControl->setProperty("ordinalPosition", ordinalPosition++);
|
||||
scene->setItemIndexMethod(indexMethod);
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_present()
|
||||
{
|
||||
// Not required for this implementation of QGraphicsVideoItem
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_updateNativeSize()
|
||||
{
|
||||
const QSize size = m_widgetControl ? m_widgetControl->property("nativeSize").value<QSize>() : QSize();
|
||||
if (!size.isEmpty() && m_nativeSize != size) {
|
||||
m_nativeSize = size;
|
||||
updateGeometry();
|
||||
emit q_ptr->nativeSizeChanged(m_nativeSize);
|
||||
}
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_serviceDestroyed()
|
||||
{
|
||||
m_widgetControl = 0;
|
||||
m_service = 0;
|
||||
}
|
||||
|
||||
void QGraphicsVideoItemPrivate::_q_mediaObjectDestroyed()
|
||||
{
|
||||
m_mediaObject = 0;
|
||||
clearService();
|
||||
}
|
||||
|
||||
QGraphicsVideoItem::QGraphicsVideoItem(QGraphicsItem *parent)
|
||||
: QGraphicsObject(parent)
|
||||
, d_ptr(new QGraphicsVideoItemPrivate(this))
|
||||
{
|
||||
setCacheMode(NoCache);
|
||||
setFlag(QGraphicsItem::ItemIgnoresParentOpacity);
|
||||
setFlag(QGraphicsItem::ItemSendsGeometryChanges);
|
||||
setFlag(QGraphicsItem::ItemSendsScenePositionChanges);
|
||||
}
|
||||
|
||||
QGraphicsVideoItem::~QGraphicsVideoItem()
|
||||
{
|
||||
delete d_ptr;
|
||||
}
|
||||
|
||||
QMediaObject *QGraphicsVideoItem::mediaObject() const
|
||||
{
|
||||
return d_func()->mediaObject();
|
||||
}
|
||||
|
||||
bool QGraphicsVideoItem::setMediaObject(QMediaObject *object)
|
||||
{
|
||||
return d_func()->setMediaObject(object);
|
||||
}
|
||||
|
||||
Qt::AspectRatioMode QGraphicsVideoItem::aspectRatioMode() const
|
||||
{
|
||||
return d_func()->aspectRatioMode();
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::setAspectRatioMode(Qt::AspectRatioMode mode)
|
||||
{
|
||||
d_func()->setAspectRatioMode(mode);
|
||||
}
|
||||
|
||||
QPointF QGraphicsVideoItem::offset() const
|
||||
{
|
||||
return d_func()->offset();
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::setOffset(const QPointF &offset)
|
||||
{
|
||||
d_func()->setOffset(offset);
|
||||
}
|
||||
|
||||
QSizeF QGraphicsVideoItem::size() const
|
||||
{
|
||||
return d_func()->size();
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::setSize(const QSizeF &size)
|
||||
{
|
||||
d_func()->setSize(size);
|
||||
}
|
||||
|
||||
QSizeF QGraphicsVideoItem::nativeSize() const
|
||||
{
|
||||
return d_func()->nativeSize();
|
||||
}
|
||||
|
||||
QRectF QGraphicsVideoItem::boundingRect() const
|
||||
{
|
||||
return d_func()->boundingRect();
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::paint(
|
||||
QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
|
||||
{
|
||||
Q_UNUSED(option);
|
||||
Q_D(QGraphicsVideoItem);
|
||||
QGraphicsView *view = 0;
|
||||
if (scene() && !scene()->views().isEmpty())
|
||||
view = scene()->views().first();
|
||||
d->setCurrentView(view);
|
||||
d->setTransform(painter->combinedTransform());
|
||||
if (widget && !widget->window()->testAttribute(Qt::WA_TranslucentBackground)) {
|
||||
// On Symbian, setting Qt::WA_TranslucentBackground can cause the
|
||||
// current window surface to be replaced. Because of this, it cannot
|
||||
// safely be changed from the context of the viewport paintEvent(), so we
|
||||
// queue a custom event to set the attribute.
|
||||
QEvent *event = new QEvent(UpdateViewportTransparencyEvent);
|
||||
QCoreApplication::instance()->postEvent(d, event);
|
||||
}
|
||||
const QPainter::CompositionMode oldCompositionMode = painter->compositionMode();
|
||||
painter->setCompositionMode(QPainter::CompositionMode_Source);
|
||||
painter->fillRect(d->boundingRect(), Qt::transparent);
|
||||
painter->setCompositionMode(oldCompositionMode);
|
||||
}
|
||||
|
||||
QVariant QGraphicsVideoItem::itemChange(GraphicsItemChange change, const QVariant &value)
|
||||
{
|
||||
Q_D(QGraphicsVideoItem);
|
||||
switch (change) {
|
||||
case ItemScenePositionHasChanged:
|
||||
update(boundingRect());
|
||||
break;
|
||||
case ItemVisibleChange:
|
||||
d->setVisible(value.toBool());
|
||||
break;
|
||||
case ItemZValueHasChanged:
|
||||
d->updateWidgetOrdinalPosition();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return QGraphicsItem::itemChange(change, value);
|
||||
}
|
||||
|
||||
void QGraphicsVideoItem::timerEvent(QTimerEvent *event)
|
||||
{
|
||||
QGraphicsObject::timerEvent(event);
|
||||
}
|
||||
|
||||
#include "qgraphicsvideoitem_symbian.moc"
|
||||
#include "moc_qgraphicsvideoitem.cpp"
|
||||
|
||||
QT_END_NAMESPACE
|
||||
@@ -47,10 +47,6 @@
|
||||
|
||||
#include "qmediaserviceproviderplugin.h"
|
||||
|
||||
#if defined(Q_OS_SYMBIAN)
|
||||
# include <f32file.h>
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
# include <CoreFoundation/CoreFoundation.h>
|
||||
#endif
|
||||
@@ -61,51 +57,6 @@ typedef QMap<QString,QObjectList> ObjectListMap;
|
||||
Q_GLOBAL_STATIC(ObjectListMap, staticMediaPlugins);
|
||||
|
||||
|
||||
#if defined(Q_OS_SYMBIAN)
|
||||
// XXX: Copied over from Mobility, hopefully to be removed at some point
|
||||
class DirChecker
|
||||
{
|
||||
public:
|
||||
DirChecker();
|
||||
~DirChecker();
|
||||
bool checkDir(const QDir& dir);
|
||||
|
||||
private:
|
||||
RFs rfs;
|
||||
};
|
||||
|
||||
DirChecker::DirChecker()
|
||||
{
|
||||
qt_symbian_throwIfError(rfs.Connect());
|
||||
}
|
||||
|
||||
bool DirChecker::checkDir(const QDir& dir)
|
||||
{
|
||||
bool pathFound = false;
|
||||
// In Symbian, going cdUp() in a c:/private/<uid3>/ will result in *platsec* error at fileserver (requires AllFiles capability)
|
||||
// Also, trying to cd() to a nonexistent directory causes *platsec* error. This does not cause functional harm, but should
|
||||
// nevertheless be changed to use native Symbian methods to avoid unnecessary platsec warnings (as per qpluginloader.cpp).
|
||||
// Use native Symbian code to check for directory existence, because checking
|
||||
// for files from under non-existent protected dir like E:/private/<uid> using
|
||||
// QDir::exists causes platform security violations on most apps.
|
||||
QString nativePath = QDir::toNativeSeparators(dir.absolutePath());
|
||||
TPtrC ptr = TPtrC16(static_cast<const TUint16*>(nativePath.utf16()), nativePath.length());
|
||||
TUint attributes;
|
||||
TInt err = rfs.Att(ptr, attributes);
|
||||
if (err == KErrNone) {
|
||||
// yes, the directory exists.
|
||||
pathFound = true;
|
||||
}
|
||||
return pathFound;
|
||||
}
|
||||
|
||||
DirChecker::~DirChecker()
|
||||
{
|
||||
rfs.Close();
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
QMediaPluginLoader::QMediaPluginLoader(const char *iid, const QString &location, Qt::CaseSensitivity):
|
||||
m_iid(iid)
|
||||
{
|
||||
@@ -139,10 +90,6 @@ QStringList QMediaPluginLoader::availablePlugins() const
|
||||
QStringList paths;
|
||||
QStringList plugins;
|
||||
|
||||
#if defined(Q_OS_SYMBIAN)
|
||||
DirChecker dirChecker;
|
||||
#endif
|
||||
|
||||
#if defined(Q_OS_MAC)
|
||||
QString imageSuffix(qgetenv("DYLD_IMAGE_SUFFIX"));
|
||||
|
||||
@@ -174,31 +121,26 @@ QStringList QMediaPluginLoader::availablePlugins() const
|
||||
|
||||
foreach (const QString &path, paths) {
|
||||
QDir typeDir(path + m_location);
|
||||
#if defined(Q_OS_SYMBIAN)
|
||||
if (dirChecker.checkDir(typeDir))
|
||||
#endif
|
||||
{
|
||||
foreach (const QString &file, typeDir.entryList(QDir::Files)) {
|
||||
foreach (const QString &file, typeDir.entryList(QDir::Files)) {
|
||||
#if defined(Q_OS_MAC)
|
||||
if (!imageSuffix.isEmpty()) { // Only add appropriate images
|
||||
if (file.lastIndexOf(imageSuffix, -6) == -1)
|
||||
continue;
|
||||
} else { // Ignore any images with common suffixes
|
||||
if (file.endsWith(QLatin1String("_debug.dylib")) ||
|
||||
file.endsWith(QLatin1String("_profile.dylib")))
|
||||
continue;
|
||||
}
|
||||
#elif defined(Q_OS_UNIX)
|
||||
// Ignore separate debug files
|
||||
if (file.endsWith(QLatin1String(".debug")))
|
||||
if (!imageSuffix.isEmpty()) { // Only add appropriate images
|
||||
if (file.lastIndexOf(imageSuffix, -6) == -1)
|
||||
continue;
|
||||
#elif defined(Q_OS_WIN)
|
||||
// Ignore non-dlls
|
||||
if (!file.endsWith(QLatin1String(".dll"), Qt::CaseInsensitive))
|
||||
} else { // Ignore any images with common suffixes
|
||||
if (file.endsWith(QLatin1String("_debug.dylib")) ||
|
||||
file.endsWith(QLatin1String("_profile.dylib")))
|
||||
continue;
|
||||
#endif
|
||||
plugins << typeDir.absoluteFilePath(file);
|
||||
}
|
||||
#elif defined(Q_OS_UNIX)
|
||||
// Ignore separate debug files
|
||||
if (file.endsWith(QLatin1String(".debug")))
|
||||
continue;
|
||||
#elif defined(Q_OS_WIN)
|
||||
// Ignore non-dlls
|
||||
if (!file.endsWith(QLatin1String(".dll"), Qt::CaseInsensitive))
|
||||
continue;
|
||||
#endif
|
||||
plugins << typeDir.absoluteFilePath(file);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -57,18 +57,18 @@
|
||||
#define QTM_PACKAGE_TAG ""
|
||||
|
||||
#include <QtCore/qglobal.h>
|
||||
#if defined(QTM_BUILD_UNITTESTS) && (defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)) && defined(QT_MAKEDLL)
|
||||
#if defined(QTM_BUILD_UNITTESTS) && (defined(Q_OS_WIN)) && defined(QT_MAKEDLL)
|
||||
# define QM_AUTOTEST_EXPORT Q_DECL_EXPORT
|
||||
#elif defined(QTM_BUILD_UNITTESTS) && (defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)) && defined(QT_DLL)
|
||||
#elif defined(QTM_BUILD_UNITTESTS) && (defined(Q_OS_WIN)) && defined(QT_DLL)
|
||||
# define QM_AUTOTEST_EXPORT Q_DECL_IMPORT
|
||||
#elif defined(QTM_BUILD_UNITTESTS) && !(defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)) && defined(QT_SHARED)
|
||||
#elif defined(QTM_BUILD_UNITTESTS) && !(defined(Q_OS_WIN)) && defined(QT_SHARED)
|
||||
# define QM_AUTOTEST_EXPORT Q_DECL_EXPORT
|
||||
#else
|
||||
# define QM_AUTOTEST_EXPORT
|
||||
#endif
|
||||
|
||||
|
||||
#if defined(Q_OS_WIN) || defined(Q_OS_SYMBIAN)
|
||||
#if defined(Q_OS_WIN)
|
||||
# if defined(QT_NODLL)
|
||||
# undef QT_MAKEDLL
|
||||
# undef QT_DLL
|
||||
@@ -160,11 +160,6 @@
|
||||
# define Q_LOCATION_EXPORT Q_DECL_IMPORT
|
||||
# define Q_MULTIMEDIA_EXPORT Q_DECL_IMPORT
|
||||
# define Q_MESSAGING_EXPORT Q_DECL_IMPORT
|
||||
# if QTM_SERVICEFW_SYMBIAN_DATABASEMANAGER_SERVER
|
||||
# define Q_SERVICEFW_EXPORT
|
||||
# else
|
||||
# define Q_SERVICEFW_EXPORT Q_DECL_IMPORT
|
||||
# endif
|
||||
# define Q_SYSINFO_EXPORT Q_DECL_IMPORT
|
||||
# define Q_SENSORS_EXPORT Q_DECL_IMPORT
|
||||
# define Q_FEEDBACK_EXPORT Q_DECL_IMPORT
|
||||
@@ -211,17 +206,6 @@
|
||||
#endif
|
||||
|
||||
|
||||
#ifdef QTM_SERVICEFW_SYMBIAN_DATABASEMANAGER_SERVER
|
||||
# ifdef Q_SERVICEFW_EXPORT
|
||||
# undef Q_SERVICEFW_EXPORT
|
||||
# endif
|
||||
# define Q_SERVICEFW_EXPORT
|
||||
# ifdef QM_AUTOTEST_EXPORT
|
||||
# undef QM_AUTOTEST_EXPORT
|
||||
# endif
|
||||
# define QM_AUTOTEST_EXPORT
|
||||
#endif
|
||||
|
||||
// The namespace is hardcoded as moc has issues resolving
|
||||
// macros which would be a prerequisite for a dynmamic namespace
|
||||
#define QTM_NAMESPACE QtMobility
|
||||
|
||||
@@ -76,12 +76,6 @@ QVideoWidgetControlBackend::QVideoWidgetControlBackend(
|
||||
layout->setMargin(0);
|
||||
layout->setSpacing(0);
|
||||
|
||||
#ifdef Q_OS_SYMBIAN
|
||||
// On some cases the flag is not reset automatically
|
||||
// This would lead to viewfinder not being visible on Symbian
|
||||
control->videoWidget()->setAttribute(Qt::WA_WState_ExplicitShowHide, false);
|
||||
#endif // Q_OS_SYMBIAN
|
||||
|
||||
layout->addWidget(control->videoWidget());
|
||||
|
||||
widget->setLayout(layout);
|
||||
|
||||
@@ -1,497 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include <QtGui/qx11info_x11.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
#include <QtCore/qvariant.h>
|
||||
#include <qvideosurfaceformat.h>
|
||||
|
||||
#include "qxvideosurface_maemo5_p.h"
|
||||
|
||||
//#define DEBUG_XV_SURFACE
|
||||
|
||||
struct XvFormatRgb
|
||||
{
|
||||
QVideoFrame::PixelFormat pixelFormat;
|
||||
int bits_per_pixel;
|
||||
int format;
|
||||
int num_planes;
|
||||
|
||||
int depth;
|
||||
unsigned int red_mask;
|
||||
unsigned int green_mask;
|
||||
unsigned int blue_mask;
|
||||
|
||||
};
|
||||
|
||||
bool operator ==(const XvImageFormatValues &format, const XvFormatRgb &rgb)
|
||||
{
|
||||
return format.type == XvRGB
|
||||
&& format.bits_per_pixel == rgb.bits_per_pixel
|
||||
&& format.format == rgb.format
|
||||
&& format.num_planes == rgb.num_planes
|
||||
&& format.depth == rgb.depth
|
||||
&& format.red_mask == rgb.red_mask
|
||||
&& format.blue_mask == rgb.blue_mask;
|
||||
}
|
||||
|
||||
static const XvFormatRgb qt_xvRgbLookup[] =
|
||||
{
|
||||
{ QVideoFrame::Format_ARGB32, 32, XvPacked, 1, 32, 0x00FF0000, 0x0000FF00, 0x000000FF },
|
||||
{ QVideoFrame::Format_RGB32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
|
||||
{ QVideoFrame::Format_RGB24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
|
||||
{ QVideoFrame::Format_RGB565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F },
|
||||
{ QVideoFrame::Format_BGRA32, 32, XvPacked, 1, 32, 0xFF000000, 0x00FF0000, 0x0000FF00 },
|
||||
{ QVideoFrame::Format_BGR32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
|
||||
{ QVideoFrame::Format_BGR24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF },
|
||||
{ QVideoFrame::Format_BGR565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F }
|
||||
};
|
||||
|
||||
struct XvFormatYuv
|
||||
{
|
||||
QVideoFrame::PixelFormat pixelFormat;
|
||||
int bits_per_pixel;
|
||||
int format;
|
||||
int num_planes;
|
||||
|
||||
unsigned int y_sample_bits;
|
||||
unsigned int u_sample_bits;
|
||||
unsigned int v_sample_bits;
|
||||
unsigned int horz_y_period;
|
||||
unsigned int horz_u_period;
|
||||
unsigned int horz_v_period;
|
||||
unsigned int vert_y_period;
|
||||
unsigned int vert_u_period;
|
||||
unsigned int vert_v_period;
|
||||
char component_order[32];
|
||||
};
|
||||
|
||||
bool operator ==(const XvImageFormatValues &format, const XvFormatYuv &yuv)
|
||||
{
|
||||
return format.type == XvYUV
|
||||
&& format.bits_per_pixel == yuv.bits_per_pixel
|
||||
&& format.format == yuv.format
|
||||
&& format.num_planes == yuv.num_planes
|
||||
&& format.y_sample_bits == yuv.y_sample_bits
|
||||
&& format.u_sample_bits == yuv.u_sample_bits
|
||||
&& format.v_sample_bits == yuv.v_sample_bits
|
||||
&& format.horz_y_period == yuv.horz_y_period
|
||||
&& format.horz_u_period == yuv.horz_u_period
|
||||
&& format.horz_v_period == yuv.horz_v_period
|
||||
&& format.horz_y_period == yuv.vert_y_period
|
||||
&& format.vert_u_period == yuv.vert_u_period
|
||||
&& format.vert_v_period == yuv.vert_v_period
|
||||
&& qstrncmp(format.component_order, yuv.component_order, 32) == 0;
|
||||
}
|
||||
|
||||
static const XvFormatYuv qt_xvYuvLookup[] =
|
||||
{
|
||||
{ QVideoFrame::Format_YUV444 , 24, XvPacked, 1, 8, 8, 8, 1, 1, 1, 1, 1, 1, "YUV" },
|
||||
{ QVideoFrame::Format_YUV420P, 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV" },
|
||||
{ QVideoFrame::Format_YV12 , 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU" },
|
||||
{ QVideoFrame::Format_UYVY , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "UYVY" },
|
||||
{ QVideoFrame::Format_YUYV , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "YUY2" },
|
||||
{ QVideoFrame::Format_YUYV , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "YUYV" },
|
||||
{ QVideoFrame::Format_NV12 , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV" },
|
||||
{ QVideoFrame::Format_NV12 , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU" },
|
||||
{ QVideoFrame::Format_Y8 , 8 , XvPlanar, 1, 8, 0, 0, 1, 0, 0, 1, 0, 0, "Y" }
|
||||
};
|
||||
|
||||
QXVideoSurface::QXVideoSurface(QObject *parent)
|
||||
: QAbstractVideoSurface(parent)
|
||||
, m_winId(0)
|
||||
, m_portId(0)
|
||||
, m_gc(0)
|
||||
, m_image(0)
|
||||
, m_colorKey(24,0,24)
|
||||
{
|
||||
}
|
||||
|
||||
QXVideoSurface::~QXVideoSurface()
|
||||
{
|
||||
if (m_gc)
|
||||
XFreeGC(QX11Info::display(), m_gc);
|
||||
|
||||
if (m_portId != 0)
|
||||
XvUngrabPort(QX11Info::display(), m_portId, 0);
|
||||
}
|
||||
|
||||
WId QXVideoSurface::winId() const
|
||||
{
|
||||
return m_winId;
|
||||
}
|
||||
|
||||
void QXVideoSurface::setWinId(WId id)
|
||||
{
|
||||
if (id == m_winId)
|
||||
return;
|
||||
|
||||
#ifdef DEBUG_XV_SURFACE
|
||||
qDebug() << "QXVideoSurface::setWinId" << id;
|
||||
#endif
|
||||
|
||||
if (m_image)
|
||||
XFree(m_image);
|
||||
|
||||
if (m_gc) {
|
||||
XFreeGC(QX11Info::display(), m_gc);
|
||||
m_gc = 0;
|
||||
}
|
||||
|
||||
if (m_portId != 0)
|
||||
XvUngrabPort(QX11Info::display(), m_portId, 0);
|
||||
|
||||
QList<QVideoFrame::PixelFormat> prevFormats = m_supportedPixelFormats;
|
||||
m_supportedPixelFormats.clear();
|
||||
m_formatIds.clear();
|
||||
|
||||
m_winId = id;
|
||||
|
||||
if (m_winId && findPort()) {
|
||||
querySupportedFormats();
|
||||
|
||||
m_gc = XCreateGC(QX11Info::display(), m_winId, 0, 0);
|
||||
|
||||
if (m_image) {
|
||||
m_image = 0;
|
||||
|
||||
if (!start(surfaceFormat()))
|
||||
QAbstractVideoSurface::stop();
|
||||
}
|
||||
} else if (m_image) {
|
||||
m_image = 0;
|
||||
|
||||
QAbstractVideoSurface::stop();
|
||||
}
|
||||
|
||||
if (m_supportedPixelFormats != prevFormats) {
|
||||
#ifdef DEBUG_XV_SURFACE
|
||||
qDebug() << "QXVideoSurface: supportedFormatsChanged";
|
||||
#endif
|
||||
emit supportedFormatsChanged();
|
||||
}
|
||||
}
|
||||
|
||||
QRect QXVideoSurface::displayRect() const
|
||||
{
|
||||
return m_displayRect;
|
||||
}
|
||||
|
||||
void QXVideoSurface::setDisplayRect(const QRect &rect)
|
||||
{
|
||||
m_displayRect = rect;
|
||||
}
|
||||
|
||||
QColor QXVideoSurface::colorKey() const
|
||||
{
|
||||
return m_colorKey;
|
||||
}
|
||||
|
||||
void QXVideoSurface::setColorKey(QColor key)
|
||||
{
|
||||
m_colorKey = key;
|
||||
}
|
||||
|
||||
int QXVideoSurface::getAttribute(const char *attribute) const
|
||||
{
|
||||
if (m_portId != 0) {
|
||||
Display *display = QX11Info::display();
|
||||
|
||||
Atom atom = XInternAtom(display, attribute, True);
|
||||
|
||||
int value = 0;
|
||||
|
||||
XvGetPortAttribute(display, m_portId, atom, &value);
|
||||
|
||||
return value;
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
void QXVideoSurface::setAttribute(const char *attribute, int value)
|
||||
{
|
||||
if (m_portId != 0) {
|
||||
Display *display = QX11Info::display();
|
||||
|
||||
Atom atom = XInternAtom(display, attribute, True);
|
||||
|
||||
XvSetPortAttribute(display, m_portId, atom, value);
|
||||
}
|
||||
}
|
||||
|
||||
QList<QVideoFrame::PixelFormat> QXVideoSurface::supportedPixelFormats(
|
||||
QAbstractVideoBuffer::HandleType handleType) const
|
||||
{
|
||||
if ( handleType == QAbstractVideoBuffer::NoHandle ||
|
||||
handleType == QAbstractVideoBuffer::XvShmImageHandle )
|
||||
return m_supportedPixelFormats;
|
||||
else
|
||||
return QList<QVideoFrame::PixelFormat>();
|
||||
}
|
||||
|
||||
bool QXVideoSurface::start(const QVideoSurfaceFormat &format)
|
||||
{
|
||||
#ifdef DEBUG_XV_SURFACE
|
||||
qDebug() << "QXVideoSurface::start" << format;
|
||||
#endif
|
||||
|
||||
m_lastFrame = QVideoFrame();
|
||||
|
||||
if (m_image)
|
||||
XFree(m_image);
|
||||
|
||||
m_xvFormatId = 0;
|
||||
for (int i = 0; i < m_supportedPixelFormats.count(); ++i) {
|
||||
if (m_supportedPixelFormats.at(i) == format.pixelFormat()) {
|
||||
m_xvFormatId = m_formatIds.at(i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (m_xvFormatId == 0) {
|
||||
setError(UnsupportedFormatError);
|
||||
} else {
|
||||
XvImage *image = XvShmCreateImage(
|
||||
QX11Info::display(),
|
||||
m_portId,
|
||||
m_xvFormatId,
|
||||
0,
|
||||
format.frameWidth(),
|
||||
format.frameHeight(),
|
||||
&m_shminfo
|
||||
);
|
||||
|
||||
if (!image) {
|
||||
setError(ResourceError);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_shminfo.shmid = shmget(IPC_PRIVATE, image->data_size, IPC_CREAT | 0777);
|
||||
m_shminfo.shmaddr = image->data = (char*)shmat(m_shminfo.shmid, 0, 0);
|
||||
m_shminfo.readOnly = False;
|
||||
|
||||
if (!XShmAttach(QX11Info::display(), &m_shminfo)) {
|
||||
qWarning() << "XShmAttach failed" << format;
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!image) {
|
||||
setError(ResourceError);
|
||||
} else {
|
||||
m_viewport = format.viewport();
|
||||
m_image = image;
|
||||
|
||||
quint32 c = m_colorKey.rgb();
|
||||
quint16 colorKey16 = ((c >> 3) & 0x001f)
|
||||
| ((c >> 5) & 0x07e0)
|
||||
| ((c >> 8) & 0xf800);
|
||||
|
||||
setAttribute("XV_AUTOPAINT_COLORKEY", 0);
|
||||
setAttribute("XV_COLORKEY", colorKey16);
|
||||
setAttribute("XV_OMAP_VSYNC", 1);
|
||||
setAttribute("XV_DOUBLE_BUFFER", 0);
|
||||
|
||||
QVideoSurfaceFormat newFormat = format;
|
||||
newFormat.setProperty("portId", QVariant(quint64(m_portId)));
|
||||
newFormat.setProperty("xvFormatId", m_xvFormatId);
|
||||
newFormat.setProperty("dataSize", image->data_size);
|
||||
|
||||
return QAbstractVideoSurface::start(newFormat);
|
||||
}
|
||||
}
|
||||
|
||||
if (m_image) {
|
||||
m_image = 0;
|
||||
|
||||
QAbstractVideoSurface::stop();
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void QXVideoSurface::stop()
|
||||
{
|
||||
if (m_image) {
|
||||
XFree(m_image);
|
||||
m_image = 0;
|
||||
m_lastFrame = QVideoFrame();
|
||||
|
||||
QAbstractVideoSurface::stop();
|
||||
}
|
||||
}
|
||||
|
||||
bool QXVideoSurface::present(const QVideoFrame &frame)
|
||||
{
|
||||
if (!m_image) {
|
||||
setError(StoppedError);
|
||||
return false;
|
||||
} else if (m_image->width != frame.width() || m_image->height != frame.height()) {
|
||||
setError(IncorrectFormatError);
|
||||
return false;
|
||||
} else {
|
||||
m_lastFrame = frame;
|
||||
|
||||
if (!m_lastFrame.map(QAbstractVideoBuffer::ReadOnly)) {
|
||||
qWarning() << "Failed to map video frame";
|
||||
setError(IncorrectFormatError);
|
||||
return false;
|
||||
} else {
|
||||
bool presented = false;
|
||||
|
||||
if (frame.handleType() != QAbstractVideoBuffer::XvShmImageHandle &&
|
||||
m_image->data_size > m_lastFrame.mappedBytes()) {
|
||||
qWarning("Insufficient frame buffer size");
|
||||
setError(IncorrectFormatError);
|
||||
} else if (frame.handleType() != QAbstractVideoBuffer::XvShmImageHandle &&
|
||||
m_image->num_planes > 0 &&
|
||||
m_image->pitches[0] != m_lastFrame.bytesPerLine()) {
|
||||
qWarning("Incompatible frame pitches");
|
||||
setError(IncorrectFormatError);
|
||||
} else {
|
||||
XvImage *img = 0;
|
||||
|
||||
if (frame.handleType() == QAbstractVideoBuffer::XvShmImageHandle) {
|
||||
img = frame.handle().value<XvImage*>();
|
||||
} else {
|
||||
img = m_image;
|
||||
memcpy(m_image->data, m_lastFrame.bits(), qMin(m_lastFrame.mappedBytes(), m_image->data_size));
|
||||
}
|
||||
|
||||
if (img)
|
||||
XvShmPutImage(
|
||||
QX11Info::display(),
|
||||
m_portId,
|
||||
m_winId,
|
||||
m_gc,
|
||||
img,
|
||||
m_viewport.x(),
|
||||
m_viewport.y(),
|
||||
m_viewport.width(),
|
||||
m_viewport.height(),
|
||||
m_displayRect.x(),
|
||||
m_displayRect.y(),
|
||||
m_displayRect.width(),
|
||||
m_displayRect.height(),
|
||||
false);
|
||||
|
||||
presented = true;
|
||||
}
|
||||
|
||||
m_lastFrame.unmap();
|
||||
|
||||
return presented;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QXVideoSurface::repaintLastFrame()
|
||||
{
|
||||
if (m_lastFrame.isValid())
|
||||
present(QVideoFrame(m_lastFrame));
|
||||
}
|
||||
|
||||
bool QXVideoSurface::findPort()
|
||||
{
|
||||
unsigned int count = 0;
|
||||
XvAdaptorInfo *adaptors = 0;
|
||||
bool portFound = false;
|
||||
|
||||
if (XvQueryAdaptors(QX11Info::display(), m_winId, &count, &adaptors) == Success) {
|
||||
for (unsigned int i = 0; i < count && !portFound; ++i) {
|
||||
if (adaptors[i].type & XvImageMask) {
|
||||
m_portId = adaptors[i].base_id;
|
||||
|
||||
for (unsigned int j = 0; j < adaptors[i].num_ports && !portFound; ++j, ++m_portId)
|
||||
portFound = XvGrabPort(QX11Info::display(), m_portId, 0) == Success;
|
||||
}
|
||||
}
|
||||
XvFreeAdaptorInfo(adaptors);
|
||||
}
|
||||
|
||||
if (!portFound)
|
||||
qWarning() << "QXVideoSurface::findPort: failed to find XVideo port";
|
||||
|
||||
return portFound;
|
||||
}
|
||||
|
||||
void QXVideoSurface::querySupportedFormats()
|
||||
{
|
||||
int count = 0;
|
||||
if (XvImageFormatValues *imageFormats = XvListImageFormats(
|
||||
QX11Info::display(), m_portId, &count)) {
|
||||
const int rgbCount = sizeof(qt_xvRgbLookup) / sizeof(XvFormatRgb);
|
||||
const int yuvCount = sizeof(qt_xvYuvLookup) / sizeof(XvFormatYuv);
|
||||
|
||||
for (int i = 0; i < count; ++i) {
|
||||
switch (imageFormats[i].type) {
|
||||
case XvRGB:
|
||||
for (int j = 0; j < rgbCount; ++j) {
|
||||
if (imageFormats[i] == qt_xvRgbLookup[j]) {
|
||||
m_supportedPixelFormats.append(qt_xvRgbLookup[j].pixelFormat);
|
||||
m_formatIds.append(imageFormats[i].id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case XvYUV:
|
||||
for (int j = 0; j < yuvCount; ++j) {
|
||||
//skip YUV420P and YV12 formats, they don't work correctly and slow,
|
||||
//YUV2 == YUYV is just slow
|
||||
if (imageFormats[i] == qt_xvYuvLookup[j] &&
|
||||
qt_xvYuvLookup[j].pixelFormat != QVideoFrame::Format_YUV420P &&
|
||||
qt_xvYuvLookup[j].pixelFormat != QVideoFrame::Format_YV12) {
|
||||
m_supportedPixelFormats.append(qt_xvYuvLookup[j].pixelFormat);
|
||||
m_formatIds.append(imageFormats[i].id);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
XFree(imageFormats);
|
||||
}
|
||||
|
||||
#ifdef DEBUG_XV_SURFACE
|
||||
qDebug() << "Supported pixel formats:" << m_supportedPixelFormats;
|
||||
#endif
|
||||
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Mobility Components.
|
||||
**
|
||||
** $QT_BEGIN_LICENSE:LGPL$
|
||||
** GNU Lesser General Public License Usage
|
||||
** This file may be used under the terms of the GNU Lesser General Public
|
||||
** License version 2.1 as published by the Free Software Foundation and
|
||||
** appearing in the file LICENSE.LGPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU Lesser
|
||||
** General Public License version 2.1 requirements will be met:
|
||||
** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
|
||||
**
|
||||
** In addition, as a special exception, Nokia gives you certain additional
|
||||
** rights. These rights are described in the Nokia Qt LGPL Exception
|
||||
** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU General
|
||||
** Public License version 3.0 as published by the Free Software Foundation
|
||||
** and appearing in the file LICENSE.GPL included in the packaging of this
|
||||
** file. Please review the following information to ensure the GNU General
|
||||
** Public License version 3.0 requirements will be met:
|
||||
** http://www.gnu.org/copyleft/gpl.html.
|
||||
**
|
||||
** Other Usage
|
||||
** Alternatively, this file may be used in accordance with the terms and
|
||||
** conditions contained in a signed written agreement between you and Nokia.
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
**
|
||||
** $QT_END_LICENSE$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#ifndef QXVIDEOSURFACE_MAEMO5_H
|
||||
#define QXVIDEOSURFACE_MAEMO5_H
|
||||
|
||||
#include <QtCore/qhash.h>
|
||||
#include <QtGui/qwidget.h>
|
||||
#include <qabstractvideosurface.h>
|
||||
|
||||
#include <X11/Xlib.h>
|
||||
#include <sys/ipc.h>
|
||||
#include <sys/shm.h>
|
||||
#include <X11/extensions/XShm.h>
|
||||
#include <X11/Xlib.h>
|
||||
#include <X11/extensions/Xv.h>
|
||||
#include <X11/extensions/Xvlib.h>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
class QXVideoSurface : public QAbstractVideoSurface
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
QXVideoSurface(QObject *parent = 0);
|
||||
~QXVideoSurface();
|
||||
|
||||
WId winId() const;
|
||||
void setWinId(WId id);
|
||||
|
||||
QRect displayRect() const;
|
||||
void setDisplayRect(const QRect &rect);
|
||||
|
||||
QColor colorKey() const;
|
||||
void setColorKey(QColor key);
|
||||
|
||||
QList<QVideoFrame::PixelFormat> supportedPixelFormats(
|
||||
QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const;
|
||||
|
||||
QVideoFrame lastFrame() const { return m_lastFrame; }
|
||||
|
||||
public slots:
|
||||
bool start(const QVideoSurfaceFormat &format);
|
||||
void stop();
|
||||
|
||||
bool present(const QVideoFrame &frame);
|
||||
void repaintLastFrame();
|
||||
|
||||
private:
|
||||
WId m_winId;
|
||||
XvPortID m_portId;
|
||||
int m_xvFormatId;
|
||||
GC m_gc;
|
||||
XvImage *m_image;
|
||||
XShmSegmentInfo m_shminfo;
|
||||
QList<QVideoFrame::PixelFormat> m_supportedPixelFormats;
|
||||
QVector<int> m_formatIds;
|
||||
QRect m_viewport;
|
||||
QRect m_displayRect;
|
||||
QColor m_colorKey;
|
||||
|
||||
QVideoFrame m_lastFrame;
|
||||
|
||||
bool findPort();
|
||||
void querySupportedFormats();
|
||||
|
||||
int getAttribute(const char *attribute) const;
|
||||
void setAttribute(const char *attribute, int value);
|
||||
};
|
||||
|
||||
Q_DECLARE_METATYPE(XvImage*)
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user