7#include <QtCore/qspan.h>
8#include <QtCore/qthread.h>
9#include <QLoggingCategory>
17using QtMultimediaPrivate::QPlatformAudioSourceStream;
18using QtMultimediaPrivate::QPlatformAudioIOStream;
19using QtMultimediaPrivate::runAudioCallback;
20using QtMultimediaPrivate::withTemporaryBuffer;
23 std::optional<qsizetype> ringbufferSize,
24 QQnxSndAudioSource *parent,
float volume,
25 std::optional<NativePeriodFrames> nativePeriodFrames)
49 return openPcmDevice();
60 .direction = SND_PCM_STREAM_CAPTURE,
61 .deviceId = m_audioDevice.id(),
63 .category = lcQnxSndInput(),
64 .streamLabel =
"Capture",
65 .periodCountEnvVar =
"QT_QNXSND_INPUT_PERIODS",
66 .periodFrames = m_nativePeriodFrames
67 ? std::optional<uint32_t>{ qToUnderlying(*m_nativePeriodFrames) }
76 m_nativeFormat = r.nativeFormat;
84 Q_ASSERT(!m_workerThread || !m_workerThread->isRunning());
89 if (
const int err = snd_pcm_close(m_handle); err < 0) {
90 qCWarning(lcQnxSndInput) <<
"snd_pcm_close failed:"
91 << snd_strerror(err) <<
"(" << err <<
")";
99 setQIODevice(ioDevice);
100 createQIODeviceConnections(ioDevice);
107 QIODevice *ioDevice = createRingbufferReaderDevice();
108 setQIODevice(ioDevice);
109 createQIODeviceConnections(ioDevice);
117 m_audioCallback = std::move(audioCallback);
126 m_suspended.store(
true, std::memory_order_release);
134 m_suspended.store(
false, std::memory_order_release);
141 disconnectQIODeviceConnections();
147 m_parent.store(
nullptr, std::memory_order_release);
149 finalizeQIODevice(shutdownPolicy);
150 if (shutdownPolicy == ShutdownPolicy::DiscardRingbuffer)
158 if (
auto *parent = m_parent.load(std::memory_order_acquire))
159 parent->updateStreamIdle(streamIsIdle);
166 if (!m_wakePipe.open())
167 qCWarning(lcQnxSndInput) <<
"wake pipe creation failed; worker wakeups degraded";
169 m_workerThread.reset(QThread::create([
this] {
172 m_workerThread->setObjectName(u"QQnxSndAudioSourceStream");
175 m_workerThread->start();
186 if (
const int err = snd_pcm_drop(m_handle); err < 0) {
187 qCWarning(lcQnxSndInput) <<
"snd_pcm_drop failed:"
188 << snd_strerror(err) <<
"(" << err <<
")";
191 if (m_workerThread) {
192 m_workerThread->wait();
205 handleSndPcmError(err);
210 if (isStopRequested(
std::memory_order_acquire))
213 if (m_suspended.load(std::memory_order_acquire)) {
216 m_wakePipe.waitForWake();
231 if (!processOnePeriod()) {
232 if (!isStopRequested(
std::memory_order_acquire))
244 const snd_pcm_sframes_t avail = snd_pcm_avail_update(m_handle);
246 if (isStopRequested(
std::memory_order_acquire))
249 if (
const int err = recoverFromXrun(
static_cast<
int>(avail)); err < 0) {
250 handleSndPcmError(err);
258 const snd_pcm_uframes_t framesToRead =
259 std::min<snd_pcm_uframes_t>(
static_cast<snd_pcm_uframes_t>(avail), m_periodFrames);
262 const int nativeBytesPerFrame =
static_cast<
int>(m_format.channelCount()
263 * QAudioHelperInternal::bytesPerSample(m_nativeFormat));
264 const size_t hostBytes =
static_cast<size_t>(framesToRead) * nativeBytesPerFrame;
266 return withTemporaryBuffer(hostBytes, [&](QSpan<std::byte> hostBufferSpan) ->
bool {
267 const snd_pcm_sframes_t framesRead =
268 snd_pcm_readi(m_handle, hostBufferSpan.data(), framesToRead);
269 if (framesRead == -EAGAIN)
271 if (framesRead < 1) {
272 if (isStopRequested(
std::memory_order_acquire))
274 if (
const int err = recoverFromXrun(
static_cast<
int>(framesRead)); err < 0) {
275 handleSndPcmError(err);
283 const qsizetype bytesRead =
static_cast<qsizetype>(framesRead) * nativeBytesPerFrame;
284 QSpan<
const std::byte> filled = hostBufferSpan.first(bytesRead);
286 if (m_audioCallback) {
287 runAudioCallback(*m_audioCallback, filled, m_format, volume(), m_nativeFormat);
289 const uint64_t framesWritten =
290 QPlatformAudioSourceStream::process(filled, framesRead, m_nativeFormat);
291 if (framesWritten !=
static_cast<uint64_t>(framesRead)) {
292 invokeOnAppThread([self = shared_from_this()] {
294 self->updateStreamIdle(
true);
307 invokeOnAppThread([self = shared_from_this(), err] {
308 qCWarning(lcQnxSndInput) <<
"audio input error, stopping stream:"
309 << (err ? snd_strerror(err) :
"I/O error");
310 if (
auto *parent = self->m_parent.load(std::memory_order_acquire))
311 self->handleIOError(parent);
328 m_stream->stop(ShutdownPolicy::DiscardRingbuffer);
QQnxSndAudioSource(QAudioDevice, const QAudioFormat &, QObject *parent)
~QQnxSndAudioSource() override
void setWorkerRealtimePriority(const QLoggingCategory &category)
int startPcm(snd_pcm_t *handle)
int recoverFromXrun(snd_pcm_t *handle, int err)
PollOutcome pollPcm(snd_pcm_t *handle, const WakePipe &wake)
PcmOpenResult openConfiguredPcm(const PcmOpenConfig &config)
QT_BEGIN_NAMESPACE Q_STATIC_LOGGING_CATEGORY(lcSynthesizedIterableAccess, "qt.iterable.synthesized", QtWarningMsg)
void updateStreamIdle(bool) override
bool start(AudioCallback)
QtMultimediaPrivate::QPlatformAudioSourceStream::AudioCallback AudioCallback
void stop(ShutdownPolicy)
QQnxSndAudioSourceStream(QAudioDevice, const QAudioFormat &, std::optional< qsizetype > ringbufferSize, QQnxSndAudioSource *parent, float volume, std::optional< NativePeriodFrames > nativePeriodFrames)
snd_pcm_uframes_t periodFrames