summaryrefslogtreecommitdiff
path: root/src/plugins
diff options
context:
space:
mode:
Diffstat (limited to 'src/plugins')
-rw-r--r--src/plugins/alsa/alsa.json3
-rw-r--r--src/plugins/alsa/alsa.pro23
-rw-r--r--src/plugins/alsa/qalsaaudiodeviceinfo.cpp464
-rw-r--r--src/plugins/alsa/qalsaaudiodeviceinfo.h122
-rw-r--r--src/plugins/alsa/qalsaaudioinput.cpp882
-rw-r--r--src/plugins/alsa/qalsaaudioinput.h188
-rw-r--r--src/plugins/alsa/qalsaaudiooutput.cpp860
-rw-r--r--src/plugins/alsa/qalsaaudiooutput.h170
-rw-r--r--src/plugins/alsa/qalsaplugin.cpp74
-rw-r--r--src/plugins/alsa/qalsaplugin.h67
-rw-r--r--src/plugins/android/src/mediacapture/qandroidcapturesession.cpp4
-rw-r--r--src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp12
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.h1
-rw-r--r--src/plugins/avfoundation/camera/avfcamerasession.mm10
-rw-r--r--src/plugins/avfoundation/camera/avfvideorenderercontrol.h7
-rw-r--r--src/plugins/avfoundation/camera/avfvideorenderercontrol.mm45
-rw-r--r--src/plugins/directshow/camera/camera.pri2
-rw-r--r--src/plugins/directshow/camera/dscamerasession.cpp10
-rw-r--r--src/plugins/directshow/camera/dsimagecapturecontrol.cpp8
-rw-r--r--src/plugins/directshow/camera/dsvideowidgetcontrol.cpp4
-rw-r--r--src/plugins/directshow/camera/dsvideowidgetcontrol.h12
-rw-r--r--src/plugins/directshow/directshow.pro4
-rw-r--r--src/plugins/directshow/player/directshoweventloop.cpp2
-rw-r--r--src/plugins/directshow/player/directshowglobal.h10
-rw-r--r--src/plugins/directshow/player/directshowioreader.cpp2
-rw-r--r--src/plugins/directshow/player/directshowiosource.cpp6
-rw-r--r--src/plugins/directshow/player/directshowmetadatacontrol.cpp2
-rw-r--r--src/plugins/directshow/player/directshowplayercontrol.h4
-rw-r--r--src/plugins/directshow/player/directshowplayerservice.cpp8
-rw-r--r--src/plugins/directshow/player/directshowsamplescheduler.cpp6
-rw-r--r--src/plugins/directshow/player/videosurfacefilter.cpp2
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinflash.h2
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinlocks.cpp4
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp2
-rw-r--r--src/plugins/gstreamer/camerabin/camerabinsession.h2
-rw-r--r--src/plugins/gstreamer/camerabin/camerabuttonlistener_meego.cpp4
-rw-r--r--src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp4
-rw-r--r--src/plugins/plugins.pro15
-rw-r--r--src/plugins/qnx/camera/bbcamerasession.cpp10
-rw-r--r--src/plugins/qt7/mediaplayer/qt7playercontrol.mm2
-rw-r--r--src/plugins/qt7/qcvdisplaylink.h2
-rw-r--r--src/plugins/qt7/qt7movierenderer.h4
-rw-r--r--src/plugins/qt7/qt7movievideowidget.h4
-rw-r--r--src/plugins/qt7/qt7movievideowidget.mm14
-rw-r--r--src/plugins/qt7/qt7movieviewoutput.h2
-rw-r--r--src/plugins/qt7/qt7movieviewoutput.mm2
-rw-r--r--src/plugins/qt7/qt7movieviewrenderer.mm2
-rw-r--r--src/plugins/qt7/qt7videooutput.mm4
-rw-r--r--src/plugins/resourcepolicy/resourcepolicyimpl.cpp2
-rw-r--r--src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp490
-rw-r--r--src/plugins/windowsaudio/qwindowsaudiodeviceinfo.h114
-rw-r--r--src/plugins/windowsaudio/qwindowsaudioinput.cpp752
-rw-r--r--src/plugins/windowsaudio/qwindowsaudioinput.h179
-rw-r--r--src/plugins/windowsaudio/qwindowsaudiooutput.cpp762
-rw-r--r--src/plugins/windowsaudio/qwindowsaudiooutput.h171
-rw-r--r--src/plugins/windowsaudio/qwindowsaudioplugin.cpp74
-rw-r--r--src/plugins/windowsaudio/qwindowsaudioplugin.h67
-rw-r--r--src/plugins/windowsaudio/windowsaudio.json3
-rw-r--r--src/plugins/windowsaudio/windowsaudio.pro23
-rw-r--r--src/plugins/wmf/decoder/mfdecoderservice.cpp2
-rw-r--r--src/plugins/wmf/decoder/mfdecodersourcereader.cpp2
-rw-r--r--src/plugins/wmf/player/mfplayercontrol.cpp2
-rw-r--r--src/plugins/wmf/player/mfplayersession.cpp2
63 files changed, 5643 insertions, 95 deletions
diff --git a/src/plugins/alsa/alsa.json b/src/plugins/alsa/alsa.json
new file mode 100644
index 000000000..c2b22dfec
--- /dev/null
+++ b/src/plugins/alsa/alsa.json
@@ -0,0 +1,3 @@
+{
+ "Keys": ["alsa"]
+}
diff --git a/src/plugins/alsa/alsa.pro b/src/plugins/alsa/alsa.pro
new file mode 100644
index 000000000..481c57eaf
--- /dev/null
+++ b/src/plugins/alsa/alsa.pro
@@ -0,0 +1,23 @@
+TARGET = qtaudio_alsa
+QT += multimedia-private
+
+PLUGIN_TYPE = audio
+PLUGIN_CLASS_NAME = QAlsaPlugin
+load(qt_plugin)
+
+LIBS += -lasound
+
+HEADERS += \
+ qalsaplugin.h \
+ qalsaaudiodeviceinfo.h \
+ qalsaaudioinput.h \
+ qalsaaudiooutput.h
+
+SOURCES += \
+ qalsaplugin.cpp \
+ qalsaaudiodeviceinfo.cpp \
+ qalsaaudioinput.cpp \
+ qalsaaudiooutput.cpp
+
+OTHER_FILES += \
+ alsa.json
diff --git a/src/plugins/alsa/qalsaaudiodeviceinfo.cpp b/src/plugins/alsa/qalsaaudiodeviceinfo.cpp
new file mode 100644
index 000000000..1e75c4661
--- /dev/null
+++ b/src/plugins/alsa/qalsaaudiodeviceinfo.cpp
@@ -0,0 +1,464 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// INTERNAL USE ONLY: Do NOT use for any other purpose.
+//
+
+#include "qalsaaudiodeviceinfo.h"
+
+#include <alsa/version.h>
+
+QT_BEGIN_NAMESPACE
+
+QAlsaAudioDeviceInfo::QAlsaAudioDeviceInfo(QByteArray dev, QAudio::Mode mode)
+{
+ handle = 0;
+
+ device = QLatin1String(dev);
+ this->mode = mode;
+
+ checkSurround();
+}
+
+QAlsaAudioDeviceInfo::~QAlsaAudioDeviceInfo()
+{
+ close();
+}
+
+bool QAlsaAudioDeviceInfo::isFormatSupported(const QAudioFormat& format) const
+{
+ return testSettings(format);
+}
+
+QAudioFormat QAlsaAudioDeviceInfo::preferredFormat() const
+{
+ QAudioFormat nearest;
+ if(mode == QAudio::AudioOutput) {
+ nearest.setSampleRate(44100);
+ nearest.setChannelCount(2);
+ nearest.setByteOrder(QAudioFormat::LittleEndian);
+ nearest.setSampleType(QAudioFormat::SignedInt);
+ nearest.setSampleSize(16);
+ nearest.setCodec(QLatin1String("audio/pcm"));
+ } else {
+ nearest.setSampleRate(8000);
+ nearest.setChannelCount(1);
+ nearest.setSampleType(QAudioFormat::UnSignedInt);
+ nearest.setSampleSize(8);
+ nearest.setCodec(QLatin1String("audio/pcm"));
+ if(!testSettings(nearest)) {
+ nearest.setChannelCount(2);
+ nearest.setSampleSize(16);
+ nearest.setSampleType(QAudioFormat::SignedInt);
+ }
+ }
+ return nearest;
+}
+
+QString QAlsaAudioDeviceInfo::deviceName() const
+{
+ return device;
+}
+
+QStringList QAlsaAudioDeviceInfo::supportedCodecs()
+{
+ updateLists();
+ return codecz;
+}
+
+QList<int> QAlsaAudioDeviceInfo::supportedSampleRates()
+{
+ updateLists();
+ return sampleRatez;
+}
+
+QList<int> QAlsaAudioDeviceInfo::supportedChannelCounts()
+{
+ updateLists();
+ return channelz;
+}
+
+QList<int> QAlsaAudioDeviceInfo::supportedSampleSizes()
+{
+ updateLists();
+ return sizez;
+}
+
+QList<QAudioFormat::Endian> QAlsaAudioDeviceInfo::supportedByteOrders()
+{
+ updateLists();
+ return byteOrderz;
+}
+
+QList<QAudioFormat::SampleType> QAlsaAudioDeviceInfo::supportedSampleTypes()
+{
+ updateLists();
+ return typez;
+}
+
+bool QAlsaAudioDeviceInfo::open()
+{
+ int err = 0;
+ QString dev = device;
+ QList<QByteArray> devices = availableDevices(mode);
+
+ if(dev.compare(QLatin1String("default")) == 0) {
+#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+ if (devices.size() > 0)
+ dev = QLatin1String(devices.first().constData());
+ else
+ return false;
+#else
+ dev = QLatin1String("hw:0,0");
+#endif
+ } else {
+#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+ dev = device;
+#else
+ int idx = 0;
+ char *name;
+
+ QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
+
+ while (snd_card_get_name(idx,&name) == 0) {
+ if(dev.contains(QLatin1String(name)))
+ break;
+ idx++;
+ }
+ dev = QString(QLatin1String("hw:%1,0")).arg(idx);
+#endif
+ }
+ if(mode == QAudio::AudioOutput) {
+ err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
+ } else {
+ err=snd_pcm_open( &handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
+ }
+ if(err < 0) {
+ handle = 0;
+ return false;
+ }
+ return true;
+}
+
+void QAlsaAudioDeviceInfo::close()
+{
+ if(handle)
+ snd_pcm_close(handle);
+ handle = 0;
+}
+
+bool QAlsaAudioDeviceInfo::testSettings(const QAudioFormat& format) const
+{
+ // Set nearest to closest settings that do work.
+ // See if what is in settings will work (return value).
+ int err = -1;
+ snd_pcm_t* pcmHandle;
+ snd_pcm_hw_params_t *params;
+ QString dev;
+
+#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+ dev = device;
+ if (dev.compare(QLatin1String("default")) == 0) {
+ QList<QByteArray> devices = availableDevices(QAudio::AudioOutput);
+ if (!devices.isEmpty())
+ dev = QLatin1String(devices.first().constData());
+ }
+#else
+ if (dev.compare(QLatin1String("default")) == 0) {
+ dev = QLatin1String("hw:0,0");
+ } else {
+ int idx = 0;
+ char *name;
+
+ QString shortName = device.mid(device.indexOf(QLatin1String("="),0)+1);
+
+ while(snd_card_get_name(idx,&name) == 0) {
+ if(shortName.compare(QLatin1String(name)) == 0)
+ break;
+ idx++;
+ }
+ dev = QString(QLatin1String("hw:%1,0")).arg(idx);
+ }
+#endif
+
+ snd_pcm_stream_t stream = mode == QAudio::AudioOutput
+ ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE;
+
+ if (snd_pcm_open(&pcmHandle, dev.toLocal8Bit().constData(), stream, 0) < 0)
+ return false;
+
+ snd_pcm_nonblock(pcmHandle, 0);
+ snd_pcm_hw_params_alloca(&params);
+ snd_pcm_hw_params_any(pcmHandle, params);
+
+ // set the values!
+ snd_pcm_hw_params_set_channels(pcmHandle, params, format.channelCount());
+ snd_pcm_hw_params_set_rate(pcmHandle, params, format.sampleRate(), 0);
+
+ snd_pcm_format_t pcmFormat = SND_PCM_FORMAT_UNKNOWN;
+ switch (format.sampleSize()) {
+ case 8:
+ if (format.sampleType() == QAudioFormat::SignedInt)
+ pcmFormat = SND_PCM_FORMAT_S8;
+ else if (format.sampleType() == QAudioFormat::UnSignedInt)
+ pcmFormat = SND_PCM_FORMAT_U8;
+ break;
+ case 16:
+ if (format.sampleType() == QAudioFormat::SignedInt) {
+ pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
+ ? SND_PCM_FORMAT_S16_LE : SND_PCM_FORMAT_S16_BE;
+ } else if (format.sampleType() == QAudioFormat::UnSignedInt) {
+ pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
+ ? SND_PCM_FORMAT_U16_LE : SND_PCM_FORMAT_U16_BE;
+ }
+ break;
+ case 32:
+ if (format.sampleType() == QAudioFormat::SignedInt) {
+ pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
+ ? SND_PCM_FORMAT_S32_LE : SND_PCM_FORMAT_S32_BE;
+ } else if (format.sampleType() == QAudioFormat::UnSignedInt) {
+ pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
+ ? SND_PCM_FORMAT_U32_LE : SND_PCM_FORMAT_U32_BE;
+ } else if (format.sampleType() == QAudioFormat::Float) {
+ pcmFormat = format.byteOrder() == QAudioFormat::LittleEndian
+ ? SND_PCM_FORMAT_FLOAT_LE : SND_PCM_FORMAT_FLOAT_BE;
+ }
+ }
+
+ if (pcmFormat != SND_PCM_FORMAT_UNKNOWN)
+ err = snd_pcm_hw_params_set_format(pcmHandle, params, pcmFormat);
+
+ // For now, just accept only audio/pcm codec
+ if (!format.codec().startsWith(QLatin1String("audio/pcm")))
+ err = -1;
+
+ if (err >= 0 && format.channelCount() != -1) {
+ err = snd_pcm_hw_params_test_channels(pcmHandle, params, format.channelCount());
+ if (err >= 0)
+ err = snd_pcm_hw_params_set_channels(pcmHandle, params, format.channelCount());
+ }
+
+ if (err >= 0 && format.sampleRate() != -1) {
+ err = snd_pcm_hw_params_test_rate(pcmHandle, params, format.sampleRate(), 0);
+ if (err >= 0)
+ err = snd_pcm_hw_params_set_rate(pcmHandle, params, format.sampleRate(), 0);
+ }
+
+ if (err >= 0 && pcmFormat != SND_PCM_FORMAT_UNKNOWN)
+ err = snd_pcm_hw_params_set_format(pcmHandle, params, pcmFormat);
+
+ if (err >= 0)
+ err = snd_pcm_hw_params(pcmHandle, params);
+
+ snd_pcm_close(pcmHandle);
+
+ return (err == 0);
+}
+
+void QAlsaAudioDeviceInfo::updateLists()
+{
+ // redo all lists based on current settings
+ sampleRatez.clear();
+ channelz.clear();
+ sizez.clear();
+ byteOrderz.clear();
+ typez.clear();
+ codecz.clear();
+
+ if(!handle)
+ open();
+
+ if(!handle)
+ return;
+
+ for(int i=0; i<(int)MAX_SAMPLE_RATES; i++) {
+ //if(snd_pcm_hw_params_test_rate(handle, params, SAMPLE_RATES[i], dir) == 0)
+ sampleRatez.append(SAMPLE_RATES[i]);
+ }
+ channelz.append(1);
+ channelz.append(2);
+ if (surround40) channelz.append(4);
+ if (surround51) channelz.append(6);
+ if (surround71) channelz.append(8);
+ sizez.append(8);
+ sizez.append(16);
+ sizez.append(32);
+ byteOrderz.append(QAudioFormat::LittleEndian);
+ byteOrderz.append(QAudioFormat::BigEndian);
+ typez.append(QAudioFormat::SignedInt);
+ typez.append(QAudioFormat::UnSignedInt);
+ typez.append(QAudioFormat::Float);
+ codecz.append(QLatin1String("audio/pcm"));
+ close();
+}
+
+QList<QByteArray> QAlsaAudioDeviceInfo::availableDevices(QAudio::Mode mode)
+{
+ QList<QByteArray> devices;
+ QByteArray filter;
+
+#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+ // Create a list of all current audio devices that support mode
+ void **hints;
+ char *name, *descr, *io;
+ int card = -1;
+
+ if(mode == QAudio::AudioInput) {
+ filter = "Input";
+ } else {
+ filter = "Output";
+ }
+
+ while (snd_card_next(&card) == 0 && card >= 0) {
+ if (snd_device_name_hint(card, "pcm", &hints) < 0)
+ continue;
+
+ void **n = hints;
+ while (*n != NULL) {
+ name = snd_device_name_get_hint(*n, "NAME");
+ if (name != 0 && qstrcmp(name, "null") != 0) {
+ descr = snd_device_name_get_hint(*n, "DESC");
+ io = snd_device_name_get_hint(*n, "IOID");
+
+ if ((descr != NULL) && ((io == NULL) || (io == filter))) {
+ QString deviceName = QLatin1String(name);
+ QString deviceDescription = QLatin1String(descr);
+ if (deviceDescription.contains(QLatin1String("Default Audio Device")))
+ devices.prepend(deviceName.toLocal8Bit().constData());
+ else
+ devices.append(deviceName.toLocal8Bit().constData());
+ }
+
+ free(descr);
+ free(io);
+ }
+ free(name);
+ ++n;
+ }
+
+ snd_device_name_free_hint(hints);
+ }
+#else
+ int idx = 0;
+ char* name;
+
+ while(snd_card_get_name(idx,&name) == 0) {
+ devices.append(name);
+ idx++;
+ }
+#endif
+
+ if (devices.size() > 0)
+ devices.append("default");
+
+ return devices;
+}
+
+QByteArray QAlsaAudioDeviceInfo::defaultInputDevice()
+{
+ QList<QByteArray> devices = availableDevices(QAudio::AudioInput);
+ if(devices.size() == 0)
+ return QByteArray();
+
+ return devices.first();
+}
+
+QByteArray QAlsaAudioDeviceInfo::defaultOutputDevice()
+{
+ QList<QByteArray> devices = availableDevices(QAudio::AudioOutput);
+ if(devices.size() == 0)
+ return QByteArray();
+
+ return devices.first();
+}
+
+void QAlsaAudioDeviceInfo::checkSurround()
+{
+ surround40 = false;
+ surround51 = false;
+ surround71 = false;
+
+ void **hints;
+ char *name, *descr, *io;
+ int card = -1;
+
+ while (snd_card_next(&card) == 0 && card >= 0) {
+ if (snd_device_name_hint(card, "pcm", &hints) < 0)
+ continue;
+
+ void **n = hints;
+ while (*n != NULL) {
+ name = snd_device_name_get_hint(*n, "NAME");
+ descr = snd_device_name_get_hint(*n, "DESC");
+ io = snd_device_name_get_hint(*n, "IOID");
+ if((name != NULL) && (descr != NULL)) {
+ QString deviceName = QLatin1String(name);
+ if (mode == QAudio::AudioOutput) {
+ if(deviceName.contains(QLatin1String("surround40")))
+ surround40 = true;
+ if(deviceName.contains(QLatin1String("surround51")))
+ surround51 = true;
+ if(deviceName.contains(QLatin1String("surround71")))
+ surround71 = true;
+ }
+ }
+ if(name != NULL)
+ free(name);
+ if(descr != NULL)
+ free(descr);
+ if(io != NULL)
+ free(io);
+ ++n;
+ }
+
+ snd_device_name_free_hint(hints);
+ }
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/alsa/qalsaaudiodeviceinfo.h b/src/plugins/alsa/qalsaaudiodeviceinfo.h
new file mode 100644
index 000000000..c1840ee9a
--- /dev/null
+++ b/src/plugins/alsa/qalsaaudiodeviceinfo.h
@@ -0,0 +1,122 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+
+#ifndef QALSAAUDIODEVICEINFO_H
+#define QALSAAUDIODEVICEINFO_H
+
+#include <alsa/asoundlib.h>
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qdebug.h>
+
+#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qaudiodeviceinfo.h>
+#include <QtMultimedia/qaudiosystem.h>
+
+QT_BEGIN_NAMESPACE
+
+
+const unsigned int MAX_SAMPLE_RATES = 5;
+const unsigned int SAMPLE_RATES[] =
+ { 8000, 11025, 22050, 44100, 48000 };
+
+class QAlsaAudioDeviceInfo : public QAbstractAudioDeviceInfo
+{
+ Q_OBJECT
+public:
+ QAlsaAudioDeviceInfo(QByteArray dev,QAudio::Mode mode);
+ ~QAlsaAudioDeviceInfo();
+
+ bool testSettings(const QAudioFormat& format) const;
+ void updateLists();
+ QAudioFormat preferredFormat() const;
+ bool isFormatSupported(const QAudioFormat& format) const;
+ QString deviceName() const;
+ QStringList supportedCodecs();
+ QList<int> supportedSampleRates();
+ QList<int> supportedChannelCounts();
+ QList<int> supportedSampleSizes();
+ QList<QAudioFormat::Endian> supportedByteOrders();
+ QList<QAudioFormat::SampleType> supportedSampleTypes();
+ static QByteArray defaultInputDevice();
+ static QByteArray defaultOutputDevice();
+ static QList<QByteArray> availableDevices(QAudio::Mode);
+
+private:
+ bool open();
+ void close();
+
+ void checkSurround();
+ bool surround40;
+ bool surround51;
+ bool surround71;
+
+ QString device;
+ QAudio::Mode mode;
+ QAudioFormat nearest;
+ QList<int> sampleRatez;
+ QList<int> channelz;
+ QList<int> sizez;
+ QList<QAudioFormat::Endian> byteOrderz;
+ QStringList codecz;
+ QList<QAudioFormat::SampleType> typez;
+ snd_pcm_t* handle;
+ snd_pcm_hw_params_t *params;
+};
+
+QT_END_NAMESPACE
+
+
+#endif // QALSAAUDIODEVICEINFO_H
diff --git a/src/plugins/alsa/qalsaaudioinput.cpp b/src/plugins/alsa/qalsaaudioinput.cpp
new file mode 100644
index 000000000..902dd57d7
--- /dev/null
+++ b/src/plugins/alsa/qalsaaudioinput.cpp
@@ -0,0 +1,882 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// INTERNAL USE ONLY: Do NOT use for any other purpose.
+//
+
+#include <QtCore/qcoreapplication.h>
+#include <QtMultimedia/private/qaudiohelpers_p.h>
+#include "qalsaaudioinput.h"
+#include "qalsaaudiodeviceinfo.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define DEBUG_AUDIO 1
+
+QAlsaAudioInput::QAlsaAudioInput(const QByteArray &device)
+{
+ bytesAvailable = 0;
+ handle = 0;
+ ahandler = 0;
+ access = SND_PCM_ACCESS_RW_INTERLEAVED;
+ pcmformat = SND_PCM_FORMAT_S16;
+ buffer_size = 0;
+ period_size = 0;
+ buffer_time = 100000;
+ period_time = 20000;
+ totalTimeValue = 0;
+ intervalTime = 1000;
+ errorState = QAudio::NoError;
+ deviceState = QAudio::StoppedState;
+ audioSource = 0;
+ pullMode = true;
+ resuming = false;
+
+ m_volume = 1.0f;
+
+ m_device = device;
+
+ timer = new QTimer(this);
+ connect(timer,SIGNAL(timeout()),SLOT(userFeed()));
+}
+
+QAlsaAudioInput::~QAlsaAudioInput()
+{
+ close();
+ disconnect(timer, SIGNAL(timeout()));
+ QCoreApplication::processEvents();
+ delete timer;
+}
+
+void QAlsaAudioInput::setVolume(qreal vol)
+{
+ m_volume = vol;
+}
+
+qreal QAlsaAudioInput::volume() const
+{
+ return m_volume;
+}
+
+QAudio::Error QAlsaAudioInput::error() const
+{
+ return errorState;
+}
+
+QAudio::State QAlsaAudioInput::state() const
+{
+ return deviceState;
+}
+
+void QAlsaAudioInput::setFormat(const QAudioFormat& fmt)
+{
+ if (deviceState == QAudio::StoppedState)
+ settings = fmt;
+}
+
+QAudioFormat QAlsaAudioInput::format() const
+{
+ return settings;
+}
+
+int QAlsaAudioInput::xrun_recovery(int err)
+{
+ int count = 0;
+ bool reset = false;
+
+ if(err == -EPIPE) {
+ errorState = QAudio::UnderrunError;
+ err = snd_pcm_prepare(handle);
+ if(err < 0)
+ reset = true;
+ else {
+ bytesAvailable = checkBytesReady();
+ if (bytesAvailable <= 0)
+ reset = true;
+ }
+
+ } else if((err == -ESTRPIPE)||(err == -EIO)) {
+ errorState = QAudio::IOError;
+ while((err = snd_pcm_resume(handle)) == -EAGAIN){
+ usleep(100);
+ count++;
+ if(count > 5) {
+ reset = true;
+ break;
+ }
+ }
+ if(err < 0) {
+ err = snd_pcm_prepare(handle);
+ if(err < 0)
+ reset = true;
+ }
+ }
+ if(reset) {
+ close();
+ open();
+ snd_pcm_prepare(handle);
+ return 0;
+ }
+ return err;
+}
+
+int QAlsaAudioInput::setFormat()
+{
+ snd_pcm_format_t format = SND_PCM_FORMAT_UNKNOWN;
+
+ if(settings.sampleSize() == 8) {
+ format = SND_PCM_FORMAT_U8;
+ } else if(settings.sampleSize() == 16) {
+ if(settings.sampleType() == QAudioFormat::SignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ format = SND_PCM_FORMAT_S16_LE;
+ else
+ format = SND_PCM_FORMAT_S16_BE;
+ } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ format = SND_PCM_FORMAT_U16_LE;
+ else
+ format = SND_PCM_FORMAT_U16_BE;
+ }
+ } else if(settings.sampleSize() == 24) {
+ if(settings.sampleType() == QAudioFormat::SignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ format = SND_PCM_FORMAT_S24_LE;
+ else
+ format = SND_PCM_FORMAT_S24_BE;
+ } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ format = SND_PCM_FORMAT_U24_LE;
+ else
+ format = SND_PCM_FORMAT_U24_BE;
+ }
+ } else if(settings.sampleSize() == 32) {
+ if(settings.sampleType() == QAudioFormat::SignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ format = SND_PCM_FORMAT_S32_LE;
+ else
+ format = SND_PCM_FORMAT_S32_BE;
+ } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ format = SND_PCM_FORMAT_U32_LE;
+ else
+ format = SND_PCM_FORMAT_U32_BE;
+ } else if(settings.sampleType() == QAudioFormat::Float) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ format = SND_PCM_FORMAT_FLOAT_LE;
+ else
+ format = SND_PCM_FORMAT_FLOAT_BE;
+ }
+ } else if(settings.sampleSize() == 64) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ format = SND_PCM_FORMAT_FLOAT64_LE;
+ else
+ format = SND_PCM_FORMAT_FLOAT64_BE;
+ }
+
+ return format != SND_PCM_FORMAT_UNKNOWN
+ ? snd_pcm_hw_params_set_format( handle, hwparams, format)
+ : -1;
+}
+
+void QAlsaAudioInput::start(QIODevice* device)
+{
+ if(deviceState != QAudio::StoppedState)
+ close();
+
+ if(!pullMode && audioSource)
+ delete audioSource;
+
+ pullMode = true;
+ audioSource = device;
+
+ deviceState = QAudio::ActiveState;
+
+ if( !open() )
+ return;
+
+ emit stateChanged(deviceState);
+}
+
+QIODevice* QAlsaAudioInput::start()
+{
+ if(deviceState != QAudio::StoppedState)
+ close();
+
+ if(!pullMode && audioSource)
+ delete audioSource;
+
+ pullMode = false;
+ audioSource = new InputPrivate(this);
+ audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
+
+ deviceState = QAudio::IdleState;
+
+ if( !open() )
+ return 0;
+
+ emit stateChanged(deviceState);
+
+ return audioSource;
+}
+
+void QAlsaAudioInput::stop()
+{
+ if(deviceState == QAudio::StoppedState)
+ return;
+
+ deviceState = QAudio::StoppedState;
+
+ close();
+ emit stateChanged(deviceState);
+}
+
+bool QAlsaAudioInput::open()
+{
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
+#endif
+ clockStamp.restart();
+ timeStamp.restart();
+ elapsedTimeOffset = 0;
+
+ int dir;
+ int err = 0;
+ int count=0;
+ unsigned int sampleRate=settings.sampleRate();
+
+ if (!settings.isValid()) {
+ qWarning("QAudioInput: open error, invalid format.");
+ } else if (settings.sampleRate() <= 0) {
+ qWarning("QAudioInput: open error, invalid sample rate (%d).",
+ settings.sampleRate());
+ } else {
+ err = -1;
+ }
+
+ if (err == 0) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit errorChanged(errorState);
+ return false;
+ }
+
+
+ QString dev = QString(QLatin1String(m_device.constData()));
+ QList<QByteArray> devices = QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioInput);
+ if(dev.compare(QLatin1String("default")) == 0) {
+#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+ if (devices.size() > 0)
+ dev = QLatin1String(devices.first());
+ else
+ return false;
+#else
+ dev = QLatin1String("hw:0,0");
+#endif
+ } else {
+#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+ dev = QLatin1String(m_device);
+#else
+ int idx = 0;
+ char *name;
+
+ QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData());
+
+ while(snd_card_get_name(idx,&name) == 0) {
+ if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0)
+ break;
+ idx++;
+ }
+ dev = QString(QLatin1String("hw:%1,0")).arg(idx);
+#endif
+ }
+
+ // Step 1: try and open the device
+ while((count < 5) && (err < 0)) {
+ err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_CAPTURE,0);
+ if(err < 0)
+ count++;
+ }
+ if (( err < 0)||(handle == 0)) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return false;
+ }
+ snd_pcm_nonblock( handle, 0 );
+
+ // Step 2: Set the desired HW parameters.
+ snd_pcm_hw_params_alloca( &hwparams );
+
+ bool fatal = false;
+ QString errMessage;
+ unsigned int chunks = 8;
+
+ err = snd_pcm_hw_params_any( handle, hwparams );
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_any: err = %1").arg(err);
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 );
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_access( handle, hwparams, access );
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_access: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = setFormat();
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_format: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channelCount() );
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_channels: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &sampleRate, 0 );
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir);
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir);
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir);
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params(handle, hwparams);
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioInput: snd_pcm_hw_params: err = %1").arg(err);
+ }
+ }
+ if( err < 0) {
+ qWarning()<<errMessage;
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return false;
+ }
+ snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames);
+ buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames);
+ snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir);
+ period_size = snd_pcm_frames_to_bytes(handle,period_frames);
+ snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir);
+ snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir);
+
+ // Step 3: Set the desired SW parameters.
+ snd_pcm_sw_params_t *swparams;
+ snd_pcm_sw_params_alloca(&swparams);
+ snd_pcm_sw_params_current(handle, swparams);
+ snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames);
+ snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames);
+ snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames);
+ snd_pcm_sw_params(handle, swparams);
+
+ // Step 4: Prepare audio
+ ringBuffer.resize(buffer_size);
+ snd_pcm_prepare( handle );
+ snd_pcm_start(handle);
+
+ // Step 5: Setup timer
+ bytesAvailable = checkBytesReady();
+
+ if(pullMode)
+ connect(audioSource,SIGNAL(readyRead()),this,SLOT(userFeed()));
+
+ // Step 6: Start audio processing
+ chunks = buffer_size/period_size;
+ timer->start(period_time*chunks/2000);
+
+ errorState = QAudio::NoError;
+
+ totalTimeValue = 0;
+
+ return true;
+}
+
+void QAlsaAudioInput::close()
+{
+ timer->stop();
+
+ if ( handle ) {
+ snd_pcm_drop( handle );
+ snd_pcm_close( handle );
+ handle = 0;
+ }
+}
+
+int QAlsaAudioInput::checkBytesReady()
+{
+ if(resuming)
+ bytesAvailable = period_size;
+ else if(deviceState != QAudio::ActiveState
+ && deviceState != QAudio::IdleState)
+ bytesAvailable = 0;
+ else {
+ int frames = snd_pcm_avail_update(handle);
+ if (frames < 0) {
+ bytesAvailable = frames;
+ } else {
+ if((int)frames > (int)buffer_frames)
+ frames = buffer_frames;
+ bytesAvailable = snd_pcm_frames_to_bytes(handle, frames);
+ }
+ }
+ return bytesAvailable;
+}
+
+int QAlsaAudioInput::bytesReady() const
+{
+ return qMax(bytesAvailable, 0);
+}
+
+qint64 QAlsaAudioInput::read(char* data, qint64 len)
+{
+ // Read in some audio data and write it to QIODevice, pull mode
+ if ( !handle )
+ return 0;
+
+ int bytesRead = 0;
+ int bytesInRingbufferBeforeRead = ringBuffer.bytesOfDataInBuffer();
+
+ if (ringBuffer.bytesOfDataInBuffer() < len) {
+
+ // bytesAvaiable is saved as a side effect of checkBytesReady().
+ int bytesToRead = checkBytesReady();
+
+ if (bytesToRead < 0) {
+ // bytesAvailable as negative is error code, try to recover from it.
+ xrun_recovery(bytesToRead);
+ bytesToRead = checkBytesReady();
+ if (bytesToRead < 0) {
+ // recovery failed must stop and set error.
+ close();
+ errorState = QAudio::IOError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return 0;
+ }
+ }
+
+ bytesToRead = qMin<qint64>(len, bytesToRead);
+ bytesToRead = qMin<qint64>(ringBuffer.freeBytes(), bytesToRead);
+ bytesToRead -= bytesToRead % period_size;
+
+ int count=0;
+ int err = 0;
+ while(count < 5 && bytesToRead > 0) {
+ char buffer[bytesToRead];
+ int chunks = bytesToRead / period_size;
+ int frames = chunks * period_frames;
+ if (frames > (int)buffer_frames)
+ frames = buffer_frames;
+
+ int readFrames = snd_pcm_readi(handle, buffer, frames);
+ bytesRead = snd_pcm_frames_to_bytes(handle, readFrames);
+ if (m_volume < 1.0f)
+ QAudioHelperInternal::qMultiplySamples(m_volume, settings, buffer, buffer, bytesRead);
+
+ if (readFrames >= 0) {
+ ringBuffer.write(buffer, bytesRead);
+#ifdef DEBUG_AUDIO
+ qDebug() << QString::fromLatin1("read in bytes = %1 (frames=%2)").arg(bytesRead).arg(readFrames).toLatin1().constData();
+#endif
+ break;
+ } else if((readFrames == -EAGAIN) || (readFrames == -EINTR)) {
+ errorState = QAudio::IOError;
+ err = 0;
+ break;
+ } else {
+ if(readFrames == -EPIPE) {
+ errorState = QAudio::UnderrunError;
+ err = snd_pcm_prepare(handle);
+ } else if(readFrames == -ESTRPIPE) {
+ err = snd_pcm_prepare(handle);
+ }
+ if(err != 0) break;
+ }
+ count++;
+ }
+
+ }
+
+ bytesRead += bytesInRingbufferBeforeRead;
+
+ if (bytesRead > 0) {
+ // got some send it onward
+#ifdef DEBUG_AUDIO
+ qDebug() << "frames to write to QIODevice = " <<
+ snd_pcm_bytes_to_frames( handle, (int)bytesRead ) << " (" << bytesRead << ") bytes";
+#endif
+ if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
+ return 0;
+
+ if (pullMode) {
+ qint64 l = 0;
+ qint64 bytesWritten = 0;
+ while (ringBuffer.bytesOfDataInBuffer() > 0) {
+ l = audioSource->write(ringBuffer.availableData(), ringBuffer.availableDataBlockSize());
+ if (l > 0) {
+ ringBuffer.readBytes(l);
+ bytesWritten += l;
+ } else {
+ break;
+ }
+ }
+
+ if (l < 0) {
+ close();
+ errorState = QAudio::IOError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ } else if (l == 0 && bytesWritten == 0) {
+ if (deviceState != QAudio::IdleState) {
+ errorState = QAudio::NoError;
+ deviceState = QAudio::IdleState;
+ emit stateChanged(deviceState);
+ }
+ } else {
+ bytesAvailable -= bytesWritten;
+ totalTimeValue += bytesWritten;
+ resuming = false;
+ if (deviceState != QAudio::ActiveState) {
+ errorState = QAudio::NoError;
+ deviceState = QAudio::ActiveState;
+ emit stateChanged(deviceState);
+ }
+ }
+
+ return bytesWritten;
+ } else {
+ while (ringBuffer.bytesOfDataInBuffer() > 0) {
+ int size = ringBuffer.availableDataBlockSize();
+ memcpy(data, ringBuffer.availableData(), size);
+ data += size;
+ ringBuffer.readBytes(size);
+ }
+
+ bytesAvailable -= bytesRead;
+ totalTimeValue += bytesRead;
+ resuming = false;
+ if (deviceState != QAudio::ActiveState) {
+ errorState = QAudio::NoError;
+ deviceState = QAudio::ActiveState;
+ emit stateChanged(deviceState);
+ }
+
+ return bytesRead;
+ }
+ }
+
+ return 0;
+}
+
+void QAlsaAudioInput::resume()
+{
+ if(deviceState == QAudio::SuspendedState) {
+ int err = 0;
+
+ if(handle) {
+ err = snd_pcm_prepare( handle );
+ if(err < 0)
+ xrun_recovery(err);
+
+ err = snd_pcm_start(handle);
+ if(err < 0)
+ xrun_recovery(err);
+
+ bytesAvailable = buffer_size;
+ }
+ resuming = true;
+ deviceState = QAudio::ActiveState;
+ int chunks = buffer_size/period_size;
+ timer->start(period_time*chunks/2000);
+ emit stateChanged(deviceState);
+ }
+}
+
+void QAlsaAudioInput::setBufferSize(int value)
+{
+ buffer_size = value;
+}
+
+int QAlsaAudioInput::bufferSize() const
+{
+ return buffer_size;
+}
+
+int QAlsaAudioInput::periodSize() const
+{
+ return period_size;
+}
+
+void QAlsaAudioInput::setNotifyInterval(int ms)
+{
+ intervalTime = qMax(0, ms);
+}
+
+int QAlsaAudioInput::notifyInterval() const
+{
+ return intervalTime;
+}
+
+qint64 QAlsaAudioInput::processedUSecs() const
+{
+ qint64 result = qint64(1000000) * totalTimeValue /
+ (settings.channelCount()*(settings.sampleSize()/8)) /
+ settings.sampleRate();
+
+ return result;
+}
+
+void QAlsaAudioInput::suspend()
+{
+ if(deviceState == QAudio::ActiveState||resuming) {
+ timer->stop();
+ deviceState = QAudio::SuspendedState;
+ emit stateChanged(deviceState);
+ }
+}
+
+void QAlsaAudioInput::userFeed()
+{
+ if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
+ return;
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() IN";
+#endif
+ deviceReady();
+}
+
+bool QAlsaAudioInput::deviceReady()
+{
+ if(pullMode) {
+ // reads some audio data and writes it to QIODevice
+ read(0, buffer_size);
+ } else {
+ // emits readyRead() so user will call read() on QIODevice to get some audio data
+ InputPrivate* a = qobject_cast<InputPrivate*>(audioSource);
+ a->trigger();
+ }
+ bytesAvailable = checkBytesReady();
+
+ if(deviceState != QAudio::ActiveState)
+ return true;
+
+ if (bytesAvailable < 0) {
+ // bytesAvailable as negative is error code, try to recover from it.
+ xrun_recovery(bytesAvailable);
+ bytesAvailable = checkBytesReady();
+ if (bytesAvailable < 0) {
+ // recovery failed must stop and set error.
+ close();
+ errorState = QAudio::IOError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return 0;
+ }
+ }
+
+ if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
+ emit notify();
+ elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
+ timeStamp.restart();
+ }
+ return true;
+}
+
+qint64 QAlsaAudioInput::elapsedUSecs() const
+{
+ if (deviceState == QAudio::StoppedState)
+ return 0;
+
+ return clockStamp.elapsed()*1000;
+}
+
+void QAlsaAudioInput::reset()
+{
+ if(handle)
+ snd_pcm_reset(handle);
+ stop();
+ bytesAvailable = 0;
+}
+
+void QAlsaAudioInput::drain()
+{
+ if(handle)
+ snd_pcm_drain(handle);
+}
+
+InputPrivate::InputPrivate(QAlsaAudioInput* audio)
+{
+ audioDevice = qobject_cast<QAlsaAudioInput*>(audio);
+}
+
+InputPrivate::~InputPrivate()
+{
+}
+
+qint64 InputPrivate::readData( char* data, qint64 len)
+{
+ return audioDevice->read(data,len);
+}
+
+qint64 InputPrivate::writeData(const char* data, qint64 len)
+{
+ Q_UNUSED(data)
+ Q_UNUSED(len)
+ return 0;
+}
+
+void InputPrivate::trigger()
+{
+ emit readyRead();
+}
+
+RingBuffer::RingBuffer() :
+ m_head(0),
+ m_tail(0)
+{
+}
+
+void RingBuffer::resize(int size)
+{
+ m_data.resize(size);
+}
+
+int RingBuffer::bytesOfDataInBuffer() const
+{
+ if (m_head < m_tail)
+ return m_tail - m_head;
+ else if (m_tail < m_head)
+ return m_data.size() + m_tail - m_head;
+ else
+ return 0;
+}
+
+int RingBuffer::freeBytes() const
+{
+ if (m_head > m_tail)
+ return m_head - m_tail - 1;
+ else if (m_tail > m_head)
+ return m_data.size() - m_tail + m_head - 1;
+ else
+ return m_data.size() - 1;
+}
+
+const char *RingBuffer::availableData() const
+{
+ return (m_data.constData() + m_head);
+}
+
+int RingBuffer::availableDataBlockSize() const
+{
+ if (m_head > m_tail)
+ return m_data.size() - m_head;
+ else if (m_tail > m_head)
+ return m_tail - m_head;
+ else
+ return 0;
+}
+
+void RingBuffer::readBytes(int bytes)
+{
+ m_head = (m_head + bytes) % m_data.size();
+}
+
+void RingBuffer::write(char *data, int len)
+{
+ if (m_tail + len < m_data.size()) {
+ memcpy(m_data.data() + m_tail, data, len);
+ m_tail += len;
+ } else {
+ int bytesUntilEnd = m_data.size() - m_tail;
+ memcpy(m_data.data() + m_tail, data, bytesUntilEnd);
+ if (len - bytesUntilEnd > 0)
+ memcpy(m_data.data(), data + bytesUntilEnd, len - bytesUntilEnd);
+ m_tail = len - bytesUntilEnd;
+ }
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qalsaaudioinput.cpp"
diff --git a/src/plugins/alsa/qalsaaudioinput.h b/src/plugins/alsa/qalsaaudioinput.h
new file mode 100644
index 000000000..6af566c8b
--- /dev/null
+++ b/src/plugins/alsa/qalsaaudioinput.h
@@ -0,0 +1,188 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+
+#ifndef QAUDIOINPUTALSA_H
+#define QAUDIOINPUTALSA_H
+
+#include <alsa/asoundlib.h>
+
+#include <QtCore/qfile.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qdatetime.h>
+
+#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qaudiodeviceinfo.h>
+#include <QtMultimedia/qaudiosystem.h>
+
+QT_BEGIN_NAMESPACE
+
+
+class InputPrivate;
+
+class RingBuffer
+{
+public:
+ RingBuffer();
+
+ void resize(int size);
+
+ int bytesOfDataInBuffer() const;
+ int freeBytes() const;
+
+ const char *availableData() const;
+ int availableDataBlockSize() const;
+ void readBytes(int bytes);
+
+ void write(char *data, int len);
+
+private:
+ int m_head;
+ int m_tail;
+
+ QByteArray m_data;
+};
+
+class QAlsaAudioInput : public QAbstractAudioInput
+{
+ Q_OBJECT
+public:
+ QAlsaAudioInput(const QByteArray &device);
+ ~QAlsaAudioInput();
+
+ qint64 read(char* data, qint64 len);
+
+ void start(QIODevice* device);
+ QIODevice* start();
+ void stop();
+ void reset();
+ void suspend();
+ void resume();
+ int bytesReady() const;
+ int periodSize() const;
+ void setBufferSize(int value);
+ int bufferSize() const;
+ void setNotifyInterval(int milliSeconds);
+ int notifyInterval() const;
+ qint64 processedUSecs() const;
+ qint64 elapsedUSecs() const;
+ QAudio::Error error() const;
+ QAudio::State state() const;
+ void setFormat(const QAudioFormat& fmt);
+ QAudioFormat format() const;
+ void setVolume(qreal);
+ qreal volume() const;
+ bool resuming;
+ snd_pcm_t* handle;
+ qint64 totalTimeValue;
+ QIODevice* audioSource;
+ QAudioFormat settings;
+ QAudio::Error errorState;
+ QAudio::State deviceState;
+
+private slots:
+ void userFeed();
+ bool deviceReady();
+
+private:
+ int checkBytesReady();
+ int xrun_recovery(int err);
+ int setFormat();
+ bool open();
+ void close();
+ void drain();
+
+ QTimer* timer;
+ QTime timeStamp;
+ QTime clockStamp;
+ qint64 elapsedTimeOffset;
+ int intervalTime;
+ RingBuffer ringBuffer;
+ int bytesAvailable;
+ QByteArray m_device;
+ bool pullMode;
+ int buffer_size;
+ int period_size;
+ unsigned int buffer_time;
+ unsigned int period_time;
+ snd_pcm_uframes_t buffer_frames;
+ snd_pcm_uframes_t period_frames;
+ snd_async_handler_t* ahandler;
+ snd_pcm_access_t access;
+ snd_pcm_format_t pcmformat;
+ snd_timestamp_t* timestamp;
+ snd_pcm_hw_params_t *hwparams;
+ qreal m_volume;
+};
+
+class InputPrivate : public QIODevice
+{
+ Q_OBJECT
+public:
+ InputPrivate(QAlsaAudioInput* audio);
+ ~InputPrivate();
+
+ qint64 readData( char* data, qint64 len);
+ qint64 writeData(const char* data, qint64 len);
+
+ void trigger();
+private:
+ QAlsaAudioInput *audioDevice;
+};
+
+QT_END_NAMESPACE
+
+
+#endif
diff --git a/src/plugins/alsa/qalsaaudiooutput.cpp b/src/plugins/alsa/qalsaaudiooutput.cpp
new file mode 100644
index 000000000..192b63596
--- /dev/null
+++ b/src/plugins/alsa/qalsaaudiooutput.cpp
@@ -0,0 +1,860 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// INTERNAL USE ONLY: Do NOT use for any other purpose.
+//
+
+#include <QtCore/qcoreapplication.h>
+#include <QtMultimedia/private/qaudiohelpers_p.h>
+#include "qalsaaudiooutput.h"
+#include "qalsaaudiodeviceinfo.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define DEBUG_AUDIO 1
+
+QAlsaAudioOutput::QAlsaAudioOutput(const QByteArray &device)
+{
+ bytesAvailable = 0;
+ handle = 0;
+ ahandler = 0;
+ access = SND_PCM_ACCESS_RW_INTERLEAVED;
+ pcmformat = SND_PCM_FORMAT_S16;
+ buffer_frames = 0;
+ period_frames = 0;
+ buffer_size = 0;
+ period_size = 0;
+ buffer_time = 100000;
+ period_time = 20000;
+ totalTimeValue = 0;
+ intervalTime = 1000;
+ audioBuffer = 0;
+ errorState = QAudio::NoError;
+ deviceState = QAudio::StoppedState;
+ audioSource = 0;
+ pullMode = true;
+ resuming = false;
+ opened = false;
+
+ m_volume = 1.0f;
+
+ m_device = device;
+
+ timer = new QTimer(this);
+ connect(timer,SIGNAL(timeout()),SLOT(userFeed()));
+}
+
+QAlsaAudioOutput::~QAlsaAudioOutput()
+{
+ close();
+ disconnect(timer, SIGNAL(timeout()));
+ QCoreApplication::processEvents();
+ delete timer;
+}
+
+void QAlsaAudioOutput::setVolume(qreal vol)
+{
+ m_volume = vol;
+}
+
+qreal QAlsaAudioOutput::volume() const
+{
+ return m_volume;
+}
+
+QAudio::Error QAlsaAudioOutput::error() const
+{
+ return errorState;
+}
+
+QAudio::State QAlsaAudioOutput::state() const
+{
+ return deviceState;
+}
+
+void QAlsaAudioOutput::async_callback(snd_async_handler_t *ahandler)
+{
+ QAlsaAudioOutput* audioOut;
+
+ audioOut = static_cast<QAlsaAudioOutput*>
+ (snd_async_handler_get_callback_private(ahandler));
+
+ if (audioOut && (audioOut->deviceState == QAudio::ActiveState || audioOut->resuming))
+ audioOut->feedback();
+}
+
+int QAlsaAudioOutput::xrun_recovery(int err)
+{
+ int count = 0;
+ bool reset = false;
+
+ if(err == -EPIPE) {
+ errorState = QAudio::UnderrunError;
+ emit errorChanged(errorState);
+ err = snd_pcm_prepare(handle);
+ if(err < 0)
+ reset = true;
+
+ } else if((err == -ESTRPIPE)||(err == -EIO)) {
+ errorState = QAudio::IOError;
+ emit errorChanged(errorState);
+ while((err = snd_pcm_resume(handle)) == -EAGAIN){
+ usleep(100);
+ count++;
+ if(count > 5) {
+ reset = true;
+ break;
+ }
+ }
+ if(err < 0) {
+ err = snd_pcm_prepare(handle);
+ if(err < 0)
+ reset = true;
+ }
+ }
+ if(reset) {
+ close();
+ open();
+ snd_pcm_prepare(handle);
+ return 0;
+ }
+ return err;
+}
+
+int QAlsaAudioOutput::setFormat()
+{
+ snd_pcm_format_t pcmformat = SND_PCM_FORMAT_UNKNOWN;
+
+ if(settings.sampleSize() == 8) {
+ pcmformat = SND_PCM_FORMAT_U8;
+
+ } else if(settings.sampleSize() == 16) {
+ if(settings.sampleType() == QAudioFormat::SignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ pcmformat = SND_PCM_FORMAT_S16_LE;
+ else
+ pcmformat = SND_PCM_FORMAT_S16_BE;
+ } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ pcmformat = SND_PCM_FORMAT_U16_LE;
+ else
+ pcmformat = SND_PCM_FORMAT_U16_BE;
+ }
+ } else if(settings.sampleSize() == 24) {
+ if(settings.sampleType() == QAudioFormat::SignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ pcmformat = SND_PCM_FORMAT_S24_LE;
+ else
+ pcmformat = SND_PCM_FORMAT_S24_BE;
+ } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ pcmformat = SND_PCM_FORMAT_U24_LE;
+ else
+ pcmformat = SND_PCM_FORMAT_U24_BE;
+ }
+ } else if(settings.sampleSize() == 32) {
+ if(settings.sampleType() == QAudioFormat::SignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ pcmformat = SND_PCM_FORMAT_S32_LE;
+ else
+ pcmformat = SND_PCM_FORMAT_S32_BE;
+ } else if(settings.sampleType() == QAudioFormat::UnSignedInt) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ pcmformat = SND_PCM_FORMAT_U32_LE;
+ else
+ pcmformat = SND_PCM_FORMAT_U32_BE;
+ } else if(settings.sampleType() == QAudioFormat::Float) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ pcmformat = SND_PCM_FORMAT_FLOAT_LE;
+ else
+ pcmformat = SND_PCM_FORMAT_FLOAT_BE;
+ }
+ } else if(settings.sampleSize() == 64) {
+ if(settings.byteOrder() == QAudioFormat::LittleEndian)
+ pcmformat = SND_PCM_FORMAT_FLOAT64_LE;
+ else
+ pcmformat = SND_PCM_FORMAT_FLOAT64_BE;
+ }
+
+ return pcmformat != SND_PCM_FORMAT_UNKNOWN
+ ? snd_pcm_hw_params_set_format( handle, hwparams, pcmformat)
+ : -1;
+}
+
+void QAlsaAudioOutput::start(QIODevice* device)
+{
+ if(deviceState != QAudio::StoppedState)
+ deviceState = QAudio::StoppedState;
+
+ errorState = QAudio::NoError;
+
+ // Handle change of mode
+ if(audioSource && !pullMode) {
+ delete audioSource;
+ audioSource = 0;
+ }
+
+ close();
+
+ pullMode = true;
+ audioSource = device;
+
+ deviceState = QAudio::ActiveState;
+
+ open();
+
+ emit stateChanged(deviceState);
+}
+
+QIODevice* QAlsaAudioOutput::start()
+{
+ if(deviceState != QAudio::StoppedState)
+ deviceState = QAudio::StoppedState;
+
+ errorState = QAudio::NoError;
+
+ // Handle change of mode
+ if(audioSource && !pullMode) {
+ delete audioSource;
+ audioSource = 0;
+ }
+
+ close();
+
+ audioSource = new OutputPrivate(this);
+ audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
+ pullMode = false;
+
+ deviceState = QAudio::IdleState;
+
+ open();
+
+ emit stateChanged(deviceState);
+
+ return audioSource;
+}
+
+void QAlsaAudioOutput::stop()
+{
+ if(deviceState == QAudio::StoppedState)
+ return;
+ errorState = QAudio::NoError;
+ deviceState = QAudio::StoppedState;
+ close();
+ emit stateChanged(deviceState);
+}
+
+bool QAlsaAudioOutput::open()
+{
+ if(opened)
+ return true;
+
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
+#endif
+ timeStamp.restart();
+ elapsedTimeOffset = 0;
+
+ int dir;
+ int err = 0;
+ int count=0;
+ unsigned int sampleRate=settings.sampleRate();
+
+ if (!settings.isValid()) {
+ qWarning("QAudioOutput: open error, invalid format.");
+ } else if (settings.sampleRate() <= 0) {
+ qWarning("QAudioOutput: open error, invalid sample rate (%d).",
+ settings.sampleRate());
+ } else {
+ err = -1;
+ }
+
+ if (err == 0) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit errorChanged(errorState);
+ return false;
+ }
+
+ QString dev = QString(QLatin1String(m_device.constData()));
+ QList<QByteArray> devices = QAlsaAudioDeviceInfo::availableDevices(QAudio::AudioOutput);
+ if(dev.compare(QLatin1String("default")) == 0) {
+#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+ if (devices.size() > 0)
+ dev = QLatin1String(devices.first());
+ else
+ return false;
+#else
+ dev = QLatin1String("hw:0,0");
+#endif
+ } else {
+#if(SND_LIB_MAJOR == 1 && SND_LIB_MINOR == 0 && SND_LIB_SUBMINOR >= 14)
+ dev = QLatin1String(m_device);
+#else
+ int idx = 0;
+ char *name;
+
+ QString shortName = QLatin1String(m_device.mid(m_device.indexOf('=',0)+1).constData());
+
+ while (snd_card_get_name(idx,&name) == 0) {
+ if(qstrncmp(shortName.toLocal8Bit().constData(),name,shortName.length()) == 0)
+ break;
+ idx++;
+ }
+ dev = QString(QLatin1String("hw:%1,0")).arg(idx);
+#endif
+ }
+
+ // Step 1: try and open the device
+ while((count < 5) && (err < 0)) {
+ err=snd_pcm_open(&handle,dev.toLocal8Bit().constData(),SND_PCM_STREAM_PLAYBACK,0);
+ if(err < 0)
+ count++;
+ }
+ if (( err < 0)||(handle == 0)) {
+ errorState = QAudio::OpenError;
+ emit errorChanged(errorState);
+ deviceState = QAudio::StoppedState;
+ return false;
+ }
+ snd_pcm_nonblock( handle, 0 );
+
+ // Step 2: Set the desired HW parameters.
+ snd_pcm_hw_params_alloca( &hwparams );
+
+ bool fatal = false;
+ QString errMessage;
+ unsigned int chunks = 8;
+
+ err = snd_pcm_hw_params_any( handle, hwparams );
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_any: err = %1").arg(err);
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_rate_resample( handle, hwparams, 1 );
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_resample: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_access( handle, hwparams, access );
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_access: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = setFormat();
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_format: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_channels( handle, hwparams, (unsigned int)settings.channelCount() );
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_channels: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_rate_near( handle, hwparams, &sampleRate, 0 );
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_rate_near: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ unsigned int maxBufferTime = 0;
+ unsigned int minBufferTime = 0;
+ unsigned int maxPeriodTime = 0;
+ unsigned int minPeriodTime = 0;
+
+ err = snd_pcm_hw_params_get_buffer_time_max(hwparams, &maxBufferTime, &dir);
+ if ( err >= 0)
+ err = snd_pcm_hw_params_get_buffer_time_min(hwparams, &minBufferTime, &dir);
+ if ( err >= 0)
+ err = snd_pcm_hw_params_get_period_time_max(hwparams, &maxPeriodTime, &dir);
+ if ( err >= 0)
+ err = snd_pcm_hw_params_get_period_time_min(hwparams, &minPeriodTime, &dir);
+
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioOutput: buffer/period min and max: err = %1").arg(err);
+ } else {
+ if (maxBufferTime < buffer_time || buffer_time < minBufferTime || maxPeriodTime < period_time || minPeriodTime > period_time) {
+#ifdef DEBUG_AUDIO
+ qDebug()<<"defaults out of range";
+ qDebug()<<"pmin="<<minPeriodTime<<", pmax="<<maxPeriodTime<<", bmin="<<minBufferTime<<", bmax="<<maxBufferTime;
+#endif
+ period_time = minPeriodTime;
+ if (period_time*4 <= maxBufferTime) {
+ // Use 4 periods if possible
+ buffer_time = period_time*4;
+ chunks = 4;
+ } else if (period_time*2 <= maxBufferTime) {
+ // Use 2 periods if possible
+ buffer_time = period_time*2;
+ chunks = 2;
+ } else {
+ qWarning()<<"QAudioOutput: alsa only supports single period!";
+ fatal = true;
+ }
+#ifdef DEBUG_AUDIO
+ qDebug()<<"used: buffer_time="<<buffer_time<<", period_time="<<period_time;
+#endif
+ }
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_buffer_time_near(handle, hwparams, &buffer_time, &dir);
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_buffer_time_near: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_period_time_near(handle, hwparams, &period_time, &dir);
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_period_time_near: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params_set_periods_near(handle, hwparams, &chunks, &dir);
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params_set_periods_near: err = %1").arg(err);
+ }
+ }
+ if ( !fatal ) {
+ err = snd_pcm_hw_params(handle, hwparams);
+ if ( err < 0 ) {
+ fatal = true;
+ errMessage = QString::fromLatin1("QAudioOutput: snd_pcm_hw_params: err = %1").arg(err);
+ }
+ }
+ if( err < 0) {
+ qWarning()<<errMessage;
+ errorState = QAudio::OpenError;
+ emit errorChanged(errorState);
+ deviceState = QAudio::StoppedState;
+ return false;
+ }
+ snd_pcm_hw_params_get_buffer_size(hwparams,&buffer_frames);
+ buffer_size = snd_pcm_frames_to_bytes(handle,buffer_frames);
+ snd_pcm_hw_params_get_period_size(hwparams,&period_frames, &dir);
+ period_size = snd_pcm_frames_to_bytes(handle,period_frames);
+ snd_pcm_hw_params_get_buffer_time(hwparams,&buffer_time, &dir);
+ snd_pcm_hw_params_get_period_time(hwparams,&period_time, &dir);
+
+ // Step 3: Set the desired SW parameters.
+ snd_pcm_sw_params_t *swparams;
+ snd_pcm_sw_params_alloca(&swparams);
+ snd_pcm_sw_params_current(handle, swparams);
+ snd_pcm_sw_params_set_start_threshold(handle,swparams,period_frames);
+ snd_pcm_sw_params_set_stop_threshold(handle,swparams,buffer_frames);
+ snd_pcm_sw_params_set_avail_min(handle, swparams,period_frames);
+ snd_pcm_sw_params(handle, swparams);
+
+ // Step 4: Prepare audio
+ if(audioBuffer == 0)
+ audioBuffer = new char[snd_pcm_frames_to_bytes(handle,buffer_frames)];
+ snd_pcm_prepare( handle );
+ snd_pcm_start(handle);
+
+ // Step 5: Setup callback and timer fallback
+ snd_async_add_pcm_handler(&ahandler, handle, async_callback, this);
+ bytesAvailable = bytesFree();
+
+ // Step 6: Start audio processing
+ timer->start(period_time/1000);
+
+ clockStamp.restart();
+ timeStamp.restart();
+ elapsedTimeOffset = 0;
+ errorState = QAudio::NoError;
+ totalTimeValue = 0;
+ opened = true;
+
+ return true;
+}
+
+void QAlsaAudioOutput::close()
+{
+ timer->stop();
+
+ if ( handle ) {
+ snd_pcm_drain( handle );
+ snd_pcm_close( handle );
+ handle = 0;
+ delete [] audioBuffer;
+ audioBuffer=0;
+ }
+ if(!pullMode && audioSource) {
+ delete audioSource;
+ audioSource = 0;
+ }
+ opened = false;
+}
+
+int QAlsaAudioOutput::bytesFree() const
+{
+ if(resuming)
+ return period_size;
+
+ if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
+ return 0;
+
+ int frames = snd_pcm_avail_update(handle);
+ if (frames == -EPIPE) {
+ // Try and handle buffer underrun
+ int err = snd_pcm_recover(handle, frames, 0);
+ if (err < 0)
+ return 0;
+ else
+ frames = snd_pcm_avail_update(handle);
+ } else if (frames < 0) {
+ return 0;
+ }
+
+ if ((int)frames > (int)buffer_frames)
+ frames = buffer_frames;
+
+ return snd_pcm_frames_to_bytes(handle, frames);
+}
+
+qint64 QAlsaAudioOutput::write( const char *data, qint64 len )
+{
+ // Write out some audio data
+ if ( !handle )
+ return 0;
+#ifdef DEBUG_AUDIO
+ qDebug()<<"frames to write out = "<<
+ snd_pcm_bytes_to_frames( handle, (int)len )<<" ("<<len<<") bytes";
+#endif
+ int frames, err;
+ int space = bytesFree();
+
+ if (!space)
+ return 0;
+
+ if (len < space)
+ space = len;
+
+ frames = snd_pcm_bytes_to_frames(handle, space);
+
+ if (m_volume < 1.0f) {
+ char out[space];
+ QAudioHelperInternal::qMultiplySamples(m_volume, settings, data, out, space);
+ err = snd_pcm_writei(handle, out, frames);
+ } else {
+ err = snd_pcm_writei(handle, data, frames);
+ }
+
+ if(err > 0) {
+ totalTimeValue += err;
+ resuming = false;
+ errorState = QAudio::NoError;
+ if (deviceState != QAudio::ActiveState) {
+ deviceState = QAudio::ActiveState;
+ emit stateChanged(deviceState);
+ }
+ return snd_pcm_frames_to_bytes( handle, err );
+ } else
+ err = xrun_recovery(err);
+
+ if(err < 0) {
+ close();
+ errorState = QAudio::FatalError;
+ emit errorChanged(errorState);
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ }
+ return 0;
+}
+
+int QAlsaAudioOutput::periodSize() const
+{
+ return period_size;
+}
+
+void QAlsaAudioOutput::setBufferSize(int value)
+{
+ if(deviceState == QAudio::StoppedState)
+ buffer_size = value;
+}
+
+int QAlsaAudioOutput::bufferSize() const
+{
+ return buffer_size;
+}
+
+void QAlsaAudioOutput::setNotifyInterval(int ms)
+{
+ intervalTime = qMax(0, ms);
+}
+
+int QAlsaAudioOutput::notifyInterval() const
+{
+ return intervalTime;
+}
+
+qint64 QAlsaAudioOutput::processedUSecs() const
+{
+ return qint64(1000000) * totalTimeValue / settings.sampleRate();
+}
+
+void QAlsaAudioOutput::resume()
+{
+ if(deviceState == QAudio::SuspendedState) {
+ int err = 0;
+
+ if(handle) {
+ err = snd_pcm_prepare( handle );
+ if(err < 0)
+ xrun_recovery(err);
+
+ err = snd_pcm_start(handle);
+ if(err < 0)
+ xrun_recovery(err);
+
+ bytesAvailable = (int)snd_pcm_frames_to_bytes(handle, buffer_frames);
+ }
+ resuming = true;
+
+ deviceState = QAudio::ActiveState;
+
+ errorState = QAudio::NoError;
+ timer->start(period_time/1000);
+ emit stateChanged(deviceState);
+ }
+}
+
+void QAlsaAudioOutput::setFormat(const QAudioFormat& fmt)
+{
+ if (deviceState == QAudio::StoppedState)
+ settings = fmt;
+}
+
+QAudioFormat QAlsaAudioOutput::format() const
+{
+ return settings;
+}
+
+void QAlsaAudioOutput::suspend()
+{
+ if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState || resuming) {
+ timer->stop();
+ deviceState = QAudio::SuspendedState;
+ errorState = QAudio::NoError;
+ emit stateChanged(deviceState);
+ }
+}
+
+void QAlsaAudioOutput::userFeed()
+{
+ if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
+ return;
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :userFeed() OUT";
+#endif
+ if(deviceState == QAudio::IdleState)
+ bytesAvailable = bytesFree();
+
+ deviceReady();
+}
+
+void QAlsaAudioOutput::feedback()
+{
+ updateAvailable();
+}
+
+
+void QAlsaAudioOutput::updateAvailable()
+{
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :updateAvailable()";
+#endif
+ bytesAvailable = bytesFree();
+}
+
+bool QAlsaAudioOutput::deviceReady()
+{
+ if(pullMode) {
+ int l = 0;
+ int chunks = bytesAvailable/period_size;
+ if(chunks==0) {
+ bytesAvailable = bytesFree();
+ return false;
+ }
+#ifdef DEBUG_AUDIO
+ qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes";
+ qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<period_size*chunks;
+#endif
+ int input = period_frames*chunks;
+ if(input > (int)buffer_frames)
+ input = buffer_frames;
+ l = audioSource->read(audioBuffer,snd_pcm_frames_to_bytes(handle, input));
+
+ // reading can take a while and stream may have been stopped
+ if (!handle)
+ return false;
+
+ if(l > 0) {
+ // Got some data to output
+ if(deviceState != QAudio::ActiveState)
+ return true;
+ qint64 bytesWritten = write(audioBuffer,l);
+ if (bytesWritten != l)
+ audioSource->seek(audioSource->pos()-(l-bytesWritten));
+ bytesAvailable = bytesFree();
+
+ } else if(l == 0) {
+ // Did not get any data to output
+ bytesAvailable = bytesFree();
+ if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) {
+ // Underrun
+ if (deviceState != QAudio::IdleState) {
+ errorState = QAudio::UnderrunError;
+ emit errorChanged(errorState);
+ deviceState = QAudio::IdleState;
+ emit stateChanged(deviceState);
+ }
+ }
+
+ } else if(l < 0) {
+ close();
+ deviceState = QAudio::StoppedState;
+ errorState = QAudio::IOError;
+ emit errorChanged(errorState);
+ emit stateChanged(deviceState);
+ }
+ } else {
+ bytesAvailable = bytesFree();
+ if(bytesAvailable > snd_pcm_frames_to_bytes(handle, buffer_frames-period_frames)) {
+ // Underrun
+ if (deviceState != QAudio::IdleState) {
+ errorState = QAudio::UnderrunError;
+ emit errorChanged(errorState);
+ deviceState = QAudio::IdleState;
+ emit stateChanged(deviceState);
+ }
+ }
+ }
+
+ if(deviceState != QAudio::ActiveState)
+ return true;
+
+ if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
+ emit notify();
+ elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
+ timeStamp.restart();
+ }
+ return true;
+}
+
+qint64 QAlsaAudioOutput::elapsedUSecs() const
+{
+ if (deviceState == QAudio::StoppedState)
+ return 0;
+
+ return clockStamp.elapsed()*1000;
+}
+
+void QAlsaAudioOutput::reset()
+{
+ if(handle)
+ snd_pcm_reset(handle);
+
+ stop();
+}
+
+OutputPrivate::OutputPrivate(QAlsaAudioOutput* audio)
+{
+ audioDevice = qobject_cast<QAlsaAudioOutput*>(audio);
+}
+
+OutputPrivate::~OutputPrivate() {}
+
+qint64 OutputPrivate::readData( char* data, qint64 len)
+{
+ Q_UNUSED(data)
+ Q_UNUSED(len)
+
+ return 0;
+}
+
+qint64 OutputPrivate::writeData(const char* data, qint64 len)
+{
+ int retry = 0;
+ qint64 written = 0;
+ if((audioDevice->deviceState == QAudio::ActiveState)
+ ||(audioDevice->deviceState == QAudio::IdleState)) {
+ while(written < len) {
+ int chunk = audioDevice->write(data+written,(len-written));
+ if(chunk <= 0)
+ retry++;
+ written+=chunk;
+ if(retry > 10)
+ return written;
+ }
+ }
+ return written;
+
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qalsaaudiooutput.cpp"
diff --git a/src/plugins/alsa/qalsaaudiooutput.h b/src/plugins/alsa/qalsaaudiooutput.h
new file mode 100644
index 000000000..67976a55b
--- /dev/null
+++ b/src/plugins/alsa/qalsaaudiooutput.h
@@ -0,0 +1,170 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QAUDIOOUTPUTALSA_H
+#define QAUDIOOUTPUTALSA_H
+
+#include <alsa/asoundlib.h>
+
+#include <QtCore/qfile.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qdatetime.h>
+
+#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qaudiodeviceinfo.h>
+#include <QtMultimedia/qaudiosystem.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAlsaAudioOutput : public QAbstractAudioOutput
+{
+ friend class OutputPrivate;
+ Q_OBJECT
+public:
+ QAlsaAudioOutput(const QByteArray &device);
+ ~QAlsaAudioOutput();
+
+ qint64 write( const char *data, qint64 len );
+
+ void start(QIODevice* device);
+ QIODevice* start();
+ void stop();
+ void reset();
+ void suspend();
+ void resume();
+ int bytesFree() const;
+ int periodSize() const;
+ void setBufferSize(int value);
+ int bufferSize() const;
+ void setNotifyInterval(int milliSeconds);
+ int notifyInterval() const;
+ qint64 processedUSecs() const;
+ qint64 elapsedUSecs() const;
+ QAudio::Error error() const;
+ QAudio::State state() const;
+ void setFormat(const QAudioFormat& fmt);
+ QAudioFormat format() const;
+ void setVolume(qreal);
+ qreal volume() const;
+
+
+ QIODevice* audioSource;
+ QAudioFormat settings;
+ QAudio::Error errorState;
+ QAudio::State deviceState;
+
+private slots:
+ void userFeed();
+ void feedback();
+ void updateAvailable();
+ bool deviceReady();
+
+signals:
+ void processMore();
+
+private:
+ bool opened;
+ bool pullMode;
+ bool resuming;
+ int buffer_size;
+ int period_size;
+ int intervalTime;
+ qint64 totalTimeValue;
+ unsigned int buffer_time;
+ unsigned int period_time;
+ snd_pcm_uframes_t buffer_frames;
+ snd_pcm_uframes_t period_frames;
+ static void async_callback(snd_async_handler_t *ahandler);
+ int xrun_recovery(int err);
+
+ int setFormat();
+ bool open();
+ void close();
+
+ QTimer* timer;
+ QByteArray m_device;
+ int bytesAvailable;
+ QTime timeStamp;
+ QTime clockStamp;
+ qint64 elapsedTimeOffset;
+ char* audioBuffer;
+ snd_pcm_t* handle;
+ snd_async_handler_t* ahandler;
+ snd_pcm_access_t access;
+ snd_pcm_format_t pcmformat;
+ snd_timestamp_t* timestamp;
+ snd_pcm_hw_params_t *hwparams;
+ qreal m_volume;
+};
+
+class OutputPrivate : public QIODevice
+{
+ friend class QAlsaAudioOutput;
+ Q_OBJECT
+public:
+ OutputPrivate(QAlsaAudioOutput* audio);
+ ~OutputPrivate();
+
+ qint64 readData( char* data, qint64 len);
+ qint64 writeData(const char* data, qint64 len);
+
+private:
+ QAlsaAudioOutput *audioDevice;
+};
+
+QT_END_NAMESPACE
+
+
+#endif
diff --git a/src/plugins/alsa/qalsaplugin.cpp b/src/plugins/alsa/qalsaplugin.cpp
new file mode 100644
index 000000000..6ed36580b
--- /dev/null
+++ b/src/plugins/alsa/qalsaplugin.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qalsaplugin.h"
+#include "qalsaaudiodeviceinfo.h"
+#include "qalsaaudioinput.h"
+#include "qalsaaudiooutput.h"
+
+QT_BEGIN_NAMESPACE
+
+QAlsaPlugin::QAlsaPlugin(QObject *parent)
+ : QAudioSystemPlugin(parent)
+{
+}
+
+QList<QByteArray> QAlsaPlugin::availableDevices(QAudio::Mode mode) const
+{
+ return QAlsaAudioDeviceInfo::availableDevices(mode);
+}
+
+QAbstractAudioInput *QAlsaPlugin::createInput(const QByteArray &device)
+{
+ return new QAlsaAudioInput(device);
+}
+
+QAbstractAudioOutput *QAlsaPlugin::createOutput(const QByteArray &device)
+{
+ return new QAlsaAudioOutput(device);
+}
+
+QAbstractAudioDeviceInfo *QAlsaPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
+{
+ return new QAlsaAudioDeviceInfo(device, mode);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/alsa/qalsaplugin.h b/src/plugins/alsa/qalsaplugin.h
new file mode 100644
index 000000000..6f524ac4a
--- /dev/null
+++ b/src/plugins/alsa/qalsaplugin.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QALSAPLUGIN_H
+#define QALSAPLUGIN_H
+
+#include <QtMultimedia/qaudiosystemplugin.h>
+
+QT_BEGIN_NAMESPACE
+
+class QAlsaPlugin : public QAudioSystemPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "alsa.json")
+
+public:
+ QAlsaPlugin(QObject *parent = 0);
+ ~QAlsaPlugin() {}
+
+ QList<QByteArray> availableDevices(QAudio::Mode mode) const Q_DECL_OVERRIDE;
+ QAbstractAudioInput *createInput(const QByteArray &device) Q_DECL_OVERRIDE;
+ QAbstractAudioOutput *createOutput(const QByteArray &device) Q_DECL_OVERRIDE;
+ QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode) Q_DECL_OVERRIDE;
+};
+
+QT_END_NAMESPACE
+
+#endif // QALSAPLUGIN_H
diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp
index 3962baba8..f40ec2498 100644
--- a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp
+++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp
@@ -389,7 +389,7 @@ void QAndroidCaptureSession::applySettings()
}
// video settings
- if (m_cameraSession && m_videoSettingsDirty) {
+ if (m_cameraSession && m_cameraSession->camera() && m_videoSettingsDirty) {
if (m_videoSettings.resolution().isEmpty()) {
m_videoSettings.setResolution(m_defaultSettings.videoResolution);
m_resolutionDirty = true;
@@ -466,6 +466,8 @@ void QAndroidCaptureSession::onCameraOpened()
qSort(m_supportedResolutions.begin(), m_supportedResolutions.end(), qt_sizeLessThan);
qSort(m_supportedFramerates.begin(), m_supportedFramerates.end());
+
+ applySettings();
}
QAndroidCaptureSession::CaptureProfile QAndroidCaptureSession::getProfile(int id)
diff --git a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
index ce73263d3..7b0c58277 100644
--- a/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
+++ b/src/plugins/android/src/mediaplayer/qandroidmediaplayercontrol.cpp
@@ -63,12 +63,12 @@ QAndroidMediaPlayerControl::QAndroidMediaPlayerControl(QObject *parent)
{
connect(mMediaPlayer, SIGNAL(bufferingUpdate(qint32)),
this, SLOT(onBufferChanged(qint32)));
- connect(mMediaPlayer, SIGNAL(info(qint32, qint32)),
- this, SLOT(onInfo(qint32, qint32)));
- connect(mMediaPlayer, SIGNAL(error(qint32, qint32)),
- this, SLOT(onError(qint32, qint32)));
- connect(mMediaPlayer, SIGNAL(mediaPlayerInfo(qint32, qint32)),
- this, SLOT(onMediaPlayerInfo(qint32, qint32)));
+ connect(mMediaPlayer, SIGNAL(info(qint32,qint32)),
+ this, SLOT(onInfo(qint32,qint32)));
+ connect(mMediaPlayer, SIGNAL(error(qint32,qint32)),
+ this, SLOT(onError(qint32,qint32)));
+ connect(mMediaPlayer, SIGNAL(mediaPlayerInfo(qint32,qint32)),
+ this, SLOT(onMediaPlayerInfo(qint32,qint32)));
connect(mMediaPlayer, SIGNAL(videoSizeChanged(qint32,qint32)),
this, SLOT(onVideoSizeChanged(qint32,qint32)));
}
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.h b/src/plugins/avfoundation/camera/avfcamerasession.h
index 48681aae7..2630a35f7 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.h
+++ b/src/plugins/avfoundation/camera/avfcamerasession.h
@@ -64,6 +64,7 @@ public:
void setVideoOutput(AVFVideoRendererControl *output);
AVCaptureSession *captureSession() const { return m_captureSession; }
+ AVCaptureDevice *videoCaptureDevice() const;
QCamera::State state() const;
QCamera::State requestedState() const { return m_state; }
diff --git a/src/plugins/avfoundation/camera/avfcamerasession.mm b/src/plugins/avfoundation/camera/avfcamerasession.mm
index 5f1385a7e..93c2bacd0 100644
--- a/src/plugins/avfoundation/camera/avfcamerasession.mm
+++ b/src/plugins/avfoundation/camera/avfcamerasession.mm
@@ -155,7 +155,15 @@ void AVFCameraSession::setVideoOutput(AVFVideoRendererControl *output)
{
m_videoOutput = output;
if (output)
- output->configureAVCaptureSession(m_captureSession);
+ output->configureAVCaptureSession(this);
+}
+
+AVCaptureDevice *AVFCameraSession::videoCaptureDevice() const
+{
+ if (m_videoInput)
+ return m_videoInput.device;
+
+ return 0;
}
QCamera::State AVFCameraSession::state() const
diff --git a/src/plugins/avfoundation/camera/avfvideorenderercontrol.h b/src/plugins/avfoundation/camera/avfvideorenderercontrol.h
index 57978c05a..c080451c3 100644
--- a/src/plugins/avfoundation/camera/avfvideorenderercontrol.h
+++ b/src/plugins/avfoundation/camera/avfvideorenderercontrol.h
@@ -66,7 +66,7 @@ public:
QAbstractVideoSurface *surface() const;
void setSurface(QAbstractVideoSurface *surface);
- void configureAVCaptureSession(AVCaptureSession *captureSession);
+ void configureAVCaptureSession(AVFCameraSession *cameraSession);
void syncHandleViewfinderFrame(const QVideoFrame &frame);
Q_SIGNALS:
@@ -74,13 +74,16 @@ Q_SIGNALS:
private Q_SLOTS:
void handleViewfinderFrame();
+ void updateCaptureConnection();
private:
QAbstractVideoSurface *m_surface;
AVFCaptureFramesDelegate *m_viewfinderFramesDelegate;
- AVCaptureSession *m_captureSession;
+ AVFCameraSession *m_cameraSession;
AVCaptureVideoDataOutput *m_videoDataOutput;
+ bool m_needsHorizontalMirroring;
+
QVideoFrame m_lastViewfinderFrame;
QMutex m_vfMutex;
};
diff --git a/src/plugins/avfoundation/camera/avfvideorenderercontrol.mm b/src/plugins/avfoundation/camera/avfvideorenderercontrol.mm
index 6efa3cbf8..1a2054452 100644
--- a/src/plugins/avfoundation/camera/avfvideorenderercontrol.mm
+++ b/src/plugins/avfoundation/camera/avfvideorenderercontrol.mm
@@ -146,13 +146,14 @@ private:
AVFVideoRendererControl::AVFVideoRendererControl(QObject *parent)
: QVideoRendererControl(parent)
, m_surface(0)
+ , m_needsHorizontalMirroring(false)
{
m_viewfinderFramesDelegate = [[AVFCaptureFramesDelegate alloc] initWithRenderer:this];
}
AVFVideoRendererControl::~AVFVideoRendererControl()
{
- [m_captureSession removeOutput:m_videoDataOutput];
+ [m_cameraSession->captureSession() removeOutput:m_videoDataOutput];
[m_viewfinderFramesDelegate release];
}
@@ -169,9 +170,13 @@ void AVFVideoRendererControl::setSurface(QAbstractVideoSurface *surface)
}
}
-void AVFVideoRendererControl::configureAVCaptureSession(AVCaptureSession *captureSession)
+void AVFVideoRendererControl::configureAVCaptureSession(AVFCameraSession *cameraSession)
{
- m_captureSession = captureSession;
+ m_cameraSession = cameraSession;
+ connect(m_cameraSession, SIGNAL(readyToConfigureConnections()),
+ this, SLOT(updateCaptureConnection()));
+
+ m_needsHorizontalMirroring = false;
m_videoDataOutput = [[[AVCaptureVideoDataOutput alloc] init] autorelease];
@@ -188,7 +193,23 @@ void AVFVideoRendererControl::configureAVCaptureSession(AVCaptureSession *captur
[NSNumber numberWithInt:kCVPixelFormatType_32BGRA]
forKey:(id)kCVPixelBufferPixelFormatTypeKey];
- [m_captureSession addOutput:m_videoDataOutput];
+ [m_cameraSession->captureSession() addOutput:m_videoDataOutput];
+}
+
+void AVFVideoRendererControl::updateCaptureConnection()
+{
+ AVCaptureConnection *connection = [m_videoDataOutput connectionWithMediaType:AVMediaTypeVideo];
+ if (connection == nil || !m_cameraSession->videoCaptureDevice())
+ return;
+
+ // Frames of front-facing cameras should be mirrored horizontally (it's the default when using
+ // AVCaptureVideoPreviewLayer but not with AVCaptureVideoDataOutput)
+ if (connection.isVideoMirroringSupported)
+ connection.videoMirrored = m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
+
+ // If the connection does't support mirroring, we'll have to do it ourselves
+ m_needsHorizontalMirroring = !connection.isVideoMirrored
+ && m_cameraSession->videoCaptureDevice().position == AVCaptureDevicePositionFront;
}
//can be called from non main thread
@@ -203,6 +224,22 @@ void AVFVideoRendererControl::syncHandleViewfinderFrame(const QVideoFrame &frame
}
m_lastViewfinderFrame = frame;
+
+ if (m_needsHorizontalMirroring) {
+ m_lastViewfinderFrame.map(QAbstractVideoBuffer::ReadOnly);
+
+ // no deep copy
+ QImage image(m_lastViewfinderFrame.bits(),
+ m_lastViewfinderFrame.size().width(),
+ m_lastViewfinderFrame.size().height(),
+ m_lastViewfinderFrame.bytesPerLine(),
+ QImage::Format_RGB32);
+
+ QImage mirrored = image.mirrored(true, false);
+
+ m_lastViewfinderFrame.unmap();
+ m_lastViewfinderFrame = QVideoFrame(mirrored);
+ }
}
void AVFVideoRendererControl::handleViewfinderFrame()
diff --git a/src/plugins/directshow/camera/camera.pri b/src/plugins/directshow/camera/camera.pri
index 7081abd37..2c0fe55b1 100644
--- a/src/plugins/directshow/camera/camera.pri
+++ b/src/plugins/directshow/camera/camera.pri
@@ -2,7 +2,7 @@ INCLUDEPATH += $$PWD
DEFINES += QMEDIA_DIRECTSHOW_CAMERA
-win32-g++: DEFINES += QT_NO_WMSDK
+mingw: DEFINES += QT_NO_WMSDK
win32: DEFINES += _CRT_SECURE_NO_WARNINGS
diff --git a/src/plugins/directshow/camera/dscamerasession.cpp b/src/plugins/directshow/camera/dscamerasession.cpp
index 801367f68..1ecc368e7 100644
--- a/src/plugins/directshow/camera/dscamerasession.cpp
+++ b/src/plugins/directshow/camera/dscamerasession.cpp
@@ -81,7 +81,7 @@ public:
STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject)
{
- if (NULL == ppvObject)
+ if (NULL == ppvObject)
return E_POINTER;
if (riid == IID_IUnknown /*__uuidof(IUnknown) */ ) {
*ppvObject = static_cast<IUnknown*>(this);
@@ -382,9 +382,9 @@ QSize DSCameraSession::frameSize() const
void DSCameraSession::setFrameSize(const QSize& s)
{
- if (supportedResolutions(pixelF).contains(s))
+ if (supportedResolutions(pixelF).contains(s))
m_windowSize = s;
- else
+ else
qWarning() << "frame size if not supported for current pixel format, no change";
}
@@ -467,8 +467,8 @@ void DSCameraSession::setPixelFormat(QVideoFrame::PixelFormat fmt)
QList<QSize> DSCameraSession::supportedResolutions(QVideoFrame::PixelFormat format)
{
- if (!resolutions.contains(format))
- return QList<QSize>();
+ if (!resolutions.contains(format))
+ return QList<QSize>();
return resolutions.value(format);
}
diff --git a/src/plugins/directshow/camera/dsimagecapturecontrol.cpp b/src/plugins/directshow/camera/dsimagecapturecontrol.cpp
index f1332235f..e689b13d1 100644
--- a/src/plugins/directshow/camera/dsimagecapturecontrol.cpp
+++ b/src/plugins/directshow/camera/dsimagecapturecontrol.cpp
@@ -49,10 +49,10 @@ DSImageCaptureControl::DSImageCaptureControl(DSCameraSession *session)
:QCameraImageCaptureControl(session), m_session(session), m_ready(false)
{
connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState()));
- connect(m_session, SIGNAL(imageCaptured(const int, QImage)),
- this, SIGNAL(imageCaptured(const int, QImage)));
- connect(m_session, SIGNAL(imageSaved(const int, const QString &)),
- this, SIGNAL(imageSaved(const int, const QString &)));
+ connect(m_session, SIGNAL(imageCaptured(int,QImage)),
+ this, SIGNAL(imageCaptured(int,QImage)));
+ connect(m_session, SIGNAL(imageSaved(int,QString)),
+ this, SIGNAL(imageSaved(int,QString)));
connect(m_session, SIGNAL(readyForCaptureChanged(bool)),
this, SIGNAL(readyForCaptureChanged(bool)));
}
diff --git a/src/plugins/directshow/camera/dsvideowidgetcontrol.cpp b/src/plugins/directshow/camera/dsvideowidgetcontrol.cpp
index fde9f554e..35b795546 100644
--- a/src/plugins/directshow/camera/dsvideowidgetcontrol.cpp
+++ b/src/plugins/directshow/camera/dsvideowidgetcontrol.cpp
@@ -120,7 +120,7 @@ DSVideoWidgetControl::DSVideoWidgetControl(DSCameraSession* session, QObject *pa
m_widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding);
m_widget->setAlignment(Qt::AlignCenter);
m_widget->setAttribute(Qt::WA_NoSystemBackground, true);
-
+
surface = new DSVideoWidgetSurface(m_widget);
QPalette palette;
@@ -157,7 +157,7 @@ bool DSVideoWidgetControl::eventFilter(QObject *object, QEvent *e)
case QEvent::PolishRequest:
m_widget->ensurePolished();
break;
-
+
default:
// Do nothing
break;
diff --git a/src/plugins/directshow/camera/dsvideowidgetcontrol.h b/src/plugins/directshow/camera/dsvideowidgetcontrol.h
index ac40fbd7c..2a8775aee 100644
--- a/src/plugins/directshow/camera/dsvideowidgetcontrol.h
+++ b/src/plugins/directshow/camera/dsvideowidgetcontrol.h
@@ -82,15 +82,15 @@ class DSVideoWidgetSurface : public QAbstractVideoSurface
class DSVideoWidgetControl : public QVideoWidgetControl
{
Q_OBJECT
-
+
DSVideoWidgetSurface* surface;
public: // Constructor & Destructor
-
+
DSVideoWidgetControl(DSCameraSession* session, QObject *parent = 0);
virtual ~DSVideoWidgetControl();
public: // QVideoWidgetControl
-
+
QWidget *videoWidget();
// Aspect Ratio
@@ -118,10 +118,10 @@ public: // QVideoWidgetControl
void setSaturation(int saturation);
public: // Internal
-
+
bool eventFilter(QObject *object, QEvent *event);
-
-/*
+
+/*
Q_SIGNALS: // QVideoWidgetControl
void fullScreenChanged(bool fullScreen);
diff --git a/src/plugins/directshow/directshow.pro b/src/plugins/directshow/directshow.pro
index 97e72a107..4dad97113 100644
--- a/src/plugins/directshow/directshow.pro
+++ b/src/plugins/directshow/directshow.pro
@@ -16,9 +16,7 @@ qtHaveModule(widgets) {
DEFINES += HAVE_WIDGETS
}
-win32-g++ {
- DEFINES += NO_DSHOW_STRSAFE
-}
+mingw: DEFINES += NO_DSHOW_STRSAFE
!config_wmf: include(player/player.pri)
include(camera/camera.pri)
diff --git a/src/plugins/directshow/player/directshoweventloop.cpp b/src/plugins/directshow/player/directshoweventloop.cpp
index 74f210b9a..a8726eddd 100644
--- a/src/plugins/directshow/player/directshoweventloop.cpp
+++ b/src/plugins/directshow/player/directshoweventloop.cpp
@@ -94,7 +94,7 @@ void DirectShowEventLoop::wait(QMutex *mutex)
HANDLE handles[] = { m_eventHandle, m_waitHandle };
while (::WaitForMultipleObjects(2, handles, false, INFINITE) == WAIT_OBJECT_0)
processEvents();
-
+
mutex->lock();
}
diff --git a/src/plugins/directshow/player/directshowglobal.h b/src/plugins/directshow/player/directshowglobal.h
index b604a8d0e..f2a7fef03 100644
--- a/src/plugins/directshow/player/directshowglobal.h
+++ b/src/plugins/directshow/player/directshowglobal.h
@@ -76,7 +76,7 @@ DECLARE_INTERFACE_(IFilterGraph2 ,IGraphBuilder)
STDMETHOD(ReconnectEx)(THIS_ IPin *, const AM_MEDIA_TYPE *) PURE;
STDMETHOD(RenderEx)(IPin *, DWORD, DWORD *) PURE;
};
-#undef INTERFACE
+#undef INTERFACE
#endif
#ifndef __IAMFilterMiscFlags_INTERFACE_DEFINED__
@@ -89,7 +89,7 @@ DECLARE_INTERFACE_(IAMFilterMiscFlags ,IUnknown)
STDMETHOD_(ULONG,Release)(THIS) PURE;
STDMETHOD_(ULONG,GetMiscFlags)(THIS) PURE;
};
-#undef INTERFACE
+#undef INTERFACE
#endif
#ifndef __IFileSourceFilter_INTERFACE_DEFINED__
@@ -103,7 +103,7 @@ DECLARE_INTERFACE_(IFileSourceFilter ,IUnknown)
STDMETHOD(Load)(THIS_ LPCOLESTR, const AM_MEDIA_TYPE *) PURE;
STDMETHOD(GetCurFile)(THIS_ LPOLESTR *ppszFileName, AM_MEDIA_TYPE *) PURE;
};
-#undef INTERFACE
+#undef INTERFACE
#endif
#ifndef __IAMOpenProgress_INTERFACE_DEFINED__
@@ -117,7 +117,7 @@ DECLARE_INTERFACE_(IAMOpenProgress ,IUnknown)
STDMETHOD(QueryProgress)(THIS_ LONGLONG *, LONGLONG *) PURE;
STDMETHOD(AbortOperation)(THIS) PURE;
};
-#undef INTERFACE
+#undef INTERFACE
#endif
#ifndef __IFilterChain_INTERFACE_DEFINED__
@@ -133,7 +133,7 @@ DECLARE_INTERFACE_(IFilterChain ,IUnknown)
STDMETHOD(StopChain)(IBaseFilter *, IBaseFilter *) PURE;
STDMETHOD(RemoveChain)(IBaseFilter *, IBaseFilter *) PURE;
};
-#undef INTERFACE
+#undef INTERFACE
#endif
#endif
diff --git a/src/plugins/directshow/player/directshowioreader.cpp b/src/plugins/directshow/player/directshowioreader.cpp
index c0ea4ebbd..38928f1ae 100644
--- a/src/plugins/directshow/player/directshowioreader.cpp
+++ b/src/plugins/directshow/player/directshowioreader.cpp
@@ -259,7 +259,7 @@ HRESULT DirectShowIOReader::SyncReadAligned(IMediaSample *pSample)
if (SUCCEEDED(hr))
pSample->SetActualDataLength(bytesRead);
-
+
return hr;
} else {
m_synchronousPosition = position;
diff --git a/src/plugins/directshow/player/directshowiosource.cpp b/src/plugins/directshow/player/directshowiosource.cpp
index f382b3b9c..acce1de28 100644
--- a/src/plugins/directshow/player/directshowiosource.cpp
+++ b/src/plugins/directshow/player/directshowiosource.cpp
@@ -336,7 +336,7 @@ HRESULT DirectShowIOSource::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt)
return VFW_E_NOT_STOPPED;
} else if (m_peerPin) {
return VFW_E_ALREADY_CONNECTED;
- } else {
+ } else {
HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED;
m_peerPin = pReceivePin;
@@ -424,7 +424,7 @@ HRESULT DirectShowIOSource::tryConnect(IPin *pin, const AM_MEDIA_TYPE *type)
|| m_allocator->GetProperties(&properties) == S_OK) {
if (properties.cbAlign == 0)
properties.cbAlign = 1;
-
+
ALLOCATOR_PROPERTIES actualProperties;
if (SUCCEEDED(hr = m_allocator->SetProperties(&properties, &actualProperties)))
hr = memPin->NotifyAllocator(m_allocator, TRUE);
@@ -435,7 +435,7 @@ HRESULT DirectShowIOSource::tryConnect(IPin *pin, const AM_MEDIA_TYPE *type)
}
}
memPin->Release();
- }
+ }
if (!SUCCEEDED(hr))
pin->Disconnect();
}
diff --git a/src/plugins/directshow/player/directshowmetadatacontrol.cpp b/src/plugins/directshow/player/directshowmetadatacontrol.cpp
index d60bce31a..51d9574ba 100644
--- a/src/plugins/directshow/player/directshowmetadatacontrol.cpp
+++ b/src/plugins/directshow/player/directshowmetadatacontrol.cpp
@@ -255,7 +255,7 @@ static QVariant getValue(IWMHeaderInfo *header, const wchar_t *key)
WORD word;
if (header->GetAttributeByName(
&streamNumber,
- key,
+ key,
&type,
reinterpret_cast<BYTE *>(&word),
&size) == S_OK) {
diff --git a/src/plugins/directshow/player/directshowplayercontrol.h b/src/plugins/directshow/player/directshowplayercontrol.h
index 175937169..478389de6 100644
--- a/src/plugins/directshow/player/directshowplayercontrol.h
+++ b/src/plugins/directshow/player/directshowplayercontrol.h
@@ -79,7 +79,7 @@ public:
bool isVideoAvailable() const;
bool isSeekable() const;
-
+
QMediaTimeRange availablePlaybackRanges() const;
qreal playbackRate() const;
@@ -140,7 +140,7 @@ private:
bool m_seekable;
QMediaContent m_media;
QString m_errorString;
-
+
};
#endif
diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp
index 9d74be592..0afeacb85 100644
--- a/src/plugins/directshow/player/directshowplayerservice.cpp
+++ b/src/plugins/directshow/player/directshowplayerservice.cpp
@@ -563,7 +563,7 @@ void DirectShowPlayerService::releaseGraph()
}
m_graph->Abort();
}
-
+
m_pendingTasks = ReleaseGraph;
::SetEvent(m_taskHandle);
@@ -803,7 +803,7 @@ void DirectShowPlayerService::doStop(QMutexLocker *locker)
m_pendingTasks |= Seek;
m_executedTasks &= ~(Play | Pause);
-
+
QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange)));
}
@@ -852,7 +852,7 @@ void DirectShowPlayerService::doSetRate(QMutexLocker *locker)
seeking->Release();
} else if (m_rate != 1.0) {
- m_rate = 1.0;
+ m_rate = 1.0;
}
QCoreApplication::postEvent(this, new QEvent(QEvent::Type(RateChange)));
}
@@ -980,7 +980,7 @@ void DirectShowPlayerService::setAudioOutput(IBaseFilter *filter)
m_loop->wait(&m_mutex);
}
m_audioOutput->Release();
- }
+ }
m_audioOutput = filter;
diff --git a/src/plugins/directshow/player/directshowsamplescheduler.cpp b/src/plugins/directshow/player/directshowsamplescheduler.cpp
index 36aefa049..2f61784a7 100644
--- a/src/plugins/directshow/player/directshowsamplescheduler.cpp
+++ b/src/plugins/directshow/player/directshowsamplescheduler.cpp
@@ -308,10 +308,10 @@ void DirectShowSampleScheduler::run(REFERENCE_TIME startTime)
for (DirectShowTimedSample *sample = m_head; sample; sample = sample->nextSample()) {
sample->schedule(m_clock, m_startTime, m_timeoutEvent);
}
-
+
if (!(m_state & Flushing))
::ResetEvent(m_flushEvent);
-
+
if (!m_head)
::SetEvent(m_timeoutEvent);
@@ -338,7 +338,7 @@ void DirectShowSampleScheduler::stop()
for (DirectShowTimedSample *sample = m_head; sample; sample = sample->remove()) {
sample->unschedule(m_clock);
-
+
m_semaphore.release(1);
}
diff --git a/src/plugins/directshow/player/videosurfacefilter.cpp b/src/plugins/directshow/player/videosurfacefilter.cpp
index b8a775bb7..b6cc232ab 100644
--- a/src/plugins/directshow/player/videosurfacefilter.cpp
+++ b/src/plugins/directshow/player/videosurfacefilter.cpp
@@ -81,7 +81,7 @@ VideoSurfaceFilter::~VideoSurfaceFilter()
}
HRESULT VideoSurfaceFilter::QueryInterface(REFIID riid, void **ppvObject)
-{
+{
// 2dd74950-a890-11d1-abe8-00a0c905f375
static const GUID iid_IAmFilterMiscFlags = {
0x2dd74950, 0xa890, 0x11d1, {0xab, 0xe8, 0x00, 0xa0, 0xc9, 0x05, 0xf3, 0x75} };
diff --git a/src/plugins/gstreamer/camerabin/camerabinflash.h b/src/plugins/gstreamer/camerabin/camerabinflash.h
index d94b4a87c..f5484101f 100644
--- a/src/plugins/gstreamer/camerabin/camerabinflash.h
+++ b/src/plugins/gstreamer/camerabin/camerabinflash.h
@@ -66,7 +66,7 @@ public:
bool isFlashReady() const;
private:
- CameraBinSession *m_session;
+ CameraBinSession *m_session;
};
QT_END_NAMESPACE
diff --git a/src/plugins/gstreamer/camerabin/camerabinlocks.cpp b/src/plugins/gstreamer/camerabin/camerabinlocks.cpp
index 464fa8043..cf105d482 100644
--- a/src/plugins/gstreamer/camerabin/camerabinlocks.cpp
+++ b/src/plugins/gstreamer/camerabin/camerabinlocks.cpp
@@ -54,8 +54,8 @@ CameraBinLocks::CameraBinLocks(CameraBinSession *session)
m_session(session),
m_focus(m_session->cameraFocusControl())
{
- connect(m_focus, SIGNAL(_q_focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)),
- this, SLOT(updateFocusStatus(QCamera::LockStatus, QCamera::LockChangeReason)));
+ connect(m_focus, SIGNAL(_q_focusStatusChanged(QCamera::LockStatus,QCamera::LockChangeReason)),
+ this, SLOT(updateFocusStatus(QCamera::LockStatus,QCamera::LockChangeReason)));
}
CameraBinLocks::~CameraBinLocks()
diff --git a/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp b/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp
index d6df4208e..d8f1616d9 100644
--- a/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp
+++ b/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp
@@ -65,7 +65,7 @@ CamerabinResourcePolicy::CamerabinResourcePolicy(QObject *parent) :
m_resource->setAlwaysReply();
m_resource->initAndConnect();
- connect(m_resource, SIGNAL(resourcesGranted(const QList<ResourcePolicy::ResourceType>)),
+ connect(m_resource, SIGNAL(resourcesGranted(QList<ResourcePolicy::ResourceType>)),
SLOT(handleResourcesGranted()));
connect(m_resource, SIGNAL(resourcesDenied()), SIGNAL(resourcesDenied()));
connect(m_resource, SIGNAL(lostResources()), SLOT(handleResourcesLost()));
diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.h b/src/plugins/gstreamer/camerabin/camerabinsession.h
index 3332f4c78..d77217906 100644
--- a/src/plugins/gstreamer/camerabin/camerabinsession.h
+++ b/src/plugins/gstreamer/camerabin/camerabinsession.h
@@ -114,7 +114,7 @@ public:
void setCaptureMode(QCamera::CaptureModes mode);
QUrl outputLocation() const;
- bool setOutputLocation(const QUrl& sink);
+ bool setOutputLocation(const QUrl& sink);
QDir defaultDir(QCamera::CaptureModes mode) const;
QString generateFileName(const QString &prefix, const QDir &dir, const QString &ext) const;
diff --git a/src/plugins/gstreamer/camerabin/camerabuttonlistener_meego.cpp b/src/plugins/gstreamer/camerabin/camerabuttonlistener_meego.cpp
index c49539df3..84bcac9fc 100644
--- a/src/plugins/gstreamer/camerabin/camerabuttonlistener_meego.cpp
+++ b/src/plugins/gstreamer/camerabin/camerabuttonlistener_meego.cpp
@@ -54,7 +54,7 @@ CameraButtonListener::CameraButtonListener(QObject *parent) :
m_shutterPressed(false)
{
m_keys = new MeeGo::QmKeys(this);
- connect(m_keys, SIGNAL(keyEvent(MeeGo::QmKeys::Key, MeeGo::QmKeys::State)),
+ connect(m_keys, SIGNAL(keyEvent(MeeGo::QmKeys::Key,MeeGo::QmKeys::State)),
this, SLOT(handleQmKeyEvent(MeeGo::QmKeys::Key,MeeGo::QmKeys::State)));
}
@@ -65,7 +65,7 @@ CameraButtonListener::~CameraButtonListener()
void CameraButtonListener::handleQmKeyEvent(MeeGo::QmKeys::Key key, MeeGo::QmKeys::State state)
{
if (key == MeeGo::QmKeys::Camera) {
- QWidget *window = QApplication::focusWidget();
+ QWidget *window = QApplication::focusWidget();
bool focusPressed = (state == MeeGo::QmKeys::KeyHalfDown) ||
(state == MeeGo::QmKeys::KeyDown);
diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp
index d61e6a010..da6120181 100644
--- a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp
+++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp
@@ -491,7 +491,7 @@ void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput)
}
}
- QGstreamerVideoRendererInterface* renderer = qobject_cast<QGstreamerVideoRendererInterface*>(videoOutput);
+ QGstreamerVideoRendererInterface* renderer = qobject_cast<QGstreamerVideoRendererInterface*>(videoOutput);
m_renderer = renderer;
@@ -646,7 +646,7 @@ void QGstreamerPlayerSession::finishVideoOutputChange()
m_pendingVideoSink = 0;
gst_object_unref(GST_OBJECT(srcPad));
return;
- }
+ }
if (m_usingColorspaceElement) {
gst_element_set_state(m_colorSpace, GST_STATE_NULL);
diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro
index c10759955..2677e269b 100644
--- a/src/plugins/plugins.pro
+++ b/src/plugins/plugins.pro
@@ -21,11 +21,10 @@ qnx:!blackberry {
SUBDIRS += qnx-audio
}
-win32 {
- SUBDIRS += audiocapture
-}
+win32:!winrt {
+ SUBDIRS += audiocapture \
+ windowsaudio
-win32 {
config_directshow: SUBDIRS += directshow
config_wmf: SUBDIRS += wmf
}
@@ -37,12 +36,14 @@ unix:!mac:!android {
SUBDIRS += audiocapture
}
- # v4l is turned off because it is not supported in Qt 5
- # !maemo*:SUBDIRS += v4l
-
config_pulseaudio {
SUBDIRS += pulseaudio
+ } else:config_alsa {
+ SUBDIRS += alsa
}
+
+ # v4l is turned off because it is not supported in Qt 5
+ # !maemo*:SUBDIRS += v4l
}
mac:!simulator {
diff --git a/src/plugins/qnx/camera/bbcamerasession.cpp b/src/plugins/qnx/camera/bbcamerasession.cpp
index d673040ab..59db66f4a 100644
--- a/src/plugins/qnx/camera/bbcamerasession.cpp
+++ b/src/plugins/qnx/camera/bbcamerasession.cpp
@@ -92,10 +92,20 @@ static QString errorToString(camera_error_t error)
case CAMERA_EMICINUSE:
return QLatin1String("Microphone in use already");
#ifndef Q_OS_BLACKBERRY_TABLET
+ case CAMERA_ENODATA:
+ return QLatin1String("Data does not exist");
+ case CAMERA_EBUSY:
+ return QLatin1String("Camera busy");
case CAMERA_EDESKTOPCAMERAINUSE:
return QLatin1String("Desktop camera in use already");
case CAMERA_ENOSPC:
return QLatin1String("Disk is full");
+ case CAMERA_EPOWERDOWN:
+ return QLatin1String("Camera in power down state");
+ case CAMERA_3ALOCKED:
+ return QLatin1String("3A have been locked");
+// case CAMERA_EVIEWFINDERFROZEN: // not yet available in 10.2 NDK
+// return QLatin1String("Freeze flag set");
#endif
default:
return QLatin1String("Unknown error");
diff --git a/src/plugins/qt7/mediaplayer/qt7playercontrol.mm b/src/plugins/qt7/mediaplayer/qt7playercontrol.mm
index c987f6952..c48660778 100644
--- a/src/plugins/qt7/mediaplayer/qt7playercontrol.mm
+++ b/src/plugins/qt7/mediaplayer/qt7playercontrol.mm
@@ -51,7 +51,7 @@ QT_USE_NAMESPACE
QT7PlayerControl::QT7PlayerControl(QObject *parent)
: QMediaPlayerControl(parent)
-{
+{
}
QT7PlayerControl::~QT7PlayerControl()
diff --git a/src/plugins/qt7/qcvdisplaylink.h b/src/plugins/qt7/qcvdisplaylink.h
index 2d4809c49..f59cc92bf 100644
--- a/src/plugins/qt7/qcvdisplaylink.h
+++ b/src/plugins/qt7/qcvdisplaylink.h
@@ -73,7 +73,7 @@ public:
protected:
virtual bool event(QEvent *);
-
+
private:
CVDisplayLinkRef m_displayLink;
QMutex m_displayLinkMutex;
diff --git a/src/plugins/qt7/qt7movierenderer.h b/src/plugins/qt7/qt7movierenderer.h
index aed39ad9a..bdc7c5260 100644
--- a/src/plugins/qt7/qt7movierenderer.h
+++ b/src/plugins/qt7/qt7movierenderer.h
@@ -80,7 +80,7 @@ public:
private Q_SLOTS:
void updateVideoFrame(const CVTimeStamp &ts);
-
+
private:
void setupVideoOutput();
bool createPixelBufferVisualContext();
@@ -92,7 +92,7 @@ private:
QCvDisplayLink *m_displayLink;
#ifdef QUICKTIME_C_API_AVAILABLE
- QTVisualContextRef m_visualContext;
+ QTVisualContextRef m_visualContext;
bool m_usingGLContext;
const QGLContext *m_currentGLContext;
QSize m_pixelBufferContextGeometry;
diff --git a/src/plugins/qt7/qt7movievideowidget.h b/src/plugins/qt7/qt7movievideowidget.h
index fd0c3d075..cd61869fa 100644
--- a/src/plugins/qt7/qt7movievideowidget.h
+++ b/src/plugins/qt7/qt7movievideowidget.h
@@ -95,7 +95,7 @@ public:
private Q_SLOTS:
void updateVideoFrame(const CVTimeStamp &ts);
-
+
private:
void setupVideoOutput();
bool createVisualContext();
@@ -108,7 +108,7 @@ private:
QCvDisplayLink *m_displayLink;
#ifdef QUICKTIME_C_API_AVAILABLE
- QTVisualContextRef m_visualContext;
+ QTVisualContextRef m_visualContext;
#endif
bool m_fullscreen;
diff --git a/src/plugins/qt7/qt7movievideowidget.mm b/src/plugins/qt7/qt7movievideowidget.mm
index 11f2ad32c..ec9367bcc 100644
--- a/src/plugins/qt7/qt7movievideowidget.mm
+++ b/src/plugins/qt7/qt7movievideowidget.mm
@@ -76,7 +76,7 @@ public:
m_nativeSize(640,480),
m_aspectRatioMode(Qt::KeepAspectRatio)
{
- setAutoFillBackground(false);
+ setAutoFillBackground(false);
}
void initializeGL()
@@ -151,7 +151,7 @@ public:
void setNativeSize(const QSize &size)
{
- m_nativeSize = size;
+ m_nativeSize = size;
}
void setAspectRatioMode(Qt::AspectRatioMode mode)
@@ -184,8 +184,8 @@ private:
QT7MovieVideoWidget::QT7MovieVideoWidget(QObject *parent)
:QT7VideoWidgetControl(parent),
- m_movie(0),
- m_videoWidget(0),
+ m_movie(0),
+ m_videoWidget(0),
m_fullscreen(false),
m_aspectRatioMode(Qt::KeepAspectRatio),
m_brightness(0),
@@ -325,7 +325,7 @@ bool QT7MovieVideoWidget::isFullScreen() const
void QT7MovieVideoWidget::setFullScreen(bool fullScreen)
{
- m_fullscreen = fullScreen;
+ m_fullscreen = fullScreen;
}
QSize QT7MovieVideoWidget::nativeSize() const
@@ -341,7 +341,7 @@ Qt::AspectRatioMode QT7MovieVideoWidget::aspectRatioMode() const
void QT7MovieVideoWidget::setAspectRatioMode(Qt::AspectRatioMode mode)
{
m_aspectRatioMode = mode;
- m_videoWidget->setAspectRatioMode(mode);
+ m_videoWidget->setAspectRatioMode(mode);
}
int QT7MovieVideoWidget::brightness() const
@@ -410,7 +410,7 @@ void QT7MovieVideoWidget::updateColors()
void QT7MovieVideoWidget::updateVideoFrame(const CVTimeStamp &ts)
{
#ifdef QUICKTIME_C_API_AVAILABLE
- AutoReleasePool pool;
+ AutoReleasePool pool;
// check for new frame
if (m_visualContext && QTVisualContextIsNewImageAvailable(m_visualContext, &ts)) {
CVOpenGLTextureRef currentFrame = NULL;
diff --git a/src/plugins/qt7/qt7movieviewoutput.h b/src/plugins/qt7/qt7movieviewoutput.h
index ea3d248f8..2e3ed2c7c 100644
--- a/src/plugins/qt7/qt7movieviewoutput.h
+++ b/src/plugins/qt7/qt7movieviewoutput.h
@@ -91,7 +91,7 @@ public:
int saturation() const;
void setSaturation(int saturation);
-
+
private:
void setupVideoOutput();
diff --git a/src/plugins/qt7/qt7movieviewoutput.mm b/src/plugins/qt7/qt7movieviewoutput.mm
index c6a2bbafe..4fcf01d39 100644
--- a/src/plugins/qt7/qt7movieviewoutput.mm
+++ b/src/plugins/qt7/qt7movieviewoutput.mm
@@ -166,7 +166,7 @@ QT7MovieViewOutput::QT7MovieViewOutput(QObject *parent)
m_contrast(0),
m_hue(0),
m_saturation(0)
-{
+{
}
QT7MovieViewOutput::~QT7MovieViewOutput()
diff --git a/src/plugins/qt7/qt7movieviewrenderer.mm b/src/plugins/qt7/qt7movieviewrenderer.mm
index 0e1571f92..a59031577 100644
--- a/src/plugins/qt7/qt7movieviewrenderer.mm
+++ b/src/plugins/qt7/qt7movieviewrenderer.mm
@@ -278,7 +278,7 @@ QT7MovieViewRenderer::QT7MovieViewRenderer(QObject *parent)
m_fbo(0),
m_ciContext(0),
m_pendingRenderEvent(false)
-{
+{
}
QT7MovieViewRenderer::~QT7MovieViewRenderer()
diff --git a/src/plugins/qt7/qt7videooutput.mm b/src/plugins/qt7/qt7videooutput.mm
index 6c657c279..53486c4e4 100644
--- a/src/plugins/qt7/qt7videooutput.mm
+++ b/src/plugins/qt7/qt7videooutput.mm
@@ -47,10 +47,10 @@ QT_USE_NAMESPACE
/*
QT7VideoOutputControl::QT7VideoOutputControl(QObject *parent)
- :QVideoOutputControl(parent),
+ :QVideoOutputControl(parent),
m_session(0),
m_output(QVideoOutputControl::NoOutput)
-{
+{
}
QT7VideoOutputControl::~QT7VideoOutputControl()
diff --git a/src/plugins/resourcepolicy/resourcepolicyimpl.cpp b/src/plugins/resourcepolicy/resourcepolicyimpl.cpp
index aed9ccb09..0acabc001 100644
--- a/src/plugins/resourcepolicy/resourcepolicyimpl.cpp
+++ b/src/plugins/resourcepolicy/resourcepolicyimpl.cpp
@@ -61,7 +61,7 @@ ResourcePolicyImpl::ResourcePolicyImpl(QObject *parent)
m_resourceSet->update();
- connect(m_resourceSet, SIGNAL(resourcesGranted(const QList<ResourcePolicy::ResourceType>)),
+ connect(m_resourceSet, SIGNAL(resourcesGranted(QList<ResourcePolicy::ResourceType>)),
this, SLOT(handleResourcesGranted()));
connect(m_resourceSet, SIGNAL(resourcesDenied()),
this, SLOT(handleResourcesDenied()));
diff --git a/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp b/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp
new file mode 100644
index 000000000..d37056a5f
--- /dev/null
+++ b/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.cpp
@@ -0,0 +1,490 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// INTERNAL USE ONLY: Do NOT use for any other purpose.
+//
+
+
+#include <QtCore/qt_windows.h>
+#include <mmsystem.h>
+#include "qwindowsaudiodeviceinfo.h"
+
+#if defined(Q_CC_MINGW) && !defined(__MINGW64_VERSION_MAJOR)
+struct IBaseFilter; // Needed for strmif.h from stock MinGW.
+struct _DDPIXELFORMAT;
+typedef struct _DDPIXELFORMAT* LPDDPIXELFORMAT;
+#endif
+
+#include <strmif.h>
+#if !defined(Q_CC_MINGW) || defined(__MINGW64_VERSION_MAJOR)
+# include <uuids.h>
+#else
+
+extern GUID CLSID_AudioInputDeviceCategory;
+extern GUID CLSID_AudioRendererCategory;
+extern GUID IID_ICreateDevEnum;
+extern GUID CLSID_SystemDeviceEnum;
+
+#ifndef __ICreateDevEnum_INTERFACE_DEFINED__
+#define __ICreateDevEnum_INTERFACE_DEFINED__
+
+DECLARE_INTERFACE_(ICreateDevEnum, IUnknown)
+{
+ STDMETHOD(CreateClassEnumerator)(REFCLSID clsidDeviceClass,
+ IEnumMoniker **ppEnumMoniker,
+ DWORD dwFlags) PURE;
+};
+
+#endif // __ICreateDevEnum_INTERFACE_DEFINED__
+
+#ifndef __IErrorLog_INTERFACE_DEFINED__
+#define __IErrorLog_INTERFACE_DEFINED__
+
+DECLARE_INTERFACE_(IErrorLog, IUnknown)
+{
+ STDMETHOD(AddError)(THIS_ LPCOLESTR, EXCEPINFO *) PURE;
+};
+
+#endif /* __IErrorLog_INTERFACE_DEFINED__ */
+
+#ifndef __IPropertyBag_INTERFACE_DEFINED__
+#define __IPropertyBag_INTERFACE_DEFINED__
+
+const GUID IID_IPropertyBag = {0x55272A00, 0x42CB, 0x11CE, {0x81, 0x35, 0x00, 0xAA, 0x00, 0x4B, 0xB8, 0x51}};
+
+DECLARE_INTERFACE_(IPropertyBag, IUnknown)
+{
+ STDMETHOD(Read)(THIS_ LPCOLESTR, VARIANT *, IErrorLog *) PURE;
+ STDMETHOD(Write)(THIS_ LPCOLESTR, VARIANT *) PURE;
+};
+
+#endif /* __IPropertyBag_INTERFACE_DEFINED__ */
+
+#endif // defined(Q_CC_MINGW) && !defined(__MINGW64_VERSION_MAJOR)
+
+QT_BEGIN_NAMESPACE
+
+// For mingw toolchain mmsystem.h only defines half the defines, so add if needed.
+#ifndef WAVE_FORMAT_44M08
+#define WAVE_FORMAT_44M08 0x00000100
+#define WAVE_FORMAT_44S08 0x00000200
+#define WAVE_FORMAT_44M16 0x00000400
+#define WAVE_FORMAT_44S16 0x00000800
+#define WAVE_FORMAT_48M08 0x00001000
+#define WAVE_FORMAT_48S08 0x00002000
+#define WAVE_FORMAT_48M16 0x00004000
+#define WAVE_FORMAT_48S16 0x00008000
+#define WAVE_FORMAT_96M08 0x00010000
+#define WAVE_FORMAT_96S08 0x00020000
+#define WAVE_FORMAT_96M16 0x00040000
+#define WAVE_FORMAT_96S16 0x00080000
+#endif
+
+
+QWindowsAudioDeviceInfo::QWindowsAudioDeviceInfo(QByteArray dev, QAudio::Mode mode)
+{
+ QDataStream ds(&dev, QIODevice::ReadOnly);
+ ds >> devId >> device;
+ this->mode = mode;
+
+ updateLists();
+}
+
+QWindowsAudioDeviceInfo::~QWindowsAudioDeviceInfo()
+{
+ close();
+}
+
+bool QWindowsAudioDeviceInfo::isFormatSupported(const QAudioFormat& format) const
+{
+ return testSettings(format);
+}
+
+QAudioFormat QWindowsAudioDeviceInfo::preferredFormat() const
+{
+ QAudioFormat nearest;
+ if (mode == QAudio::AudioOutput) {
+ nearest.setSampleRate(44100);
+ nearest.setChannelCount(2);
+ nearest.setByteOrder(QAudioFormat::LittleEndian);
+ nearest.setSampleType(QAudioFormat::SignedInt);
+ nearest.setSampleSize(16);
+ nearest.setCodec(QLatin1String("audio/pcm"));
+ } else {
+ nearest.setSampleRate(11025);
+ nearest.setChannelCount(1);
+ nearest.setByteOrder(QAudioFormat::LittleEndian);
+ nearest.setSampleType(QAudioFormat::SignedInt);
+ nearest.setSampleSize(8);
+ nearest.setCodec(QLatin1String("audio/pcm"));
+ }
+ return nearest;
+}
+
+QString QWindowsAudioDeviceInfo::deviceName() const
+{
+ return device;
+}
+
+QStringList QWindowsAudioDeviceInfo::supportedCodecs()
+{
+ updateLists();
+ return codecz;
+}
+
+QList<int> QWindowsAudioDeviceInfo::supportedSampleRates()
+{
+ updateLists();
+ return sampleRatez;
+}
+
+QList<int> QWindowsAudioDeviceInfo::supportedChannelCounts()
+{
+ updateLists();
+ return channelz;
+}
+
+QList<int> QWindowsAudioDeviceInfo::supportedSampleSizes()
+{
+ updateLists();
+ return sizez;
+}
+
+QList<QAudioFormat::Endian> QWindowsAudioDeviceInfo::supportedByteOrders()
+{
+ updateLists();
+ return byteOrderz;
+}
+
+QList<QAudioFormat::SampleType> QWindowsAudioDeviceInfo::supportedSampleTypes()
+{
+ updateLists();
+ return typez;
+}
+
+
+bool QWindowsAudioDeviceInfo::open()
+{
+ return true;
+}
+
+void QWindowsAudioDeviceInfo::close()
+{
+}
+
+bool QWindowsAudioDeviceInfo::testSettings(const QAudioFormat& format) const
+{
+ // Set nearest to closest settings that do work.
+ // See if what is in settings will work (return value).
+
+ bool failed = false;
+ bool match = false;
+
+ // check codec
+ for( int i = 0; i < codecz.count(); i++) {
+ if (format.codec() == codecz.at(i))
+ match = true;
+ }
+ if (!match) failed = true;
+
+ // check channel
+ match = false;
+ if (!failed) {
+ for (int i = 0; i < channelz.count(); i++) {
+ if (format.channelCount() == channelz.at(i)) {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ failed = true;
+ }
+
+ // check sampleRate
+ match = false;
+ if (!failed) {
+ for (int i = 0; i < sampleRatez.count(); i++) {
+ if (format.sampleRate() == sampleRatez.at(i)) {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ failed = true;
+ }
+
+ // check sample size
+ match = false;
+ if (!failed) {
+ for( int i = 0; i < sizez.count(); i++) {
+ if (format.sampleSize() == sizez.at(i)) {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ failed = true;
+ }
+
+ // check byte order
+ match = false;
+ if (!failed) {
+ for( int i = 0; i < byteOrderz.count(); i++) {
+ if (format.byteOrder() == byteOrderz.at(i)) {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ failed = true;
+ }
+
+ // check sample type
+ match = false;
+ if (!failed) {
+ for( int i = 0; i < typez.count(); i++) {
+ if (format.sampleType() == typez.at(i)) {
+ match = true;
+ break;
+ }
+ }
+ if (!match)
+ failed = true;
+ }
+
+ if(!failed) {
+ // settings work
+ return true;
+ }
+ return false;
+}
+
+void QWindowsAudioDeviceInfo::updateLists()
+{
+ // redo all lists based on current settings
+ bool match = false;
+ DWORD fmt = 0;
+
+ if(mode == QAudio::AudioOutput) {
+ WAVEOUTCAPS woc;
+ if (waveOutGetDevCaps(devId, &woc, sizeof(WAVEOUTCAPS)) == MMSYSERR_NOERROR) {
+ match = true;
+ fmt = woc.dwFormats;
+ }
+ } else {
+ WAVEINCAPS woc;
+ if (waveInGetDevCaps(devId, &woc, sizeof(WAVEINCAPS)) == MMSYSERR_NOERROR) {
+ match = true;
+ fmt = woc.dwFormats;
+ }
+ }
+ sizez.clear();
+ sampleRatez.clear();
+ channelz.clear();
+ byteOrderz.clear();
+ typez.clear();
+ codecz.clear();
+
+ if(match) {
+ if ((fmt & WAVE_FORMAT_1M08)
+ || (fmt & WAVE_FORMAT_1S08)
+ || (fmt & WAVE_FORMAT_2M08)
+ || (fmt & WAVE_FORMAT_2S08)
+ || (fmt & WAVE_FORMAT_4M08)
+ || (fmt & WAVE_FORMAT_4S08)
+ || (fmt & WAVE_FORMAT_48M08)
+ || (fmt & WAVE_FORMAT_48S08)
+ || (fmt & WAVE_FORMAT_96M08)
+ || (fmt & WAVE_FORMAT_96S08)
+ ) {
+ sizez.append(8);
+ }
+ if ((fmt & WAVE_FORMAT_1M16)
+ || (fmt & WAVE_FORMAT_1S16)
+ || (fmt & WAVE_FORMAT_2M16)
+ || (fmt & WAVE_FORMAT_2S16)
+ || (fmt & WAVE_FORMAT_4M16)
+ || (fmt & WAVE_FORMAT_4S16)
+ || (fmt & WAVE_FORMAT_48M16)
+ || (fmt & WAVE_FORMAT_48S16)
+ || (fmt & WAVE_FORMAT_96M16)
+ || (fmt & WAVE_FORMAT_96S16)
+ ) {
+ sizez.append(16);
+ }
+ if ((fmt & WAVE_FORMAT_1M08)
+ || (fmt & WAVE_FORMAT_1S08)
+ || (fmt & WAVE_FORMAT_1M16)
+ || (fmt & WAVE_FORMAT_1S16)) {
+ sampleRatez.append(11025);
+ }
+ if ((fmt & WAVE_FORMAT_2M08)
+ || (fmt & WAVE_FORMAT_2S08)
+ || (fmt & WAVE_FORMAT_2M16)
+ || (fmt & WAVE_FORMAT_2S16)) {
+ sampleRatez.append(22050);
+ }
+ if ((fmt & WAVE_FORMAT_4M08)
+ || (fmt & WAVE_FORMAT_4S08)
+ || (fmt & WAVE_FORMAT_4M16)
+ || (fmt & WAVE_FORMAT_4S16)) {
+ sampleRatez.append(44100);
+ }
+ if ((fmt & WAVE_FORMAT_48M08)
+ || (fmt & WAVE_FORMAT_48S08)
+ || (fmt & WAVE_FORMAT_48M16)
+ || (fmt & WAVE_FORMAT_48S16)) {
+ sampleRatez.append(48000);
+ }
+ if ((fmt & WAVE_FORMAT_96M08)
+ || (fmt & WAVE_FORMAT_96S08)
+ || (fmt & WAVE_FORMAT_96M16)
+ || (fmt & WAVE_FORMAT_96S16)) {
+ sampleRatez.append(96000);
+ }
+ channelz.append(1);
+ channelz.append(2);
+ if (mode == QAudio::AudioOutput) {
+ channelz.append(4);
+ channelz.append(6);
+ channelz.append(8);
+ }
+
+ byteOrderz.append(QAudioFormat::LittleEndian);
+
+ typez.append(QAudioFormat::SignedInt);
+ typez.append(QAudioFormat::UnSignedInt);
+
+ codecz.append(QLatin1String("audio/pcm"));
+ }
+ if (sampleRatez.count() > 0)
+ sampleRatez.prepend(8000);
+}
+
+QList<QByteArray> QWindowsAudioDeviceInfo::availableDevices(QAudio::Mode mode)
+{
+ Q_UNUSED(mode)
+
+ QList<QByteArray> devices;
+ //enumerate device fullnames through directshow api
+ CoInitialize(NULL);
+ ICreateDevEnum *pDevEnum = NULL;
+ IEnumMoniker *pEnum = NULL;
+ // Create the System device enumerator
+ HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL,
+ CLSCTX_INPROC_SERVER, IID_ICreateDevEnum,
+ reinterpret_cast<void **>(&pDevEnum));
+
+ unsigned long iNumDevs = mode == QAudio::AudioOutput ? waveOutGetNumDevs() : waveInGetNumDevs();
+ if (SUCCEEDED(hr)) {
+ // Create the enumerator for the audio input/output category
+ if (pDevEnum->CreateClassEnumerator(
+ mode == QAudio::AudioOutput ? CLSID_AudioRendererCategory : CLSID_AudioInputDeviceCategory,
+ &pEnum, 0) == S_OK) {
+ pEnum->Reset();
+ // go through and find all audio devices
+ IMoniker *pMoniker = NULL;
+ while (pEnum->Next(1, &pMoniker, NULL) == S_OK) {
+ IPropertyBag *pPropBag;
+ hr = pMoniker->BindToStorage(0,0,IID_IPropertyBag,
+ reinterpret_cast<void **>(&pPropBag));
+ if (FAILED(hr)) {
+ pMoniker->Release();
+ continue; // skip this one
+ }
+ // Find if it is a wave device
+ VARIANT var;
+ VariantInit(&var);
+ hr = pPropBag->Read(mode == QAudio::AudioOutput ? L"WaveOutID" : L"WaveInID", &var, 0);
+ if (SUCCEEDED(hr)) {
+ LONG waveID = var.lVal;
+ if (waveID >= 0 && waveID < LONG(iNumDevs)) {
+ VariantClear(&var);
+ // Find the description
+ hr = pPropBag->Read(L"FriendlyName", &var, 0);
+ if (SUCCEEDED(hr)) {
+ QByteArray device;
+ QDataStream ds(&device, QIODevice::WriteOnly);
+ ds << quint32(waveID) << QString::fromWCharArray(var.bstrVal);
+ devices.append(device);
+ }
+ }
+ }
+
+ pPropBag->Release();
+ pMoniker->Release();
+ }
+ }
+ }
+ CoUninitialize();
+
+ return devices;
+}
+
+QByteArray QWindowsAudioDeviceInfo::defaultOutputDevice()
+{
+ QByteArray defaultDevice;
+ QDataStream ds(&defaultDevice, QIODevice::WriteOnly);
+ ds << quint32(WAVE_MAPPER) // device ID for default device
+ << QStringLiteral("Default Output Device");
+
+ return defaultDevice;
+}
+
+QByteArray QWindowsAudioDeviceInfo::defaultInputDevice()
+{
+ QByteArray defaultDevice;
+ QDataStream ds(&defaultDevice, QIODevice::WriteOnly);
+ ds << quint32(WAVE_MAPPER) // device ID for default device
+ << QStringLiteral("Default Input Device");
+
+ return defaultDevice;
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.h b/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.h
new file mode 100644
index 000000000..817b803f6
--- /dev/null
+++ b/src/plugins/windowsaudio/qwindowsaudiodeviceinfo.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+
+#ifndef QWINDOWSAUDIODEVICEINFO_H
+#define QWINDOWSAUDIODEVICEINFO_H
+
+#include <QtCore/qbytearray.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qlist.h>
+#include <QtCore/qdebug.h>
+
+#include <QtMultimedia/qaudiodeviceinfo.h>
+#include <QtMultimedia/qaudiosystem.h>
+
+
+QT_BEGIN_NAMESPACE
+
+
+const unsigned int MAX_SAMPLE_RATES = 5;
+const unsigned int SAMPLE_RATES[] = { 8000, 11025, 22050, 44100, 48000 };
+
+class QWindowsAudioDeviceInfo : public QAbstractAudioDeviceInfo
+{
+ Q_OBJECT
+
+public:
+ QWindowsAudioDeviceInfo(QByteArray dev,QAudio::Mode mode);
+ ~QWindowsAudioDeviceInfo();
+
+ bool open();
+ void close();
+
+ bool testSettings(const QAudioFormat& format) const;
+ void updateLists();
+ QAudioFormat preferredFormat() const;
+ bool isFormatSupported(const QAudioFormat& format) const;
+ QString deviceName() const;
+ QStringList supportedCodecs();
+ QList<int> supportedSampleRates();
+ QList<int> supportedChannelCounts();
+ QList<int> supportedSampleSizes();
+ QList<QAudioFormat::Endian> supportedByteOrders();
+ QList<QAudioFormat::SampleType> supportedSampleTypes();
+ static QByteArray defaultInputDevice();
+ static QByteArray defaultOutputDevice();
+ static QList<QByteArray> availableDevices(QAudio::Mode);
+
+private:
+ QAudio::Mode mode;
+ QString device;
+ quint32 devId;
+ QAudioFormat nearest;
+ QList<int> sampleRatez;
+ QList<int> channelz;
+ QList<int> sizez;
+ QList<QAudioFormat::Endian> byteOrderz;
+ QStringList codecz;
+ QList<QAudioFormat::SampleType> typez;
+};
+
+QT_END_NAMESPACE
+
+
+#endif // QWINDOWSAUDIODEVICEINFO_H
diff --git a/src/plugins/windowsaudio/qwindowsaudioinput.cpp b/src/plugins/windowsaudio/qwindowsaudioinput.cpp
new file mode 100644
index 000000000..26f0641bf
--- /dev/null
+++ b/src/plugins/windowsaudio/qwindowsaudioinput.cpp
@@ -0,0 +1,752 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// INTERNAL USE ONLY: Do NOT use for any other purpose.
+//
+
+
+#include "qwindowsaudioinput.h"
+
+QT_BEGIN_NAMESPACE
+
+//#define DEBUG_AUDIO 1
+
+QWindowsAudioInput::QWindowsAudioInput(const QByteArray &device)
+{
+ bytesAvailable = 0;
+ buffer_size = 0;
+ period_size = 0;
+ m_device = device;
+ totalTimeValue = 0;
+ intervalTime = 1000;
+ errorState = QAudio::NoError;
+ deviceState = QAudio::StoppedState;
+ audioSource = 0;
+ pullMode = true;
+ resuming = false;
+ finished = false;
+ waveBlockOffset = 0;
+
+ mixerID = 0;
+ memset(&mixerLineControls, 0, sizeof(mixerLineControls));
+ initMixer();
+}
+
+QWindowsAudioInput::~QWindowsAudioInput()
+{
+ stop();
+ closeMixer();
+}
+
+void QT_WIN_CALLBACK QWindowsAudioInput::waveInProc( HWAVEIN hWaveIn, UINT uMsg,
+ DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
+{
+ Q_UNUSED(dwParam1)
+ Q_UNUSED(dwParam2)
+ Q_UNUSED(hWaveIn)
+
+ QWindowsAudioInput* qAudio;
+ qAudio = (QWindowsAudioInput*)(dwInstance);
+ if(!qAudio)
+ return;
+
+ QMutexLocker(&qAudio->mutex);
+
+ switch(uMsg) {
+ case WIM_OPEN:
+ break;
+ case WIM_DATA:
+ if(qAudio->waveFreeBlockCount > 0)
+ qAudio->waveFreeBlockCount--;
+ qAudio->feedback();
+ break;
+ case WIM_CLOSE:
+ qAudio->finished = true;
+ break;
+ default:
+ return;
+ }
+}
+
+WAVEHDR* QWindowsAudioInput::allocateBlocks(int size, int count)
+{
+ int i;
+ unsigned char* buffer;
+ WAVEHDR* blocks;
+ DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count;
+
+ if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
+ totalBufferSize)) == 0) {
+ qWarning("QAudioInput: Memory allocation error");
+ return 0;
+ }
+ blocks = (WAVEHDR*)buffer;
+ buffer += sizeof(WAVEHDR)*count;
+ for(i = 0; i < count; i++) {
+ blocks[i].dwBufferLength = size;
+ blocks[i].lpData = (LPSTR)buffer;
+ blocks[i].dwBytesRecorded=0;
+ blocks[i].dwUser = 0L;
+ blocks[i].dwFlags = 0L;
+ blocks[i].dwLoops = 0L;
+ result = waveInPrepareHeader(hWaveIn,&blocks[i], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR) {
+ qWarning("QAudioInput: Can't prepare block %d",i);
+ return 0;
+ }
+ buffer += size;
+ }
+ return blocks;
+}
+
+void QWindowsAudioInput::freeBlocks(WAVEHDR* blockArray)
+{
+ WAVEHDR* blocks = blockArray;
+
+ int count = buffer_size/period_size;
+
+ for(int i = 0; i < count; i++) {
+ waveInUnprepareHeader(hWaveIn,blocks, sizeof(WAVEHDR));
+ blocks++;
+ }
+ HeapFree(GetProcessHeap(), 0, blockArray);
+}
+
+QAudio::Error QWindowsAudioInput::error() const
+{
+ return errorState;
+}
+
+QAudio::State QWindowsAudioInput::state() const
+{
+ return deviceState;
+}
+
+#ifndef DRVM_MAPPER_CONSOLEVOICECOM_GET
+ #ifndef DRVM_MAPPER
+ #define DRVM_MAPPER 0x2000
+ #endif
+ #ifndef DRVM_MAPPER_STATUS
+ #define DRVM_MAPPER_STATUS (DRVM_MAPPER+0)
+ #endif
+ #define DRVM_USER 0x4000
+ #define DRVM_MAPPER_RECONFIGURE (DRVM_MAPPER+1)
+ #define DRVM_MAPPER_PREFERRED_GET (DRVM_MAPPER+21)
+ #define DRVM_MAPPER_CONSOLEVOICECOM_GET (DRVM_MAPPER+23)
+#endif
+
+void QWindowsAudioInput::setVolume(qreal volume)
+{
+ for (DWORD i = 0; i < mixerLineControls.cControls; i++) {
+
+ MIXERCONTROLDETAILS controlDetails;
+ controlDetails.cbStruct = sizeof(MIXERCONTROLDETAILS);
+ controlDetails.dwControlID = mixerLineControls.pamxctrl[i].dwControlID;
+ controlDetails.cChannels = 1;
+
+ if ((mixerLineControls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_FADER) ||
+ (mixerLineControls.pamxctrl[i].dwControlType == MIXERCONTROL_CONTROLTYPE_VOLUME)) {
+ MIXERCONTROLDETAILS_UNSIGNED controlDetailsUnsigned;
+ controlDetailsUnsigned.dwValue = qBound(DWORD(0), DWORD(65535.0 * volume + 0.5), DWORD(65535));
+ controlDetails.cMultipleItems = 0;
+ controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
+ controlDetails.paDetails = &controlDetailsUnsigned;
+ mixerSetControlDetails(mixerID, &controlDetails, MIXER_SETCONTROLDETAILSF_VALUE);
+ }
+ }
+}
+
+qreal QWindowsAudioInput::volume() const
+{
+ DWORD volume = 0;
+ for (DWORD i = 0; i < mixerLineControls.cControls; i++) {
+ if ((mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_FADER) &&
+ (mixerLineControls.pamxctrl[i].dwControlType != MIXERCONTROL_CONTROLTYPE_VOLUME)) {
+ continue;
+ }
+
+ MIXERCONTROLDETAILS controlDetails;
+ controlDetails.cbStruct = sizeof(controlDetails);
+ controlDetails.dwControlID = mixerLineControls.pamxctrl[i].dwControlID;
+ controlDetails.cChannels = 1;
+ controlDetails.cMultipleItems = 0;
+ controlDetails.cbDetails = sizeof(MIXERCONTROLDETAILS_UNSIGNED);
+ MIXERCONTROLDETAILS_UNSIGNED detailsUnsigned;
+ controlDetails.paDetails = &detailsUnsigned;
+ memset(controlDetails.paDetails, 0, controlDetails.cbDetails);
+
+ MMRESULT result = mixerGetControlDetails(mixerID, &controlDetails, MIXER_GETCONTROLDETAILSF_VALUE);
+ if (result != MMSYSERR_NOERROR)
+ continue;
+ if (controlDetails.cbDetails < sizeof(MIXERCONTROLDETAILS_UNSIGNED))
+ continue;
+ volume = detailsUnsigned.dwValue;
+ break;
+ }
+
+ return volume / 65535.0;
+}
+
+void QWindowsAudioInput::setFormat(const QAudioFormat& fmt)
+{
+ if (deviceState == QAudio::StoppedState)
+ settings = fmt;
+}
+
+QAudioFormat QWindowsAudioInput::format() const
+{
+ return settings;
+}
+
+void QWindowsAudioInput::start(QIODevice* device)
+{
+ if(deviceState != QAudio::StoppedState)
+ close();
+
+ if(!pullMode && audioSource)
+ delete audioSource;
+
+ pullMode = true;
+ audioSource = device;
+
+ deviceState = QAudio::ActiveState;
+
+ if(!open())
+ return;
+
+ emit stateChanged(deviceState);
+}
+
+QIODevice* QWindowsAudioInput::start()
+{
+ if(deviceState != QAudio::StoppedState)
+ close();
+
+ if(!pullMode && audioSource)
+ delete audioSource;
+
+ pullMode = false;
+ audioSource = new InputPrivate(this);
+ audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered);
+
+ deviceState = QAudio::IdleState;
+
+ if(!open())
+ return 0;
+
+ emit stateChanged(deviceState);
+
+ return audioSource;
+}
+
+void QWindowsAudioInput::stop()
+{
+ if(deviceState == QAudio::StoppedState)
+ return;
+
+ close();
+ emit stateChanged(deviceState);
+}
+
+bool QWindowsAudioInput::open()
+{
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
+#endif
+ header = 0;
+
+ period_size = 0;
+
+ if (!settings.isValid()) {
+ qWarning("QAudioInput: open error, invalid format.");
+ } else if (settings.channelCount() <= 0) {
+ qWarning("QAudioInput: open error, invalid number of channels (%d).",
+ settings.channelCount());
+ } else if (settings.sampleSize() <= 0) {
+ qWarning("QAudioInput: open error, invalid sample size (%d).",
+ settings.sampleSize());
+ } else if (settings.sampleRate() < 8000 || settings.sampleRate() > 96000) {
+ qWarning("QAudioInput: open error, sample rate out of range (%d).", settings.sampleRate());
+ } else if (buffer_size == 0) {
+
+ buffer_size
+ = (settings.sampleRate()
+ * settings.channelCount()
+ * settings.sampleSize()
+ + 39) / 40;
+ period_size = buffer_size / 5;
+ } else {
+ period_size = buffer_size / 5;
+ }
+
+ if (period_size == 0) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return false;
+ }
+
+ timeStamp.restart();
+ elapsedTimeOffset = 0;
+ wfx.nSamplesPerSec = settings.sampleRate();
+ wfx.wBitsPerSample = settings.sampleSize();
+ wfx.nChannels = settings.channelCount();
+ wfx.cbSize = 0;
+
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
+ wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
+
+ QDataStream ds(&m_device, QIODevice::ReadOnly);
+ quint32 deviceId;
+ ds >> deviceId;
+
+ if (waveInOpen(&hWaveIn, UINT_PTR(deviceId), &wfx,
+ (DWORD_PTR)&waveInProc,
+ (DWORD_PTR) this,
+ CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ qWarning("QAudioInput: failed to open audio device");
+ return false;
+ }
+ waveBlocks = allocateBlocks(period_size, buffer_size/period_size);
+ waveBlockOffset = 0;
+
+ if(waveBlocks == 0) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ qWarning("QAudioInput: failed to allocate blocks. open failed");
+ return false;
+ }
+
+ mutex.lock();
+ waveFreeBlockCount = buffer_size/period_size;
+ mutex.unlock();
+
+ for(int i=0; i<buffer_size/period_size; i++) {
+ result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR) {
+ qWarning("QAudioInput: failed to setup block %d,err=%d",i,result);
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return false;
+ }
+ }
+ result = waveInStart(hWaveIn);
+ if(result) {
+ qWarning("QAudioInput: failed to start audio input");
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return false;
+ }
+ timeStampOpened.restart();
+ elapsedTimeOffset = 0;
+ totalTimeValue = 0;
+ errorState = QAudio::NoError;
+ return true;
+}
+
+void QWindowsAudioInput::close()
+{
+ if(deviceState == QAudio::StoppedState)
+ return;
+
+ deviceState = QAudio::StoppedState;
+ waveInReset(hWaveIn);
+
+ mutex.lock();
+ for (int i=0; i<waveFreeBlockCount; i++)
+ waveInUnprepareHeader(hWaveIn,&waveBlocks[i],sizeof(WAVEHDR));
+ freeBlocks(waveBlocks);
+ mutex.unlock();
+
+ waveInClose(hWaveIn);
+
+ int count = 0;
+ while(!finished && count < 500) {
+ count++;
+ Sleep(10);
+ }
+}
+
+void QWindowsAudioInput::initMixer()
+{
+ QDataStream ds(&m_device, QIODevice::ReadOnly);
+ quint32 inputDevice;
+ ds >> inputDevice;
+
+ if (int(inputDevice) < 0)
+ return;
+
+ // Get the Mixer ID from the Sound Device ID
+ UINT mixerIntID = 0;
+ if (mixerGetID((HMIXEROBJ)(quintptr(inputDevice)), &mixerIntID, MIXER_OBJECTF_WAVEIN) != MMSYSERR_NOERROR)
+ return;
+ mixerID = (HMIXEROBJ)mixerIntID;
+
+ // Get the Destination (Recording) Line Infomation
+ MIXERLINE mixerLine;
+ mixerLine.cbStruct = sizeof(MIXERLINE);
+ mixerLine.dwComponentType = MIXERLINE_COMPONENTTYPE_DST_WAVEIN;
+ if (mixerGetLineInfo(mixerID, &mixerLine, MIXER_GETLINEINFOF_COMPONENTTYPE) != MMSYSERR_NOERROR)
+ return;
+
+ // Set all the Destination (Recording) Line Controls
+ if (mixerLine.cControls > 0) {
+ mixerLineControls.cbStruct = sizeof(MIXERLINECONTROLS);
+ mixerLineControls.dwLineID = mixerLine.dwLineID;
+ mixerLineControls.cControls = mixerLine.cControls;
+ mixerLineControls.cbmxctrl = sizeof(MIXERCONTROL);
+ mixerLineControls.pamxctrl = new MIXERCONTROL[mixerLineControls.cControls];
+ if (mixerGetLineControls(mixerID, &mixerLineControls, MIXER_GETLINECONTROLSF_ALL) != MMSYSERR_NOERROR)
+ closeMixer();
+ }
+}
+
+void QWindowsAudioInput::closeMixer()
+{
+ delete[] mixerLineControls.pamxctrl;
+ memset(&mixerLineControls, 0, sizeof(mixerLineControls));
+}
+
+int QWindowsAudioInput::bytesReady() const
+{
+ if(period_size == 0 || buffer_size == 0)
+ return 0;
+
+ int buf = ((buffer_size/period_size)-waveFreeBlockCount)*period_size;
+ if(buf < 0)
+ buf = 0;
+ return buf;
+}
+
+qint64 QWindowsAudioInput::read(char* data, qint64 len)
+{
+ bool done = false;
+
+ char* p = data;
+ qint64 l = 0;
+ qint64 written = 0;
+ while(!done) {
+ // Read in some audio data
+ if(waveBlocks[header].dwBytesRecorded > 0 && waveBlocks[header].dwFlags & WHDR_DONE) {
+ if(pullMode) {
+ l = audioSource->write(waveBlocks[header].lpData + waveBlockOffset,
+ waveBlocks[header].dwBytesRecorded - waveBlockOffset);
+#ifdef DEBUG_AUDIO
+ qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l;
+#endif
+ if(l < 0) {
+ // error
+ qWarning("QAudioInput: IOError");
+ errorState = QAudio::IOError;
+
+ } else if(l == 0) {
+ // cant write to IODevice
+ qWarning("QAudioInput: IOError, can't write to QIODevice");
+ errorState = QAudio::IOError;
+
+ } else {
+ totalTimeValue += l;
+ errorState = QAudio::NoError;
+ if (deviceState != QAudio::ActiveState) {
+ deviceState = QAudio::ActiveState;
+ emit stateChanged(deviceState);
+ }
+ resuming = false;
+ }
+ } else {
+ l = qMin<qint64>(len, waveBlocks[header].dwBytesRecorded - waveBlockOffset);
+ // push mode
+ memcpy(p, waveBlocks[header].lpData + waveBlockOffset, l);
+
+ len -= l;
+
+#ifdef DEBUG_AUDIO
+ qDebug()<<"IN: "<<waveBlocks[header].dwBytesRecorded<<", OUT: "<<l;
+#endif
+ totalTimeValue += l;
+ errorState = QAudio::NoError;
+ if (deviceState != QAudio::ActiveState) {
+ deviceState = QAudio::ActiveState;
+ emit stateChanged(deviceState);
+ }
+ resuming = false;
+ }
+ } else {
+ //no data, not ready yet, next time
+ break;
+ }
+
+ if (l < waveBlocks[header].dwBytesRecorded - waveBlockOffset) {
+ waveBlockOffset += l;
+ done = true;
+ } else {
+ waveBlockOffset = 0;
+
+ waveInUnprepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
+
+ mutex.lock();
+ waveFreeBlockCount++;
+ mutex.unlock();
+
+ waveBlocks[header].dwBytesRecorded=0;
+ waveBlocks[header].dwFlags = 0L;
+ result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR) {
+ result = waveInPrepareHeader(hWaveIn,&waveBlocks[header], sizeof(WAVEHDR));
+ qWarning("QAudioInput: failed to prepare block %d,err=%d",header,result);
+ errorState = QAudio::IOError;
+
+ mutex.lock();
+ waveFreeBlockCount--;
+ mutex.unlock();
+
+ return 0;
+ }
+ result = waveInAddBuffer(hWaveIn, &waveBlocks[header], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR) {
+ qWarning("QAudioInput: failed to setup block %d,err=%d",header,result);
+ errorState = QAudio::IOError;
+
+ mutex.lock();
+ waveFreeBlockCount--;
+ mutex.unlock();
+
+ return 0;
+ }
+ header++;
+ if(header >= buffer_size/period_size)
+ header = 0;
+ p+=l;
+
+ mutex.lock();
+ if(!pullMode) {
+ if(len < period_size || waveFreeBlockCount == buffer_size/period_size)
+ done = true;
+ } else {
+ if(waveFreeBlockCount == buffer_size/period_size)
+ done = true;
+ }
+ mutex.unlock();
+ }
+
+ written+=l;
+ }
+#ifdef DEBUG_AUDIO
+ qDebug()<<"read in len="<<written;
+#endif
+ return written;
+}
+
+void QWindowsAudioInput::resume()
+{
+ if(deviceState == QAudio::SuspendedState) {
+ deviceState = QAudio::ActiveState;
+ for(int i=0; i<buffer_size/period_size; i++) {
+ result = waveInAddBuffer(hWaveIn, &waveBlocks[i], sizeof(WAVEHDR));
+ if(result != MMSYSERR_NOERROR) {
+ qWarning("QAudioInput: failed to setup block %d,err=%d",i,result);
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return;
+ }
+ }
+
+ mutex.lock();
+ waveFreeBlockCount = buffer_size/period_size;
+ mutex.unlock();
+
+ header = 0;
+ resuming = true;
+ waveBlockOffset = 0;
+ waveInStart(hWaveIn);
+ QTimer::singleShot(20,this,SLOT(feedback()));
+ emit stateChanged(deviceState);
+ }
+}
+
+void QWindowsAudioInput::setBufferSize(int value)
+{
+ buffer_size = value;
+}
+
+int QWindowsAudioInput::bufferSize() const
+{
+ return buffer_size;
+}
+
+int QWindowsAudioInput::periodSize() const
+{
+ return period_size;
+}
+
+void QWindowsAudioInput::setNotifyInterval(int ms)
+{
+ intervalTime = qMax(0, ms);
+}
+
+int QWindowsAudioInput::notifyInterval() const
+{
+ return intervalTime;
+}
+
+qint64 QWindowsAudioInput::processedUSecs() const
+{
+ if (deviceState == QAudio::StoppedState)
+ return 0;
+ qint64 result = qint64(1000000) * totalTimeValue /
+ (settings.channelCount()*(settings.sampleSize()/8)) /
+ settings.sampleRate();
+
+ return result;
+}
+
+void QWindowsAudioInput::suspend()
+{
+ if(deviceState == QAudio::ActiveState) {
+ waveInReset(hWaveIn);
+ deviceState = QAudio::SuspendedState;
+ emit stateChanged(deviceState);
+ }
+}
+
+void QWindowsAudioInput::feedback()
+{
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback() INPUT "<<this;
+#endif
+ if(!(deviceState==QAudio::StoppedState||deviceState==QAudio::SuspendedState))
+ QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection);
+}
+
+bool QWindowsAudioInput::deviceReady()
+{
+ bytesAvailable = bytesReady();
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :deviceReady() INPUT";
+#endif
+ if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
+ return true;
+
+ if(pullMode) {
+ // reads some audio data and writes it to QIODevice
+ read(0, buffer_size);
+ } else {
+ // emits readyRead() so user will call read() on QIODevice to get some audio data
+ InputPrivate* a = qobject_cast<InputPrivate*>(audioSource);
+ a->trigger();
+ }
+
+ if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
+ emit notify();
+ elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
+ timeStamp.restart();
+ }
+ return true;
+}
+
+qint64 QWindowsAudioInput::elapsedUSecs() const
+{
+ if (deviceState == QAudio::StoppedState)
+ return 0;
+
+ return timeStampOpened.elapsed()*1000;
+}
+
+void QWindowsAudioInput::reset()
+{
+ stop();
+ if (period_size > 0)
+ waveFreeBlockCount = buffer_size / period_size;
+}
+
+InputPrivate::InputPrivate(QWindowsAudioInput* audio)
+{
+ audioDevice = qobject_cast<QWindowsAudioInput*>(audio);
+}
+
+InputPrivate::~InputPrivate() {}
+
+qint64 InputPrivate::readData( char* data, qint64 len)
+{
+ // push mode, user read() called
+ if(audioDevice->deviceState != QAudio::ActiveState &&
+ audioDevice->deviceState != QAudio::IdleState)
+ return 0;
+ // Read in some audio data
+ return audioDevice->read(data,len);
+}
+
+qint64 InputPrivate::writeData(const char* data, qint64 len)
+{
+ Q_UNUSED(data)
+ Q_UNUSED(len)
+
+ emit readyRead();
+ return 0;
+}
+
+void InputPrivate::trigger()
+{
+ emit readyRead();
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwindowsaudioinput.cpp"
diff --git a/src/plugins/windowsaudio/qwindowsaudioinput.h b/src/plugins/windowsaudio/qwindowsaudioinput.h
new file mode 100644
index 000000000..7498bca5d
--- /dev/null
+++ b/src/plugins/windowsaudio/qwindowsaudioinput.h
@@ -0,0 +1,179 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QWINDOWSAUDIOINPUT_H
+#define QWINDOWSAUDIOINPUT_H
+
+#include <QtCore/qt_windows.h>
+#include <mmsystem.h>
+
+#include <QtCore/qfile.h>
+#include <QtCore/qdebug.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qmutex.h>
+
+#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qaudiodeviceinfo.h>
+#include <QtMultimedia/qaudiosystem.h>
+
+
+QT_BEGIN_NAMESPACE
+
+
+// For compat with 4.6
+#if !defined(QT_WIN_CALLBACK)
+# if defined(Q_CC_MINGW)
+# define QT_WIN_CALLBACK CALLBACK __attribute__ ((force_align_arg_pointer))
+# else
+# define QT_WIN_CALLBACK CALLBACK
+# endif
+#endif
+
+class QWindowsAudioInput : public QAbstractAudioInput
+{
+ Q_OBJECT
+public:
+ QWindowsAudioInput(const QByteArray &device);
+ ~QWindowsAudioInput();
+
+ qint64 read(char* data, qint64 len);
+
+ void setFormat(const QAudioFormat& fmt);
+ QAudioFormat format() const;
+ QIODevice* start();
+ void start(QIODevice* device);
+ void stop();
+ void reset();
+ void suspend();
+ void resume();
+ int bytesReady() const;
+ int periodSize() const;
+ void setBufferSize(int value);
+ int bufferSize() const;
+ void setNotifyInterval(int milliSeconds);
+ int notifyInterval() const;
+ qint64 processedUSecs() const;
+ qint64 elapsedUSecs() const;
+ QAudio::Error error() const;
+ QAudio::State state() const;
+ void setVolume(qreal volume);
+ qreal volume() const;
+
+ QIODevice* audioSource;
+ QAudioFormat settings;
+ QAudio::Error errorState;
+ QAudio::State deviceState;
+
+private:
+ qint32 buffer_size;
+ qint32 period_size;
+ qint32 header;
+ QByteArray m_device;
+ int bytesAvailable;
+ int intervalTime;
+ QTime timeStamp;
+ qint64 elapsedTimeOffset;
+ QTime timeStampOpened;
+ qint64 totalTimeValue;
+ bool pullMode;
+ bool resuming;
+ WAVEFORMATEX wfx;
+ HWAVEIN hWaveIn;
+ MMRESULT result;
+ WAVEHDR* waveBlocks;
+ volatile bool finished;
+ volatile int waveFreeBlockCount;
+ int waveBlockOffset;
+
+ QMutex mutex;
+ static void QT_WIN_CALLBACK waveInProc( HWAVEIN hWaveIn, UINT uMsg,
+ DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 );
+
+ WAVEHDR* allocateBlocks(int size, int count);
+ void freeBlocks(WAVEHDR* blockArray);
+ bool open();
+ void close();
+
+ void initMixer();
+ void closeMixer();
+ HMIXEROBJ mixerID;
+ MIXERLINECONTROLS mixerLineControls;
+
+private slots:
+ void feedback();
+ bool deviceReady();
+
+signals:
+ void processMore();
+};
+
+class InputPrivate : public QIODevice
+{
+ Q_OBJECT
+public:
+ InputPrivate(QWindowsAudioInput* audio);
+ ~InputPrivate();
+
+ qint64 readData( char* data, qint64 len);
+ qint64 writeData(const char* data, qint64 len);
+
+ void trigger();
+private:
+ QWindowsAudioInput *audioDevice;
+};
+
+QT_END_NAMESPACE
+
+#endif
diff --git a/src/plugins/windowsaudio/qwindowsaudiooutput.cpp b/src/plugins/windowsaudio/qwindowsaudiooutput.cpp
new file mode 100644
index 000000000..1c8882eeb
--- /dev/null
+++ b/src/plugins/windowsaudio/qwindowsaudiooutput.cpp
@@ -0,0 +1,762 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// INTERNAL USE ONLY: Do NOT use for any other purpose.
+//
+
+#include "qwindowsaudiooutput.h"
+#include <QtEndian>
+
+#ifndef SPEAKER_FRONT_LEFT
+ #define SPEAKER_FRONT_LEFT 0x00000001
+ #define SPEAKER_FRONT_RIGHT 0x00000002
+ #define SPEAKER_FRONT_CENTER 0x00000004
+ #define SPEAKER_LOW_FREQUENCY 0x00000008
+ #define SPEAKER_BACK_LEFT 0x00000010
+ #define SPEAKER_BACK_RIGHT 0x00000020
+ #define SPEAKER_FRONT_LEFT_OF_CENTER 0x00000040
+ #define SPEAKER_FRONT_RIGHT_OF_CENTER 0x00000080
+ #define SPEAKER_BACK_CENTER 0x00000100
+ #define SPEAKER_SIDE_LEFT 0x00000200
+ #define SPEAKER_SIDE_RIGHT 0x00000400
+ #define SPEAKER_TOP_CENTER 0x00000800
+ #define SPEAKER_TOP_FRONT_LEFT 0x00001000
+ #define SPEAKER_TOP_FRONT_CENTER 0x00002000
+ #define SPEAKER_TOP_FRONT_RIGHT 0x00004000
+ #define SPEAKER_TOP_BACK_LEFT 0x00008000
+ #define SPEAKER_TOP_BACK_CENTER 0x00010000
+ #define SPEAKER_TOP_BACK_RIGHT 0x00020000
+ #define SPEAKER_RESERVED 0x7FFC0000
+ #define SPEAKER_ALL 0x80000000
+#endif
+
+#ifndef _WAVEFORMATEXTENSIBLE_
+
+ #define _WAVEFORMATEXTENSIBLE_
+ typedef struct
+ {
+ WAVEFORMATEX Format; // Base WAVEFORMATEX data
+ union
+ {
+ WORD wValidBitsPerSample; // Valid bits in each sample container
+ WORD wSamplesPerBlock; // Samples per block of audio data; valid
+ // if wBitsPerSample=0 (but rarely used).
+ WORD wReserved; // Zero if neither case above applies.
+ } Samples;
+ DWORD dwChannelMask; // Positions of the audio channels
+ GUID SubFormat; // Format identifier GUID
+ } WAVEFORMATEXTENSIBLE, *PWAVEFORMATEXTENSIBLE, *LPPWAVEFORMATEXTENSIBLE;
+ typedef const WAVEFORMATEXTENSIBLE* LPCWAVEFORMATEXTENSIBLE;
+
+#endif
+
+#if !defined(WAVE_FORMAT_EXTENSIBLE)
+#define WAVE_FORMAT_EXTENSIBLE 0xFFFE
+#endif
+
+//#define DEBUG_AUDIO 1
+
+QT_BEGIN_NAMESPACE
+
+QWindowsAudioOutput::QWindowsAudioOutput(const QByteArray &device)
+{
+ bytesAvailable = 0;
+ buffer_size = 0;
+ period_size = 0;
+ m_device = device;
+ totalTimeValue = 0;
+ intervalTime = 1000;
+ audioBuffer = 0;
+ errorState = QAudio::NoError;
+ deviceState = QAudio::StoppedState;
+ audioSource = 0;
+ pullMode = true;
+ finished = false;
+ volumeCache = (qreal)1.;
+}
+
+QWindowsAudioOutput::~QWindowsAudioOutput()
+{
+ mutex.lock();
+ finished = true;
+ mutex.unlock();
+
+ close();
+}
+
+void CALLBACK QWindowsAudioOutput::waveOutProc( HWAVEOUT hWaveOut, UINT uMsg,
+ DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 )
+{
+ Q_UNUSED(dwParam1)
+ Q_UNUSED(dwParam2)
+ Q_UNUSED(hWaveOut)
+
+ QWindowsAudioOutput* qAudio;
+ qAudio = (QWindowsAudioOutput*)(dwInstance);
+ if(!qAudio)
+ return;
+
+ QMutexLocker(&qAudio->mutex);
+
+ switch(uMsg) {
+ case WOM_OPEN:
+ qAudio->feedback();
+ break;
+ case WOM_CLOSE:
+ return;
+ case WOM_DONE:
+ if(qAudio->finished || qAudio->buffer_size == 0 || qAudio->period_size == 0) {
+ return;
+ }
+ qAudio->waveFreeBlockCount++;
+ if(qAudio->waveFreeBlockCount >= qAudio->buffer_size/qAudio->period_size)
+ qAudio->waveFreeBlockCount = qAudio->buffer_size/qAudio->period_size;
+ qAudio->feedback();
+ break;
+ default:
+ return;
+ }
+}
+
+WAVEHDR* QWindowsAudioOutput::allocateBlocks(int size, int count)
+{
+ int i;
+ unsigned char* buffer;
+ WAVEHDR* blocks;
+ DWORD totalBufferSize = (size + sizeof(WAVEHDR))*count;
+
+ if((buffer=(unsigned char*)HeapAlloc(GetProcessHeap(),HEAP_ZERO_MEMORY,
+ totalBufferSize)) == 0) {
+ qWarning("QAudioOutput: Memory allocation error");
+ return 0;
+ }
+ blocks = (WAVEHDR*)buffer;
+ buffer += sizeof(WAVEHDR)*count;
+ for(i = 0; i < count; i++) {
+ blocks[i].dwBufferLength = size;
+ blocks[i].lpData = (LPSTR)buffer;
+ buffer += size;
+ }
+ return blocks;
+}
+
+void QWindowsAudioOutput::freeBlocks(WAVEHDR* blockArray)
+{
+ WAVEHDR* blocks = blockArray;
+
+ int count = buffer_size/period_size;
+
+ for(int i = 0; i < count; i++) {
+ waveOutUnprepareHeader(hWaveOut,blocks, sizeof(WAVEHDR));
+ blocks++;
+ }
+ HeapFree(GetProcessHeap(), 0, blockArray);
+}
+
+QAudioFormat QWindowsAudioOutput::format() const
+{
+ return settings;
+}
+
+void QWindowsAudioOutput::setFormat(const QAudioFormat& fmt)
+{
+ if (deviceState == QAudio::StoppedState)
+ settings = fmt;
+}
+
+void QWindowsAudioOutput::start(QIODevice* device)
+{
+ if(deviceState != QAudio::StoppedState)
+ close();
+
+ if(!pullMode && audioSource)
+ delete audioSource;
+
+ pullMode = true;
+ audioSource = device;
+
+ deviceState = QAudio::ActiveState;
+
+ if(!open())
+ return;
+
+ emit stateChanged(deviceState);
+}
+
+QIODevice* QWindowsAudioOutput::start()
+{
+ if(deviceState != QAudio::StoppedState)
+ close();
+
+ if(!pullMode && audioSource)
+ delete audioSource;
+
+ pullMode = false;
+ audioSource = new OutputPrivate(this);
+ audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered);
+
+ deviceState = QAudio::IdleState;
+
+ if(!open())
+ return 0;
+
+ emit stateChanged(deviceState);
+
+ return audioSource;
+}
+
+void QWindowsAudioOutput::stop()
+{
+ if(deviceState == QAudio::StoppedState)
+ return;
+ close();
+ if(!pullMode && audioSource) {
+ delete audioSource;
+ audioSource = 0;
+ }
+ emit stateChanged(deviceState);
+}
+
+bool QWindowsAudioOutput::open()
+{
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :open()";
+#endif
+
+ period_size = 0;
+
+ if (!settings.isValid()) {
+ qWarning("QAudioOutput: open error, invalid format.");
+ } else if (settings.channelCount() <= 0) {
+ qWarning("QAudioOutput: open error, invalid number of channels (%d).",
+ settings.channelCount());
+ } else if (settings.sampleSize() <= 0) {
+ qWarning("QAudioOutput: open error, invalid sample size (%d).",
+ settings.sampleSize());
+ } else if (settings.sampleRate() < 8000 || settings.sampleRate() > 96000) {
+ qWarning("QAudioOutput: open error, sample rate out of range (%d).", settings.sampleRate());
+ } else if (buffer_size == 0) {
+ // Default buffer size, 200ms, default period size is 40ms
+ buffer_size
+ = (settings.sampleRate()
+ * settings.channelCount()
+ * settings.sampleSize()
+ + 39) / 40;
+ period_size = buffer_size / 5;
+ } else {
+ period_size = buffer_size / 5;
+ }
+
+ if (period_size == 0) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ return false;
+ }
+
+ waveBlocks = allocateBlocks(period_size, buffer_size/period_size);
+
+ mutex.lock();
+ waveFreeBlockCount = buffer_size/period_size;
+ mutex.unlock();
+
+ waveCurrentBlock = 0;
+
+ if(audioBuffer == 0)
+ audioBuffer = new char[buffer_size];
+
+ timeStamp.restart();
+ elapsedTimeOffset = 0;
+
+ wfx.nSamplesPerSec = settings.sampleRate();
+ wfx.wBitsPerSample = settings.sampleSize();
+ wfx.nChannels = settings.channelCount();
+ wfx.cbSize = 0;
+
+ bool surround = false;
+
+ if (settings.channelCount() > 2)
+ surround = true;
+
+ wfx.wFormatTag = WAVE_FORMAT_PCM;
+ wfx.nBlockAlign = (wfx.wBitsPerSample >> 3) * wfx.nChannels;
+ wfx.nAvgBytesPerSec = wfx.nBlockAlign * wfx.nSamplesPerSec;
+
+ QDataStream ds(&m_device, QIODevice::ReadOnly);
+ quint32 deviceId;
+ ds >> deviceId;
+
+ if (!surround) {
+ if (waveOutOpen(&hWaveOut, UINT_PTR(deviceId), &wfx,
+ (DWORD_PTR)&waveOutProc,
+ (DWORD_PTR) this,
+ CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ qWarning("QAudioOutput: open error");
+ return false;
+ }
+ } else {
+ WAVEFORMATEXTENSIBLE wfex;
+ wfex.Format.wFormatTag = WAVE_FORMAT_EXTENSIBLE;
+ wfex.Format.nChannels = settings.channelCount();
+ wfex.Format.wBitsPerSample = settings.sampleSize();
+ wfex.Format.nSamplesPerSec = settings.sampleRate();
+ wfex.Format.nBlockAlign = wfex.Format.nChannels*wfex.Format.wBitsPerSample/8;
+ wfex.Format.nAvgBytesPerSec=wfex.Format.nSamplesPerSec*wfex.Format.nBlockAlign;
+ wfex.Samples.wValidBitsPerSample=wfex.Format.wBitsPerSample;
+ static const GUID _KSDATAFORMAT_SUBTYPE_PCM = {
+ 0x00000001, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}};
+ wfex.SubFormat=_KSDATAFORMAT_SUBTYPE_PCM;
+ wfex.Format.cbSize=22;
+
+ wfex.dwChannelMask = SPEAKER_FRONT_LEFT | SPEAKER_FRONT_RIGHT;
+ if (settings.channelCount() >= 4)
+ wfex.dwChannelMask |= SPEAKER_BACK_LEFT | SPEAKER_BACK_RIGHT;
+ if (settings.channelCount() >= 6)
+ wfex.dwChannelMask |= SPEAKER_FRONT_CENTER | SPEAKER_LOW_FREQUENCY;
+ if (settings.channelCount() == 8)
+ wfex.dwChannelMask |= SPEAKER_SIDE_LEFT | SPEAKER_SIDE_RIGHT;
+
+ if (waveOutOpen(&hWaveOut, UINT_PTR(deviceId), &wfex.Format,
+ (DWORD_PTR)&waveOutProc,
+ (DWORD_PTR) this,
+ CALLBACK_FUNCTION) != MMSYSERR_NOERROR) {
+ errorState = QAudio::OpenError;
+ deviceState = QAudio::StoppedState;
+ emit stateChanged(deviceState);
+ qWarning("QAudioOutput: open error");
+ return false;
+ }
+ }
+
+ totalTimeValue = 0;
+ timeStampOpened.restart();
+ elapsedTimeOffset = 0;
+
+ setVolume(volumeCache);
+
+ errorState = QAudio::NoError;
+ if(pullMode) {
+ deviceState = QAudio::ActiveState;
+ QTimer::singleShot(10, this, SLOT(feedback()));
+ } else
+ deviceState = QAudio::IdleState;
+
+ return true;
+}
+
+void QWindowsAudioOutput::close()
+{
+ if(deviceState == QAudio::StoppedState)
+ return;
+
+ deviceState = QAudio::StoppedState;
+ errorState = QAudio::NoError;
+ int delay = (buffer_size-bytesFree())*1000/(settings.sampleRate()
+ *settings.channelCount()*(settings.sampleSize()/8));
+ waveOutReset(hWaveOut);
+ Sleep(delay+10);
+
+ freeBlocks(waveBlocks);
+ waveOutClose(hWaveOut);
+ delete [] audioBuffer;
+ audioBuffer = 0;
+ buffer_size = 0;
+}
+
+int QWindowsAudioOutput::bytesFree() const
+{
+ int buf;
+ buf = waveFreeBlockCount*period_size;
+
+ return buf;
+}
+
+int QWindowsAudioOutput::periodSize() const
+{
+ return period_size;
+}
+
+void QWindowsAudioOutput::setBufferSize(int value)
+{
+ if(deviceState == QAudio::StoppedState)
+ buffer_size = value;
+}
+
+int QWindowsAudioOutput::bufferSize() const
+{
+ return buffer_size;
+}
+
+void QWindowsAudioOutput::setNotifyInterval(int ms)
+{
+ intervalTime = qMax(0, ms);
+}
+
+int QWindowsAudioOutput::notifyInterval() const
+{
+ return intervalTime;
+}
+
+qint64 QWindowsAudioOutput::processedUSecs() const
+{
+ if (deviceState == QAudio::StoppedState)
+ return 0;
+ qint64 result = qint64(1000000) * totalTimeValue /
+ (settings.channelCount()*(settings.sampleSize()/8)) /
+ settings.sampleRate();
+
+ return result;
+}
+
+qint64 QWindowsAudioOutput::write( const char *data, qint64 len )
+{
+ // Write out some audio data
+ if (deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
+ return 0;
+
+ char* p = (char*)data;
+ int l = (int)len;
+
+ QByteArray reverse;
+ if (settings.byteOrder() == QAudioFormat::BigEndian) {
+
+ switch (settings.sampleSize()) {
+ case 8:
+ // No need to convert
+ break;
+
+ case 16:
+ reverse.resize(l);
+ for (qint64 i = 0; i < (l >> 1); i++)
+ *((qint16*)reverse.data() + i) = qFromBigEndian(*((qint16*)data + i));
+ p = reverse.data();
+ break;
+
+ case 32:
+ reverse.resize(l);
+ for (qint64 i = 0; i < (l >> 2); i++)
+ *((qint32*)reverse.data() + i) = qFromBigEndian(*((qint32*)data + i));
+ p = reverse.data();
+ break;
+ }
+ }
+
+ WAVEHDR* current;
+ int remain;
+ current = &waveBlocks[waveCurrentBlock];
+ while(l > 0) {
+ mutex.lock();
+ if(waveFreeBlockCount==0) {
+ mutex.unlock();
+ break;
+ }
+ mutex.unlock();
+
+ if(current->dwFlags & WHDR_PREPARED)
+ waveOutUnprepareHeader(hWaveOut, current, sizeof(WAVEHDR));
+
+ if(l < period_size)
+ remain = l;
+ else
+ remain = period_size;
+ memcpy(current->lpData, p, remain);
+
+ l -= remain;
+ p += remain;
+ current->dwBufferLength = remain;
+ waveOutPrepareHeader(hWaveOut, current, sizeof(WAVEHDR));
+ waveOutWrite(hWaveOut, current, sizeof(WAVEHDR));
+
+ mutex.lock();
+ waveFreeBlockCount--;
+#ifdef DEBUG_AUDIO
+ qDebug("write out l=%d, waveFreeBlockCount=%d",
+ current->dwBufferLength,waveFreeBlockCount);
+#endif
+ mutex.unlock();
+
+ totalTimeValue += current->dwBufferLength;
+ waveCurrentBlock++;
+ waveCurrentBlock %= buffer_size/period_size;
+ current = &waveBlocks[waveCurrentBlock];
+ current->dwUser = 0;
+ errorState = QAudio::NoError;
+ if (deviceState != QAudio::ActiveState) {
+ deviceState = QAudio::ActiveState;
+ emit stateChanged(deviceState);
+ }
+ }
+ return (len-l);
+}
+
+void QWindowsAudioOutput::resume()
+{
+ if(deviceState == QAudio::SuspendedState) {
+ deviceState = QAudio::ActiveState;
+ errorState = QAudio::NoError;
+ waveOutRestart(hWaveOut);
+ QTimer::singleShot(10, this, SLOT(feedback()));
+ emit stateChanged(deviceState);
+ }
+}
+
+void QWindowsAudioOutput::suspend()
+{
+ if(deviceState == QAudio::ActiveState || deviceState == QAudio::IdleState) {
+ int delay = (buffer_size-bytesFree())*1000/(settings.sampleRate()
+ *settings.channelCount()*(settings.sampleSize()/8));
+ waveOutPause(hWaveOut);
+ Sleep(delay+10);
+ deviceState = QAudio::SuspendedState;
+ errorState = QAudio::NoError;
+ emit stateChanged(deviceState);
+ }
+}
+
+void QWindowsAudioOutput::feedback()
+{
+#ifdef DEBUG_AUDIO
+ QTime now(QTime::currentTime());
+ qDebug()<<now.second()<<"s "<<now.msec()<<"ms :feedback()";
+#endif
+ bytesAvailable = bytesFree();
+
+ if(!(deviceState==QAudio::StoppedState||deviceState==QAudio::SuspendedState)) {
+ if(bytesAvailable >= period_size)
+ QMetaObject::invokeMethod(this, "deviceReady", Qt::QueuedConnection);
+ }
+}
+
+bool QWindowsAudioOutput::deviceReady()
+{
+ if(deviceState == QAudio::StoppedState || deviceState == QAudio::SuspendedState)
+ return false;
+
+ if(pullMode) {
+ int chunks = bytesAvailable/period_size;
+#ifdef DEBUG_AUDIO
+ qDebug()<<"deviceReady() avail="<<bytesAvailable<<" bytes, period size="<<period_size<<" bytes";
+ qDebug()<<"deviceReady() no. of chunks that can fit ="<<chunks<<", chunks in bytes ="<<chunks*period_size;
+#endif
+ bool startup = false;
+ if(totalTimeValue == 0)
+ startup = true;
+
+ bool full=false;
+
+ mutex.lock();
+ if (waveFreeBlockCount==0) full = true;
+ mutex.unlock();
+
+ if (full) {
+#ifdef DEBUG_AUDIO
+ qDebug() << "Skipping data as unable to write";
+#endif
+ if ((timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
+ emit notify();
+ elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
+ timeStamp.restart();
+ }
+ return true;
+ }
+
+ if(startup)
+ waveOutPause(hWaveOut);
+ int input = period_size*chunks;
+ int l = audioSource->read(audioBuffer,input);
+ if(l > 0) {
+ int out= write(audioBuffer,l);
+ if(out > 0) {
+ if (deviceState != QAudio::ActiveState) {
+ deviceState = QAudio::ActiveState;
+ emit stateChanged(deviceState);
+ }
+ }
+ if ( out < l) {
+ // Didn't write all data
+ audioSource->seek(audioSource->pos()-(l-out));
+ }
+ if (startup)
+ waveOutRestart(hWaveOut);
+ } else if(l == 0) {
+ bytesAvailable = bytesFree();
+
+ int check = 0;
+
+ mutex.lock();
+ check = waveFreeBlockCount;
+ mutex.unlock();
+
+ if(check == buffer_size/period_size) {
+ if (deviceState != QAudio::IdleState) {
+ errorState = QAudio::UnderrunError;
+ deviceState = QAudio::IdleState;
+ emit stateChanged(deviceState);
+ }
+ }
+
+ } else if(l < 0) {
+ bytesAvailable = bytesFree();
+ if (errorState != QAudio::IOError)
+ errorState = QAudio::IOError;
+ }
+ } else {
+ int buffered;
+
+ mutex.lock();
+ buffered = waveFreeBlockCount;
+ mutex.unlock();
+
+ if (buffered >= buffer_size/period_size && deviceState == QAudio::ActiveState) {
+ if (deviceState != QAudio::IdleState) {
+ errorState = QAudio::UnderrunError;
+ deviceState = QAudio::IdleState;
+ emit stateChanged(deviceState);
+ }
+ }
+ }
+ if(deviceState != QAudio::ActiveState && deviceState != QAudio::IdleState)
+ return true;
+
+ if(intervalTime && (timeStamp.elapsed() + elapsedTimeOffset) > intervalTime) {
+ emit notify();
+ elapsedTimeOffset = timeStamp.elapsed() + elapsedTimeOffset - intervalTime;
+ timeStamp.restart();
+ }
+
+ return true;
+}
+
+qint64 QWindowsAudioOutput::elapsedUSecs() const
+{
+ if (deviceState == QAudio::StoppedState)
+ return 0;
+
+ return timeStampOpened.elapsed()*1000;
+}
+
+QAudio::Error QWindowsAudioOutput::error() const
+{
+ return errorState;
+}
+
+QAudio::State QWindowsAudioOutput::state() const
+{
+ return deviceState;
+}
+
+void QWindowsAudioOutput::setVolume(qreal v)
+{
+ const qreal normalizedVolume = qBound(qreal(0.0), v, qreal(1.0));
+ if (deviceState != QAudio::ActiveState) {
+ volumeCache = normalizedVolume;
+ return;
+ }
+ const quint16 scaled = normalizedVolume * 0xFFFF;
+ DWORD vol = MAKELONG(scaled, scaled);
+ MMRESULT res = waveOutSetVolume(hWaveOut, vol);
+ if (res == MMSYSERR_NOERROR)
+ volumeCache = normalizedVolume;
+}
+
+qreal QWindowsAudioOutput::volume() const
+{
+ return volumeCache;
+}
+
+void QWindowsAudioOutput::reset()
+{
+ close();
+}
+
+OutputPrivate::OutputPrivate(QWindowsAudioOutput* audio)
+{
+ audioDevice = qobject_cast<QWindowsAudioOutput*>(audio);
+}
+
+OutputPrivate::~OutputPrivate() {}
+
+qint64 OutputPrivate::readData( char* data, qint64 len)
+{
+ Q_UNUSED(data)
+ Q_UNUSED(len)
+
+ return 0;
+}
+
+qint64 OutputPrivate::writeData(const char* data, qint64 len)
+{
+ int retry = 0;
+ qint64 written = 0;
+
+ if((audioDevice->deviceState == QAudio::ActiveState)
+ ||(audioDevice->deviceState == QAudio::IdleState)) {
+ qint64 l = len;
+ while(written < l) {
+ int chunk = audioDevice->write(data+written,(l-written));
+ if(chunk <= 0)
+ retry++;
+ else
+ written+=chunk;
+
+ if(retry > 10)
+ return written;
+ }
+ audioDevice->deviceState = QAudio::ActiveState;
+ }
+ return written;
+}
+
+QT_END_NAMESPACE
+
+#include "moc_qwindowsaudiooutput.cpp"
diff --git a/src/plugins/windowsaudio/qwindowsaudiooutput.h b/src/plugins/windowsaudio/qwindowsaudiooutput.h
new file mode 100644
index 000000000..77f23f701
--- /dev/null
+++ b/src/plugins/windowsaudio/qwindowsaudiooutput.h
@@ -0,0 +1,171 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists for the convenience
+// of other Qt classes. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#ifndef QWINDOWSAUDIOOUTPUT_H
+#define QWINDOWSAUDIOOUTPUT_H
+
+#include <QtCore/qt_windows.h>
+#include <mmsystem.h>
+
+#include <QtCore/qdebug.h>
+#include <QtCore/qtimer.h>
+#include <QtCore/qstring.h>
+#include <QtCore/qstringlist.h>
+#include <QtCore/qdatetime.h>
+#include <QtCore/qmutex.h>
+
+#include <QtMultimedia/qaudio.h>
+#include <QtMultimedia/qaudiodeviceinfo.h>
+#include <QtMultimedia/qaudiosystem.h>
+
+// For compat with 4.6
+#if !defined(QT_WIN_CALLBACK)
+# if defined(Q_CC_MINGW)
+# define QT_WIN_CALLBACK CALLBACK __attribute__ ((force_align_arg_pointer))
+# else
+# define QT_WIN_CALLBACK CALLBACK
+# endif
+#endif
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsAudioOutput : public QAbstractAudioOutput
+{
+ Q_OBJECT
+public:
+ QWindowsAudioOutput(const QByteArray &device);
+ ~QWindowsAudioOutput();
+
+ qint64 write( const char *data, qint64 len );
+
+ void setFormat(const QAudioFormat& fmt);
+ QAudioFormat format() const;
+ QIODevice* start();
+ void start(QIODevice* device);
+ void stop();
+ void reset();
+ void suspend();
+ void resume();
+ int bytesFree() const;
+ int periodSize() const;
+ void setBufferSize(int value);
+ int bufferSize() const;
+ void setNotifyInterval(int milliSeconds);
+ int notifyInterval() const;
+ qint64 processedUSecs() const;
+ qint64 elapsedUSecs() const;
+ QAudio::Error error() const;
+ QAudio::State state() const;
+ void setVolume(qreal);
+ qreal volume() const;
+
+ QIODevice* audioSource;
+ QAudioFormat settings;
+ QAudio::Error errorState;
+ QAudio::State deviceState;
+
+private slots:
+ void feedback();
+ bool deviceReady();
+
+private:
+ QByteArray m_device;
+ bool resuming;
+ int bytesAvailable;
+ QTime timeStamp;
+ qint64 elapsedTimeOffset;
+ QTime timeStampOpened;
+ qint32 buffer_size;
+ qint32 period_size;
+ qint64 totalTimeValue;
+ bool pullMode;
+ int intervalTime;
+ qreal volumeCache;
+ static void QT_WIN_CALLBACK waveOutProc( HWAVEOUT hWaveOut, UINT uMsg,
+ DWORD_PTR dwInstance, DWORD_PTR dwParam1, DWORD_PTR dwParam2 );
+
+ QMutex mutex;
+
+ WAVEHDR* allocateBlocks(int size, int count);
+ void freeBlocks(WAVEHDR* blockArray);
+ bool open();
+ void close();
+
+ WAVEFORMATEX wfx;
+ HWAVEOUT hWaveOut;
+ MMRESULT result;
+ WAVEHDR header;
+ WAVEHDR* waveBlocks;
+ volatile bool finished;
+ volatile int waveFreeBlockCount;
+ int waveCurrentBlock;
+ char* audioBuffer;
+};
+
+class OutputPrivate : public QIODevice
+{
+ Q_OBJECT
+public:
+ OutputPrivate(QWindowsAudioOutput* audio);
+ ~OutputPrivate();
+
+ qint64 readData( char* data, qint64 len);
+ qint64 writeData(const char* data, qint64 len);
+
+private:
+ QWindowsAudioOutput *audioDevice;
+};
+
+QT_END_NAMESPACE
+
+
+#endif // QWINDOWSAUDIOOUTPUT_H
diff --git a/src/plugins/windowsaudio/qwindowsaudioplugin.cpp b/src/plugins/windowsaudio/qwindowsaudioplugin.cpp
new file mode 100644
index 000000000..79aabdbf9
--- /dev/null
+++ b/src/plugins/windowsaudio/qwindowsaudioplugin.cpp
@@ -0,0 +1,74 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#include "qwindowsaudioplugin.h"
+#include "qwindowsaudiodeviceinfo.h"
+#include "qwindowsaudioinput.h"
+#include "qwindowsaudiooutput.h"
+
+QT_BEGIN_NAMESPACE
+
+QWindowsAudioPlugin::QWindowsAudioPlugin(QObject *parent)
+ : QAudioSystemPlugin(parent)
+{
+}
+
+QList<QByteArray> QWindowsAudioPlugin::availableDevices(QAudio::Mode mode) const
+{
+ return QWindowsAudioDeviceInfo::availableDevices(mode);
+}
+
+QAbstractAudioInput *QWindowsAudioPlugin::createInput(const QByteArray &device)
+{
+ return new QWindowsAudioInput(device);
+}
+
+QAbstractAudioOutput *QWindowsAudioPlugin::createOutput(const QByteArray &device)
+{
+ return new QWindowsAudioOutput(device);
+}
+
+QAbstractAudioDeviceInfo *QWindowsAudioPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode)
+{
+ return new QWindowsAudioDeviceInfo(device, mode);
+}
+
+QT_END_NAMESPACE
diff --git a/src/plugins/windowsaudio/qwindowsaudioplugin.h b/src/plugins/windowsaudio/qwindowsaudioplugin.h
new file mode 100644
index 000000000..3305f0553
--- /dev/null
+++ b/src/plugins/windowsaudio/qwindowsaudioplugin.h
@@ -0,0 +1,67 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** This file is part of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia 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.
+**
+**
+** $QT_END_LICENSE$
+**
+****************************************************************************/
+
+#ifndef QWINDOWSAUDIOPLUGIN_H
+#define QWINDOWSAUDIOPLUGIN_H
+
+#include <QtMultimedia/qaudiosystemplugin.h>
+
+QT_BEGIN_NAMESPACE
+
+class QWindowsAudioPlugin : public QAudioSystemPlugin
+{
+ Q_OBJECT
+
+ Q_PLUGIN_METADATA(IID "org.qt-project.qt.audiosystemfactory/5.0" FILE "windowsaudio.json")
+
+public:
+ QWindowsAudioPlugin(QObject *parent = 0);
+ ~QWindowsAudioPlugin() {}
+
+ QList<QByteArray> availableDevices(QAudio::Mode mode) const Q_DECL_OVERRIDE;
+ QAbstractAudioInput *createInput(const QByteArray &device) Q_DECL_OVERRIDE;
+ QAbstractAudioOutput *createOutput(const QByteArray &device) Q_DECL_OVERRIDE;
+ QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode) Q_DECL_OVERRIDE;
+};
+
+QT_END_NAMESPACE
+
+#endif // QWINDOWSAUDIOPLUGIN_H
diff --git a/src/plugins/windowsaudio/windowsaudio.json b/src/plugins/windowsaudio/windowsaudio.json
new file mode 100644
index 000000000..a31d52107
--- /dev/null
+++ b/src/plugins/windowsaudio/windowsaudio.json
@@ -0,0 +1,3 @@
+{
+ "Keys": ["default"]
+}
diff --git a/src/plugins/windowsaudio/windowsaudio.pro b/src/plugins/windowsaudio/windowsaudio.pro
new file mode 100644
index 000000000..a1a327953
--- /dev/null
+++ b/src/plugins/windowsaudio/windowsaudio.pro
@@ -0,0 +1,23 @@
+TARGET = qtaudio_windows
+QT += multimedia-private
+
+PLUGIN_TYPE = audio
+PLUGIN_CLASS_NAME = QWindowsAudioPlugin
+load(qt_plugin)
+
+LIBS += -lwinmm -lstrmiids -lole32 -loleaut32
+
+HEADERS += \
+ qwindowsaudioplugin.h \
+ qwindowsaudiodeviceinfo.h \
+ qwindowsaudioinput.h \
+ qwindowsaudiooutput.h
+
+SOURCES += \
+ qwindowsaudioplugin.cpp \
+ qwindowsaudiodeviceinfo.cpp \
+ qwindowsaudioinput.cpp \
+ qwindowsaudiooutput.cpp
+
+OTHER_FILES += \
+ windowsaudio.json
diff --git a/src/plugins/wmf/decoder/mfdecoderservice.cpp b/src/plugins/wmf/decoder/mfdecoderservice.cpp
index 9a06eed81..7e9175d7a 100644
--- a/src/plugins/wmf/decoder/mfdecoderservice.cpp
+++ b/src/plugins/wmf/decoder/mfdecoderservice.cpp
@@ -64,4 +64,4 @@ void MFAudioDecoderService::releaseControl(QMediaControl *control)
if (control && control->inherits("MFAudioDecoderControl")) {
delete control;
}
-} \ No newline at end of file
+}
diff --git a/src/plugins/wmf/decoder/mfdecodersourcereader.cpp b/src/plugins/wmf/decoder/mfdecodersourcereader.cpp
index 0aa2cafb8..c26e3518f 100644
--- a/src/plugins/wmf/decoder/mfdecodersourcereader.cpp
+++ b/src/plugins/wmf/decoder/mfdecodersourcereader.cpp
@@ -196,4 +196,4 @@ STDMETHODIMP MFDecoderSourceReader::OnFlush(DWORD)
STDMETHODIMP MFDecoderSourceReader::OnEvent(DWORD, IMFMediaEvent*)
{
return S_OK;
-} \ No newline at end of file
+}
diff --git a/src/plugins/wmf/player/mfplayercontrol.cpp b/src/plugins/wmf/player/mfplayercontrol.cpp
index 68ffa7b6f..a50c213c4 100644
--- a/src/plugins/wmf/player/mfplayercontrol.cpp
+++ b/src/plugins/wmf/player/mfplayercontrol.cpp
@@ -58,7 +58,7 @@ MFPlayerControl::MFPlayerControl(MFPlayerSession *session)
QObject::connect(m_session, SIGNAL(audioAvailable()), this, SLOT(handleAudioAvailable()));
QObject::connect(m_session, SIGNAL(durationUpdate(qint64)), this, SLOT(handleDurationUpdate(qint64)));
QObject::connect(m_session, SIGNAL(seekableUpdate(bool)), this, SLOT(handleSeekableUpdate(bool)));
- QObject::connect(m_session, SIGNAL(error(QMediaPlayer::Error, QString, bool)), this, SLOT(handleError(QMediaPlayer::Error, QString, bool)));
+ QObject::connect(m_session, SIGNAL(error(QMediaPlayer::Error,QString,bool)), this, SLOT(handleError(QMediaPlayer::Error,QString,bool)));
QObject::connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64)));
QObject::connect(m_session, SIGNAL(volumeChanged(int)), this, SIGNAL(volumeChanged(int)));
QObject::connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool)));
diff --git a/src/plugins/wmf/player/mfplayersession.cpp b/src/plugins/wmf/player/mfplayersession.cpp
index 04b46ec0d..183f0231d 100644
--- a/src/plugins/wmf/player/mfplayersession.cpp
+++ b/src/plugins/wmf/player/mfplayersession.cpp
@@ -93,7 +93,7 @@ MFPlayerSession::MFPlayerSession(MFPlayerService *playerService)
, m_audioSampleGrabberNode(0)
, m_videoProbeMFT(0)
{
- QObject::connect(this, SIGNAL(sessionEvent(IMFMediaEvent *)), this, SLOT(handleSessionEvent(IMFMediaEvent *)));
+ QObject::connect(this, SIGNAL(sessionEvent(IMFMediaEvent*)), this, SLOT(handleSessionEvent(IMFMediaEvent*)));
m_pendingState = NoPending;
ZeroMemory(&m_state, sizeof(m_state));