Fix the Qt4 bindings and link to libshairport dynamically
[deb_shairplay.git] / src / bindings / qt4 / raopservice.cpp
1 #include "raopservice.h"
2 #include "raopcallbackhandler.h"
3
4 #include <QThread>
5 #include <QDebug>
6
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
32 typedef struct {
33 QThread * cb_thread;
34 RaopCallbackHandler * cb_handler;
35 void * cb_session;
36 } audio_session_t;
37
38 static void*
39 audio_init_cb(void *cls, int bits, int channels, int samplerate)
40 {
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),
57 Q_ARG(int, bits),
58 Q_ARG(int, channels),
59 Q_ARG(int, samplerate));
60 return audio_session;
61 }
62
63 static void
64 audio_process_cb(void *cls, void *session, const void *buffer, int buflen)
65 {
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),
71 Q_ARG(void*, (void*)buffer),
72 Q_ARG(int, buflen));
73 }
74
75 static void
76 audio_destroy_cb(void *cls, void *session)
77 {
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);
92 }
93
94 static void
95 audio_flush_cb(void *cls, void *session)
96 {
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));
102 }
103
104 static void
105 audio_set_volume_cb(void *cls, void *session, float volume)
106 {
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),
112 Q_ARG(float, volume));
113 }
114
115 static void
116 audio_set_metadata_cb(void *cls, void *session, const void *buffer, int buflen)
117 {
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),
123 Q_ARG(void*, (void*)buffer),
124 Q_ARG(int, buflen));
125 }
126
127 static void
128 audio_set_coverart_cb(void *cls, void *session, const void *buffer, int buflen)
129 {
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),
135 Q_ARG(void*, (void*)buffer),
136 Q_ARG(int, buflen));
137 }
138
139 RaopService::RaopService(QObject *parent) :
140 QObject(parent),
141 m_raop(0)
142 {
143 }
144
145 RaopService::~RaopService()
146 {
147 this->stop();
148 raop_destroy(m_raop);
149 }
150
151 bool RaopService::init(int max_clients, RaopCallbacks *callbacks)
152 {
153 raop_callbacks_t raop_cbs;
154
155 raop_cbs.cls = callbacks;
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) {
166 return false;
167 }
168 return true;
169 }
170
171 bool RaopService::isRunning()
172 {
173 return (raop_is_running(m_raop) != 0);
174 }
175
176 bool RaopService::start(quint16 port, const QByteArray & hwaddr)
177 {
178 int ret;
179 ret = raop_start(m_raop, &port, hwaddr.data(), hwaddr.size(), 0);
180 if (ret < 0) {
181 return false;
182 }
183 return true;
184 }
185
186 void RaopService::stop()
187 {
188 if (m_raop) {
189 raop_stop(m_raop);
190 }
191 }