+/**
+ * Copyright (C) 2012 Juho Vähä-Herttua
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining
+ * a copy of this software and associated documentation files (the
+ * "Software"), to deal in the Software without restriction, including
+ * without limitation the rights to use, copy, modify, merge, publish,
+ * distribute, sublicense, and/or sell copies of the Software, and to
+ * permit persons to whom the Software is furnished to do so, subject to
+ * the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included
+ * in all copies or substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+ * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
#include "raopservice.h"
+#include "raopcallbackhandler.h"
+#include <QThread>
#include <QDebug>
-#include <shairplay/raop.h>
-
#define RSA_KEY \
"-----BEGIN RSA PRIVATE KEY-----\n"\
"MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\n"\
"2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\n"\
"-----END RSA PRIVATE KEY-----\n"
+typedef struct {
+ QThread * cb_thread;
+ RaopCallbackHandler * cb_handler;
+ void * cb_session;
+} audio_session_t;
static void*
audio_init_cb(void *cls, int bits, int channels, int samplerate)
{
- void *session;
- QMetaObject::invokeMethod((QObject*)cls, "audioInit", Qt::BlockingQueuedConnection,
- Q_ARG(void*, (void*)&session),
+ audio_session_t *audio_session = 0;
+
+ audio_session = (audio_session_t *)calloc(1, sizeof(audio_session_t));
+ audio_session->cb_thread = new QThread();
+ audio_session->cb_thread->start();
+
+ /* This whole hack is required because QAudioOutput
+ * needs to be created in a QThread, threads created
+ * outside Qt are not allowed (they have no eventloop) */
+ audio_session->cb_handler = new RaopCallbackHandler();
+ audio_session->cb_handler->moveToThread(audio_session->cb_thread);
+ audio_session->cb_handler->init((RaopAudioHandler *)cls);
+
+ QMetaObject::invokeMethod(audio_session->cb_handler, "audioInit",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(void*, (void*)&audio_session->cb_session),
Q_ARG(int, bits),
Q_ARG(int, channels),
Q_ARG(int, samplerate));
- return session;
+ return audio_session;
}
static void
audio_process_cb(void *cls, void *session, const void *buffer, int buflen)
{
- QMetaObject::invokeMethod((QObject*)cls, "audioProcess", Qt::BlockingQueuedConnection,
- Q_ARG(void*, session),
+ Q_UNUSED(cls)
+ audio_session_t *audio_session = (audio_session_t *)session;
+ QMetaObject::invokeMethod(audio_session->cb_handler, "audioProcess",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(void*, audio_session->cb_session),
Q_ARG(void*, (void*)buffer),
Q_ARG(int, buflen));
}
static void
audio_destroy_cb(void *cls, void *session)
{
- QMetaObject::invokeMethod((QObject*)cls, "audioDestroy", Qt::BlockingQueuedConnection,
- Q_ARG(void*, session));
+ Q_UNUSED(cls)
+ audio_session_t *audio_session = (audio_session_t *)session;
+ QMetaObject::invokeMethod(audio_session->cb_handler, "audioDestroy",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(void*, audio_session->cb_session));
+
+ // Wait until the session thread has finished
+ audio_session->cb_thread->quit();
+ audio_session->cb_thread->wait();
+
+ // Delete all session variables
+ delete audio_session->cb_handler;
+ delete audio_session->cb_thread;
+ free(audio_session);
}
static void
audio_flush_cb(void *cls, void *session)
{
- QMetaObject::invokeMethod((QObject*)cls, "audioFlush", Qt::BlockingQueuedConnection,
- Q_ARG(void*, session));
+ Q_UNUSED(cls)
+ audio_session_t *audio_session = (audio_session_t *)session;
+ QMetaObject::invokeMethod(audio_session->cb_handler, "audioFlush",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(void*, audio_session->cb_session));
}
static void
audio_set_volume_cb(void *cls, void *session, float volume)
{
- QMetaObject::invokeMethod((QObject*)cls, "audioSetVolume", Qt::BlockingQueuedConnection,
- Q_ARG(void*, session),
+ Q_UNUSED(cls)
+ audio_session_t *audio_session = (audio_session_t *)session;
+ QMetaObject::invokeMethod(audio_session->cb_handler, "audioSetVolume",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(void*, audio_session->cb_session),
Q_ARG(float, volume));
}
static void
audio_set_metadata_cb(void *cls, void *session, const void *buffer, int buflen)
{
- QMetaObject::invokeMethod((QObject*)cls, "audioSetVolume", Qt::BlockingQueuedConnection,
- Q_ARG(void*, session),
+ Q_UNUSED(cls)
+ audio_session_t *audio_session = (audio_session_t *)session;
+ QMetaObject::invokeMethod(audio_session->cb_handler, "audioSetVolume",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(void*, audio_session->cb_session),
Q_ARG(void*, (void*)buffer),
Q_ARG(int, buflen));
}
static void
audio_set_coverart_cb(void *cls, void *session, const void *buffer, int buflen)
{
- QMetaObject::invokeMethod((QObject*)cls, "audioSetVolume", Qt::BlockingQueuedConnection,
- Q_ARG(void*, session),
+ Q_UNUSED(cls)
+ audio_session_t *audio_session = (audio_session_t *)session;
+ QMetaObject::invokeMethod(audio_session->cb_handler, "audioSetVolume",
+ Qt::BlockingQueuedConnection,
+ Q_ARG(void*, audio_session->cb_session),
Q_ARG(void*, (void*)buffer),
Q_ARG(int, buflen));
}
QObject(parent),
m_raop(0)
{
- /* This whole hack is required because QAudioOutput
- * needs to be created in a QThread, threads created
- * outside Qt are not allowed (they have no eventloop) */
- m_handler.moveToThread(&m_thread);
}
RaopService::~RaopService()
raop_destroy(m_raop);
}
-bool RaopService::init(int max_clients, RaopCallbacks *callbacks)
+bool RaopService::init(int max_clients, RaopAudioHandler *callbacks)
{
raop_callbacks_t raop_cbs;
- m_handler.init(callbacks);
- raop_cbs.cls = &m_handler;
+ raop_cbs.cls = callbacks;
raop_cbs.audio_init = &audio_init_cb;
raop_cbs.audio_process = &audio_process_cb;
raop_cbs.audio_destroy = &audio_destroy_cb;
raop_cbs.audio_set_metadata = &audio_set_metadata_cb;
raop_cbs.audio_set_coverart = &audio_set_coverart_cb;
- m_raop = raop_init(max_clients, &raop_cbs, RSA_KEY);
+ m_raop = raop_init(max_clients, &raop_cbs, RSA_KEY, 0);
if (!m_raop) {
- printf("Foobar\n");
return false;
}
return true;
}
+void RaopService::setLogLevel(int level)
+{
+ Q_UNUSED(level)
+}
+
+void RaopService::setLogHandler(RaopLogHandler *logger)
+{
+ Q_UNUSED(logger)
+}
+
bool RaopService::isRunning()
{
return (raop_is_running(m_raop) != 0);
bool RaopService::start(quint16 port, const QByteArray & hwaddr)
{
int ret;
- m_thread.start();
ret = raop_start(m_raop, &port, hwaddr.data(), hwaddr.size(), 0);
if (ret < 0) {
- m_thread.quit();
- m_thread.wait();
return false;
}
return true;
}
+
void RaopService::stop()
{
if (m_raop) {
raop_stop(m_raop);
}
- if (m_thread.isRunning()) {
- m_thread.quit();
- m_thread.wait();
- }
}