Rearrange the automatic tests.
Split them into unit and integration tests. Integration tests really need to be run on the real platform (not in a VM etc) since they are somewhat unstable or nonfunctional otherwise. A few tests were previously broken by QUrl changes and they were repaired. Removed one test since it was not providing a lot of value. There are still a number of tests that rely on Q_AUTOTEST_EXPORT symbols. Change-Id: Ic402abf0af946baa5945075d975b3f584f9ef280 Reviewed-by: Kalle Lehtonen <kalle.ju.lehtonen@nokia.com>
This commit is contained in:
committed by
Qt by Nokia
parent
7dfb883df6
commit
e3a8c165ea
3
tests/auto/integration/integration.pro
Normal file
3
tests/auto/integration/integration.pro
Normal file
@@ -0,0 +1,3 @@
|
||||
TEMPLATE = subdirs
|
||||
|
||||
SUBDIRS += multimedia.pro
|
||||
9
tests/auto/integration/multimedia.pro
Normal file
9
tests/auto/integration/multimedia.pro
Normal file
@@ -0,0 +1,9 @@
|
||||
|
||||
TEMPLATE = subdirs
|
||||
SUBDIRS += \
|
||||
qaudioinput \
|
||||
qaudiooutput \
|
||||
qmediaplayerbackend \
|
||||
qcamerabackend \
|
||||
qsoundeffect
|
||||
|
||||
12
tests/auto/integration/qaudioinput/qaudioinput.pro
Normal file
12
tests/auto/integration/qaudioinput/qaudioinput.pro
Normal file
@@ -0,0 +1,12 @@
|
||||
TARGET = tst_qaudioinput
|
||||
|
||||
QT += core multimedia-private testlib
|
||||
CONFIG += no_private_qt_headers_warning
|
||||
|
||||
# This is more of a system test
|
||||
# CONFIG += testcase
|
||||
|
||||
DEFINES += SRCDIR=\\\"$$PWD/\\\"
|
||||
|
||||
HEADERS += wavheader.h
|
||||
SOURCES += wavheader.cpp tst_qaudioinput.cpp
|
||||
850
tests/auto/integration/qaudioinput/tst_qaudioinput.cpp
Executable file
850
tests/auto/integration/qaudioinput/tst_qaudioinput.cpp
Executable file
@@ -0,0 +1,850 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 test suite of the Qt Toolkit.
|
||||
**
|
||||
** $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 <QtTest/QtTest>
|
||||
#include <QtCore/qlocale.h>
|
||||
|
||||
#include <qaudioinput.h>
|
||||
#include <qaudiodeviceinfo.h>
|
||||
#include <qaudioformat.h>
|
||||
#include <qaudio.h>
|
||||
|
||||
#include "wavheader.h"
|
||||
|
||||
//TESTED_COMPONENT=src/multimedia
|
||||
|
||||
#define AUDIO_BUFFER 192000
|
||||
|
||||
#ifndef QTRY_VERIFY2
|
||||
#define QTRY_VERIFY2(__expr,__msg) \
|
||||
do { \
|
||||
const int __step = 50; \
|
||||
const int __timeout = 5000; \
|
||||
if (!(__expr)) { \
|
||||
QTest::qWait(0); \
|
||||
} \
|
||||
for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
|
||||
QTest::qWait(__step); \
|
||||
} \
|
||||
QVERIFY2(__expr,__msg); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
class tst_QAudioInput : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
tst_QAudioInput(QObject* parent=0) : QObject(parent) {}
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void format();
|
||||
void invalidFormat_data();
|
||||
void invalidFormat();
|
||||
|
||||
void bufferSize();
|
||||
|
||||
void notifyInterval();
|
||||
void disableNotifyInterval();
|
||||
|
||||
void stopWhileStopped();
|
||||
void suspendWhileStopped();
|
||||
void resumeWhileStopped();
|
||||
|
||||
void pull();
|
||||
void pullSuspendResume();
|
||||
|
||||
void push();
|
||||
void pushSuspendResume();
|
||||
|
||||
void reset();
|
||||
|
||||
void cleanupTestCase();
|
||||
|
||||
private:
|
||||
QString formatToFileName(const QAudioFormat &format);
|
||||
QString workingDir();
|
||||
|
||||
QAudioDeviceInfo audioDevice;
|
||||
QList<QAudioFormat> testFormats;
|
||||
QList<QFile*> audioFiles;
|
||||
|
||||
QScopedPointer<QByteArray> m_byteArray;
|
||||
QScopedPointer<QBuffer> m_buffer;
|
||||
};
|
||||
|
||||
QString tst_QAudioInput::formatToFileName(const QAudioFormat &format)
|
||||
{
|
||||
const QString formatEndian = (format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
? QString("LE") : QString("BE");
|
||||
|
||||
const QString formatSigned = (format.sampleType() == QAudioFormat::SignedInt)
|
||||
? QString("signed") : QString("unsigned");
|
||||
|
||||
return QString("%1_%2_%3_%4_%5")
|
||||
.arg(format.frequency())
|
||||
.arg(format.sampleSize())
|
||||
.arg(formatSigned)
|
||||
.arg(formatEndian)
|
||||
.arg(format.channels());
|
||||
}
|
||||
|
||||
|
||||
QString tst_QAudioInput::workingDir()
|
||||
{
|
||||
QDir working(QString(SRCDIR));
|
||||
|
||||
if (working.exists())
|
||||
return QString(SRCDIR);
|
||||
|
||||
return QDir::currentPath();
|
||||
}
|
||||
|
||||
void tst_QAudioInput::initTestCase()
|
||||
{
|
||||
qRegisterMetaType<QAudioFormat>();
|
||||
|
||||
// Only perform tests if audio output device exists
|
||||
const QList<QAudioDeviceInfo> devices =
|
||||
QAudioDeviceInfo::availableDevices(QAudio::AudioInput);
|
||||
|
||||
if (devices.size() <= 0)
|
||||
QSKIP("No audio backend", SkipAll);
|
||||
|
||||
audioDevice = QAudioDeviceInfo::defaultInputDevice();
|
||||
|
||||
|
||||
QAudioFormat format;
|
||||
|
||||
format.setCodec("audio/pcm");
|
||||
|
||||
if (audioDevice.isFormatSupported(audioDevice.preferredFormat()))
|
||||
testFormats.append(audioDevice.preferredFormat());
|
||||
|
||||
// PCM 8000 mono S8
|
||||
format.setFrequency(8000);
|
||||
format.setSampleSize(8);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
format.setChannels(1);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
// PCM 11025 mono S16LE
|
||||
format.setFrequency(11025);
|
||||
format.setSampleSize(16);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
// PCM 22050 mono S16LE
|
||||
format.setFrequency(22050);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
// PCM 22050 stereo S16LE
|
||||
format.setChannels(2);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
// PCM 44100 stereo S16LE
|
||||
format.setFrequency(44100);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
// PCM 48000 stereo S16LE
|
||||
format.setFrequency(48000);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
QVERIFY(testFormats.size());
|
||||
|
||||
foreach (const QAudioFormat &format, testFormats) {
|
||||
QFile* file = new QFile(workingDir() + formatToFileName(format) + QString(".wav"));
|
||||
audioFiles.append(file);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioInput::format()
|
||||
{
|
||||
QAudioInput audioInput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QAudioFormat requested = audioDevice.preferredFormat();
|
||||
QAudioFormat actual = audioInput.format();
|
||||
|
||||
QVERIFY2((requested.channels() == actual.channels()),
|
||||
QString("channels: requested=%1, actual=%2").arg(requested.channels()).arg(actual.channels()).toLocal8Bit().constData());
|
||||
QVERIFY2((requested.frequency() == actual.frequency()),
|
||||
QString("frequency: requested=%1, actual=%2").arg(requested.frequency()).arg(actual.frequency()).toLocal8Bit().constData());
|
||||
QVERIFY2((requested.sampleSize() == actual.sampleSize()),
|
||||
QString("sampleSize: requested=%1, actual=%2").arg(requested.sampleSize()).arg(actual.sampleSize()).toLocal8Bit().constData());
|
||||
QVERIFY2((requested.codec() == actual.codec()),
|
||||
QString("codec: requested=%1, actual=%2").arg(requested.codec()).arg(actual.codec()).toLocal8Bit().constData());
|
||||
QVERIFY2((requested.byteOrder() == actual.byteOrder()),
|
||||
QString("byteOrder: requested=%1, actual=%2").arg(requested.byteOrder()).arg(actual.byteOrder()).toLocal8Bit().constData());
|
||||
QVERIFY2((requested.sampleType() == actual.sampleType()),
|
||||
QString("sampleType: requested=%1, actual=%2").arg(requested.sampleType()).arg(actual.sampleType()).toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
void tst_QAudioInput::invalidFormat_data()
|
||||
{
|
||||
QTest::addColumn<QAudioFormat>("invalidFormat");
|
||||
|
||||
QAudioFormat format;
|
||||
|
||||
QTest::newRow("Null Format")
|
||||
<< format;
|
||||
|
||||
format = audioDevice.preferredFormat();
|
||||
format.setChannelCount(0);
|
||||
QTest::newRow("Channel count 0")
|
||||
<< format;
|
||||
|
||||
format = audioDevice.preferredFormat();
|
||||
format.setSampleRate(0);
|
||||
QTest::newRow("Sample rate 0")
|
||||
<< format;
|
||||
|
||||
format = audioDevice.preferredFormat();
|
||||
format.setSampleSize(0);
|
||||
QTest::newRow("Sample size 0")
|
||||
<< format;
|
||||
}
|
||||
|
||||
void tst_QAudioInput::invalidFormat()
|
||||
{
|
||||
QFETCH(QAudioFormat, invalidFormat);
|
||||
|
||||
QVERIFY2(!audioDevice.isFormatSupported(invalidFormat),
|
||||
"isFormatSupported() is returning true on an invalid format");
|
||||
|
||||
QAudioInput audioInput(invalidFormat, this);
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
|
||||
audioInput.start();
|
||||
|
||||
// Check that error is raised
|
||||
QTRY_VERIFY2((audioInput.error() == QAudio::OpenError),"error() was not set to QAudio::OpenError after start()");
|
||||
}
|
||||
|
||||
void tst_QAudioInput::bufferSize()
|
||||
{
|
||||
QAudioInput audioInput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
|
||||
|
||||
audioInput.setBufferSize(512);
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(512)");
|
||||
QVERIFY2((audioInput.bufferSize() == 512),
|
||||
QString("bufferSize: requested=512, actual=%2").arg(audioInput.bufferSize()).toLocal8Bit().constData());
|
||||
|
||||
audioInput.setBufferSize(4096);
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(4096)");
|
||||
QVERIFY2((audioInput.bufferSize() == 4096),
|
||||
QString("bufferSize: requested=4096, actual=%2").arg(audioInput.bufferSize()).toLocal8Bit().constData());
|
||||
|
||||
audioInput.setBufferSize(8192);
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(8192)");
|
||||
QVERIFY2((audioInput.bufferSize() == 8192),
|
||||
QString("bufferSize: requested=8192, actual=%2").arg(audioInput.bufferSize()).toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
void tst_QAudioInput::notifyInterval()
|
||||
{
|
||||
QAudioInput audioInput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
|
||||
|
||||
audioInput.setNotifyInterval(50);
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(50)");
|
||||
QVERIFY2((audioInput.notifyInterval() == 50),
|
||||
QString("notifyInterval: requested=50, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
|
||||
|
||||
audioInput.setNotifyInterval(100);
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(100)");
|
||||
QVERIFY2((audioInput.notifyInterval() == 100),
|
||||
QString("notifyInterval: requested=100, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
|
||||
|
||||
audioInput.setNotifyInterval(250);
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(250)");
|
||||
QVERIFY2((audioInput.notifyInterval() == 250),
|
||||
QString("notifyInterval: requested=250, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
|
||||
|
||||
audioInput.setNotifyInterval(1000);
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(1000)");
|
||||
QVERIFY2((audioInput.notifyInterval() == 1000),
|
||||
QString("notifyInterval: requested=1000, actual=%2").arg(audioInput.notifyInterval()).toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
void tst_QAudioInput::disableNotifyInterval()
|
||||
{
|
||||
// Sets an invalid notification interval (QAudioInput::setNotifyInterval(0))
|
||||
// Checks that
|
||||
// - No error is raised (QAudioInput::error() returns QAudio::NoError)
|
||||
// - if <= 0, set to zero and disable notify signal
|
||||
|
||||
QAudioInput audioInput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
|
||||
|
||||
audioInput.setNotifyInterval(0);
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(0)");
|
||||
QVERIFY2((audioInput.notifyInterval() == 0),
|
||||
"notifyInterval() is not zero after setNotifyInterval(0)");
|
||||
|
||||
audioInput.setNotifyInterval(-1);
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(-1)");
|
||||
QVERIFY2((audioInput.notifyInterval() == 0),
|
||||
"notifyInterval() is not zero after setNotifyInterval(-1)");
|
||||
|
||||
//start and run to check if notify() is emitted
|
||||
if (audioFiles.size() > 0) {
|
||||
QAudioInput audioInputCheck(testFormats.at(0), this);
|
||||
audioInputCheck.setNotifyInterval(0);
|
||||
QSignalSpy notifySignal(&audioInputCheck, SIGNAL(notify()));
|
||||
audioFiles.at(0)->open(QIODevice::WriteOnly);
|
||||
audioInputCheck.start(audioFiles.at(0));
|
||||
QTest::qWait(3000); // 3 seconds should be plenty
|
||||
audioInputCheck.stop();
|
||||
QVERIFY2((notifySignal.count() == 0),
|
||||
QString("didn't disable notify interval: shouldn't have got any but got %1").arg(notifySignal.count()).toLocal8Bit().constData());
|
||||
audioFiles.at(0)->close();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioInput::stopWhileStopped()
|
||||
{
|
||||
// Calls QAudioInput::stop() when object is already in StoppedState
|
||||
// Checks that
|
||||
// - No state change occurs
|
||||
// - No error is raised (QAudioInput::error() returns QAudio::NoError)
|
||||
|
||||
QAudioInput audioInput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
|
||||
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
|
||||
audioInput.stop();
|
||||
|
||||
// Check that no state transition occurred
|
||||
QVERIFY2((stateSignal.count() == 0), "stop() while stopped is emitting a signal and it shouldn't");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
|
||||
}
|
||||
|
||||
void tst_QAudioInput::suspendWhileStopped()
|
||||
{
|
||||
// Calls QAudioInput::suspend() when object is already in StoppedState
|
||||
// Checks that
|
||||
// - No state change occurs
|
||||
// - No error is raised (QAudioInput::error() returns QAudio::NoError)
|
||||
|
||||
QAudioInput audioInput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
|
||||
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
|
||||
audioInput.suspend();
|
||||
|
||||
// Check that no state transition occurred
|
||||
QVERIFY2((stateSignal.count() == 0), "stop() while suspended is emitting a signal and it shouldn't");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
|
||||
}
|
||||
|
||||
void tst_QAudioInput::resumeWhileStopped()
|
||||
{
|
||||
// Calls QAudioInput::resume() when object is already in StoppedState
|
||||
// Checks that
|
||||
// - No state change occurs
|
||||
// - No error is raised (QAudioInput::error() returns QAudio::NoError)
|
||||
|
||||
QAudioInput audioInput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
|
||||
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
|
||||
audioInput.resume();
|
||||
|
||||
// Check that no state transition occurred
|
||||
QVERIFY2((stateSignal.count() == 0), "resume() while stopped is emitting a signal and it shouldn't");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after resume()");
|
||||
}
|
||||
|
||||
void tst_QAudioInput::pull()
|
||||
{
|
||||
for(int i=0; i<audioFiles.count(); i++) {
|
||||
QAudioInput audioInput(testFormats.at(i), this);
|
||||
|
||||
audioInput.setNotifyInterval(100);
|
||||
|
||||
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
|
||||
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
audioFiles.at(i)->open(QIODevice::WriteOnly);
|
||||
WavHeader wavHeader(testFormats.at(i));
|
||||
QVERIFY(wavHeader.write(*audioFiles.at(i)));
|
||||
|
||||
audioInput.start(audioFiles.at(i));
|
||||
|
||||
// Check that QAudioInput immediately transitions to ActiveState or IdleState
|
||||
QTRY_VERIFY2((stateSignal.count() > 0),"didn't emit signals on start()");
|
||||
QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
|
||||
"didn't transition to ActiveState or IdleState after start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
QVERIFY(audioInput.periodSize() > 0);
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that 'elapsed' increases
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
|
||||
|
||||
// Allow some recording to happen
|
||||
QTest::qWait(3000); // 3 seconds should be plenty
|
||||
|
||||
stateSignal.clear();
|
||||
|
||||
qint64 processedUs = audioInput.processedUSecs();
|
||||
|
||||
audioInput.stop();
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
|
||||
|
||||
QVERIFY2((processedUs > 2800000 && processedUs < 3200000),
|
||||
QString("processedUSecs() doesn't fall in acceptable range, should be 3040000 (%1)").arg(processedUs).toLocal8Bit().constData());
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
|
||||
QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
|
||||
QVERIFY2((notifySignal.count() > 20 && notifySignal.count() < 40),
|
||||
QString("notify() signals emitted (%1) should be 30").arg(notifySignal.count()).toLocal8Bit().constData());
|
||||
|
||||
WavHeader::writeDataLength(*audioFiles.at(i),audioFiles.at(i)->pos()-WavHeader::headerLength());
|
||||
audioFiles.at(i)->close();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioInput::pullSuspendResume()
|
||||
{
|
||||
for(int i=0; i<audioFiles.count(); i++) {
|
||||
QAudioInput audioInput(testFormats.at(i), this);
|
||||
|
||||
audioInput.setNotifyInterval(100);
|
||||
|
||||
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
|
||||
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
audioFiles.at(i)->open(QIODevice::WriteOnly);
|
||||
WavHeader wavHeader(testFormats.at(i));
|
||||
QVERIFY(wavHeader.write(*audioFiles.at(i)));
|
||||
|
||||
audioInput.start(audioFiles.at(i));
|
||||
|
||||
// Check that QAudioInput immediately transitions to ActiveState or IdleState
|
||||
QTRY_VERIFY2((stateSignal.count() > 0),"didn't emit signals on start()");
|
||||
QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
|
||||
"didn't transition to ActiveState or IdleState after start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
QVERIFY(audioInput.periodSize() > 0);
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that 'elapsed' increases
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
|
||||
|
||||
// Allow some recording to happen
|
||||
QTest::qWait(3000); // 3 seconds should be plenty
|
||||
|
||||
QVERIFY2((audioInput.state() == QAudio::ActiveState),
|
||||
"didn't transition to ActiveState after some recording");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after some recording");
|
||||
|
||||
stateSignal.clear();
|
||||
|
||||
audioInput.suspend();
|
||||
|
||||
// Give backends running in separate threads a chance to suspend.
|
||||
QTest::qWait(100);
|
||||
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioInput.state() == QAudio::SuspendedState), "didn't transitions to SuspendedState after stop()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that only 'elapsed', and not 'processed' increases while suspended
|
||||
qint64 elapsedUs = audioInput.elapsedUSecs();
|
||||
qint64 processedUs = audioInput.processedUSecs();
|
||||
QTest::qWait(1000);
|
||||
QVERIFY(audioInput.elapsedUSecs() > elapsedUs);
|
||||
QVERIFY(audioInput.processedUSecs() == processedUs);
|
||||
|
||||
audioInput.resume();
|
||||
|
||||
// Give backends running in separate threads a chance to resume.
|
||||
QTest::qWait(100);
|
||||
|
||||
// Check that QAudioInput immediately transitions to ActiveState
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal after resume(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioInput.state() == QAudio::ActiveState), "didn't transition to ActiveState after resume()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
|
||||
stateSignal.clear();
|
||||
|
||||
processedUs = audioInput.processedUSecs();
|
||||
|
||||
audioInput.stop();
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
|
||||
|
||||
QVERIFY2((processedUs > 2800000 && processedUs < 3200000),
|
||||
QString("processedUSecs() doesn't fall in acceptable range, should be 3040000 (%1)").arg(processedUs).toLocal8Bit().constData());
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
|
||||
QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
|
||||
QVERIFY2((notifySignal.count() > 20 && notifySignal.count() < 40),
|
||||
QString("notify() signals emitted (%1) should be 30").arg(notifySignal.count()).toLocal8Bit().constData());
|
||||
|
||||
WavHeader::writeDataLength(*audioFiles.at(i),audioFiles.at(i)->pos()-WavHeader::headerLength());
|
||||
audioFiles.at(i)->close();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioInput::push()
|
||||
{
|
||||
for(int i=0; i<audioFiles.count(); i++) {
|
||||
QAudioInput audioInput(testFormats.at(i), this);
|
||||
|
||||
audioInput.setNotifyInterval(100);
|
||||
|
||||
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
|
||||
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
audioFiles.at(i)->open(QIODevice::WriteOnly);
|
||||
WavHeader wavHeader(testFormats.at(i));
|
||||
QVERIFY(wavHeader.write(*audioFiles.at(i)));
|
||||
|
||||
QIODevice* feed = audioInput.start();
|
||||
|
||||
// Check that QAudioInput immediately transitions to IdleState
|
||||
QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
|
||||
QVERIFY2((audioInput.state() == QAudio::IdleState),
|
||||
"didn't transition to IdleState after start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
QVERIFY(audioInput.periodSize() > 0);
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that 'elapsed' increases
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
|
||||
|
||||
qint64 totalBytesRead = 0;
|
||||
bool firstBuffer = true;
|
||||
QByteArray buffer(AUDIO_BUFFER, 0);
|
||||
qint64 len = (testFormats.at(i).frequency()*testFormats.at(i).channels()*(testFormats.at(i).sampleSize()/8)*2); // 2 seconds
|
||||
while (totalBytesRead < len) {
|
||||
if (audioInput.bytesReady() >= audioInput.periodSize()) {
|
||||
qint64 bytesRead = feed->read(buffer.data(), audioInput.periodSize());
|
||||
audioFiles.at(i)->write(buffer.constData(),bytesRead);
|
||||
totalBytesRead+=bytesRead;
|
||||
if (firstBuffer && bytesRead) {
|
||||
// Check for transition to ActiveState when data is provided
|
||||
QVERIFY2((stateSignal.count() == 1),"didn't emit ActiveState signal on data");
|
||||
QVERIFY2((audioInput.state() == QAudio::ActiveState),
|
||||
"didn't transition to ActiveState after data");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
firstBuffer = false;
|
||||
}
|
||||
} else
|
||||
QTest::qWait(20);
|
||||
}
|
||||
|
||||
QTest::qWait(1000);
|
||||
|
||||
stateSignal.clear();
|
||||
|
||||
qint64 processedUs = audioInput.processedUSecs();
|
||||
|
||||
audioInput.stop();
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
|
||||
|
||||
QVERIFY2((processedUs > 1800000 && processedUs < 2200000),
|
||||
QString("processedUSecs() doesn't fall in acceptable range, should be 2040000 (%1)").arg(processedUs).toLocal8Bit().constData());
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
|
||||
QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
|
||||
QVERIFY2((notifySignal.count() > 20 && notifySignal.count() < 40),
|
||||
QString("notify() signals emitted (%1) should be 30").arg(notifySignal.count()).toLocal8Bit().constData());
|
||||
|
||||
WavHeader::writeDataLength(*audioFiles.at(i),audioFiles.at(i)->pos()-WavHeader::headerLength());
|
||||
audioFiles.at(i)->close();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioInput::pushSuspendResume()
|
||||
{
|
||||
for(int i=0; i<audioFiles.count(); i++) {
|
||||
QAudioInput audioInput(testFormats.at(i), this);
|
||||
|
||||
audioInput.setNotifyInterval(100);
|
||||
|
||||
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
|
||||
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
audioFiles.at(i)->open(QIODevice::WriteOnly);
|
||||
WavHeader wavHeader(testFormats.at(i));
|
||||
QVERIFY(wavHeader.write(*audioFiles.at(i)));
|
||||
|
||||
QIODevice* feed = audioInput.start();
|
||||
|
||||
// Check that QAudioInput immediately transitions to IdleState
|
||||
QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
|
||||
QVERIFY2((audioInput.state() == QAudio::IdleState),
|
||||
"didn't transition to IdleState after start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
QVERIFY(audioInput.periodSize() > 0);
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that 'elapsed' increases
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((audioInput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
|
||||
|
||||
qint64 totalBytesRead = 0;
|
||||
bool firstBuffer = true;
|
||||
QByteArray buffer(AUDIO_BUFFER, 0);
|
||||
qint64 len = (testFormats.at(i).frequency()*testFormats.at(i).channels()*(testFormats.at(i).sampleSize()/8)); // 1 seconds
|
||||
while (totalBytesRead < len) {
|
||||
if (audioInput.bytesReady() >= audioInput.periodSize()) {
|
||||
qint64 bytesRead = feed->read(buffer.data(), audioInput.periodSize());
|
||||
audioFiles.at(i)->write(buffer.constData(),bytesRead);
|
||||
totalBytesRead+=bytesRead;
|
||||
if (firstBuffer && bytesRead) {
|
||||
// Check for transition to ActiveState when data is provided
|
||||
QVERIFY2((stateSignal.count() == 1),"didn't emit ActiveState signal on data");
|
||||
QVERIFY2((audioInput.state() == QAudio::ActiveState),
|
||||
"didn't transition to ActiveState after data");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
firstBuffer = false;
|
||||
}
|
||||
} else
|
||||
QTest::qWait(20);
|
||||
}
|
||||
stateSignal.clear();
|
||||
|
||||
audioInput.suspend();
|
||||
|
||||
// Give backends running in separate threads a chance to suspend
|
||||
QTest::qWait(100);
|
||||
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioInput.state() == QAudio::SuspendedState), "didn't transitions to SuspendedState after stop()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that only 'elapsed', and not 'processed' increases while suspended
|
||||
qint64 elapsedUs = audioInput.elapsedUSecs();
|
||||
qint64 processedUs = audioInput.processedUSecs();
|
||||
QTest::qWait(1000);
|
||||
QVERIFY(audioInput.elapsedUSecs() > elapsedUs);
|
||||
QVERIFY(audioInput.processedUSecs() == processedUs);
|
||||
|
||||
audioInput.resume();
|
||||
|
||||
// Give backends running in separate threads a chance to resume.
|
||||
QTest::qWait(100);
|
||||
|
||||
// Check that QAudioInput immediately transitions to Active or IdleState
|
||||
QVERIFY2((stateSignal.count() > 0),"didn't emit signals on resume()");
|
||||
QVERIFY2((audioInput.state() == QAudio::ActiveState || audioInput.state() == QAudio::IdleState),
|
||||
"didn't transition to ActiveState or IdleState after resume()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
|
||||
QVERIFY(audioInput.periodSize() > 0);
|
||||
|
||||
// Let it play out what is in buffer and go to Idle before continue
|
||||
QTest::qWait(1000);
|
||||
stateSignal.clear();
|
||||
|
||||
// Read another seconds worth
|
||||
totalBytesRead = 0;
|
||||
firstBuffer = true;
|
||||
while (totalBytesRead < len) {
|
||||
if (audioInput.bytesReady() >= audioInput.periodSize()) {
|
||||
qint64 bytesRead = feed->read(buffer.data(), audioInput.periodSize());
|
||||
audioFiles.at(i)->write(buffer.constData(),bytesRead);
|
||||
totalBytesRead+=bytesRead;
|
||||
} else
|
||||
QTest::qWait(20);
|
||||
}
|
||||
stateSignal.clear();
|
||||
|
||||
processedUs = audioInput.processedUSecs();
|
||||
|
||||
audioInput.stop();
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
|
||||
|
||||
QVERIFY2((processedUs > 1800000 && processedUs < 2200000),
|
||||
QString("processedUSecs() doesn't fall in acceptable range, should be 2040000 (%1)").arg(processedUs).toLocal8Bit().constData());
|
||||
QVERIFY2((audioInput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
|
||||
|
||||
WavHeader::writeDataLength(*audioFiles.at(i),audioFiles.at(i)->pos()-WavHeader::headerLength());
|
||||
audioFiles.at(i)->close();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioInput::reset()
|
||||
{
|
||||
for(int i=0; i<audioFiles.count(); i++) {
|
||||
|
||||
// Try both push/pull.. the vagaries of Active vs Idle are tested elsewhere
|
||||
{
|
||||
QAudioInput audioInput(testFormats.at(i), this);
|
||||
|
||||
audioInput.setNotifyInterval(100);
|
||||
|
||||
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
|
||||
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
|
||||
|
||||
QIODevice* device = audioInput.start();
|
||||
// Check that QAudioInput immediately transitions to IdleState
|
||||
QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit IdleState signal on start()");
|
||||
QVERIFY2((audioInput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
QVERIFY(audioInput.periodSize() > 0);
|
||||
QTRY_VERIFY2((audioInput.bytesReady() > 0), "no bytes available after starting");
|
||||
|
||||
// Trigger a read
|
||||
QByteArray data = device->read(1);
|
||||
|
||||
QTRY_VERIFY2((audioInput.state() == QAudio::ActiveState), "didn't transition to ActiveState after read()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
stateSignal.clear();
|
||||
|
||||
audioInput.reset();
|
||||
QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit StoppedState signal after reset()");
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after reset()");
|
||||
QVERIFY2((audioInput.bytesReady() == 0), "buffer not cleared after reset()");
|
||||
}
|
||||
|
||||
{
|
||||
QAudioInput audioInput(testFormats.at(i), this);
|
||||
QBuffer buffer;
|
||||
|
||||
audioInput.setNotifyInterval(100);
|
||||
|
||||
QSignalSpy notifySignal(&audioInput, SIGNAL(notify()));
|
||||
QSignalSpy stateSignal(&audioInput, SIGNAL(stateChanged(QAudio::State)));
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
QVERIFY2((audioInput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
|
||||
|
||||
audioInput.start(&buffer);
|
||||
|
||||
// Check that QAudioInput immediately transitions to ActiveState
|
||||
QTRY_VERIFY2((stateSignal.count() >= 1),"didn't emit state changed signal on start()");
|
||||
QTRY_VERIFY2((audioInput.state() == QAudio::ActiveState), "didn't transition to ActiveState after start()");
|
||||
QVERIFY2((audioInput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
QVERIFY(audioInput.periodSize() > 0);
|
||||
QTRY_VERIFY2((audioInput.bytesReady() > 0), "no bytes available after starting");
|
||||
stateSignal.clear();
|
||||
|
||||
audioInput.reset();
|
||||
QTRY_VERIFY2((stateSignal.count() == 1),"didn't emit StoppedState signal after reset()");
|
||||
QVERIFY2((audioInput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after reset()");
|
||||
QVERIFY2((audioInput.bytesReady() == 0), "buffer not cleared after reset()");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioInput::cleanupTestCase()
|
||||
{
|
||||
QFile* file;
|
||||
|
||||
foreach (file, audioFiles) {
|
||||
file->remove();
|
||||
delete file;
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QAudioInput)
|
||||
|
||||
#include "tst_qaudioinput.moc"
|
||||
205
tests/auto/integration/qaudioinput/wavheader.cpp
Executable file
205
tests/auto/integration/qaudioinput/wavheader.cpp
Executable file
@@ -0,0 +1,205 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 test suite of the Qt Toolkit.
|
||||
**
|
||||
** $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/qendian.h>
|
||||
#include "wavheader.h"
|
||||
|
||||
|
||||
struct chunk
|
||||
{
|
||||
char id[4];
|
||||
quint32 size;
|
||||
};
|
||||
|
||||
struct RIFFHeader
|
||||
{
|
||||
chunk descriptor; // "RIFF"
|
||||
char type[4]; // "WAVE"
|
||||
};
|
||||
|
||||
struct WAVEHeader
|
||||
{
|
||||
chunk descriptor;
|
||||
quint16 audioFormat;
|
||||
quint16 numChannels;
|
||||
quint32 sampleRate;
|
||||
quint32 byteRate;
|
||||
quint16 blockAlign;
|
||||
quint16 bitsPerSample;
|
||||
};
|
||||
|
||||
struct DATAHeader
|
||||
{
|
||||
chunk descriptor;
|
||||
};
|
||||
|
||||
struct CombinedHeader
|
||||
{
|
||||
RIFFHeader riff;
|
||||
WAVEHeader wave;
|
||||
DATAHeader data;
|
||||
};
|
||||
|
||||
static const int HeaderLength = sizeof(CombinedHeader);
|
||||
|
||||
|
||||
WavHeader::WavHeader(const QAudioFormat &format, qint64 dataLength)
|
||||
: m_format(format)
|
||||
, m_dataLength(dataLength)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool WavHeader::read(QIODevice &device)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (!device.isSequential())
|
||||
result = device.seek(0);
|
||||
// else, assume that current position is the start of the header
|
||||
|
||||
if (result) {
|
||||
CombinedHeader header;
|
||||
result = (device.read(reinterpret_cast<char *>(&header), HeaderLength) == HeaderLength);
|
||||
if (result) {
|
||||
if ((memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0
|
||||
|| memcmp(&header.riff.descriptor.id, "RIFX", 4) == 0)
|
||||
&& memcmp(&header.riff.type, "WAVE", 4) == 0
|
||||
&& memcmp(&header.wave.descriptor.id, "fmt ", 4) == 0
|
||||
&& header.wave.audioFormat == 1 // PCM
|
||||
) {
|
||||
if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0)
|
||||
m_format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
else
|
||||
m_format.setByteOrder(QAudioFormat::BigEndian);
|
||||
|
||||
m_format.setChannels(qFromLittleEndian<quint16>(header.wave.numChannels));
|
||||
m_format.setCodec("audio/pcm");
|
||||
m_format.setFrequency(qFromLittleEndian<quint32>(header.wave.sampleRate));
|
||||
m_format.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample));
|
||||
|
||||
switch(header.wave.bitsPerSample) {
|
||||
case 8:
|
||||
m_format.setSampleType(QAudioFormat::UnSignedInt);
|
||||
break;
|
||||
case 16:
|
||||
m_format.setSampleType(QAudioFormat::SignedInt);
|
||||
break;
|
||||
default:
|
||||
result = false;
|
||||
}
|
||||
|
||||
m_dataLength = device.size() - HeaderLength;
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WavHeader::write(QIODevice &device)
|
||||
{
|
||||
CombinedHeader header;
|
||||
|
||||
memset(&header, 0, HeaderLength);
|
||||
|
||||
// RIFF header
|
||||
if (m_format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
memcpy(header.riff.descriptor.id,"RIFF",4);
|
||||
else
|
||||
memcpy(header.riff.descriptor.id,"RIFX",4);
|
||||
qToLittleEndian<quint32>(quint32(m_dataLength + HeaderLength - 8),
|
||||
reinterpret_cast<unsigned char*>(&header.riff.descriptor.size));
|
||||
memcpy(header.riff.type, "WAVE",4);
|
||||
|
||||
// WAVE header
|
||||
memcpy(header.wave.descriptor.id,"fmt ",4);
|
||||
qToLittleEndian<quint32>(quint32(16),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.descriptor.size));
|
||||
qToLittleEndian<quint16>(quint16(1),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.audioFormat));
|
||||
qToLittleEndian<quint16>(quint16(m_format.channels()),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.numChannels));
|
||||
qToLittleEndian<quint32>(quint32(m_format.frequency()),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.sampleRate));
|
||||
qToLittleEndian<quint32>(quint32(m_format.frequency() * m_format.channels() * m_format.sampleSize() / 8),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.byteRate));
|
||||
qToLittleEndian<quint16>(quint16(m_format.channels() * m_format.sampleSize() / 8),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.blockAlign));
|
||||
qToLittleEndian<quint16>(quint16(m_format.sampleSize()),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample));
|
||||
|
||||
// DATA header
|
||||
memcpy(header.data.descriptor.id,"data",4);
|
||||
qToLittleEndian<quint32>(quint32(m_dataLength),
|
||||
reinterpret_cast<unsigned char*>(&header.data.descriptor.size));
|
||||
|
||||
return (device.write(reinterpret_cast<const char *>(&header), HeaderLength) == HeaderLength);
|
||||
}
|
||||
|
||||
const QAudioFormat& WavHeader::format() const
|
||||
{
|
||||
return m_format;
|
||||
}
|
||||
|
||||
qint64 WavHeader::dataLength() const
|
||||
{
|
||||
return m_dataLength;
|
||||
}
|
||||
|
||||
qint64 WavHeader::headerLength()
|
||||
{
|
||||
return HeaderLength;
|
||||
}
|
||||
|
||||
bool WavHeader::writeDataLength(QIODevice &device, qint64 dataLength)
|
||||
{
|
||||
bool result = false;
|
||||
if (!device.isSequential()) {
|
||||
device.seek(40);
|
||||
unsigned char dataLengthLE[4];
|
||||
qToLittleEndian<quint32>(quint32(dataLength), dataLengthLE);
|
||||
result = (device.write(reinterpret_cast<const char *>(dataLengthLE), 4) == 4);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
80
tests/auto/integration/qaudioinput/wavheader.h
Executable file
80
tests/auto/integration/qaudioinput/wavheader.h
Executable file
@@ -0,0 +1,80 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the test suite of the Qt Toolkit.
|
||||
**
|
||||
** $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 WAVHEADER_H
|
||||
#define WAVHEADER_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qfile.h>
|
||||
#include <qaudioformat.h>
|
||||
|
||||
/**
|
||||
* Helper class for parsing WAV file headers.
|
||||
*
|
||||
* See https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
|
||||
*/
|
||||
class WavHeader
|
||||
{
|
||||
public:
|
||||
WavHeader(const QAudioFormat &format = QAudioFormat(),
|
||||
qint64 dataLength = 0);
|
||||
|
||||
// Reads WAV header and seeks to start of data
|
||||
bool read(QIODevice &device);
|
||||
|
||||
// Writes WAV header
|
||||
bool write(QIODevice &device);
|
||||
|
||||
const QAudioFormat& format() const;
|
||||
qint64 dataLength() const;
|
||||
|
||||
static qint64 headerLength();
|
||||
|
||||
static bool writeDataLength(QIODevice &device, qint64 dataLength);
|
||||
|
||||
private:
|
||||
QAudioFormat m_format;
|
||||
qint64 m_dataLength;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
12
tests/auto/integration/qaudiooutput/qaudiooutput.pro
Normal file
12
tests/auto/integration/qaudiooutput/qaudiooutput.pro
Normal file
@@ -0,0 +1,12 @@
|
||||
TARGET = tst_qaudiooutput
|
||||
|
||||
QT += core multimedia-private testlib
|
||||
CONFIG += no_private_qt_headers_warning
|
||||
|
||||
# This is more of a system test
|
||||
# CONFIG += testcase
|
||||
|
||||
DEFINES += SRCDIR=\\\"$$PWD/\\\"
|
||||
|
||||
HEADERS += wavheader.h
|
||||
SOURCES += wavheader.cpp tst_qaudiooutput.cpp
|
||||
957
tests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp
Executable file
957
tests/auto/integration/qaudiooutput/tst_qaudiooutput.cpp
Executable file
@@ -0,0 +1,957 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 test suite of the Qt Toolkit.
|
||||
**
|
||||
** $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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//TESTED_COMPONENT=src/multimedia
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <QtCore/qlocale.h>
|
||||
|
||||
#include <qaudiooutput.h>
|
||||
#include <qaudiodeviceinfo.h>
|
||||
#include <qaudioformat.h>
|
||||
#include <qaudio.h>
|
||||
|
||||
#include "wavheader.h"
|
||||
|
||||
#define AUDIO_BUFFER 192000
|
||||
|
||||
#ifndef QTRY_VERIFY2
|
||||
#define QTRY_VERIFY2(__expr,__msg) \
|
||||
do { \
|
||||
const int __step = 50; \
|
||||
const int __timeout = 5000; \
|
||||
if (!(__expr)) { \
|
||||
QTest::qWait(0); \
|
||||
} \
|
||||
for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
|
||||
QTest::qWait(__step); \
|
||||
} \
|
||||
QVERIFY2(__expr,__msg); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
class tst_QAudioOutput : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
tst_QAudioOutput(QObject* parent=0) : QObject(parent) {}
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
|
||||
void format();
|
||||
void invalidFormat_data();
|
||||
void invalidFormat();
|
||||
|
||||
void bufferSize();
|
||||
|
||||
void notifyInterval();
|
||||
void disableNotifyInterval();
|
||||
|
||||
void stopWhileStopped();
|
||||
void suspendWhileStopped();
|
||||
void resumeWhileStopped();
|
||||
|
||||
void pull();
|
||||
void pullSuspendResume();
|
||||
|
||||
void push();
|
||||
void pushSuspendResume();
|
||||
void pushUnderrun();
|
||||
|
||||
void cleanupTestCase();
|
||||
|
||||
private:
|
||||
QString formatToFileName(const QAudioFormat &format);
|
||||
QString workingDir();
|
||||
void createSineWaveData(const QAudioFormat &format, qint64 length, int frequency = 440);
|
||||
|
||||
QAudioDeviceInfo audioDevice;
|
||||
QList<QAudioFormat> testFormats;
|
||||
QList<QFile*> audioFiles;
|
||||
|
||||
QScopedPointer<QByteArray> m_byteArray;
|
||||
QScopedPointer<QBuffer> m_buffer;
|
||||
};
|
||||
|
||||
QString tst_QAudioOutput::formatToFileName(const QAudioFormat &format)
|
||||
{
|
||||
const QString formatEndian = (format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
? QString("LE") : QString("BE");
|
||||
|
||||
const QString formatSigned = (format.sampleType() == QAudioFormat::SignedInt)
|
||||
? QString("signed") : QString("unsigned");
|
||||
|
||||
return QString("%1_%2_%3_%4_%5")
|
||||
.arg(format.frequency())
|
||||
.arg(format.sampleSize())
|
||||
.arg(formatSigned)
|
||||
.arg(formatEndian)
|
||||
.arg(format.channels());
|
||||
}
|
||||
|
||||
|
||||
QString tst_QAudioOutput::workingDir()
|
||||
{
|
||||
QDir working(QString(SRCDIR));
|
||||
|
||||
if (working.exists())
|
||||
return QString(SRCDIR);
|
||||
|
||||
return QDir::currentPath();
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::createSineWaveData(const QAudioFormat &format, qint64 length, int frequency)
|
||||
{
|
||||
const int channelBytes = format.sampleSize() / 8;
|
||||
const int sampleBytes = format.channels() * channelBytes;
|
||||
|
||||
Q_ASSERT(length % sampleBytes == 0);
|
||||
Q_UNUSED(sampleBytes) // suppress warning in release builds
|
||||
|
||||
m_byteArray.reset(new QByteArray(length, 0));
|
||||
unsigned char *ptr = reinterpret_cast<unsigned char *>(m_byteArray->data());
|
||||
int sampleIndex = 0;
|
||||
|
||||
while (length) {
|
||||
const qreal x = qSin(2 * M_PI * frequency * qreal(sampleIndex % format.frequency()) / format.frequency());
|
||||
for (int i=0; i<format.channels(); ++i) {
|
||||
if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
const quint8 value = static_cast<quint8>((1.0 + x) / 2 * 255);
|
||||
*reinterpret_cast<quint8*>(ptr) = value;
|
||||
} else if (format.sampleSize() == 8 && format.sampleType() == QAudioFormat::SignedInt) {
|
||||
const qint8 value = static_cast<qint8>(x * 127);
|
||||
*reinterpret_cast<quint8*>(ptr) = value;
|
||||
} else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::UnSignedInt) {
|
||||
quint16 value = static_cast<quint16>((1.0 + x) / 2 * 65535);
|
||||
if (format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
qToLittleEndian<quint16>(value, ptr);
|
||||
else
|
||||
qToBigEndian<quint16>(value, ptr);
|
||||
} else if (format.sampleSize() == 16 && format.sampleType() == QAudioFormat::SignedInt) {
|
||||
qint16 value = static_cast<qint16>(x * 32767);
|
||||
if (format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
qToLittleEndian<qint16>(value, ptr);
|
||||
else
|
||||
qToBigEndian<qint16>(value, ptr);
|
||||
}
|
||||
|
||||
ptr += channelBytes;
|
||||
length -= channelBytes;
|
||||
}
|
||||
++sampleIndex;
|
||||
}
|
||||
|
||||
m_buffer.reset(new QBuffer(m_byteArray.data(), this));
|
||||
Q_ASSERT(m_buffer->open(QIODevice::ReadOnly));
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::initTestCase()
|
||||
{
|
||||
qRegisterMetaType<QAudioFormat>();
|
||||
|
||||
// Only perform tests if audio output device exists
|
||||
const QList<QAudioDeviceInfo> devices =
|
||||
QAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
|
||||
|
||||
if (devices.size() <= 0)
|
||||
QSKIP("No audio backend", SkipAll);
|
||||
|
||||
audioDevice = QAudioDeviceInfo::defaultOutputDevice();
|
||||
|
||||
|
||||
QAudioFormat format;
|
||||
|
||||
format.setCodec("audio/pcm");
|
||||
|
||||
if (audioDevice.isFormatSupported(audioDevice.preferredFormat()))
|
||||
testFormats.append(audioDevice.preferredFormat());
|
||||
|
||||
// PCM 8000 mono S8
|
||||
format.setFrequency(8000);
|
||||
format.setSampleSize(8);
|
||||
format.setSampleType(QAudioFormat::SignedInt);
|
||||
format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
format.setChannels(1);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
// PCM 11025 mono S16LE
|
||||
format.setFrequency(11025);
|
||||
format.setSampleSize(16);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
// PCM 22050 mono S16LE
|
||||
format.setFrequency(22050);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
// PCM 22050 stereo S16LE
|
||||
format.setChannels(2);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
// PCM 44100 stereo S16LE
|
||||
format.setFrequency(44100);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
// PCM 48000 stereo S16LE
|
||||
format.setFrequency(48000);
|
||||
if (audioDevice.isFormatSupported(format))
|
||||
testFormats.append(format);
|
||||
|
||||
QVERIFY(testFormats.size());
|
||||
|
||||
foreach (const QAudioFormat &format, testFormats) {
|
||||
qint64 len = (format.frequency()*format.channels()*(format.sampleSize()/8)*2); // 2 seconds
|
||||
createSineWaveData(format, len);
|
||||
// Write generate sine wave data to file
|
||||
QFile* file = new QFile(workingDir() + QString("generated") + formatToFileName(format) + QString(".wav"));
|
||||
if (file->open(QIODevice::WriteOnly)) {
|
||||
WavHeader wavHeader(format, len);
|
||||
wavHeader.write(*file);
|
||||
file->write(m_byteArray->data(), len);
|
||||
file->close();
|
||||
audioFiles.append(file);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::format()
|
||||
{
|
||||
QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QAudioFormat requested = audioDevice.preferredFormat();
|
||||
QAudioFormat actual = audioOutput.format();
|
||||
|
||||
QVERIFY2((requested.channels() == actual.channels()),
|
||||
QString("channels: requested=%1, actual=%2").arg(requested.channels()).arg(actual.channels()).toLocal8Bit().constData());
|
||||
QVERIFY2((requested.frequency() == actual.frequency()),
|
||||
QString("frequency: requested=%1, actual=%2").arg(requested.frequency()).arg(actual.frequency()).toLocal8Bit().constData());
|
||||
QVERIFY2((requested.sampleSize() == actual.sampleSize()),
|
||||
QString("sampleSize: requested=%1, actual=%2").arg(requested.sampleSize()).arg(actual.sampleSize()).toLocal8Bit().constData());
|
||||
QVERIFY2((requested.codec() == actual.codec()),
|
||||
QString("codec: requested=%1, actual=%2").arg(requested.codec()).arg(actual.codec()).toLocal8Bit().constData());
|
||||
QVERIFY2((requested.byteOrder() == actual.byteOrder()),
|
||||
QString("byteOrder: requested=%1, actual=%2").arg(requested.byteOrder()).arg(actual.byteOrder()).toLocal8Bit().constData());
|
||||
QVERIFY2((requested.sampleType() == actual.sampleType()),
|
||||
QString("sampleType: requested=%1, actual=%2").arg(requested.sampleType()).arg(actual.sampleType()).toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::invalidFormat_data()
|
||||
{
|
||||
QTest::addColumn<QAudioFormat>("invalidFormat");
|
||||
|
||||
QAudioFormat format;
|
||||
|
||||
QTest::newRow("Null Format")
|
||||
<< format;
|
||||
|
||||
format = audioDevice.preferredFormat();
|
||||
format.setChannelCount(0);
|
||||
QTest::newRow("Channel count 0")
|
||||
<< format;
|
||||
|
||||
format = audioDevice.preferredFormat();
|
||||
format.setSampleRate(0);
|
||||
QTest::newRow("Sample rate 0")
|
||||
<< format;
|
||||
|
||||
format = audioDevice.preferredFormat();
|
||||
format.setSampleSize(0);
|
||||
QTest::newRow("Sample size 0")
|
||||
<< format;
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::invalidFormat()
|
||||
{
|
||||
QFETCH(QAudioFormat, invalidFormat);
|
||||
|
||||
QVERIFY2(!audioDevice.isFormatSupported(invalidFormat),
|
||||
"isFormatSupported() is returning true on an invalid format");
|
||||
|
||||
QAudioOutput audioOutput(invalidFormat, this);
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
|
||||
audioOutput.start();
|
||||
// Check that error is raised
|
||||
QTRY_VERIFY2((audioOutput.error() == QAudio::OpenError),"error() was not set to QAudio::OpenError after start()");
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::bufferSize()
|
||||
{
|
||||
QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
|
||||
|
||||
audioOutput.setBufferSize(512);
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(512)");
|
||||
QVERIFY2((audioOutput.bufferSize() == 512),
|
||||
QString("bufferSize: requested=512, actual=%2").arg(audioOutput.bufferSize()).toLocal8Bit().constData());
|
||||
|
||||
audioOutput.setBufferSize(4096);
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(4096)");
|
||||
QVERIFY2((audioOutput.bufferSize() == 4096),
|
||||
QString("bufferSize: requested=4096, actual=%2").arg(audioOutput.bufferSize()).toLocal8Bit().constData());
|
||||
|
||||
audioOutput.setBufferSize(8192);
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setBufferSize(8192)");
|
||||
QVERIFY2((audioOutput.bufferSize() == 8192),
|
||||
QString("bufferSize: requested=8192, actual=%2").arg(audioOutput.bufferSize()).toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::notifyInterval()
|
||||
{
|
||||
QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
|
||||
|
||||
audioOutput.setNotifyInterval(50);
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(50)");
|
||||
QVERIFY2((audioOutput.notifyInterval() == 50),
|
||||
QString("notifyInterval: requested=50, actual=%2").arg(audioOutput.notifyInterval()).toLocal8Bit().constData());
|
||||
|
||||
audioOutput.setNotifyInterval(100);
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(100)");
|
||||
QVERIFY2((audioOutput.notifyInterval() == 100),
|
||||
QString("notifyInterval: requested=100, actual=%2").arg(audioOutput.notifyInterval()).toLocal8Bit().constData());
|
||||
|
||||
audioOutput.setNotifyInterval(250);
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(250)");
|
||||
QVERIFY2((audioOutput.notifyInterval() == 250),
|
||||
QString("notifyInterval: requested=250, actual=%2").arg(audioOutput.notifyInterval()).toLocal8Bit().constData());
|
||||
|
||||
audioOutput.setNotifyInterval(1000);
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(1000)");
|
||||
QVERIFY2((audioOutput.notifyInterval() == 1000),
|
||||
QString("notifyInterval: requested=1000, actual=%2").arg(audioOutput.notifyInterval()).toLocal8Bit().constData());
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::disableNotifyInterval()
|
||||
{
|
||||
// Sets an invalid notification interval (QAudioOutput::setNotifyInterval(0))
|
||||
// Checks that
|
||||
// - No error is raised (QAudioOutput::error() returns QAudio::NoError)
|
||||
// - if <= 0, set to zero and disable notify signal
|
||||
|
||||
QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError on creation");
|
||||
|
||||
audioOutput.setNotifyInterval(0);
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(0)");
|
||||
QVERIFY2((audioOutput.notifyInterval() == 0),
|
||||
"notifyInterval() is not zero after setNotifyInterval(0)");
|
||||
|
||||
audioOutput.setNotifyInterval(-1);
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after setNotifyInterval(-1)");
|
||||
QVERIFY2((audioOutput.notifyInterval() == 0),
|
||||
"notifyInterval() is not zero after setNotifyInterval(-1)");
|
||||
|
||||
//start and run to check if notify() is emitted
|
||||
if (audioFiles.size() > 0) {
|
||||
QAudioOutput audioOutputCheck(testFormats.at(0), this);
|
||||
audioOutputCheck.setNotifyInterval(0);
|
||||
QSignalSpy notifySignal(&audioOutputCheck, SIGNAL(notify()));
|
||||
audioFiles.at(0)->open(QIODevice::ReadOnly);
|
||||
audioOutputCheck.start(audioFiles.at(0));
|
||||
QTest::qWait(3000); // 3 seconds should be plenty
|
||||
audioOutputCheck.stop();
|
||||
QVERIFY2((notifySignal.count() == 0),
|
||||
QString("didn't disable notify interval: shouldn't have got any but got %1").arg(notifySignal.count()).toLocal8Bit().constData());
|
||||
audioFiles.at(0)->close();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::stopWhileStopped()
|
||||
{
|
||||
// Calls QAudioOutput::stop() when object is already in StoppedState
|
||||
// Checks that
|
||||
// - No state change occurs
|
||||
// - No error is raised (QAudioOutput::error() returns QAudio::NoError)
|
||||
|
||||
QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
|
||||
QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
|
||||
audioOutput.stop();
|
||||
|
||||
// Check that no state transition occurred
|
||||
QVERIFY2((stateSignal.count() == 0), "stop() while stopped is emitting a signal and it shouldn't");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::suspendWhileStopped()
|
||||
{
|
||||
// Calls QAudioOutput::suspend() when object is already in StoppedState
|
||||
// Checks that
|
||||
// - No state change occurs
|
||||
// - No error is raised (QAudioOutput::error() returns QAudio::NoError)
|
||||
|
||||
QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
|
||||
QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
|
||||
audioOutput.suspend();
|
||||
|
||||
// Check that no state transition occurred
|
||||
QVERIFY2((stateSignal.count() == 0), "stop() while suspended is emitting a signal and it shouldn't");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after stop()");
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::resumeWhileStopped()
|
||||
{
|
||||
// Calls QAudioOutput::resume() when object is already in StoppedState
|
||||
// Checks that
|
||||
// - No state change occurs
|
||||
// - No error is raised (QAudioOutput::error() returns QAudio::NoError)
|
||||
|
||||
QAudioOutput audioOutput(audioDevice.preferredFormat(), this);
|
||||
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
|
||||
QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
|
||||
audioOutput.resume();
|
||||
|
||||
// Check that no state transition occurred
|
||||
QVERIFY2((stateSignal.count() == 0), "resume() while stopped is emitting a signal and it shouldn't");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError after resume()");
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::pull()
|
||||
{
|
||||
for(int i=0; i<audioFiles.count(); i++) {
|
||||
QAudioOutput audioOutput(testFormats.at(i), this);
|
||||
|
||||
audioOutput.setNotifyInterval(100);
|
||||
|
||||
QSignalSpy notifySignal(&audioOutput, SIGNAL(notify()));
|
||||
QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
QVERIFY2((audioOutput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
audioFiles.at(i)->open(QIODevice::ReadOnly);
|
||||
audioFiles.at(i)->seek(WavHeader::headerLength());
|
||||
|
||||
audioOutput.start(audioFiles.at(i));
|
||||
|
||||
// Check that QAudioOutput immediately transitions to ActiveState
|
||||
QTRY_VERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
QVERIFY(audioOutput.periodSize() > 0);
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that 'elapsed' increases
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((audioOutput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
|
||||
|
||||
// Wait until playback finishes
|
||||
QTest::qWait(3000); // 3 seconds should be plenty
|
||||
|
||||
QVERIFY2(audioFiles.at(i)->atEnd(), "didn't play to EOF");
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit IdleState signal when at EOF, got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
|
||||
stateSignal.clear();
|
||||
|
||||
qint64 processedUs = audioOutput.processedUSecs();
|
||||
|
||||
audioOutput.stop();
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
|
||||
|
||||
QVERIFY2((processedUs == 2000000),
|
||||
QString("processedUSecs() doesn't equal file duration in us (%1)").arg(processedUs).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
|
||||
QVERIFY2((audioOutput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
|
||||
QVERIFY2((notifySignal.count() > 15 && notifySignal.count() < 25),
|
||||
QString("too many notify() signals emitted (%1)").arg(notifySignal.count()).toLocal8Bit().constData());
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::pullSuspendResume()
|
||||
{
|
||||
for(int i=0; i<audioFiles.count(); i++) {
|
||||
QAudioOutput audioOutput(testFormats.at(i), this);
|
||||
|
||||
audioOutput.setNotifyInterval(100);
|
||||
|
||||
QSignalSpy notifySignal(&audioOutput, SIGNAL(notify()));
|
||||
QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
QVERIFY2((audioOutput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
audioFiles.at(i)->open(QIODevice::ReadOnly);
|
||||
audioFiles.at(i)->seek(WavHeader::headerLength());
|
||||
|
||||
audioOutput.start(audioFiles.at(i));
|
||||
// Check that QAudioOutput immediately transitions to ActiveState
|
||||
QTRY_VERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
QVERIFY(audioOutput.periodSize() > 0);
|
||||
stateSignal.clear();
|
||||
|
||||
// Wait for half of clip to play
|
||||
QTest::qWait(1000);
|
||||
|
||||
audioOutput.suspend();
|
||||
|
||||
// Give backends running in separate threads a chance to suspend.
|
||||
QTest::qWait(100);
|
||||
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead")
|
||||
.arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::SuspendedState), "didn't transition to SuspendedState after suspend()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after suspend()");
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that only 'elapsed', and not 'processed' increases while suspended
|
||||
qint64 elapsedUs = audioOutput.elapsedUSecs();
|
||||
qint64 processedUs = audioOutput.processedUSecs();
|
||||
QTest::qWait(1000);
|
||||
QVERIFY(audioOutput.elapsedUSecs() > elapsedUs);
|
||||
QVERIFY(audioOutput.processedUSecs() == processedUs);
|
||||
|
||||
audioOutput.resume();
|
||||
|
||||
// Give backends running in separate threads a chance to suspend.
|
||||
QTest::qWait(100);
|
||||
|
||||
// Check that QAudioOutput immediately transitions to ActiveState
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal after resume(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after resume()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
|
||||
stateSignal.clear();
|
||||
|
||||
// Wait until playback finishes
|
||||
QTest::qWait(3000); // 3 seconds should be plenty
|
||||
|
||||
QVERIFY2(audioFiles.at(i)->atEnd(), "didn't play to EOF");
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit IdleState signal when at EOF, got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
|
||||
stateSignal.clear();
|
||||
|
||||
processedUs = audioOutput.processedUSecs();
|
||||
|
||||
audioOutput.stop();
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
|
||||
|
||||
QVERIFY2((processedUs == 2000000),
|
||||
QString("processedUSecs() doesn't equal file duration in us (%1)").arg(processedUs).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
|
||||
QVERIFY2((audioOutput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::push()
|
||||
{
|
||||
for(int i=0; i<audioFiles.count(); i++) {
|
||||
QAudioOutput audioOutput(testFormats.at(i), this);
|
||||
|
||||
audioOutput.setNotifyInterval(100);
|
||||
|
||||
QSignalSpy notifySignal(&audioOutput, SIGNAL(notify()));
|
||||
QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
QVERIFY2((audioOutput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
audioFiles.at(i)->open(QIODevice::ReadOnly);
|
||||
audioFiles.at(i)->seek(WavHeader::headerLength());
|
||||
|
||||
QIODevice* feed = audioOutput.start();
|
||||
|
||||
// Check that QAudioOutput immediately transitions to IdleState
|
||||
QTRY_VERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
QVERIFY(audioOutput.periodSize() > 0);
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that 'elapsed' increases
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((audioOutput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
|
||||
QVERIFY2((audioOutput.processedUSecs() == qint64(0)), "processedUSecs() is not zero after start()");
|
||||
|
||||
qint64 written = 0;
|
||||
bool firstBuffer = true;
|
||||
QByteArray buffer(AUDIO_BUFFER, 0);
|
||||
|
||||
while (written < audioFiles.at(i)->size()-WavHeader::headerLength()) {
|
||||
|
||||
if (audioOutput.bytesFree() >= audioOutput.periodSize()) {
|
||||
qint64 len = audioFiles.at(i)->read(buffer.data(),audioOutput.periodSize());
|
||||
written += feed->write(buffer.constData(), len);
|
||||
|
||||
if (firstBuffer) {
|
||||
// Check for transition to ActiveState when data is provided
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal after receiving data, got %1 signals instead")
|
||||
.arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
|
||||
firstBuffer = false;
|
||||
}
|
||||
} else
|
||||
QTest::qWait(20);
|
||||
}
|
||||
stateSignal.clear();
|
||||
|
||||
// Wait until playback finishes
|
||||
QTest::qWait(3000); // 3 seconds should be plenty
|
||||
|
||||
QVERIFY2(audioFiles.at(i)->atEnd(), "didn't play to EOF");
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit IdleState signal when at EOF, got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
|
||||
stateSignal.clear();
|
||||
|
||||
qint64 processedUs = audioOutput.processedUSecs();
|
||||
|
||||
audioOutput.stop();
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
|
||||
|
||||
QVERIFY2((processedUs == 2000000),
|
||||
QString("processedUSecs() doesn't equal file duration in us (%1)").arg(processedUs).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
|
||||
QVERIFY2((audioOutput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
|
||||
QVERIFY2((notifySignal.count() > 15 && notifySignal.count() < 25),
|
||||
QString("too many notify() signals emitted (%1)").arg(notifySignal.count()).toLocal8Bit().constData());
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::pushSuspendResume()
|
||||
{
|
||||
for(int i=0; i<audioFiles.count(); i++) {
|
||||
QAudioOutput audioOutput(testFormats.at(i), this);
|
||||
|
||||
audioOutput.setNotifyInterval(100);
|
||||
|
||||
QSignalSpy notifySignal(&audioOutput, SIGNAL(notify()));
|
||||
QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
QVERIFY2((audioOutput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
audioFiles.at(i)->open(QIODevice::ReadOnly);
|
||||
audioFiles.at(i)->seek(WavHeader::headerLength());
|
||||
|
||||
QIODevice* feed = audioOutput.start();
|
||||
|
||||
// Check that QAudioOutput immediately transitions to IdleState
|
||||
QTRY_VERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
QVERIFY(audioOutput.periodSize() > 0);
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that 'elapsed' increases
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((audioOutput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
|
||||
QVERIFY2((audioOutput.processedUSecs() == qint64(0)), "processedUSecs() is not zero after start()");
|
||||
|
||||
qint64 written = 0;
|
||||
bool firstBuffer = true;
|
||||
QByteArray buffer(AUDIO_BUFFER, 0);
|
||||
|
||||
// Play half of the clip
|
||||
while (written < (audioFiles.at(i)->size()-WavHeader::headerLength())/2) {
|
||||
|
||||
if (audioOutput.bytesFree() >= audioOutput.periodSize()) {
|
||||
qint64 len = audioFiles.at(i)->read(buffer.data(),audioOutput.periodSize());
|
||||
written += feed->write(buffer.constData(), len);
|
||||
|
||||
if (firstBuffer) {
|
||||
// Check for transition to ActiveState when data is provided
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal after receiving data, got %1 signals instead")
|
||||
.arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
|
||||
firstBuffer = false;
|
||||
}
|
||||
} else
|
||||
QTest::qWait(20);
|
||||
}
|
||||
stateSignal.clear();
|
||||
|
||||
audioOutput.suspend();
|
||||
|
||||
// Give backends running in separate threads a chance to suspend.
|
||||
QTest::qWait(100);
|
||||
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit SuspendedState signal after suspend(), got %1 signals instead")
|
||||
.arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::SuspendedState), "didn't transition to SuspendedState after suspend()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after suspend()");
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that only 'elapsed', and not 'processed' increases while suspended
|
||||
qint64 elapsedUs = audioOutput.elapsedUSecs();
|
||||
qint64 processedUs = audioOutput.processedUSecs();
|
||||
QTest::qWait(1000);
|
||||
QVERIFY(audioOutput.elapsedUSecs() > elapsedUs);
|
||||
QVERIFY(audioOutput.processedUSecs() == processedUs);
|
||||
|
||||
audioOutput.resume();
|
||||
|
||||
// Give backends running in separate threads a chance to suspend.
|
||||
QTest::qWait(100);
|
||||
|
||||
// Check that QAudioOutput immediately transitions to ActiveState
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal after resume(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after resume()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after resume()");
|
||||
stateSignal.clear();
|
||||
|
||||
// Play rest of the clip
|
||||
while (!audioFiles.at(i)->atEnd()) {
|
||||
if (audioOutput.bytesFree() >= audioOutput.periodSize()) {
|
||||
qint64 len = audioFiles.at(i)->read(buffer.data(),audioOutput.periodSize());
|
||||
written += feed->write(buffer.constData(), len);
|
||||
} else
|
||||
QTest::qWait(20);
|
||||
}
|
||||
stateSignal.clear();
|
||||
|
||||
// Wait until playback finishes
|
||||
QTest::qWait(1000); // 1 seconds should be plenty
|
||||
|
||||
QVERIFY2(audioFiles.at(i)->atEnd(), "didn't play to EOF");
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit IdleState signal when at EOF, got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
|
||||
stateSignal.clear();
|
||||
|
||||
processedUs = audioOutput.processedUSecs();
|
||||
|
||||
audioOutput.stop();
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
|
||||
|
||||
QVERIFY2((processedUs == 2000000),
|
||||
QString("processedUSecs() doesn't equal file duration in us (%1)").arg(processedUs).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
|
||||
QVERIFY2((audioOutput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::pushUnderrun()
|
||||
{
|
||||
for(int i=0; i<audioFiles.count(); i++) {
|
||||
QAudioOutput audioOutput(testFormats.at(i), this);
|
||||
|
||||
audioOutput.setNotifyInterval(100);
|
||||
|
||||
QSignalSpy notifySignal(&audioOutput, SIGNAL(notify()));
|
||||
QSignalSpy stateSignal(&audioOutput, SIGNAL(stateChanged(QAudio::State)));
|
||||
|
||||
// Check that we are in the default state before calling start
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "state() was not set to StoppedState before start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() was not set to QAudio::NoError before start()");
|
||||
QVERIFY2((audioOutput.elapsedUSecs() == qint64(0)),"elapsedUSecs() not zero on creation");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
audioFiles.at(i)->open(QIODevice::ReadOnly);
|
||||
audioFiles.at(i)->seek(WavHeader::headerLength());
|
||||
|
||||
QIODevice* feed = audioOutput.start();
|
||||
|
||||
// Check that QAudioOutput immediately transitions to IdleState
|
||||
QTRY_VERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal on start(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState after start()");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after start()");
|
||||
QVERIFY(audioOutput.periodSize() > 0);
|
||||
stateSignal.clear();
|
||||
|
||||
// Check that 'elapsed' increases
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((audioOutput.elapsedUSecs() > 0), "elapsedUSecs() is still zero after start()");
|
||||
QVERIFY2((audioOutput.processedUSecs() == qint64(0)), "processedUSecs() is not zero after start()");
|
||||
|
||||
qint64 written = 0;
|
||||
bool firstBuffer = true;
|
||||
QByteArray buffer(AUDIO_BUFFER, 0);
|
||||
|
||||
// Play half of the clip
|
||||
while (written < (audioFiles.at(i)->size()-WavHeader::headerLength())/2) {
|
||||
|
||||
if (audioOutput.bytesFree() >= audioOutput.periodSize()) {
|
||||
qint64 len = audioFiles.at(i)->read(buffer.data(),audioOutput.periodSize());
|
||||
written += feed->write(buffer.constData(), len);
|
||||
|
||||
if (firstBuffer) {
|
||||
// Check for transition to ActiveState when data is provided
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal after receiving data, got %1 signals instead")
|
||||
.arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
|
||||
firstBuffer = false;
|
||||
}
|
||||
} else
|
||||
QTest::qWait(20);
|
||||
}
|
||||
stateSignal.clear();
|
||||
|
||||
// Wait for data to be played
|
||||
QTest::qWait(1000);
|
||||
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit IdleState signal after suspend(), got %1 signals instead")
|
||||
.arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transition to IdleState, no data");
|
||||
QVERIFY2((audioOutput.error() == QAudio::UnderrunError), "error state is not equal to QAudio::UnderrunError, no data");
|
||||
stateSignal.clear();
|
||||
|
||||
firstBuffer = true;
|
||||
// Play rest of the clip
|
||||
while (!audioFiles.at(i)->atEnd()) {
|
||||
if (audioOutput.bytesFree() >= audioOutput.periodSize()) {
|
||||
qint64 len = audioFiles.at(i)->read(buffer.data(),audioOutput.periodSize());
|
||||
written += feed->write(buffer.constData(), len);
|
||||
if (firstBuffer) {
|
||||
// Check for transition to ActiveState when data is provided
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit signal after receiving data, got %1 signals instead")
|
||||
.arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::ActiveState), "didn't transition to ActiveState after receiving data");
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error state is not equal to QAudio::NoError after receiving data");
|
||||
firstBuffer = false;
|
||||
}
|
||||
} else
|
||||
QTest::qWait(20);
|
||||
}
|
||||
stateSignal.clear();
|
||||
|
||||
// Wait until playback finishes
|
||||
QTest::qWait(1000); // 1 seconds should be plenty
|
||||
|
||||
QVERIFY2(audioFiles.at(i)->atEnd(), "didn't play to EOF");
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit IdleState signal when at EOF, got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::IdleState), "didn't transitions to IdleState when at EOF");
|
||||
stateSignal.clear();
|
||||
|
||||
qint64 processedUs = audioOutput.processedUSecs();
|
||||
|
||||
audioOutput.stop();
|
||||
QTest::qWait(40);
|
||||
QVERIFY2((stateSignal.count() == 1),
|
||||
QString("didn't emit StoppedState signal after stop(), got %1 signals instead").arg(stateSignal.count()).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.state() == QAudio::StoppedState), "didn't transitions to StoppedState after stop()");
|
||||
|
||||
QVERIFY2((processedUs == 2000000),
|
||||
QString("processedUSecs() doesn't equal file duration in us (%1)").arg(processedUs).toLocal8Bit().constData());
|
||||
QVERIFY2((audioOutput.error() == QAudio::NoError), "error() is not QAudio::NoError after stop()");
|
||||
QVERIFY2((audioOutput.elapsedUSecs() == (qint64)0), "elapsedUSecs() not equal to zero in StoppedState");
|
||||
|
||||
audioFiles.at(i)->close();
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QAudioOutput::cleanupTestCase()
|
||||
{
|
||||
QFile* file;
|
||||
|
||||
foreach (file, audioFiles) {
|
||||
file->remove();
|
||||
delete file;
|
||||
}
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QAudioOutput)
|
||||
|
||||
#include "tst_qaudiooutput.moc"
|
||||
205
tests/auto/integration/qaudiooutput/wavheader.cpp
Executable file
205
tests/auto/integration/qaudiooutput/wavheader.cpp
Executable file
@@ -0,0 +1,205 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 test suite of the Qt Toolkit.
|
||||
**
|
||||
** $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/qendian.h>
|
||||
#include "wavheader.h"
|
||||
|
||||
|
||||
struct chunk
|
||||
{
|
||||
char id[4];
|
||||
quint32 size;
|
||||
};
|
||||
|
||||
struct RIFFHeader
|
||||
{
|
||||
chunk descriptor; // "RIFF"
|
||||
char type[4]; // "WAVE"
|
||||
};
|
||||
|
||||
struct WAVEHeader
|
||||
{
|
||||
chunk descriptor;
|
||||
quint16 audioFormat;
|
||||
quint16 numChannels;
|
||||
quint32 sampleRate;
|
||||
quint32 byteRate;
|
||||
quint16 blockAlign;
|
||||
quint16 bitsPerSample;
|
||||
};
|
||||
|
||||
struct DATAHeader
|
||||
{
|
||||
chunk descriptor;
|
||||
};
|
||||
|
||||
struct CombinedHeader
|
||||
{
|
||||
RIFFHeader riff;
|
||||
WAVEHeader wave;
|
||||
DATAHeader data;
|
||||
};
|
||||
|
||||
static const int HeaderLength = sizeof(CombinedHeader);
|
||||
|
||||
|
||||
WavHeader::WavHeader(const QAudioFormat &format, qint64 dataLength)
|
||||
: m_format(format)
|
||||
, m_dataLength(dataLength)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
bool WavHeader::read(QIODevice &device)
|
||||
{
|
||||
bool result = true;
|
||||
|
||||
if (!device.isSequential())
|
||||
result = device.seek(0);
|
||||
// else, assume that current position is the start of the header
|
||||
|
||||
if (result) {
|
||||
CombinedHeader header;
|
||||
result = (device.read(reinterpret_cast<char *>(&header), HeaderLength) == HeaderLength);
|
||||
if (result) {
|
||||
if ((memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0
|
||||
|| memcmp(&header.riff.descriptor.id, "RIFX", 4) == 0)
|
||||
&& memcmp(&header.riff.type, "WAVE", 4) == 0
|
||||
&& memcmp(&header.wave.descriptor.id, "fmt ", 4) == 0
|
||||
&& header.wave.audioFormat == 1 // PCM
|
||||
) {
|
||||
if (memcmp(&header.riff.descriptor.id, "RIFF", 4) == 0)
|
||||
m_format.setByteOrder(QAudioFormat::LittleEndian);
|
||||
else
|
||||
m_format.setByteOrder(QAudioFormat::BigEndian);
|
||||
|
||||
m_format.setChannels(qFromLittleEndian<quint16>(header.wave.numChannels));
|
||||
m_format.setCodec("audio/pcm");
|
||||
m_format.setFrequency(qFromLittleEndian<quint32>(header.wave.sampleRate));
|
||||
m_format.setSampleSize(qFromLittleEndian<quint16>(header.wave.bitsPerSample));
|
||||
|
||||
switch(header.wave.bitsPerSample) {
|
||||
case 8:
|
||||
m_format.setSampleType(QAudioFormat::UnSignedInt);
|
||||
break;
|
||||
case 16:
|
||||
m_format.setSampleType(QAudioFormat::SignedInt);
|
||||
break;
|
||||
default:
|
||||
result = false;
|
||||
}
|
||||
|
||||
m_dataLength = device.size() - HeaderLength;
|
||||
} else {
|
||||
result = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool WavHeader::write(QIODevice &device)
|
||||
{
|
||||
CombinedHeader header;
|
||||
|
||||
memset(&header, 0, HeaderLength);
|
||||
|
||||
// RIFF header
|
||||
if (m_format.byteOrder() == QAudioFormat::LittleEndian)
|
||||
memcpy(header.riff.descriptor.id,"RIFF",4);
|
||||
else
|
||||
memcpy(header.riff.descriptor.id,"RIFX",4);
|
||||
qToLittleEndian<quint32>(quint32(m_dataLength + HeaderLength - 8),
|
||||
reinterpret_cast<unsigned char*>(&header.riff.descriptor.size));
|
||||
memcpy(header.riff.type, "WAVE",4);
|
||||
|
||||
// WAVE header
|
||||
memcpy(header.wave.descriptor.id,"fmt ",4);
|
||||
qToLittleEndian<quint32>(quint32(16),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.descriptor.size));
|
||||
qToLittleEndian<quint16>(quint16(1),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.audioFormat));
|
||||
qToLittleEndian<quint16>(quint16(m_format.channels()),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.numChannels));
|
||||
qToLittleEndian<quint32>(quint32(m_format.frequency()),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.sampleRate));
|
||||
qToLittleEndian<quint32>(quint32(m_format.frequency() * m_format.channels() * m_format.sampleSize() / 8),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.byteRate));
|
||||
qToLittleEndian<quint16>(quint16(m_format.channels() * m_format.sampleSize() / 8),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.blockAlign));
|
||||
qToLittleEndian<quint16>(quint16(m_format.sampleSize()),
|
||||
reinterpret_cast<unsigned char*>(&header.wave.bitsPerSample));
|
||||
|
||||
// DATA header
|
||||
memcpy(header.data.descriptor.id,"data",4);
|
||||
qToLittleEndian<quint32>(quint32(m_dataLength),
|
||||
reinterpret_cast<unsigned char*>(&header.data.descriptor.size));
|
||||
|
||||
return (device.write(reinterpret_cast<const char *>(&header), HeaderLength) == HeaderLength);
|
||||
}
|
||||
|
||||
const QAudioFormat& WavHeader::format() const
|
||||
{
|
||||
return m_format;
|
||||
}
|
||||
|
||||
qint64 WavHeader::dataLength() const
|
||||
{
|
||||
return m_dataLength;
|
||||
}
|
||||
|
||||
qint64 WavHeader::headerLength()
|
||||
{
|
||||
return HeaderLength;
|
||||
}
|
||||
|
||||
bool WavHeader::writeDataLength(QIODevice &device, qint64 dataLength)
|
||||
{
|
||||
bool result = false;
|
||||
if (!device.isSequential()) {
|
||||
device.seek(40);
|
||||
unsigned char dataLengthLE[4];
|
||||
qToLittleEndian<quint32>(quint32(dataLength), dataLengthLE);
|
||||
result = (device.write(reinterpret_cast<const char *>(dataLengthLE), 4) == 4);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
80
tests/auto/integration/qaudiooutput/wavheader.h
Executable file
80
tests/auto/integration/qaudiooutput/wavheader.h
Executable file
@@ -0,0 +1,80 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 test suite of the Qt Toolkit.
|
||||
**
|
||||
** $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 WAVHEADER_H
|
||||
#define WAVHEADER_H
|
||||
|
||||
#include <QtCore/qobject.h>
|
||||
#include <QtCore/qfile.h>
|
||||
#include <qaudioformat.h>
|
||||
|
||||
/**
|
||||
* Helper class for parsing WAV file headers.
|
||||
*
|
||||
* See https://ccrma.stanford.edu/courses/422/projects/WaveFormat/
|
||||
*/
|
||||
class WavHeader
|
||||
{
|
||||
public:
|
||||
WavHeader(const QAudioFormat &format = QAudioFormat(),
|
||||
qint64 dataLength = 0);
|
||||
|
||||
// Reads WAV header and seeks to start of data
|
||||
bool read(QIODevice &device);
|
||||
|
||||
// Writes WAV header
|
||||
bool write(QIODevice &device);
|
||||
|
||||
const QAudioFormat& format() const;
|
||||
qint64 dataLength() const;
|
||||
|
||||
static qint64 headerLength();
|
||||
|
||||
static bool writeDataLength(QIODevice &device, qint64 dataLength);
|
||||
|
||||
private:
|
||||
QAudioFormat m_format;
|
||||
qint64 m_dataLength;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
10
tests/auto/integration/qcamerabackend/qcamerabackend.pro
Normal file
10
tests/auto/integration/qcamerabackend/qcamerabackend.pro
Normal file
@@ -0,0 +1,10 @@
|
||||
CONFIG += testcase
|
||||
TARGET = tst_qcamerabackend
|
||||
|
||||
QT += multimedia-private multimediawidgets-private testlib
|
||||
CONFIG += no_private_qt_headers_warning
|
||||
|
||||
# This is more of a system test
|
||||
# CONFIG += testcase
|
||||
|
||||
SOURCES += tst_qcamerabackend.cpp
|
||||
621
tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp
Normal file
621
tests/auto/integration/qcamerabackend/tst_qcamerabackend.cpp
Normal file
@@ -0,0 +1,621 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//TESTED_COMPONENT=src/multimedia
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <QtGui/QImageReader>
|
||||
#include <QDebug>
|
||||
|
||||
#include <qabstractvideosurface.h>
|
||||
#include <qcameracontrol.h>
|
||||
#include <qcameralockscontrol.h>
|
||||
#include <qcameraexposurecontrol.h>
|
||||
#include <qcameraflashcontrol.h>
|
||||
#include <qcamerafocuscontrol.h>
|
||||
#include <qcameraimagecapturecontrol.h>
|
||||
#include <qimageencodercontrol.h>
|
||||
#include <qcameraimageprocessingcontrol.h>
|
||||
#include <qcameracapturebufferformatcontrol.h>
|
||||
#include <qcameracapturedestinationcontrol.h>
|
||||
#include <qmediaservice.h>
|
||||
#include <qcamera.h>
|
||||
#include <qcameraimagecapture.h>
|
||||
#include <qgraphicsvideoitem.h>
|
||||
#include <qvideorenderercontrol.h>
|
||||
#include <qvideowidget.h>
|
||||
#include <qvideowindowcontrol.h>
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
// Eventually these will make it into qtestcase.h
|
||||
// but we might need to tweak the timeout values here.
|
||||
#ifndef QTRY_COMPARE
|
||||
#define QTRY_COMPARE(__expr, __expected) \
|
||||
do { \
|
||||
const int __step = 50; \
|
||||
const int __timeout = 10000; \
|
||||
if ((__expr) != (__expected)) { \
|
||||
QTest::qWait(0); \
|
||||
} \
|
||||
for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \
|
||||
QTest::qWait(__step); \
|
||||
} \
|
||||
QCOMPARE(__expr, __expected); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
#ifndef QTRY_VERIFY
|
||||
#define QTRY_VERIFY(__expr) \
|
||||
do { \
|
||||
const int __step = 50; \
|
||||
const int __timeout = 10000; \
|
||||
if (!(__expr)) { \
|
||||
QTest::qWait(0); \
|
||||
} \
|
||||
for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
|
||||
QTest::qWait(__step); \
|
||||
} \
|
||||
QVERIFY(__expr); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
|
||||
#define QTRY_WAIT(code, __expr) \
|
||||
do { \
|
||||
const int __step = 50; \
|
||||
const int __timeout = 10000; \
|
||||
if (!(__expr)) { \
|
||||
QTest::qWait(0); \
|
||||
} \
|
||||
for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
|
||||
do { code } while(0); \
|
||||
QTest::qWait(__step); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
/*
|
||||
This is the backend conformance test.
|
||||
|
||||
Since it relies on platform media framework and sound hardware
|
||||
it may be less stable.
|
||||
*/
|
||||
|
||||
class tst_QCameraBackend: public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public slots:
|
||||
void initTestCase();
|
||||
void cleanupTestCase();
|
||||
|
||||
private slots:
|
||||
void testAvailableDevices();
|
||||
void testDeviceDescription();
|
||||
void testCtorWithDevice();
|
||||
|
||||
void testCameraStates();
|
||||
void testCaptureMode();
|
||||
void testCameraCapture();
|
||||
void testCaptureToBuffer();
|
||||
void testCameraCaptureMetadata();
|
||||
void testExposureCompensation();
|
||||
void testExposureMode();
|
||||
private:
|
||||
};
|
||||
|
||||
void tst_QCameraBackend::initTestCase()
|
||||
{
|
||||
qRegisterMetaType<QtMultimedia::MetaData>("QtMultimedia::MetaData");
|
||||
|
||||
QCamera camera;
|
||||
if (!camera.isAvailable())
|
||||
QSKIP("Camera is not available", SkipAll);
|
||||
}
|
||||
|
||||
void tst_QCameraBackend::cleanupTestCase()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QCameraBackend::testAvailableDevices()
|
||||
{
|
||||
int deviceCount = QMediaServiceProvider::defaultServiceProvider()->devices(QByteArray(Q_MEDIASERVICE_CAMERA)).count();
|
||||
QCOMPARE(QCamera::availableDevices().count(), deviceCount);
|
||||
}
|
||||
|
||||
void tst_QCameraBackend::testDeviceDescription()
|
||||
{
|
||||
int deviceCount = QMediaServiceProvider::defaultServiceProvider()->devices(QByteArray(Q_MEDIASERVICE_CAMERA)).count();
|
||||
|
||||
if (deviceCount == 0)
|
||||
QVERIFY(QCamera::deviceDescription(QByteArray("random")).isNull());
|
||||
else {
|
||||
foreach (const QByteArray &device, QCamera::availableDevices())
|
||||
QVERIFY(QCamera::deviceDescription(device).length() > 0);
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QCameraBackend::testCtorWithDevice()
|
||||
{
|
||||
int deviceCount = QMediaServiceProvider::defaultServiceProvider()->devices(QByteArray(Q_MEDIASERVICE_CAMERA)).count();
|
||||
QCamera *camera = 0;
|
||||
|
||||
if (deviceCount == 0) {
|
||||
camera = new QCamera("random");
|
||||
QCOMPARE(camera->error(), QCamera::ServiceMissingError);
|
||||
}
|
||||
else {
|
||||
camera = new QCamera(QCamera::availableDevices().first());
|
||||
QCOMPARE(camera->error(), QCamera::NoError);
|
||||
}
|
||||
|
||||
delete camera;
|
||||
}
|
||||
|
||||
void tst_QCameraBackend::testCameraStates()
|
||||
{
|
||||
QCamera camera;
|
||||
QCameraImageCapture imageCapture(&camera);
|
||||
|
||||
QSignalSpy errorSignal(&camera, SIGNAL(error(QCamera::Error)));
|
||||
QSignalSpy stateChangedSignal(&camera, SIGNAL(stateChanged(QCamera::State)));
|
||||
QSignalSpy statusChangedSignal(&camera, SIGNAL(statusChanged(QCamera::Status)));
|
||||
|
||||
QCOMPARE(camera.state(), QCamera::UnloadedState);
|
||||
QCOMPARE(camera.status(), QCamera::UnloadedStatus);
|
||||
|
||||
camera.load();
|
||||
QCOMPARE(camera.state(), QCamera::LoadedState);
|
||||
QCOMPARE(stateChangedSignal.count(), 1);
|
||||
QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::LoadedState);
|
||||
QVERIFY(stateChangedSignal.count() > 0);
|
||||
|
||||
QTRY_COMPARE(camera.status(), QCamera::LoadedStatus);
|
||||
QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::LoadedStatus);
|
||||
|
||||
camera.unload();
|
||||
QCOMPARE(camera.state(), QCamera::UnloadedState);
|
||||
QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::UnloadedState);
|
||||
QTRY_COMPARE(camera.status(), QCamera::UnloadedStatus);
|
||||
QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::UnloadedStatus);
|
||||
|
||||
#ifdef Q_WS_MAEMO_6
|
||||
//resource policy doesn't work correctly when resource is released and immediately requested again.
|
||||
QTest::qWait(250);
|
||||
#endif
|
||||
|
||||
camera.start();
|
||||
QCOMPARE(camera.state(), QCamera::ActiveState);
|
||||
QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::ActiveState);
|
||||
QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
|
||||
QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::ActiveStatus);
|
||||
|
||||
camera.stop();
|
||||
QCOMPARE(camera.state(), QCamera::LoadedState);
|
||||
QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::LoadedState);
|
||||
QTRY_COMPARE(camera.status(), QCamera::LoadedStatus);
|
||||
QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::LoadedStatus);
|
||||
|
||||
camera.unload();
|
||||
QCOMPARE(camera.state(), QCamera::UnloadedState);
|
||||
QCOMPARE(stateChangedSignal.last().first().value<QCamera::State>(), QCamera::UnloadedState);
|
||||
QTRY_COMPARE(camera.status(), QCamera::UnloadedStatus);
|
||||
QCOMPARE(statusChangedSignal.last().first().value<QCamera::Status>(), QCamera::UnloadedStatus);
|
||||
|
||||
QCOMPARE(camera.errorString(), QString());
|
||||
QCOMPARE(errorSignal.count(), 0);
|
||||
}
|
||||
|
||||
void tst_QCameraBackend::testCaptureMode()
|
||||
{
|
||||
QCamera camera;
|
||||
|
||||
QSignalSpy errorSignal(&camera, SIGNAL(error(QCamera::Error)));
|
||||
QSignalSpy stateChangedSignal(&camera, SIGNAL(stateChanged(QCamera::State)));
|
||||
QSignalSpy captureModeSignal(&camera, SIGNAL(captureModeChanged(QCamera::CaptureMode)));
|
||||
|
||||
QCOMPARE(camera.captureMode(), QCamera::CaptureStillImage);
|
||||
|
||||
if (!camera.isCaptureModeSupported(QCamera::CaptureVideo)) {
|
||||
camera.setCaptureMode(QCamera::CaptureVideo);
|
||||
QCOMPARE(camera.captureMode(), QCamera::CaptureStillImage);
|
||||
QSKIP("Video capture not supported", SkipAll);
|
||||
}
|
||||
|
||||
camera.setCaptureMode(QCamera::CaptureVideo);
|
||||
QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
|
||||
QTRY_COMPARE(captureModeSignal.size(), 1);
|
||||
QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureMode>(), QCamera::CaptureVideo);
|
||||
captureModeSignal.clear();
|
||||
|
||||
camera.load();
|
||||
QTRY_COMPARE(camera.status(), QCamera::LoadedStatus);
|
||||
//capture mode should still be video
|
||||
QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
|
||||
|
||||
//it should be possible to switch capture mode in Loaded state
|
||||
camera.setCaptureMode(QCamera::CaptureStillImage);
|
||||
QTRY_COMPARE(captureModeSignal.size(), 1);
|
||||
QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureMode>(), QCamera::CaptureStillImage);
|
||||
captureModeSignal.clear();
|
||||
|
||||
camera.setCaptureMode(QCamera::CaptureVideo);
|
||||
QTRY_COMPARE(captureModeSignal.size(), 1);
|
||||
QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureMode>(), QCamera::CaptureVideo);
|
||||
captureModeSignal.clear();
|
||||
|
||||
camera.start();
|
||||
QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
|
||||
//capture mode should still be video
|
||||
QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
|
||||
|
||||
stateChangedSignal.clear();
|
||||
//it should be possible to switch capture mode in Active state
|
||||
camera.setCaptureMode(QCamera::CaptureStillImage);
|
||||
//camera may leave Active status, but should return to Active
|
||||
QTest::qWait(10); //camera may leave Active status async
|
||||
QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
|
||||
QCOMPARE(camera.captureMode(), QCamera::CaptureStillImage);
|
||||
QVERIFY2(stateChangedSignal.isEmpty(), "camera should not change the state during capture mode changes");
|
||||
|
||||
QCOMPARE(captureModeSignal.size(), 1);
|
||||
QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureMode>(), QCamera::CaptureStillImage);
|
||||
captureModeSignal.clear();
|
||||
|
||||
camera.setCaptureMode(QCamera::CaptureVideo);
|
||||
//camera may leave Active status, but should return to Active
|
||||
QTest::qWait(10); //camera may leave Active status async
|
||||
QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
|
||||
QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
|
||||
|
||||
QVERIFY2(stateChangedSignal.isEmpty(), "camera should not change the state during capture mode changes");
|
||||
|
||||
QCOMPARE(captureModeSignal.size(), 1);
|
||||
QCOMPARE(captureModeSignal.last().first().value<QCamera::CaptureMode>(), QCamera::CaptureVideo);
|
||||
captureModeSignal.clear();
|
||||
|
||||
camera.stop();
|
||||
QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
|
||||
camera.unload();
|
||||
QCOMPARE(camera.captureMode(), QCamera::CaptureVideo);
|
||||
|
||||
QVERIFY2(errorSignal.isEmpty(), QString("Camera error: %1").arg(camera.errorString()).toLocal8Bit());
|
||||
}
|
||||
|
||||
void tst_QCameraBackend::testCameraCapture()
|
||||
{
|
||||
QCamera camera;
|
||||
QCameraImageCapture imageCapture(&camera);
|
||||
//prevents camera to flash during the test
|
||||
camera.exposure()->setFlashMode(QCameraExposure::FlashOff);
|
||||
|
||||
QVERIFY(!imageCapture.isReadyForCapture());
|
||||
|
||||
QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage)));
|
||||
QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString)));
|
||||
QSignalSpy errorSignal(&imageCapture, SIGNAL(error(int, QCameraImageCapture::Error,QString)));
|
||||
|
||||
imageCapture.capture();
|
||||
QTRY_COMPARE(errorSignal.size(), 1);
|
||||
QCOMPARE(imageCapture.error(), QCameraImageCapture::NotReadyError);
|
||||
QCOMPARE(capturedSignal.size(), 0);
|
||||
errorSignal.clear();
|
||||
|
||||
camera.start();
|
||||
|
||||
QTRY_VERIFY(imageCapture.isReadyForCapture());
|
||||
QCOMPARE(camera.status(), QCamera::ActiveStatus);
|
||||
QCOMPARE(errorSignal.size(), 0);
|
||||
|
||||
int id = imageCapture.capture();
|
||||
|
||||
QTRY_VERIFY(!savedSignal.isEmpty());
|
||||
|
||||
QCOMPARE(capturedSignal.size(), 1);
|
||||
QCOMPARE(capturedSignal.last().first().toInt(), id);
|
||||
QCOMPARE(errorSignal.size(), 0);
|
||||
QCOMPARE(imageCapture.error(), QCameraImageCapture::NoError);
|
||||
|
||||
QCOMPARE(savedSignal.last().first().toInt(), id);
|
||||
QString location = savedSignal.last().last().toString();
|
||||
QVERIFY(!location.isEmpty());
|
||||
QVERIFY(QFileInfo(location).exists());
|
||||
QImageReader reader(location);
|
||||
reader.setScaledSize(QSize(320,240));
|
||||
QVERIFY(!reader.read().isNull());
|
||||
|
||||
QFile(location).remove();
|
||||
}
|
||||
|
||||
|
||||
void tst_QCameraBackend::testCaptureToBuffer()
|
||||
{
|
||||
QCamera camera;
|
||||
QCameraImageCapture imageCapture(&camera);
|
||||
camera.exposure()->setFlashMode(QCameraExposure::FlashOff);
|
||||
|
||||
camera.load();
|
||||
|
||||
#ifdef Q_WS_MAEMO_6
|
||||
QVERIFY(imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer));
|
||||
#endif
|
||||
|
||||
if (!imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer))
|
||||
QSKIP("Buffer capture not supported", SkipAll);
|
||||
|
||||
QTRY_COMPARE(camera.status(), QCamera::LoadedStatus);
|
||||
|
||||
QCOMPARE(imageCapture.bufferFormat(), QVideoFrame::Format_Jpeg);
|
||||
|
||||
QVERIFY(imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToFile));
|
||||
QVERIFY(imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer));
|
||||
QVERIFY(imageCapture.isCaptureDestinationSupported(
|
||||
QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile));
|
||||
|
||||
QSignalSpy destinationChangedSignal(&imageCapture, SIGNAL(captureDestinationChanged(QCameraImageCapture::CaptureDestinations)));
|
||||
|
||||
QCOMPARE(imageCapture.captureDestination(), QCameraImageCapture::CaptureToFile);
|
||||
imageCapture.setCaptureDestination(QCameraImageCapture::CaptureToBuffer);
|
||||
QCOMPARE(imageCapture.captureDestination(), QCameraImageCapture::CaptureToBuffer);
|
||||
QCOMPARE(destinationChangedSignal.size(), 1);
|
||||
QCOMPARE(destinationChangedSignal.first().first().value<QCameraImageCapture::CaptureDestinations>(),
|
||||
QCameraImageCapture::CaptureToBuffer);
|
||||
|
||||
QSignalSpy capturedSignal(&imageCapture, SIGNAL(imageCaptured(int,QImage)));
|
||||
QSignalSpy imageAvailableSignal(&imageCapture, SIGNAL(imageAvailable(int,QVideoFrame)));
|
||||
QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString)));
|
||||
QSignalSpy errorSignal(&imageCapture, SIGNAL(error(int, QCameraImageCapture::Error,QString)));
|
||||
|
||||
camera.start();
|
||||
QTRY_VERIFY(imageCapture.isReadyForCapture());
|
||||
|
||||
int id = imageCapture.capture();
|
||||
QTRY_VERIFY(!imageAvailableSignal.isEmpty());
|
||||
|
||||
QVERIFY(errorSignal.isEmpty());
|
||||
QVERIFY(!capturedSignal.isEmpty());
|
||||
QVERIFY(!imageAvailableSignal.isEmpty());
|
||||
|
||||
QTest::qWait(2000);
|
||||
QVERIFY(savedSignal.isEmpty());
|
||||
|
||||
QCOMPARE(capturedSignal.first().first().toInt(), id);
|
||||
QCOMPARE(imageAvailableSignal.first().first().toInt(), id);
|
||||
|
||||
QVideoFrame frame = imageAvailableSignal.first().last().value<QVideoFrame>();
|
||||
QVERIFY(frame.isValid());
|
||||
QCOMPARE(frame.pixelFormat(), QVideoFrame::Format_Jpeg);
|
||||
QVERIFY(!frame.size().isEmpty());
|
||||
QVERIFY(frame.map(QAbstractVideoBuffer::ReadOnly));
|
||||
QByteArray data((const char *)frame.bits(), frame.mappedBytes());
|
||||
frame.unmap();
|
||||
frame = QVideoFrame();
|
||||
|
||||
QVERIFY(!data.isEmpty());
|
||||
QBuffer buffer;
|
||||
buffer.setData(data);
|
||||
buffer.open(QIODevice::ReadOnly);
|
||||
QImageReader reader(&buffer, "JPG");
|
||||
reader.setScaledSize(QSize(640,480));
|
||||
QImage img(reader.read());
|
||||
QVERIFY(!img.isNull());
|
||||
|
||||
capturedSignal.clear();
|
||||
imageAvailableSignal.clear();
|
||||
savedSignal.clear();
|
||||
|
||||
//Capture to yuv buffer
|
||||
#ifdef Q_WS_MAEMO_6
|
||||
QVERIFY(imageCapture.supportedBufferFormats().contains(QVideoFrame::Format_UYVY));
|
||||
#endif
|
||||
|
||||
if (imageCapture.supportedBufferFormats().contains(QVideoFrame::Format_UYVY)) {
|
||||
imageCapture.setBufferFormat(QVideoFrame::Format_UYVY);
|
||||
QCOMPARE(imageCapture.bufferFormat(), QVideoFrame::Format_UYVY);
|
||||
|
||||
id = imageCapture.capture();
|
||||
QTRY_VERIFY(!imageAvailableSignal.isEmpty());
|
||||
|
||||
QVERIFY(errorSignal.isEmpty());
|
||||
QVERIFY(!capturedSignal.isEmpty());
|
||||
QVERIFY(!imageAvailableSignal.isEmpty());
|
||||
QVERIFY(savedSignal.isEmpty());
|
||||
|
||||
QTest::qWait(2000);
|
||||
QVERIFY(savedSignal.isEmpty());
|
||||
|
||||
frame = imageAvailableSignal.first().last().value<QVideoFrame>();
|
||||
QVERIFY(frame.isValid());
|
||||
|
||||
qDebug() << frame.pixelFormat();
|
||||
QCOMPARE(frame.pixelFormat(), QVideoFrame::Format_UYVY);
|
||||
QVERIFY(!frame.size().isEmpty());
|
||||
frame = QVideoFrame();
|
||||
|
||||
capturedSignal.clear();
|
||||
imageAvailableSignal.clear();
|
||||
savedSignal.clear();
|
||||
|
||||
imageCapture.setBufferFormat(QVideoFrame::Format_Jpeg);
|
||||
QCOMPARE(imageCapture.bufferFormat(), QVideoFrame::Format_Jpeg);
|
||||
}
|
||||
|
||||
//Try to capture to both buffer and file
|
||||
#ifdef Q_WS_MAEMO_6
|
||||
QVERIFY(imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile));
|
||||
#endif
|
||||
if (imageCapture.isCaptureDestinationSupported(QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile)) {
|
||||
imageCapture.setCaptureDestination(QCameraImageCapture::CaptureToBuffer | QCameraImageCapture::CaptureToFile);
|
||||
|
||||
int oldId = id;
|
||||
id = imageCapture.capture();
|
||||
QVERIFY(id != oldId);
|
||||
QTRY_VERIFY(!savedSignal.isEmpty());
|
||||
|
||||
QVERIFY(errorSignal.isEmpty());
|
||||
QVERIFY(!capturedSignal.isEmpty());
|
||||
QVERIFY(!imageAvailableSignal.isEmpty());
|
||||
QVERIFY(!savedSignal.isEmpty());
|
||||
|
||||
QCOMPARE(capturedSignal.first().first().toInt(), id);
|
||||
QCOMPARE(imageAvailableSignal.first().first().toInt(), id);
|
||||
|
||||
frame = imageAvailableSignal.first().last().value<QVideoFrame>();
|
||||
QVERIFY(frame.isValid());
|
||||
QCOMPARE(frame.pixelFormat(), QVideoFrame::Format_Jpeg);
|
||||
QVERIFY(!frame.size().isEmpty());
|
||||
|
||||
QString fileName = savedSignal.first().last().toString();
|
||||
QVERIFY(QFileInfo(fileName).exists());
|
||||
}
|
||||
}
|
||||
|
||||
void tst_QCameraBackend::testCameraCaptureMetadata()
|
||||
{
|
||||
#ifndef Q_WS_MAEMO_6
|
||||
QSKIP("Capture metadata is supported only on harmattan", SkipAll);
|
||||
#endif
|
||||
|
||||
QCamera camera;
|
||||
QCameraImageCapture imageCapture(&camera);
|
||||
camera.exposure()->setFlashMode(QCameraExposure::FlashOff);
|
||||
|
||||
QSignalSpy metadataSignal(&imageCapture, SIGNAL(imageMetadataAvailable(int,QtMultimedia::MetaData,QVariant)));
|
||||
QSignalSpy savedSignal(&imageCapture, SIGNAL(imageSaved(int,QString)));
|
||||
|
||||
camera.start();
|
||||
|
||||
QTRY_VERIFY(imageCapture.isReadyForCapture());
|
||||
|
||||
int id = imageCapture.capture(QString::fromLatin1("/dev/null"));
|
||||
QTRY_VERIFY(!savedSignal.isEmpty());
|
||||
QVERIFY(!metadataSignal.isEmpty());
|
||||
QCOMPARE(metadataSignal.first().first().toInt(), id);
|
||||
}
|
||||
|
||||
void tst_QCameraBackend::testExposureCompensation()
|
||||
{
|
||||
#if !defined(Q_WS_MAEMO_6)
|
||||
QSKIP("Capture exposure parameters are supported only on mobile platforms", SkipAll);
|
||||
#endif
|
||||
|
||||
QCamera camera;
|
||||
QCameraExposure *exposure = camera.exposure();
|
||||
|
||||
QSignalSpy exposureCompensationSignal(exposure, SIGNAL(exposureCompensationChanged(qreal)));
|
||||
|
||||
//it should be possible to set exposure parameters in Unloaded state
|
||||
QCOMPARE(exposure->exposureCompensation()+1.0, 1.0);
|
||||
exposure->setExposureCompensation(1.0);
|
||||
QCOMPARE(exposure->exposureCompensation(), 1.0);
|
||||
QTRY_COMPARE(exposureCompensationSignal.count(), 1);
|
||||
QCOMPARE(exposureCompensationSignal.last().first().toReal(), 1.0);
|
||||
|
||||
//exposureCompensationChanged should not be emitted when value is not changed
|
||||
exposure->setExposureCompensation(1.0);
|
||||
QTest::qWait(50);
|
||||
QCOMPARE(exposureCompensationSignal.count(), 1);
|
||||
|
||||
//exposure compensation should be preserved during load/start
|
||||
camera.load();
|
||||
QTRY_COMPARE(camera.status(), QCamera::LoadedStatus);
|
||||
|
||||
QCOMPARE(exposure->exposureCompensation(), 1.0);
|
||||
|
||||
exposureCompensationSignal.clear();
|
||||
exposure->setExposureCompensation(-1.0);
|
||||
QCOMPARE(exposure->exposureCompensation(), -1.0);
|
||||
QTRY_COMPARE(exposureCompensationSignal.count(), 1);
|
||||
QCOMPARE(exposureCompensationSignal.last().first().toReal(), -1.0);
|
||||
|
||||
camera.start();
|
||||
QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
|
||||
|
||||
QCOMPARE(exposure->exposureCompensation(), -1.0);
|
||||
|
||||
exposureCompensationSignal.clear();
|
||||
exposure->setExposureCompensation(1.0);
|
||||
QCOMPARE(exposure->exposureCompensation(), 1.0);
|
||||
QTRY_COMPARE(exposureCompensationSignal.count(), 1);
|
||||
QCOMPARE(exposureCompensationSignal.last().first().toReal(), 1.0);
|
||||
}
|
||||
|
||||
void tst_QCameraBackend::testExposureMode()
|
||||
{
|
||||
#if !defined(Q_WS_MAEMO_6)
|
||||
QSKIP("Capture exposure parameters are supported only on mobile platforms", SkipAll);
|
||||
#endif
|
||||
|
||||
QCamera camera;
|
||||
QCameraExposure *exposure = camera.exposure();
|
||||
|
||||
#ifdef Q_WS_MAEMO_6
|
||||
QEXPECT_FAIL("", "Camerabin reports Manual exposure instead of Auto", Continue);
|
||||
#endif
|
||||
QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureAuto);
|
||||
|
||||
// Night
|
||||
exposure->setExposureMode(QCameraExposure::ExposureNight);
|
||||
QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureNight);
|
||||
camera.start();
|
||||
QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
|
||||
QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureNight);
|
||||
|
||||
camera.unload();
|
||||
QTRY_COMPARE(camera.status(), QCamera::UnloadedStatus);
|
||||
|
||||
#ifdef Q_WS_MAEMO_6
|
||||
//resource policy doesn't work correctly when resource is released and immediately requested again.
|
||||
QTest::qWait(250);
|
||||
#endif
|
||||
|
||||
// Auto
|
||||
exposure->setExposureMode(QCameraExposure::ExposureAuto);
|
||||
QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureAuto);
|
||||
camera.start();
|
||||
QTRY_COMPARE(camera.status(), QCamera::ActiveStatus);
|
||||
QCOMPARE(exposure->exposureMode(), QCameraExposure::ExposureAuto);
|
||||
}
|
||||
|
||||
QTEST_MAIN(tst_QCameraBackend)
|
||||
|
||||
#include "tst_qcamerabackend.moc"
|
||||
@@ -0,0 +1,12 @@
|
||||
TARGET = tst_qmediaplayerbackend
|
||||
|
||||
QT += multimedia-private testlib
|
||||
CONFIG += no_private_qt_headers_warning
|
||||
|
||||
# This is more of a system test
|
||||
# CONFIG += testcase
|
||||
|
||||
DEFINES += TESTDATA_DIR=\\\"$$PWD/\\\"
|
||||
|
||||
SOURCES += \
|
||||
tst_qmediaplayerbackend.cpp
|
||||
BIN
tests/auto/integration/qmediaplayerbackend/testdata/test.wav
vendored
Normal file
BIN
tests/auto/integration/qmediaplayerbackend/testdata/test.wav
vendored
Normal file
Binary file not shown.
@@ -0,0 +1,462 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
|
||||
** All rights reserved.
|
||||
** Contact: Nokia Corporation (qt-info@nokia.com)
|
||||
**
|
||||
** This file is part of the Qt Toolkit.
|
||||
**
|
||||
** $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 <QtTest/QtTest>
|
||||
#include <QDebug>
|
||||
#include "qmediaservice.h"
|
||||
#include "qmediaplayer.h"
|
||||
|
||||
//TESTED_COMPONENT=src/multimedia
|
||||
|
||||
#ifndef TESTDATA_DIR
|
||||
#define TESTDATA_DIR "./"
|
||||
#endif
|
||||
|
||||
QT_USE_NAMESPACE
|
||||
|
||||
// Eventually these will make it into qtestcase.h
|
||||
// but we might need to tweak the timeout values here.
|
||||
#ifndef QTRY_COMPARE
|
||||
#define QTRY_COMPARE(__expr, __expected) \
|
||||
do { \
|
||||
const int __step = 50; \
|
||||
const int __timeout = 5000; \
|
||||
if ((__expr) != (__expected)) { \
|
||||
QTest::qWait(0); \
|
||||
} \
|
||||
for (int __i = 0; __i < __timeout && ((__expr) != (__expected)); __i+=__step) { \
|
||||
QTest::qWait(__step); \
|
||||
} \
|
||||
QCOMPARE(__expr, __expected); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
#ifndef QTRY_VERIFY
|
||||
#define QTRY_VERIFY(__expr) \
|
||||
do { \
|
||||
const int __step = 50; \
|
||||
const int __timeout = 5000; \
|
||||
if (!(__expr)) { \
|
||||
QTest::qWait(0); \
|
||||
} \
|
||||
for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
|
||||
QTest::qWait(__step); \
|
||||
} \
|
||||
QVERIFY(__expr); \
|
||||
} while(0)
|
||||
#endif
|
||||
|
||||
|
||||
#define QTRY_WAIT(code, __expr) \
|
||||
do { \
|
||||
const int __step = 50; \
|
||||
const int __timeout = 5000; \
|
||||
if (!(__expr)) { \
|
||||
QTest::qWait(0); \
|
||||
} \
|
||||
for (int __i = 0; __i < __timeout && !(__expr); __i+=__step) { \
|
||||
do { code } while(0); \
|
||||
QTest::qWait(__step); \
|
||||
} \
|
||||
} while(0)
|
||||
|
||||
|
||||
/*
|
||||
This is the backend conformance test.
|
||||
|
||||
Since it relies on platform media framework and sound hardware
|
||||
it may be less stable.
|
||||
*/
|
||||
|
||||
class tst_QMediaPlayerBackend : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public slots:
|
||||
void init();
|
||||
void cleanup();
|
||||
void initTestCase();
|
||||
|
||||
private slots:
|
||||
void construction();
|
||||
void loadMedia();
|
||||
void unloadMedia();
|
||||
void playPauseStop();
|
||||
void processEOS();
|
||||
void volumeAndMuted();
|
||||
void volumeAcrossFiles_data();
|
||||
void volumeAcrossFiles();
|
||||
|
||||
private:
|
||||
//one second local wav file
|
||||
QMediaContent localWavFile;
|
||||
};
|
||||
|
||||
void tst_QMediaPlayerBackend::init()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QMediaPlayerBackend::initTestCase()
|
||||
{
|
||||
QFileInfo wavFile(QLatin1String(TESTDATA_DIR "testdata/test.wav"));
|
||||
QVERIFY(wavFile.exists());
|
||||
|
||||
localWavFile = QMediaContent(QUrl::fromLocalFile(wavFile.absoluteFilePath()));
|
||||
|
||||
qRegisterMetaType<QMediaContent>();
|
||||
}
|
||||
|
||||
void tst_QMediaPlayerBackend::cleanup()
|
||||
{
|
||||
}
|
||||
|
||||
void tst_QMediaPlayerBackend::construction()
|
||||
{
|
||||
QMediaPlayer player;
|
||||
QVERIFY(player.isAvailable());
|
||||
}
|
||||
|
||||
void tst_QMediaPlayerBackend::loadMedia()
|
||||
{
|
||||
QMediaPlayer player;
|
||||
QCOMPARE(player.state(), QMediaPlayer::StoppedState);
|
||||
QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia);
|
||||
|
||||
QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State)));
|
||||
QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
|
||||
QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent)));
|
||||
|
||||
player.setMedia(localWavFile);
|
||||
|
||||
QCOMPARE(player.state(), QMediaPlayer::StoppedState);
|
||||
|
||||
QVERIFY(player.mediaStatus() != QMediaPlayer::NoMedia);
|
||||
QVERIFY(player.mediaStatus() != QMediaPlayer::InvalidMedia);
|
||||
QVERIFY(player.media() == localWavFile);
|
||||
|
||||
QCOMPARE(stateSpy.count(), 0);
|
||||
QVERIFY(statusSpy.count() > 0);
|
||||
QCOMPARE(mediaSpy.count(), 1);
|
||||
QCOMPARE(mediaSpy.last()[0].value<QMediaContent>(), localWavFile);
|
||||
|
||||
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
|
||||
|
||||
QVERIFY(player.isAudioAvailable());
|
||||
QVERIFY(!player.isVideoAvailable());
|
||||
}
|
||||
|
||||
void tst_QMediaPlayerBackend::unloadMedia()
|
||||
{
|
||||
QMediaPlayer player;
|
||||
player.setNotifyInterval(50);
|
||||
|
||||
QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State)));
|
||||
QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
|
||||
QSignalSpy mediaSpy(&player, SIGNAL(mediaChanged(QMediaContent)));
|
||||
QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
|
||||
QSignalSpy durationSpy(&player, SIGNAL(positionChanged(qint64)));
|
||||
|
||||
player.setMedia(localWavFile);
|
||||
|
||||
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
|
||||
|
||||
QVERIFY(player.position() == 0);
|
||||
QVERIFY(player.duration() > 0);
|
||||
|
||||
player.play();
|
||||
|
||||
QTest::qWait(250);
|
||||
QVERIFY(player.position() > 0);
|
||||
QVERIFY(player.duration() > 0);
|
||||
|
||||
stateSpy.clear();
|
||||
statusSpy.clear();
|
||||
mediaSpy.clear();
|
||||
positionSpy.clear();
|
||||
durationSpy.clear();
|
||||
|
||||
player.setMedia(QMediaContent());
|
||||
|
||||
QVERIFY(player.position() <= 0);
|
||||
QVERIFY(player.duration() <= 0);
|
||||
QCOMPARE(player.state(), QMediaPlayer::StoppedState);
|
||||
QCOMPARE(player.mediaStatus(), QMediaPlayer::NoMedia);
|
||||
QCOMPARE(player.media(), QMediaContent());
|
||||
|
||||
QVERIFY(!stateSpy.isEmpty());
|
||||
QVERIFY(!statusSpy.isEmpty());
|
||||
QVERIFY(!mediaSpy.isEmpty());
|
||||
QVERIFY(!positionSpy.isEmpty());
|
||||
}
|
||||
|
||||
|
||||
void tst_QMediaPlayerBackend::playPauseStop()
|
||||
{
|
||||
QMediaPlayer player;
|
||||
player.setNotifyInterval(50);
|
||||
|
||||
QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State)));
|
||||
QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
|
||||
QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
|
||||
|
||||
player.setMedia(localWavFile);
|
||||
|
||||
QCOMPARE(player.position(), qint64(0));
|
||||
|
||||
player.play();
|
||||
|
||||
QCOMPARE(player.state(), QMediaPlayer::PlayingState);
|
||||
|
||||
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
|
||||
|
||||
QCOMPARE(stateSpy.count(), 1);
|
||||
QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PlayingState);
|
||||
QTRY_VERIFY(statusSpy.count() > 0 &&
|
||||
statusSpy.last()[0].value<QMediaPlayer::MediaStatus>() == QMediaPlayer::BufferedMedia);
|
||||
|
||||
QTest::qWait(500);
|
||||
QVERIFY(player.position() > 0);
|
||||
QVERIFY(player.duration() > 0);
|
||||
QVERIFY(positionSpy.count() > 0);
|
||||
QVERIFY(positionSpy.last()[0].value<qint64>() > 0);
|
||||
|
||||
stateSpy.clear();
|
||||
statusSpy.clear();
|
||||
|
||||
player.pause();
|
||||
|
||||
QCOMPARE(player.state(), QMediaPlayer::PausedState);
|
||||
QCOMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
|
||||
|
||||
QCOMPARE(stateSpy.count(), 1);
|
||||
QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PausedState);
|
||||
|
||||
stateSpy.clear();
|
||||
statusSpy.clear();
|
||||
|
||||
player.stop();
|
||||
|
||||
QCOMPARE(player.state(), QMediaPlayer::StoppedState);
|
||||
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
|
||||
|
||||
QCOMPARE(stateSpy.count(), 1);
|
||||
QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::StoppedState);
|
||||
//it's allowed to emit statusChanged() signal async
|
||||
QTRY_COMPARE(statusSpy.count(), 1);
|
||||
QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::LoadedMedia);
|
||||
|
||||
//ensure the position is reset to 0 at stop and positionChanged(0) is emitted
|
||||
QCOMPARE(player.position(), qint64(0));
|
||||
QCOMPARE(positionSpy.last()[0].value<qint64>(), qint64(0));
|
||||
QVERIFY(player.duration() > 0);
|
||||
}
|
||||
|
||||
|
||||
void tst_QMediaPlayerBackend::processEOS()
|
||||
{
|
||||
QMediaPlayer player;
|
||||
player.setNotifyInterval(50);
|
||||
|
||||
QSignalSpy stateSpy(&player, SIGNAL(stateChanged(QMediaPlayer::State)));
|
||||
QSignalSpy statusSpy(&player, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)));
|
||||
QSignalSpy positionSpy(&player, SIGNAL(positionChanged(qint64)));
|
||||
|
||||
player.setMedia(localWavFile);
|
||||
|
||||
player.play();
|
||||
player.setPosition(900);
|
||||
|
||||
//wait up to 5 seconds for EOS
|
||||
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
|
||||
|
||||
QVERIFY(statusSpy.count() > 0);
|
||||
QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::EndOfMedia);
|
||||
|
||||
//at EOS the position stays at the end of file
|
||||
QVERIFY(player.position() > 900);
|
||||
|
||||
stateSpy.clear();
|
||||
statusSpy.clear();
|
||||
|
||||
player.play();
|
||||
|
||||
//position is reset to start
|
||||
QTRY_VERIFY(player.position() < 100);
|
||||
|
||||
QCOMPARE(player.state(), QMediaPlayer::PlayingState);
|
||||
QCOMPARE(player.mediaStatus(), QMediaPlayer::BufferedMedia);
|
||||
|
||||
QCOMPARE(stateSpy.count(), 1);
|
||||
QCOMPARE(stateSpy.last()[0].value<QMediaPlayer::State>(), QMediaPlayer::PlayingState);
|
||||
QVERIFY(statusSpy.count() > 0);
|
||||
QCOMPARE(statusSpy.last()[0].value<QMediaPlayer::MediaStatus>(), QMediaPlayer::BufferedMedia);
|
||||
|
||||
player.setPosition(900);
|
||||
//wait up to 5 seconds for EOS
|
||||
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
|
||||
|
||||
//ensure the positionChanged() signal is emitted
|
||||
QVERIFY(positionSpy.count() > 0);
|
||||
|
||||
QCOMPARE(player.mediaStatus(), QMediaPlayer::EndOfMedia);
|
||||
//position stays at the end of file
|
||||
QVERIFY(player.position() > 900);
|
||||
|
||||
//after setPosition EndOfMedia status should be reset to Loaded
|
||||
stateSpy.clear();
|
||||
statusSpy.clear();
|
||||
player.setPosition(500);
|
||||
|
||||
//this transition can be async, so allow backend to perform it
|
||||
QTRY_COMPARE(player.mediaStatus(), QMediaPlayer::LoadedMedia);
|
||||
|
||||
QCOMPARE(stateSpy.count(), 0);
|
||||
QTRY_VERIFY(statusSpy.count() > 0 &&
|
||||
statusSpy.last()[0].value<QMediaPlayer::MediaStatus>() == QMediaPlayer::LoadedMedia);
|
||||
}
|
||||
|
||||
void tst_QMediaPlayerBackend::volumeAndMuted()
|
||||
{
|
||||
//volume and muted properties should be independent
|
||||
QMediaPlayer player;
|
||||
QVERIFY(player.volume() > 0);
|
||||
QVERIFY(!player.isMuted());
|
||||
|
||||
player.setMedia(localWavFile);
|
||||
player.pause();
|
||||
|
||||
QVERIFY(player.volume() > 0);
|
||||
QVERIFY(!player.isMuted());
|
||||
|
||||
QSignalSpy volumeSpy(&player, SIGNAL(volumeChanged(int)));
|
||||
QSignalSpy mutedSpy(&player, SIGNAL(mutedChanged(bool)));
|
||||
|
||||
//setting volume to 0 should not trigger muted
|
||||
player.setVolume(0);
|
||||
QTRY_COMPARE(player.volume(), 0);
|
||||
QVERIFY(!player.isMuted());
|
||||
QCOMPARE(volumeSpy.count(), 1);
|
||||
QCOMPARE(volumeSpy.last()[0].toInt(), player.volume());
|
||||
QCOMPARE(mutedSpy.count(), 0);
|
||||
|
||||
player.setVolume(50);
|
||||
QTRY_COMPARE(player.volume(), 50);
|
||||
QVERIFY(!player.isMuted());
|
||||
QCOMPARE(volumeSpy.count(), 2);
|
||||
QCOMPARE(volumeSpy.last()[0].toInt(), player.volume());
|
||||
QCOMPARE(mutedSpy.count(), 0);
|
||||
|
||||
player.setMuted(true);
|
||||
QTRY_VERIFY(player.isMuted());
|
||||
QVERIFY(player.volume() > 0);
|
||||
QCOMPARE(volumeSpy.count(), 2);
|
||||
QCOMPARE(mutedSpy.count(), 1);
|
||||
QCOMPARE(mutedSpy.last()[0].toBool(), player.isMuted());
|
||||
|
||||
player.setMuted(false);
|
||||
QTRY_VERIFY(!player.isMuted());
|
||||
QVERIFY(player.volume() > 0);
|
||||
QCOMPARE(volumeSpy.count(), 2);
|
||||
QCOMPARE(mutedSpy.count(), 2);
|
||||
QCOMPARE(mutedSpy.last()[0].toBool(), player.isMuted());
|
||||
|
||||
}
|
||||
|
||||
void tst_QMediaPlayerBackend::volumeAcrossFiles_data()
|
||||
{
|
||||
QTest::addColumn<int>("volume");
|
||||
QTest::addColumn<bool>("muted");
|
||||
|
||||
QTest::newRow("100 unmuted") << 100 << false;
|
||||
QTest::newRow("50 unmuted") << 50 << false;
|
||||
QTest::newRow("0 unmuted") << 0 << false;
|
||||
QTest::newRow("100 muted") << 100 << true;
|
||||
QTest::newRow("50 muted") << 50 << true;
|
||||
QTest::newRow("0 muted") << 0 << true;
|
||||
}
|
||||
|
||||
void tst_QMediaPlayerBackend::volumeAcrossFiles()
|
||||
{
|
||||
QFETCH(int, volume);
|
||||
QFETCH(bool, muted);
|
||||
|
||||
QMediaPlayer player;
|
||||
|
||||
//volume and muted should not be preserved between player instances
|
||||
QVERIFY(player.volume() > 0);
|
||||
QVERIFY(!player.isMuted());
|
||||
|
||||
player.setVolume(volume);
|
||||
player.setMuted(muted);
|
||||
|
||||
QTRY_COMPARE(player.volume(), volume);
|
||||
QTRY_COMPARE(player.isMuted(), muted);
|
||||
|
||||
player.setMedia(localWavFile);
|
||||
QCOMPARE(player.volume(), volume);
|
||||
QCOMPARE(player.isMuted(), muted);
|
||||
|
||||
player.pause();
|
||||
|
||||
//to ensure the backend doesn't change volume/muted
|
||||
//async during file loading.
|
||||
QTest::qWait(50);
|
||||
|
||||
QCOMPARE(player.volume(), volume);
|
||||
QCOMPARE(player.isMuted(), muted);
|
||||
|
||||
player.setMedia(QMediaContent());
|
||||
QTest::qWait(50);
|
||||
QCOMPARE(player.volume(), volume);
|
||||
QCOMPARE(player.isMuted(), muted);
|
||||
|
||||
player.setMedia(localWavFile);
|
||||
player.pause();
|
||||
|
||||
QTest::qWait(50);
|
||||
|
||||
QCOMPARE(player.volume(), volume);
|
||||
QCOMPARE(player.isMuted(), muted);
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN(tst_QMediaPlayerBackend)
|
||||
#include "tst_qmediaplayerbackend.moc"
|
||||
|
||||
17
tests/auto/integration/qsoundeffect/qsoundeffect.pro
Normal file
17
tests/auto/integration/qsoundeffect/qsoundeffect.pro
Normal file
@@ -0,0 +1,17 @@
|
||||
TARGET = tst_qsoundeffect
|
||||
|
||||
QT += core declarative multimedia-private testlib
|
||||
CONFIG += no_private_qt_headers_warning
|
||||
|
||||
# This is more of a system test
|
||||
# CONFIG += testcase
|
||||
|
||||
SOURCES += tst_qsoundeffect.cpp
|
||||
|
||||
DEFINES += SRCDIR=\\\"$$PWD/\\\"
|
||||
|
||||
unix:!mac {
|
||||
!contains(QT_CONFIG, pulseaudio) {
|
||||
DEFINES += QT_MULTIMEDIA_QMEDIAPLAYER
|
||||
}
|
||||
}
|
||||
BIN
tests/auto/integration/qsoundeffect/test.wav
Normal file
BIN
tests/auto/integration/qsoundeffect/test.wav
Normal file
Binary file not shown.
195
tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp
Normal file
195
tests/auto/integration/qsoundeffect/tst_qsoundeffect.cpp
Normal file
@@ -0,0 +1,195 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** 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 test suite of the Qt Toolkit.
|
||||
**
|
||||
** $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$
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
//TESTED_COMPONENT=src/multimedia
|
||||
|
||||
#include <QtTest/QtTest>
|
||||
#include <QtCore/qlocale.h>
|
||||
#include <qaudiooutput.h>
|
||||
#include <qaudiodeviceinfo.h>
|
||||
#include <qaudio.h>
|
||||
#include "qsoundeffect.h"
|
||||
|
||||
|
||||
class tst_QSoundEffect : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
public:
|
||||
tst_QSoundEffect(QObject* parent=0) : QObject(parent) {}
|
||||
|
||||
private slots:
|
||||
void initTestCase();
|
||||
void testSource();
|
||||
void testLooping();
|
||||
void testVolume();
|
||||
void testMuting();
|
||||
|
||||
void testPlaying();
|
||||
void testStatus();
|
||||
|
||||
private:
|
||||
QSoundEffect* sound;
|
||||
QUrl url;
|
||||
};
|
||||
|
||||
void tst_QSoundEffect::initTestCase()
|
||||
{
|
||||
#ifdef QT_QSOUNDEFFECT_USEAPPLICATIONPATH
|
||||
url = QUrl::fromLocalFile(QCoreApplication::applicationDirPath() + QString("/test.wav"));
|
||||
#else
|
||||
url = QUrl::fromLocalFile(QString(SRCDIR "test.wav"));
|
||||
#endif
|
||||
|
||||
sound = new QSoundEffect(this);
|
||||
|
||||
QVERIFY(sound->source().isEmpty());
|
||||
QVERIFY(sound->loopCount() == 1);
|
||||
QVERIFY(sound->volume() == 1);
|
||||
QVERIFY(sound->isMuted() == false);
|
||||
}
|
||||
|
||||
void tst_QSoundEffect::testSource()
|
||||
{
|
||||
QSignalSpy readSignal(sound, SIGNAL(sourceChanged()));
|
||||
|
||||
sound->setSource(url);
|
||||
|
||||
QCOMPARE(sound->source(),url);
|
||||
QCOMPARE(readSignal.count(),1);
|
||||
|
||||
QTestEventLoop::instance().enterLoop(1);
|
||||
sound->play();
|
||||
|
||||
QTest::qWait(3000);
|
||||
}
|
||||
|
||||
void tst_QSoundEffect::testLooping()
|
||||
{
|
||||
QSignalSpy readSignal(sound, SIGNAL(loopCountChanged()));
|
||||
|
||||
sound->setLoopCount(5);
|
||||
QCOMPARE(sound->loopCount(),5);
|
||||
|
||||
sound->play();
|
||||
|
||||
// test.wav is about 200ms, wait until it has finished playing 5 times
|
||||
QTest::qWait(3000);
|
||||
|
||||
}
|
||||
|
||||
void tst_QSoundEffect::testVolume()
|
||||
{
|
||||
QSignalSpy readSignal(sound, SIGNAL(volumeChanged()));
|
||||
|
||||
sound->setVolume(0.5);
|
||||
QCOMPARE(sound->volume(),0.5);
|
||||
|
||||
QTest::qWait(20);
|
||||
QCOMPARE(readSignal.count(),1);
|
||||
}
|
||||
|
||||
void tst_QSoundEffect::testMuting()
|
||||
{
|
||||
QSignalSpy readSignal(sound, SIGNAL(mutedChanged()));
|
||||
|
||||
sound->setMuted(true);
|
||||
QCOMPARE(sound->isMuted(),true);
|
||||
|
||||
QTest::qWait(20);
|
||||
QCOMPARE(readSignal.count(),1);
|
||||
}
|
||||
|
||||
void tst_QSoundEffect::testPlaying()
|
||||
{
|
||||
sound->setLoopCount(QSoundEffect::Infinite);
|
||||
//valid source
|
||||
sound->setSource(url);
|
||||
QTestEventLoop::instance().enterLoop(1);
|
||||
sound->play();
|
||||
QTestEventLoop::instance().enterLoop(1);
|
||||
QCOMPARE(sound->isPlaying(), true);
|
||||
sound->stop();
|
||||
|
||||
//empty source
|
||||
sound->setSource(QUrl());
|
||||
QTestEventLoop::instance().enterLoop(1);
|
||||
sound->play();
|
||||
QTestEventLoop::instance().enterLoop(1);
|
||||
QCOMPARE(sound->isPlaying(), false);
|
||||
|
||||
//invalid source
|
||||
sound->setSource(QUrl((QLatin1String("invalid source"))));
|
||||
QTestEventLoop::instance().enterLoop(1);
|
||||
sound->play();
|
||||
QTestEventLoop::instance().enterLoop(1);
|
||||
QCOMPARE(sound->isPlaying(), false);
|
||||
|
||||
sound->setLoopCount(1);
|
||||
}
|
||||
|
||||
void tst_QSoundEffect::testStatus()
|
||||
{
|
||||
sound->setSource(QUrl());
|
||||
QCOMPARE(sound->status(), QSoundEffect::Null);
|
||||
|
||||
//valid source
|
||||
sound->setSource(url);
|
||||
|
||||
QTestEventLoop::instance().enterLoop(1);
|
||||
QCOMPARE(sound->status(), QSoundEffect::Ready);
|
||||
|
||||
//empty source
|
||||
sound->setSource(QUrl());
|
||||
QTestEventLoop::instance().enterLoop(1);
|
||||
QCOMPARE(sound->status(), QSoundEffect::Null);
|
||||
|
||||
//invalid source
|
||||
sound->setLoopCount(QSoundEffect::Infinite);
|
||||
|
||||
sound->setSource(QUrl(QLatin1String("invalid source")));
|
||||
QTestEventLoop::instance().enterLoop(1);
|
||||
QCOMPARE(sound->status(), QSoundEffect::Error);
|
||||
}
|
||||
|
||||
|
||||
QTEST_MAIN(tst_QSoundEffect)
|
||||
|
||||
#include "tst_qsoundeffect.moc"
|
||||
Reference in New Issue
Block a user