summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Goddard <michael.goddard@nokia.com>2012-02-17 19:10:35 +1000
committerQt by Nokia <qt-info@nokia.com>2012-02-21 07:35:35 +0100
commit2ab74b7ff298cb15d5c82476e15e568df6f3e26e (patch)
treece319d9b8fba80a0fbd1594868a1b9cf0dfb30a7
parentb9e2410a2a096a825d0f266598ae816620238ff1 (diff)
Fix a few audiodecoder things.
Beef up the autotest a little, and check the conversion. Change-Id: Ifffca118e092eb6c388db50a6eb12810a87aa32a Reviewed-by: Lev Zelenskiy <lev.zelenskiy@nokia.com> Reviewed-by: Michael Goddard <michael.goddard@nokia.com>
-rw-r--r--src/gsttools/qgstreamermessage.cpp12
-rw-r--r--src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp162
-rw-r--r--src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h6
-rw-r--r--src/plugins/gstreamer/qgstappsrc.cpp4
-rw-r--r--tests/auto/integration/multimedia.pro1
-rw-r--r--tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp224
6 files changed, 322 insertions, 87 deletions
diff --git a/src/gsttools/qgstreamermessage.cpp b/src/gsttools/qgstreamermessage.cpp
index 31618567..02b03a37 100644
--- a/src/gsttools/qgstreamermessage.cpp
+++ b/src/gsttools/qgstreamermessage.cpp
@@ -84,11 +84,15 @@ GstMessage* QGstreamerMessage::rawMessage() const
QGstreamerMessage& QGstreamerMessage::operator=(QGstreamerMessage const& rhs)
{
- if (m_message != 0)
- gst_message_unref(m_message);
+ if (rhs.m_message != m_message) {
+ if (rhs.m_message != 0)
+ gst_message_ref(rhs.m_message);
+
+ if (m_message != 0)
+ gst_message_unref(m_message);
- if ((m_message = rhs.m_message) != 0)
- gst_message_ref(m_message);
+ m_message = rhs.m_message;
+ }
return *this;
}
diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp
index 88f3a0e2..518bf778 100644
--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp
+++ b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.cpp
@@ -38,6 +38,7 @@
** $QT_END_LICENSE$
**
****************************************************************************/
+//#define DEBUG_DECODER
#include "qgstreameraudiodecodersession.h"
#include <private/qgstreamerbushelper_p.h>
@@ -56,7 +57,7 @@
#include <QtCore/qstandardpaths.h>
#include <QtCore/qurl.h>
-#define MAX_BUFFERS_IN_QUEUE 5
+#define MAX_BUFFERS_IN_QUEUE 4
QT_BEGIN_NAMESPACE
@@ -79,6 +80,8 @@ QGstreamerAudioDecoderSession::QGstreamerAudioDecoderSession(QObject *parent)
m_busHelper(0),
m_bus(0),
m_playbin(0),
+ m_outputBin(0),
+ m_audioConvert(0),
m_appSink(0),
#if defined(HAVE_GST_APPSRC)
m_appSrc(0),
@@ -86,55 +89,31 @@ QGstreamerAudioDecoderSession::QGstreamerAudioDecoderSession(QObject *parent)
mDevice(0),
m_buffersAvailable(0)
{
- // Default format
- mFormat.setChannels(2);
- mFormat.setSampleSize(16);
- mFormat.setFrequency(48000);
- mFormat.setCodec("audio/pcm");
- mFormat.setSampleType(QAudioFormat::UnSignedInt);
-
-
// Create pipeline here
m_playbin = gst_element_factory_make("playbin2", NULL);
if (m_playbin != 0) {
+ // Sort out messages
+ m_bus = gst_element_get_bus(m_playbin);
+ m_busHelper = new QGstreamerBusHelper(m_bus, this);
+ m_busHelper->installMessageFilter(this);
- int flags = 0;
- g_object_get(G_OBJECT(m_playbin), "flags", &flags, NULL);
- // make sure not to use GST_PLAY_FLAG_NATIVE_AUDIO, it prevents audio format conversion
- flags &= ~(GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_TEXT | GST_PLAY_FLAG_VIS | GST_PLAY_FLAG_NATIVE_AUDIO);
- flags |= GST_PLAY_FLAG_AUDIO;
- g_object_set(G_OBJECT(m_playbin), "flags", flags, NULL);
-
- m_appSink = (GstAppSink*)gst_element_factory_make("appsink", NULL);
- gst_object_ref(GST_OBJECT(m_appSink));
-
- GstAppSinkCallbacks callbacks;
- memset(&callbacks, 0, sizeof(callbacks));
- callbacks.new_buffer = &new_buffer;
- gst_app_sink_set_callbacks(m_appSink, &callbacks, this, NULL);
- gst_app_sink_set_max_buffers(m_appSink, MAX_BUFFERS_IN_QUEUE);
- gst_base_sink_set_sync(GST_BASE_SINK(m_appSink), FALSE);
+ // Set the rest of the pipeline up
+ setAudioFlags(true);
- GstElement *audioConvert = gst_element_factory_make("audioconvert", NULL);
+ m_audioConvert = gst_element_factory_make("audioconvert", NULL);
- GstElement *bin = gst_bin_new("audio-output-bin");
- gst_bin_add(GST_BIN(bin), audioConvert);
- gst_bin_add(GST_BIN(bin), GST_ELEMENT(m_appSink));
- gst_element_link(audioConvert, GST_ELEMENT(m_appSink));
+ m_outputBin = gst_bin_new("audio-output-bin");
+ gst_bin_add(GST_BIN(m_outputBin), m_audioConvert);
// add ghostpad
- GstPad *pad = gst_element_get_static_pad(audioConvert, "sink");
+ GstPad *pad = gst_element_get_static_pad(m_audioConvert, "sink");
Q_ASSERT(pad);
- gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("sink", pad));
+ gst_element_add_pad(GST_ELEMENT(m_outputBin), gst_ghost_pad_new("sink", pad));
gst_object_unref(GST_OBJECT(pad));
- // Sort out messages
- m_bus = gst_element_get_bus(m_playbin);
- m_busHelper = new QGstreamerBusHelper(m_bus, this);
- m_busHelper->installMessageFilter(this);
-
- g_object_set(G_OBJECT(m_playbin), "audio-sink", bin, NULL);
+ g_object_set(G_OBJECT(m_playbin), "audio-sink", m_outputBin, NULL);
+ g_signal_connect(G_OBJECT(m_playbin), "deep-notify::source", (GCallback) &QGstreamerAudioDecoderSession::configureAppSrcElement, (gpointer)this);
// Set volume to 100%
gdouble volume = 1.0;
@@ -148,9 +127,11 @@ QGstreamerAudioDecoderSession::~QGstreamerAudioDecoderSession()
stop();
delete m_busHelper;
+#if defined(HAVE_GST_APPSRC)
+ delete m_appSrc;
+#endif
gst_object_unref(GST_OBJECT(m_bus));
gst_object_unref(GST_OBJECT(m_playbin));
- gst_object_unref(GST_OBJECT(m_appSink));
}
}
@@ -160,6 +141,10 @@ void QGstreamerAudioDecoderSession::configureAppSrcElement(GObject* object, GObj
Q_UNUSED(object);
Q_UNUSED(pspec);
+ // In case we switch from appsrc to not
+ if (!self->appsrc())
+ return;
+
if (self->appsrc()->isReady())
return;
@@ -185,14 +170,14 @@ bool QGstreamerAudioDecoderSession::processBusMessage(const QGstreamerMessage &m
gst_message_parse_state_changed(gm, &oldState, &newState, &pending);
-#ifdef DEBUG_PLAYBIN
+#ifdef DEBUG_DECODER
QStringList states;
states << "GST_STATE_VOID_PENDING" << "GST_STATE_NULL" << "GST_STATE_READY" << "GST_STATE_PAUSED" << "GST_STATE_PLAYING";
qDebug() << QString("state changed: old: %1 new: %2 pending: %3") \
.arg(states[oldState]) \
.arg(states[newState]) \
- .arg(states[pending]);
+ .arg(states[pending]) << "internal" << m_state;
#endif
switch (newState) {
@@ -208,7 +193,6 @@ bool QGstreamerAudioDecoderSession::processBusMessage(const QGstreamerMessage &m
case GST_STATE_PLAYING:
if (m_state != QAudioDecoder::DecodingState)
emit stateChanged(m_state = QAudioDecoder::DecodingState);
-
break;
case GST_STATE_PAUSED:
if (m_state != QAudioDecoder::WaitingState)
@@ -222,10 +206,6 @@ bool QGstreamerAudioDecoderSession::processBusMessage(const QGstreamerMessage &m
emit stateChanged(m_state = QAudioDecoder::StoppedState);
break;
- case GST_MESSAGE_TAG:
- case GST_MESSAGE_STREAM_STATUS:
- case GST_MESSAGE_UNKNOWN:
- break;
case GST_MESSAGE_ERROR: {
GError *err;
gchar *debug;
@@ -249,8 +229,8 @@ bool QGstreamerAudioDecoderSession::processBusMessage(const QGstreamerMessage &m
g_free (debug);
}
break;
+#ifdef DEBUG_DECODER
case GST_MESSAGE_INFO:
-#ifdef DEBUG_PLAYBIN
{
GError *err;
gchar *debug;
@@ -259,31 +239,8 @@ bool QGstreamerAudioDecoderSession::processBusMessage(const QGstreamerMessage &m
g_error_free (err);
g_free (debug);
}
-#endif
- break;
- case GST_MESSAGE_BUFFERING:
- case GST_MESSAGE_STATE_DIRTY:
- case GST_MESSAGE_STEP_DONE:
- case GST_MESSAGE_CLOCK_PROVIDE:
- case GST_MESSAGE_CLOCK_LOST:
- case GST_MESSAGE_NEW_CLOCK:
- case GST_MESSAGE_STRUCTURE_CHANGE:
- case GST_MESSAGE_APPLICATION:
- case GST_MESSAGE_ELEMENT:
break;
- case GST_MESSAGE_SEGMENT_START:
- case GST_MESSAGE_SEGMENT_DONE:
- break;
- case GST_MESSAGE_LATENCY:
-#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 13)
- case GST_MESSAGE_ASYNC_START:
- case GST_MESSAGE_ASYNC_DONE:
-#if GST_VERSION_MICRO >= 23
- case GST_MESSAGE_REQUEST_STATE:
-#endif
#endif
- case GST_MESSAGE_ANY:
- break;
default:
break;
}
@@ -315,6 +272,10 @@ void QGstreamerAudioDecoderSession::setSourceFilename(const QString &fileName)
{
stop();
mDevice = 0;
+ if (m_appSrc)
+ m_appSrc->deleteLater();
+ m_appSrc = 0;
+
bool isSignalRequired = (mSource != fileName);
mSource = fileName;
if (isSignalRequired)
@@ -343,9 +304,12 @@ void QGstreamerAudioDecoderSession::start()
return;
}
+ addAppSink();
+
if (!mSource.isEmpty()) {
g_object_set(G_OBJECT(m_playbin), "uri", QUrl::fromLocalFile(mSource).toEncoded().constData(), NULL);
} else if (mDevice) {
+#if defined(HAVE_GST_APPSRC)
// make sure we can read from device
if (!mDevice->isOpen() || !mDevice->isReadable()) {
processInvalidMedia(QAudioDecoder::AccessDeniedError, "Unable to read from specified device");
@@ -357,16 +321,23 @@ void QGstreamerAudioDecoderSession::start()
m_appSrc = new QGstAppSrc(this);
m_appSrc->setStream(mDevice);
- g_signal_connect(G_OBJECT(m_playbin), "deep-notify::source", (GCallback) &QGstreamerAudioDecoderSession::configureAppSrcElement, (gpointer)this);
g_object_set(G_OBJECT(m_playbin), "uri", "appsrc://", NULL);
+#endif
} else {
return;
}
// Set audio format
if (m_appSink) {
- GstCaps *caps = QGstUtils::capsForAudioFormat(mFormat);
- gst_app_sink_set_caps(m_appSink, caps); // appsink unrefs caps
+ if (mFormat.isValid()) {
+ setAudioFlags(false);
+ GstCaps *caps = QGstUtils::capsForAudioFormat(mFormat);
+ gst_app_sink_set_caps(m_appSink, caps); // appsink unrefs caps
+ } else {
+ // We want whatever the native audio format is
+ setAudioFlags(true);
+ gst_app_sink_set_caps(m_appSink, NULL);
+ }
}
m_pendingState = QAudioDecoder::DecodingState;
@@ -382,6 +353,7 @@ void QGstreamerAudioDecoderSession::stop()
{
if (m_playbin) {
gst_element_set_state(m_playbin, GST_STATE_NULL);
+ removeAppSink();
QAudioDecoder::State oldState = m_state;
m_pendingState = m_state = QAudioDecoder::StoppedState;
@@ -476,4 +448,48 @@ GstFlowReturn QGstreamerAudioDecoderSession::new_buffer(GstAppSink *, gpointer u
return GST_FLOW_OK;
}
+void QGstreamerAudioDecoderSession::setAudioFlags(bool wantNativeAudio)
+{
+ int flags = 0;
+ if (m_playbin) {
+ g_object_get(G_OBJECT(m_playbin), "flags", &flags, NULL);
+ // make sure not to use GST_PLAY_FLAG_NATIVE_AUDIO unless desired
+ // it prevents audio format conversion
+ flags &= ~(GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_TEXT | GST_PLAY_FLAG_VIS | GST_PLAY_FLAG_NATIVE_AUDIO);
+ flags |= GST_PLAY_FLAG_AUDIO;
+ if (wantNativeAudio)
+ flags |= GST_PLAY_FLAG_NATIVE_AUDIO;
+ g_object_set(G_OBJECT(m_playbin), "flags", flags, NULL);
+ }
+}
+
+void QGstreamerAudioDecoderSession::addAppSink()
+{
+ if (m_appSink)
+ return;
+
+ m_appSink = (GstAppSink*)gst_element_factory_make("appsink", NULL);
+
+ GstAppSinkCallbacks callbacks;
+ memset(&callbacks, 0, sizeof(callbacks));
+ callbacks.new_buffer = &new_buffer;
+ gst_app_sink_set_callbacks(m_appSink, &callbacks, this, NULL);
+ gst_app_sink_set_max_buffers(m_appSink, MAX_BUFFERS_IN_QUEUE);
+ gst_base_sink_set_sync(GST_BASE_SINK(m_appSink), FALSE);
+
+ gst_bin_add(GST_BIN(m_outputBin), GST_ELEMENT(m_appSink));
+ gst_element_link(m_audioConvert, GST_ELEMENT(m_appSink));
+}
+
+void QGstreamerAudioDecoderSession::removeAppSink()
+{
+ if (!m_appSink)
+ return;
+
+ gst_element_unlink(m_audioConvert, GST_ELEMENT(m_appSink));
+ gst_bin_remove(GST_BIN(m_outputBin), GST_ELEMENT(m_appSink));
+
+ m_appSink = 0;
+}
+
QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h
index df8c8e8d..2ec6e34a 100644
--- a/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h
+++ b/src/plugins/gstreamer/audiodecoder/qgstreameraudiodecodersession.h
@@ -111,6 +111,10 @@ signals:
private:
+ void setAudioFlags(bool wantNativeAudio);
+ void addAppSink();
+ void removeAppSink();
+
void processInvalidMedia(QAudioDecoder::Error errorCode, const QString& errorString);
QAudioDecoder::State m_state;
@@ -118,6 +122,8 @@ private:
QGstreamerBusHelper *m_busHelper;
GstBus *m_bus;
GstElement *m_playbin;
+ GstElement *m_outputBin;
+ GstElement *m_audioConvert;
GstAppSink *m_appSink;
#if defined(HAVE_GST_APPSRC)
diff --git a/src/plugins/gstreamer/qgstappsrc.cpp b/src/plugins/gstreamer/qgstappsrc.cpp
index 08791ce6..7dfe9579 100644
--- a/src/plugins/gstreamer/qgstappsrc.cpp
+++ b/src/plugins/gstreamer/qgstappsrc.cpp
@@ -167,6 +167,10 @@ void QGstAppSrc::pushDataToAppSrc()
qWarning()<<"appsrc: push buffer resend";
}
}
+
+ // After reading we might be all done
+ if (m_stream->atEnd())
+ sendEOS();
} else if (m_stream->atEnd()) {
sendEOS();
}
diff --git a/tests/auto/integration/multimedia.pro b/tests/auto/integration/multimedia.pro
index 286a320b..d3f885a3 100644
--- a/tests/auto/integration/multimedia.pro
+++ b/tests/auto/integration/multimedia.pro
@@ -1,6 +1,7 @@
TEMPLATE = subdirs
SUBDIRS += \
+ qaudiodecoderbackend \
qaudiodeviceinfo \
qaudioinput \
qaudiooutput \
diff --git a/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp b/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp
index cf4e89a7..f71062af 100644
--- a/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp
+++ b/tests/auto/integration/qaudiodecoderbackend/tst_qaudiodecoderbackend.cpp
@@ -50,7 +50,7 @@ QT_USE_NAMESPACE
/*
This is the backend conformance test.
- Since it relies on platform media framework and sound hardware
+ Since it relies on platform media framework
it may be less stable.
*/
@@ -63,7 +63,8 @@ public slots:
void initTestCase();
private slots:
- void decoderTest();
+ void fileTest();
+ void deviceTest();
};
void tst_QAudioDecoderBackend::init()
@@ -78,12 +79,19 @@ void tst_QAudioDecoderBackend::cleanup()
{
}
-void tst_QAudioDecoderBackend::decoderTest()
+void tst_QAudioDecoderBackend::fileTest()
{
QAudioDecoder d;
+ bool ok;
+ QAudioBuffer buffer;
+ quint64 duration = 0;
+ int byteCount = 0;
+ int sampleCount = 0;
+
QVERIFY(d.state() == QAudioDecoder::StoppedState);
QVERIFY(d.bufferAvailable() == false);
QCOMPARE(d.sourceFilename(), QString(""));
+ QVERIFY(d.audioFormat() == QAudioFormat());
// Test local file
QFileInfo fileInfo(QFINDTESTDATA(TEST_FILE_NAME));
@@ -98,19 +106,56 @@ void tst_QAudioDecoderBackend::decoderTest()
QSignalSpy stateSpy(&d, SIGNAL(stateChanged(QAudioDecoder::State)));
d.start();
+ QTRY_VERIFY(d.state() == QAudioDecoder::DecodingState);
QTRY_VERIFY(!stateSpy.isEmpty());
QTRY_VERIFY(!readySpy.isEmpty());
QTRY_VERIFY(!bufferChangedSpy.isEmpty());
QVERIFY(d.bufferAvailable());
- bool ok;
- QAudioBuffer buffer = d.read(&ok);
+ buffer = d.read(&ok);
QVERIFY(ok);
QVERIFY(buffer.isValid());
- QCOMPARE(buffer.format(), d.audioFormat());
+
+ // Test file is 44.1K 16bit mono, 44094 samples
+ QCOMPARE(buffer.format().channelCount(), 1);
+ QCOMPARE(buffer.format().sampleRate(), 44100);
+ QCOMPARE(buffer.format().sampleSize(), 16);
+ QCOMPARE(buffer.format().sampleType(), QAudioFormat::SignedInt);
+ QCOMPARE(buffer.format().codec(), QString("audio/pcm"));
+ QCOMPARE(buffer.byteCount(), buffer.sampleCount() * 2); // 16bit mono
+
+ // The decoder should still have no format set
+ QVERIFY(d.audioFormat() == QAudioFormat());
QVERIFY(errorSpy.isEmpty());
+ duration += buffer.duration();
+ sampleCount += buffer.sampleCount();
+ byteCount += buffer.byteCount();
+
+ // Now drain the decoder
+ if (sampleCount < 44094) {
+ QTRY_COMPARE(d.bufferAvailable(), true);
+ }
+
+ while (d.bufferAvailable()) {
+ buffer = d.read(&ok);
+ QVERIFY(ok);
+ QVERIFY(buffer.isValid());
+ duration += buffer.duration();
+ sampleCount += buffer.sampleCount();
+ byteCount += buffer.byteCount();
+
+ if (sampleCount < 44094) {
+ QTRY_COMPARE(d.bufferAvailable(), true);
+ }
+ }
+
+ // Make sure the duration is roughly correct (+/- 20ms)
+ QCOMPARE(sampleCount, 44094);
+ QCOMPARE(byteCount, 44094 * 2);
+ QVERIFY(qAbs(qint64(duration) - 1000000) < 20000);
+
d.stop();
QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState);
QVERIFY(!d.bufferAvailable());
@@ -118,21 +163,171 @@ void tst_QAudioDecoderBackend::decoderTest()
bufferChangedSpy.clear();
stateSpy.clear();
- // Test source device
+ // change output audio format
+ QAudioFormat format;
+ format.setChannels(2);
+ format.setSampleSize(8);
+ format.setFrequency(11050);
+ format.setCodec("audio/pcm");
+ format.setSampleType(QAudioFormat::SignedInt);
+
+ d.setAudioFormat(format);
+
+ // We expect 1 second still, at 11050 * 2 samples == 22k samples.
+ // (at 1 byte/sample -> 22kb)
+
+ // Make sure it stuck
+ QVERIFY(d.audioFormat() == format);
+
+ duration = 0;
+ sampleCount = 0;
+ byteCount = 0;
+
+ d.start();
+ QTRY_VERIFY(d.state() == QAudioDecoder::DecodingState);
+ QTRY_VERIFY(!stateSpy.isEmpty());
+ QTRY_VERIFY(!readySpy.isEmpty());
+ QTRY_VERIFY(!bufferChangedSpy.isEmpty());
+ QVERIFY(d.bufferAvailable());
+
+ buffer = d.read(&ok);
+ QVERIFY(ok);
+ QVERIFY(buffer.isValid());
+ // See if we got the right format
+ QVERIFY(buffer.format() == format);
+
+ // The decoder should still have the same format
+ QVERIFY(d.audioFormat() == format);
+
+ QVERIFY(errorSpy.isEmpty());
+
+ duration += buffer.duration();
+ sampleCount += buffer.sampleCount();
+ byteCount += buffer.byteCount();
+
+ // Now drain the decoder
+ if (duration < 998000) {
+ QTRY_COMPARE(d.bufferAvailable(), true);
+ }
+
+ while (d.bufferAvailable()) {
+ buffer = d.read(&ok);
+ QVERIFY(ok);
+ QVERIFY(buffer.isValid());
+ duration += buffer.duration();
+ sampleCount += buffer.sampleCount();
+ byteCount += buffer.byteCount();
+
+ if (duration < 998000) {
+ QTRY_COMPARE(d.bufferAvailable(), true);
+ }
+ }
+
+ // Resampling might end up with fewer or more samples
+ // so be a bit sloppy
+ QVERIFY(qAbs(sampleCount - 22047) < 100);
+ QVERIFY(qAbs(byteCount - 22047) < 100);
+ QVERIFY(qAbs(qint64(duration) - 1000000) < 20000);
+
+ d.stop();
+ QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState);
+ QVERIFY(!d.bufferAvailable());
+}
+
+void tst_QAudioDecoderBackend::deviceTest()
+{
+ QAudioDecoder d;
+ bool ok;
+ QAudioBuffer buffer;
+ quint64 duration = 0;
+ int sampleCount = 0;
+
+ QSignalSpy readySpy(&d, SIGNAL(bufferReady()));
+ QSignalSpy bufferChangedSpy(&d, SIGNAL(bufferAvailableChanged(bool)));
+ QSignalSpy errorSpy(&d, SIGNAL(error(QAudioDecoder::Error)));
+ QSignalSpy stateSpy(&d, SIGNAL(stateChanged(QAudioDecoder::State)));
+
+ QVERIFY(d.state() == QAudioDecoder::StoppedState);
+ QVERIFY(d.bufferAvailable() == false);
+ QCOMPARE(d.sourceFilename(), QString(""));
+ QVERIFY(d.audioFormat() == QAudioFormat());
+
+ QFileInfo fileInfo(QFINDTESTDATA(TEST_FILE_NAME));
QFile file(fileInfo.absoluteFilePath());
QVERIFY(file.open(QIODevice::ReadOnly));
d.setSourceDevice(&file);
- // change output audio format
+ QVERIFY(d.sourceDevice() == &file);
+ QVERIFY(d.sourceFilename().isEmpty());
+
+ // We haven't set the format yet
+ QVERIFY(d.audioFormat() == QAudioFormat());
+
+ d.start();
+ QTRY_VERIFY(d.state() == QAudioDecoder::DecodingState);
+ QTRY_VERIFY(!stateSpy.isEmpty());
+ QTRY_VERIFY(!readySpy.isEmpty());
+ QTRY_VERIFY(!bufferChangedSpy.isEmpty());
+ QVERIFY(d.bufferAvailable());
+
+ buffer = d.read(&ok);
+ QVERIFY(ok);
+ QVERIFY(buffer.isValid());
+
+ // Test file is 44.1K 16bit mono
+ QCOMPARE(buffer.format().channelCount(), 1);
+ QCOMPARE(buffer.format().sampleRate(), 44100);
+ QCOMPARE(buffer.format().sampleSize(), 16);
+ QCOMPARE(buffer.format().sampleType(), QAudioFormat::SignedInt);
+ QCOMPARE(buffer.format().codec(), QString("audio/pcm"));
+
+ QVERIFY(errorSpy.isEmpty());
+
+ duration += buffer.duration();
+ sampleCount += buffer.sampleCount();
+
+ // Now drain the decoder
+ if (sampleCount < 44094) {
+ QTRY_COMPARE(d.bufferAvailable(), true);
+ }
+
+ while (d.bufferAvailable()) {
+ buffer = d.read(&ok);
+ QVERIFY(ok);
+ QVERIFY(buffer.isValid());
+ duration += buffer.duration();
+ sampleCount += buffer.sampleCount();
+ if (sampleCount < 44094) {
+ QTRY_COMPARE(d.bufferAvailable(), true);
+ }
+ }
+
+ // Make sure the duration is roughly correct (+/- 20ms)
+ QCOMPARE(sampleCount, 44094);
+ QVERIFY(qAbs(qint64(duration) - 1000000) < 20000);
+
+ d.stop();
+ QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState);
+ QVERIFY(!d.bufferAvailable());
+ readySpy.clear();
+ bufferChangedSpy.clear();
+ stateSpy.clear();
+
+ // Now try changing formats
QAudioFormat format;
- format.setChannels(1);
+ format.setChannels(2);
format.setSampleSize(8);
format.setFrequency(8000);
format.setCodec("audio/pcm");
format.setSampleType(QAudioFormat::SignedInt);
+
d.setAudioFormat(format);
+ // Make sure it stuck
+ QVERIFY(d.audioFormat() == format);
+
d.start();
+ QTRY_VERIFY(d.state() == QAudioDecoder::DecodingState);
QTRY_VERIFY(!stateSpy.isEmpty());
QTRY_VERIFY(!readySpy.isEmpty());
QTRY_VERIFY(!bufferChangedSpy.isEmpty());
@@ -141,9 +336,18 @@ void tst_QAudioDecoderBackend::decoderTest()
buffer = d.read(&ok);
QVERIFY(ok);
QVERIFY(buffer.isValid());
- QCOMPARE(buffer.format(), d.audioFormat());
+ // See if we got the right format
+ QVERIFY(buffer.format() == format);
+
+ // The decoder should still have the same format
+ QVERIFY(d.audioFormat() == format);
QVERIFY(errorSpy.isEmpty());
+
+ d.stop();
+ QTRY_COMPARE(d.state(), QAudioDecoder::StoppedState);
+ QVERIFY(!d.bufferAvailable());
+
}
QTEST_MAIN(tst_QAudioDecoderBackend)