Fix the Qt4 bindings and link to libshairport dynamically
[deb_shairplay.git] / src / bindings / qt4 / raopservice.cpp
CommitLineData
9434b30c 1#include "raopservice.h"
f65165f4 2#include "raopcallbackhandler.h"
9434b30c 3
f65165f4 4#include <QThread>
9434b30c
JVH
5#include <QDebug>
6
9434b30c
JVH
7#define RSA_KEY \
8"-----BEGIN RSA PRIVATE KEY-----\n"\
9"MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\n"\
10"wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\n"\
11"wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\n"\
12"/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\n"\
13"UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\n"\
14"BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\n"\
15"LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\n"\
16"NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\n"\
17"lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\n"\
18"aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\n"\
19"a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\n"\
20"oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\n"\
21"oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\n"\
22"k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\n"\
23"AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\n"\
24"cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\n"\
25"54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\n"\
26"17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\n"\
27"1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\n"\
28"LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\n"\
29"2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\n"\
30"-----END RSA PRIVATE KEY-----\n"
31
f65165f4
JVH
32typedef struct {
33 QThread * cb_thread;
34 RaopCallbackHandler * cb_handler;
35 void * cb_session;
36} audio_session_t;
9434b30c
JVH
37
38static void*
39audio_init_cb(void *cls, int bits, int channels, int samplerate)
40{
f65165f4
JVH
41 audio_session_t *audio_session = 0;
42
43 audio_session = (audio_session_t *)calloc(1, sizeof(audio_session_t));
44 audio_session->cb_thread = new QThread();
45 audio_session->cb_thread->start();
46
47 /* This whole hack is required because QAudioOutput
48 * needs to be created in a QThread, threads created
49 * outside Qt are not allowed (they have no eventloop) */
50 audio_session->cb_handler = new RaopCallbackHandler();
51 audio_session->cb_handler->moveToThread(audio_session->cb_thread);
52 audio_session->cb_handler->init((RaopCallbacks *)cls);
53
54 QMetaObject::invokeMethod(audio_session->cb_handler, "audioInit",
55 Qt::BlockingQueuedConnection,
56 Q_ARG(void*, (void*)&audio_session->cb_session),
9434b30c
JVH
57 Q_ARG(int, bits),
58 Q_ARG(int, channels),
59 Q_ARG(int, samplerate));
f65165f4 60 return audio_session;
9434b30c
JVH
61}
62
63static void
64audio_process_cb(void *cls, void *session, const void *buffer, int buflen)
65{
f65165f4
JVH
66 Q_UNUSED(cls)
67 audio_session_t *audio_session = (audio_session_t *)session;
68 QMetaObject::invokeMethod(audio_session->cb_handler, "audioProcess",
69 Qt::BlockingQueuedConnection,
70 Q_ARG(void*, audio_session->cb_session),
9434b30c
JVH
71 Q_ARG(void*, (void*)buffer),
72 Q_ARG(int, buflen));
73}
74
75static void
76audio_destroy_cb(void *cls, void *session)
77{
f65165f4
JVH
78 Q_UNUSED(cls)
79 audio_session_t *audio_session = (audio_session_t *)session;
80 QMetaObject::invokeMethod(audio_session->cb_handler, "audioDestroy",
81 Qt::BlockingQueuedConnection,
82 Q_ARG(void*, audio_session->cb_session));
83
84 // Wait until the session thread has finished
85 audio_session->cb_thread->quit();
86 audio_session->cb_thread->wait();
87
88 // Delete all session variables
89 delete audio_session->cb_handler;
90 delete audio_session->cb_thread;
91 free(audio_session);
9434b30c
JVH
92}
93
94static void
95audio_flush_cb(void *cls, void *session)
96{
f65165f4
JVH
97 Q_UNUSED(cls)
98 audio_session_t *audio_session = (audio_session_t *)session;
99 QMetaObject::invokeMethod(audio_session->cb_handler, "audioFlush",
100 Qt::BlockingQueuedConnection,
101 Q_ARG(void*, audio_session->cb_session));
9434b30c
JVH
102}
103
104static void
105audio_set_volume_cb(void *cls, void *session, float volume)
106{
f65165f4
JVH
107 Q_UNUSED(cls)
108 audio_session_t *audio_session = (audio_session_t *)session;
109 QMetaObject::invokeMethod(audio_session->cb_handler, "audioSetVolume",
110 Qt::BlockingQueuedConnection,
111 Q_ARG(void*, audio_session->cb_session),
9434b30c
JVH
112 Q_ARG(float, volume));
113}
114
115static void
116audio_set_metadata_cb(void *cls, void *session, const void *buffer, int buflen)
117{
f65165f4
JVH
118 Q_UNUSED(cls)
119 audio_session_t *audio_session = (audio_session_t *)session;
120 QMetaObject::invokeMethod(audio_session->cb_handler, "audioSetVolume",
121 Qt::BlockingQueuedConnection,
122 Q_ARG(void*, audio_session->cb_session),
9434b30c
JVH
123 Q_ARG(void*, (void*)buffer),
124 Q_ARG(int, buflen));
125}
126
127static void
128audio_set_coverart_cb(void *cls, void *session, const void *buffer, int buflen)
129{
f65165f4
JVH
130 Q_UNUSED(cls)
131 audio_session_t *audio_session = (audio_session_t *)session;
132 QMetaObject::invokeMethod(audio_session->cb_handler, "audioSetVolume",
133 Qt::BlockingQueuedConnection,
134 Q_ARG(void*, audio_session->cb_session),
9434b30c
JVH
135 Q_ARG(void*, (void*)buffer),
136 Q_ARG(int, buflen));
137}
138
139RaopService::RaopService(QObject *parent) :
140 QObject(parent),
141 m_raop(0)
142{
9434b30c
JVH
143}
144
145RaopService::~RaopService()
146{
147 this->stop();
148 raop_destroy(m_raop);
149}
150
151bool RaopService::init(int max_clients, RaopCallbacks *callbacks)
152{
153 raop_callbacks_t raop_cbs;
154
f65165f4 155 raop_cbs.cls = callbacks;
9434b30c
JVH
156 raop_cbs.audio_init = &audio_init_cb;
157 raop_cbs.audio_process = &audio_process_cb;
158 raop_cbs.audio_destroy = &audio_destroy_cb;
159 raop_cbs.audio_flush = &audio_flush_cb;
160 raop_cbs.audio_set_volume = &audio_set_volume_cb;
161 raop_cbs.audio_set_metadata = &audio_set_metadata_cb;
162 raop_cbs.audio_set_coverart = &audio_set_coverart_cb;
163
164 m_raop = raop_init(max_clients, &raop_cbs, RSA_KEY);
165 if (!m_raop) {
9434b30c
JVH
166 return false;
167 }
168 return true;
169}
170
171bool RaopService::isRunning()
172{
173 return (raop_is_running(m_raop) != 0);
174}
175
176bool RaopService::start(quint16 port, const QByteArray & hwaddr)
177{
178 int ret;
9434b30c
JVH
179 ret = raop_start(m_raop, &port, hwaddr.data(), hwaddr.size(), 0);
180 if (ret < 0) {
9434b30c
JVH
181 return false;
182 }
183 return true;
184}
185
186void RaopService::stop()
187{
188 if (m_raop) {
189 raop_stop(m_raop);
190 }
9434b30c 191}