#include #include #include #include "fmrxthread.h" FmRxThread::FmRxThread(QObject *parent) : QThread(parent), m_quit(false), m_error(false), m_fd(-1), m_mainloop(0), m_context(0), m_stream(0) { } void FmRxThread::setInputFd(int fd) { m_fd = fd; } int FmRxThread::inputFd() const { return m_fd; } bool FmRxThread::error() const { return m_error; } void FmRxThread::run() { m_quit = false; m_error = false; if (!configure()) { m_error = true; deconfigure(); return; } if (!loop()) { m_error = true; } deconfigure(); } void FmRxThread::stop() { m_quit = true; if (m_mainloop) { pa_mainloop_wakeup(m_mainloop); } } bool FmRxThread::configure() { pa_context_state_t context_state; pa_stream_state_t stream_state; m_mainloop = pa_mainloop_new(); if (!m_mainloop) { qWarning("Could not create pa_mainloop"); return false; } qDebug("FmRx thread connecting to PulseAudio"); // Find the application name QString appName = QCoreApplication::applicationName(); if (appName.isEmpty()) { // Set some default appName = "FmRx MediaService"; } // Setup the PulseAudio context m_context = pa_context_new(pa_mainloop_get_api(m_mainloop), appName.toUtf8().data()); if (!m_context) { qWarning("Could not create pa_mainloop"); return false; } if (pa_context_connect(m_context, NULL, PA_CONTEXT_NOFLAGS, NULL) < 0) { qWarning("Could not connect to PulseAudio"); return false; } do { if (pa_mainloop_iterate(m_mainloop, 1, NULL) < 0) { qWarning("pa_mainloop_iterate() failed"); return false; } context_state = pa_context_get_state(m_context); if (context_state == PA_CONTEXT_FAILED) { qWarning("Early pa_context connection fail"); return false; } if (m_quit) return true; } while (context_state != PA_CONTEXT_READY); // Setup the stream pa_sample_spec spec; spec.format = PA_SAMPLE_S16LE; spec.channels = 2; spec.rate = 48000; m_stream = pa_stream_new(m_context, "FmRx Stream", &spec, NULL); if (!m_stream) { qWarning("Could not create pa_stream"); return false; } if (pa_stream_connect_playback(m_stream, NULL, NULL, PA_STREAM_NOFLAGS, NULL, NULL) < 0) { qWarning("Could not connect stream"); return false; } do { if (pa_mainloop_iterate(m_mainloop, 1, NULL) < 0) { qWarning("pa_mainloop_iterate() failed"); return false; } stream_state = pa_stream_get_state(m_stream); if (stream_state == PA_STREAM_FAILED) { qWarning("Early pa_stream termination"); return false; } if (m_quit) return true; } while (stream_state != PA_STREAM_READY); return true; } void FmRxThread::deconfigure() { if (m_stream) { pa_stream_disconnect(m_stream); pa_stream_unref(m_stream); m_stream = 0; } if (m_context) { pa_context_disconnect(m_context); pa_context_unref(m_context); m_context = 0; } if (m_mainloop) { pa_mainloop_free(m_mainloop); m_mainloop = 0; } if (m_fd != -1) { close(m_fd); m_fd = -1; } } bool FmRxThread::loop() { // Here comes the main loop while (!m_quit) { void *buffer; size_t maxbytes = (size_t) -1; ssize_t readbytes; if (pa_stream_begin_write(m_stream, &buffer, &maxbytes) < 0) { qWarning("Failed to pa_stream_begin_write"); return false; } readbytes = read(m_fd, buffer, maxbytes); if (readbytes < 0) { qWarning("Failed to read from the FmRx pipe"); return false; } if (pa_stream_write(m_stream, buffer, readbytes, NULL, 0, PA_SEEK_RELATIVE) < 0) { qWarning("Failed to pa_stream_write"); return false; } do { if (pa_mainloop_iterate(m_mainloop, 1, NULL) < 0) { qWarning("pa_mainloop_iterate() failed"); return false; } if (m_quit) return true; } while (pa_stream_writable_size(m_stream) == 0); if (pa_stream_get_state(m_stream) != PA_STREAM_READY) { qWarning("pa_stream was lost"); return false; } if (pa_context_get_state(m_context) != PA_CONTEXT_READY) { qWarning("pa_context was lost"); return false; } } return true; }