summaryrefslogtreecommitdiff
path: root/fmrxthread.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'fmrxthread.cpp')
-rw-r--r--fmrxthread.cpp203
1 files changed, 203 insertions, 0 deletions
diff --git a/fmrxthread.cpp b/fmrxthread.cpp
new file mode 100644
index 0000000..0043754
--- /dev/null
+++ b/fmrxthread.cpp
@@ -0,0 +1,203 @@
+#include <unistd.h>
+
+#include <QtCore/QCoreApplication>
+#include <QtCore/QDebug>
+
+#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;
+}