diff options
Diffstat (limited to 'src/plugins/pulseaudio/qaudiooutput_pulse.cpp')
-rw-r--r-- | src/plugins/pulseaudio/qaudiooutput_pulse.cpp | 194 |
1 files changed, 109 insertions, 85 deletions
diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp index 2b482ba40..64a080663 100644 --- a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp +++ b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp @@ -1,6 +1,6 @@ /**************************************************************************** ** -** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies). +** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). ** Contact: http://www.qt-project.org/legal ** ** This file is part of the Qt Toolkit. @@ -170,11 +170,29 @@ QPulseAudioOutput::~QPulseAudioOutput() QCoreApplication::processEvents(); } +void QPulseAudioOutput::setError(QAudio::Error error) +{ + if (m_errorState == error) + return; + + m_errorState = error; + emit errorChanged(error); +} + QAudio::Error QPulseAudioOutput::error() const { return m_errorState; } +void QPulseAudioOutput::setState(QAudio::State state) +{ + if (m_deviceState == state) + return; + + m_deviceState = state; + emit stateChanged(state); +} + QAudio::State QPulseAudioOutput::state() const { return m_deviceState; @@ -183,19 +201,15 @@ QAudio::State QPulseAudioOutput::state() const void QPulseAudioOutput::streamUnderflowCallback() { if (m_deviceState != QAudio::IdleState && !m_resuming) { - m_errorState = QAudio::UnderrunError; - emit errorChanged(m_errorState); - m_deviceState = QAudio::IdleState; - emit stateChanged(m_deviceState); + setError(QAudio::UnderrunError); + setState(QAudio::IdleState); } } void QPulseAudioOutput::start(QIODevice *device) { - if (m_deviceState != QAudio::StoppedState) - m_deviceState = QAudio::StoppedState; - - m_errorState = QAudio::NoError; + setState(QAudio::StoppedState); + setError(QAudio::NoError); // Handle change of mode if (m_audioSource && !m_pullMode) { @@ -205,22 +219,19 @@ void QPulseAudioOutput::start(QIODevice *device) close(); + if (!open()) + return; + m_pullMode = true; m_audioSource = device; - m_deviceState = QAudio::ActiveState; - - open(); - - emit stateChanged(m_deviceState); + setState(QAudio::ActiveState); } QIODevice *QPulseAudioOutput::start() { - if (m_deviceState != QAudio::StoppedState) - m_deviceState = QAudio::StoppedState; - - m_errorState = QAudio::NoError; + setState(QAudio::StoppedState); + setError(QAudio::NoError); // Handle change of mode if (m_audioSource && !m_pullMode) { @@ -230,15 +241,14 @@ QIODevice *QPulseAudioOutput::start() close(); + if (!open()) + return Q_NULLPTR; + m_audioSource = new OutputPrivate(this); m_audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); m_pullMode = false; - m_deviceState = QAudio::IdleState; - - open(); - - emit stateChanged(m_deviceState); + setState(QAudio::IdleState); return m_audioSource; } @@ -246,33 +256,38 @@ QIODevice *QPulseAudioOutput::start() bool QPulseAudioOutput::open() { if (m_opened) + return true; + + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + + if (!pulseEngine->context() || pa_context_get_state(pulseEngine->context()) != PA_CONTEXT_READY) { + setError(QAudio::FatalError); + setState(QAudio::StoppedState); return false; + } pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format); if (!pa_sample_spec_valid(&spec)) { - m_errorState = QAudio::OpenError; - m_deviceState = QAudio::StoppedState; + setError(QAudio::OpenError); + setState(QAudio::StoppedState); return false; } m_spec = spec; m_totalTimeValue = 0; - m_elapsedTimeOffset = 0; - m_timeStamp.restart(); if (m_streamName.isNull()) m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8(); #ifdef DEBUG_PULSE - qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format); - qDebug() << "Rate: " << spec.rate; - qDebug() << "Channels: " << spec.channels; - qDebug() << "Frame size: " << pa_frame_size(&spec); + qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format); + qDebug() << "Rate: " << spec.rate; + qDebug() << "Channels: " << spec.channels; + qDebug() << "Frame size: " << pa_frame_size(&spec); #endif - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_lock(pulseEngine->mainloop()); + pulseEngine->lock(); qint64 bytesPerSecond = m_format.sampleRate() * m_format.channelCount() * m_format.sampleSize() / 8; @@ -280,7 +295,7 @@ bool QPulseAudioOutput::open() if (!m_category.isNull()) pa_proplist_sets(propList, PA_PROP_MEDIA_ROLE, m_category.toLatin1().constData()); - m_stream = pa_stream_new_with_proplist(pulseEngine->context(), m_streamName.constData(), &spec, 0, propList); + m_stream = pa_stream_new_with_proplist(pulseEngine->context(), m_streamName.constData(), &m_spec, 0, propList); pa_proplist_free(propList); pa_stream_set_state_callback(m_stream, outputStreamStateCallback, this); @@ -312,15 +327,20 @@ bool QPulseAudioOutput::open() if (pa_stream_connect_playback(m_stream, m_device.data(), (m_bufferSize > 0) ? &requestedBuffer : NULL, (pa_stream_flags_t)0, &m_chVolume, NULL) < 0) { qWarning() << "pa_stream_connect_playback() failed!"; + pa_stream_unref(m_stream); + m_stream = 0; + pulseEngine->unlock(); + setError(QAudio::OpenError); + setState(QAudio::StoppedState); return false; } - while (pa_stream_get_state(m_stream) != PA_STREAM_READY) { + while (pa_stream_get_state(m_stream) != PA_STREAM_READY) pa_threaded_mainloop_wait(pulseEngine->mainloop()); - } + const pa_buffer_attr *buffer = pa_stream_get_buffer_attr(m_stream); m_periodTime = (m_category == LOW_LATENCY_CATEGORY_NAME) ? LowLatencyPeriodTimeMs : PeriodTimeMs; - m_periodSize = pa_usec_to_bytes(m_periodTime*1000, &spec); + m_periodSize = pa_usec_to_bytes(m_periodTime*1000, &m_spec); m_bufferSize = buffer->tlength; m_maxBufferSize = buffer->maxlength; m_audioBuffer = new char[m_maxBufferSize]; @@ -333,9 +353,12 @@ bool QPulseAudioOutput::open() qDebug() << "\tFragment size: " << buffer->fragsize; #endif - pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + pulseEngine->unlock(); + + connect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioOutput::onPulseContextFailed); m_opened = true; + m_tickTimer->start(m_periodTime); m_elapsedTimeOffset = 0; @@ -347,28 +370,35 @@ bool QPulseAudioOutput::open() void QPulseAudioOutput::close() { + if (!m_opened) + return; + m_tickTimer->stop(); + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + if (m_stream) { - QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_lock(pulseEngine->mainloop()); + pulseEngine->lock(); - pa_stream_set_write_callback(m_stream, NULL, NULL); + pa_stream_set_state_callback(m_stream, 0, 0); + pa_stream_set_write_callback(m_stream, 0, 0); + pa_stream_set_underflow_callback(m_stream, 0, 0); + pa_stream_set_overflow_callback(m_stream, 0, 0); + pa_stream_set_latency_update_callback(m_stream, 0, 0); pa_operation *o = pa_stream_drain(m_stream, outputStreamDrainComplete, NULL); - if (!o) { - qWarning() << QString("pa_stream_drain(): %1").arg(pa_strerror(pa_context_errno(pa_stream_get_context(m_stream)))); - } else { + if (o) pa_operation_unref(o); - } pa_stream_disconnect(m_stream); pa_stream_unref(m_stream); m_stream = NULL; - pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + pulseEngine->unlock(); } + disconnect(pulseEngine, &QPulseAudioEngine::contextFailed, this, &QPulseAudioOutput::onPulseContextFailed); + if (!m_pullMode && m_audioSource) { delete m_audioSource; m_audioSource = 0; @@ -430,17 +460,14 @@ qint64 QPulseAudioOutput::write(const char *data, qint64 len) { QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_lock(pulseEngine->mainloop()); + pulseEngine->lock(); len = qMin(len, static_cast<qint64>(pa_stream_writable_size(m_stream))); pa_stream_write(m_stream, data, len, 0, 0, PA_SEEK_RELATIVE); - pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + pulseEngine->unlock(); m_totalTimeValue += len; - m_errorState = QAudio::NoError; - if (m_deviceState != QAudio::ActiveState) { - m_deviceState = QAudio::ActiveState; - emit stateChanged(m_deviceState); - } + setError(QAudio::NoError); + setState(QAudio::ActiveState); return len; } @@ -450,10 +477,10 @@ void QPulseAudioOutput::stop() if (m_deviceState == QAudio::StoppedState) return; - m_errorState = QAudio::NoError; - m_deviceState = QAudio::StoppedState; close(); - emit stateChanged(m_deviceState); + + setError(QAudio::NoError); + setState(QAudio::StoppedState); } int QPulseAudioOutput::bytesFree() const @@ -462,9 +489,9 @@ int QPulseAudioOutput::bytesFree() const return 0; QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_lock(pulseEngine->mainloop()); + pulseEngine->lock(); int writableSize = pa_stream_writable_size(m_stream); - pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + pulseEngine->unlock(); return writableSize; } @@ -509,30 +536,22 @@ void QPulseAudioOutput::resume() QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_lock(pulseEngine->mainloop()); + pulseEngine->lock(); pa_operation *operation = pa_stream_cork(m_stream, 0, outputStreamSuccessCallback, NULL); - - while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(pulseEngine->mainloop()); - + pulseEngine->wait(operation); pa_operation_unref(operation); operation = pa_stream_trigger(m_stream, outputStreamSuccessCallback, NULL); - - while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(pulseEngine->mainloop()); - + pulseEngine->wait(operation); pa_operation_unref(operation); - pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + pulseEngine->unlock(); - m_deviceState = QAudio::ActiveState; - - m_errorState = QAudio::NoError; m_tickTimer->start(m_periodTime); - emit stateChanged(m_deviceState); + setState(QAudio::ActiveState); + setError(QAudio::NoError); } } @@ -549,24 +568,21 @@ QAudioFormat QPulseAudioOutput::format() const void QPulseAudioOutput::suspend() { if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) { + setError(QAudio::NoError); + setState(QAudio::SuspendedState); + m_tickTimer->stop(); - m_deviceState = QAudio::SuspendedState; - m_errorState = QAudio::NoError; - emit stateChanged(m_deviceState); QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); pa_operation *operation; - pa_threaded_mainloop_lock(pulseEngine->mainloop()); + pulseEngine->lock(); operation = pa_stream_cork(m_stream, 1, outputStreamSuccessCallback, NULL); - - while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) - pa_threaded_mainloop_wait(pulseEngine->mainloop()); - + pulseEngine->wait(operation); pa_operation_unref(operation); - pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + pulseEngine->unlock(); } } @@ -601,8 +617,8 @@ qint64 OutputPrivate::writeData(const char *data, qint64 len) int retry = 0; qint64 written = 0; - if ((m_audioDevice->m_deviceState == QAudio::ActiveState) - ||(m_audioDevice->m_deviceState == QAudio::IdleState)) { + if ((m_audioDevice->m_deviceState == QAudio::ActiveState + || m_audioDevice->m_deviceState == QAudio::IdleState)) { while(written < len) { int chunk = m_audioDevice->write(data+written, (len-written)); if (chunk <= 0) @@ -623,7 +639,7 @@ void QPulseAudioOutput::setVolume(qreal vol) m_volume = vol; if (m_opened) { QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); - pa_threaded_mainloop_lock(pulseEngine->mainloop()); + pulseEngine->lock(); pa_volume_t paVolume; if (qFuzzyCompare(vol, 0.0)) { pa_cvolume_mute(&m_chVolume, m_spec.channels); @@ -641,7 +657,7 @@ void QPulseAudioOutput::setVolume(qreal vol) qWarning()<<"QAudioOutput: Failed to set volume"; else pa_operation_unref(op); - pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + pulseEngine->unlock(); } } } @@ -664,6 +680,14 @@ QString QPulseAudioOutput::category() const return m_category; } +void QPulseAudioOutput::onPulseContextFailed() +{ + close(); + + setError(QAudio::FatalError); + setState(QAudio::StoppedState); +} + QT_END_NAMESPACE #include "moc_qaudiooutput_pulse.cpp" |