diff options
author | 2017-04-02 12:49:28 -0400 | |
---|---|---|
committer | 2017-04-10 22:26:07 +0000 | |
commit | 5d01b91533af2ba8ffd7afce48b8296e14d60e55 (patch) | |
tree | ef427cbebb92d873d9a2903445c32320ab20c8eb /rtlfmradiotunercontrol.cpp | |
parent | a30670cd12e03dc874d2f22c48f497817c78ecdc (diff) |
Switch to using Qt's QAudioOutput for outputchinook_3.0.4chinook_3.0.3chinook/3.0.4chinook/3.0.33.0.43.0.3chinook
The underlying issue in the hang reported in SPEC-455 is that due to
the synchronous nature of the pa_simple_* PulseAudio API, the
pa_simple_write call used blocks when a stream is corked . That
prevents the tuner plugin's output thread from exiting when playback is
stopped, resulting in the observed hang. After examining the available
options, it seemed like switching to Qt's QAudioOutput class made sense
since it allows using the asynchronous PulseAudio API easily, and like
the QRadio class the tuner plugin implements, it is part of QtMultimedia
itself.
Note that the radio_output.* files have been removed as the code is no
longer used, and a new pair of OutputBuffer source files have been added
to contain the small class that is used to connect the RTL-SDR output to
QAudioOutput.
Bug-AGL: SPEC-455
Change-Id: I0d690143b9c70fdca24f9fbf3b016feef8ae627b
Signed-off-by: Scott Murray <scott.murray@konsulko.com>
(cherry picked from commit aeb67506173a7b8cef089fa725c3abe1f629dc67)
Diffstat (limited to 'rtlfmradiotunercontrol.cpp')
-rw-r--r-- | rtlfmradiotunercontrol.cpp | 113 |
1 files changed, 78 insertions, 35 deletions
diff --git a/rtlfmradiotunercontrol.cpp b/rtlfmradiotunercontrol.cpp index 27a8d08..937411a 100644 --- a/rtlfmradiotunercontrol.cpp +++ b/rtlfmradiotunercontrol.cpp @@ -1,6 +1,6 @@ /* * Copyright (C) 2016, The Qt Company Ltd. All Rights Reserved. - * Copyright (C) 2016, Scott Murray <scott.murray@konsulko.com> + * Copyright (C) 2016, 2017 Konsulko Group * * This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this @@ -8,12 +8,13 @@ */ #include "rtlfmradiotunercontrol.h" -#include "radio_output.h" #include "rtl_fm.h" #include <QtCore/QDebug> #include <QtCore/QTimer> #include <QtCore/QSettings> +#include <QtMultimedia/QAudioOutput> +#include "OutputBuffer.h" // Structure to describe FM band plans, all values in Hz. struct fmBandPlan { @@ -22,12 +23,23 @@ struct fmBandPlan { unsigned int freqStep; }; +// Structure to hold output context for RTL-SDR callback +struct OutputContext { + OutputBuffer *buffer; + bool muted; +}; + class RtlFmRadioTunerControl::Private { public: Private(RtlFmRadioTunerControl *parent); ~Private(); + bool muted() { return mOutputContext.muted; } + void setMuted(bool muted) { mOutputContext.muted = muted; } + void outputStart(void); + void outputStop(void); + private: RtlFmRadioTunerControl *q; @@ -38,7 +50,6 @@ public: QRadioTuner::StereoMode stereoMode; int signalStrength; int volume; - static bool muted; bool searching; QRadioTuner::Error error; QString errorString; @@ -49,6 +60,13 @@ public: struct fmBandPlan fmBandPlan; private: + void outputInit(void); + + static void rtlOutputThreadFunc(int16_t *result, int result_len, void *ctx); + + QAudioOutput* mAudioOutput; + OutputContext mOutputContext; + QMap<QString, struct fmBandPlan> knownFmBandPlans = { { QString::fromUtf8("US"), { .minFreq = 87900000, .maxFreq = 107900000, .freqStep = 200000 } }, { QString::fromUtf8("JP"), { .minFreq = 76100000, .maxFreq = 89900000, .freqStep = 100000 } }, @@ -56,14 +74,8 @@ private: { QString::fromUtf8("ITU-1"), { .minFreq = 87500000, .maxFreq = 108000000, .freqStep = 50000 } }, { QString::fromUtf8("ITU-2"), { .minFreq = 87900000, .maxFreq = 107900000, .freqStep = 50000 } } }; - - static void output_thread_fn(int16_t *result, int result_len, void *ctx); - static RadioOutputImplementation *mRadioOutput; }; -bool RtlFmRadioTunerControl::Private::muted; -RadioOutputImplementation *RtlFmRadioTunerControl::Private::mRadioOutput; - RtlFmRadioTunerControl::Private::Private(RtlFmRadioTunerControl *parent) : q(parent), state(QRadioTuner::StoppedState), @@ -76,28 +88,11 @@ RtlFmRadioTunerControl::Private::Private(RtlFmRadioTunerControl *parent) searchOne(true), step(0) { - mRadioOutput = NULL; - muted = false; - - // Initialize output - // Note that the optional ALSA support is currently not entirely - // functional and needs further debugging. - char *impl_env = getenv("RADIO_OUTPUT"); - if (impl_env) { - if (strcasecmp(impl_env, "Pulse") == 0) - mRadioOutput = new RadioOutputPulse(); - if (strcasecmp(impl_env, "Alsa") == 0) - mRadioOutput = new RadioOutputAlsa(); - } - if (!mRadioOutput) { - mRadioOutput = new RadioOutputPulse(); - if (!mRadioOutput->works) - mRadioOutput = new RadioOutputAlsa(); - } + outputInit(); // Initialize RTL-SDR dongle present = false; - if(rtl_fm_init(frequency, 200000, 48000, output_thread_fn, NULL) < 0) { + if(rtl_fm_init(frequency, 200000, 48000, rtlOutputThreadFunc, &mOutputContext) < 0) { qDebug("%s: no RTL USB adapter?", Q_FUNC_INFO); } else { present = true; @@ -132,15 +127,59 @@ RtlFmRadioTunerControl::Private::Private(RtlFmRadioTunerControl *parent) }); } -void RtlFmRadioTunerControl::Private::output_thread_fn(int16_t *result, int result_len, void *ctx) +void RtlFmRadioTunerControl::Private::outputInit(void) { - if (!muted) - mRadioOutput->play((void*) result, result_len); + // Set up the format + QAudioFormat format; + format.setSampleRate(24000); + format.setChannelCount(2); + format.setSampleSize(16); + format.setCodec("audio/pcm"); + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + + QAudioDeviceInfo info(QAudioDeviceInfo::defaultOutputDevice()); + if (!info.isFormatSupported(format)) { + qWarning() << "Raw audio format not supported by backend, cannot play audio."; + return; + } + + // Create output + mAudioOutput = new QAudioOutput(format); + mAudioOutput->setCategory(QString("radio")); + + // Initialize context to connect RTL-SDR library to QAudioOutput, + // including output buffer and muted state + mOutputContext.buffer = new OutputBuffer(format, q); + mOutputContext.muted = false; +} + +void RtlFmRadioTunerControl::Private::outputStart(void) +{ + mOutputContext.buffer->start(); + mAudioOutput->start(mOutputContext.buffer); +} + +void RtlFmRadioTunerControl::Private::outputStop(void) +{ + mAudioOutput->stop(); + mOutputContext.buffer->stop(); +} + +void RtlFmRadioTunerControl::Private::rtlOutputThreadFunc(int16_t *result, int result_len, void *ctx) +{ + const char *p = (const char *) result; + OutputContext *context = reinterpret_cast<OutputContext*>(ctx); + + if (!context->muted) { + context->buffer->writeData(p, result_len * 2); + } } RtlFmRadioTunerControl::Private::~Private() { - delete mRadioOutput; + delete mAudioOutput; + delete mOutputContext.buffer; rtl_fm_cleanup(); } @@ -174,6 +213,8 @@ void RtlFmRadioTunerControl::setBand(QRadioTuner::Band band) return; d->band = band; emit bandChanged(band); +#else + Q_UNUSED(band); #endif } @@ -297,14 +338,14 @@ void RtlFmRadioTunerControl::setVolume(int volume) bool RtlFmRadioTunerControl::isMuted() const { - return d->muted; + return d->muted(); } void RtlFmRadioTunerControl::setMuted(bool muted) { - if (d->muted == muted) + if (d->muted() == muted) return; - d->muted = muted; + d->setMuted(muted); emit mutedChanged(muted); } @@ -366,6 +407,7 @@ void RtlFmRadioTunerControl::start() //rtl_fm_stop(); rtl_fm_start(); + d->outputStart(); d->state = QRadioTuner::ActiveState; emit stateChanged(d->state); qDebug() << "RtlFmRadioTunerControl::start - exit\n"; @@ -383,6 +425,7 @@ void RtlFmRadioTunerControl::stop() } rtl_fm_stop(); + d->outputStop(); d->state = QRadioTuner::StoppedState; emit stateChanged(d->state); qDebug() << "RtlFmRadioTunerControl::stop - exit\n"; |