Initial commit to the repository
[deb_shairplay.git] / AirTV-Qt / raopservice.cpp
1 #include "raopservice.h"
2
3 #include <QDebug>
4 #include <QFile>
5
6 static void
7 audio_init(void *cls, void **session, int bits, int channels, int samplerate)
8 {
9 QMetaObject::invokeMethod((QObject*)cls, "audioInit", Qt::BlockingQueuedConnection,
10 Q_ARG(void*, (void*)session),
11 Q_ARG(int, bits),
12 Q_ARG(int, channels),
13 Q_ARG(int, samplerate));
14 }
15
16 static void
17 audio_set_volume(void *cls, void *session, float volume)
18 {
19 QMetaObject::invokeMethod((QObject*)cls, "audioSetVolume", Qt::BlockingQueuedConnection,
20 Q_ARG(void*, session),
21 Q_ARG(float, volume));
22 }
23
24 static void
25 audio_process(void *cls, void *session, const void *buffer, int buflen)
26 {
27 QMetaObject::invokeMethod((QObject*)cls, "audioProcess", Qt::BlockingQueuedConnection,
28 Q_ARG(void*, session),
29 Q_ARG(void*, (void*)buffer),
30 Q_ARG(int, buflen));
31 }
32
33 static void
34 audio_flush(void *cls, void *session)
35 {
36 QMetaObject::invokeMethod((QObject*)cls, "audioFlush", Qt::BlockingQueuedConnection,
37 Q_ARG(void*, session));
38 }
39
40 static void
41 audio_destroy(void *cls, void *session)
42 {
43 QMetaObject::invokeMethod((QObject*)cls, "audioDestroy", Qt::BlockingQueuedConnection,
44 Q_ARG(void*, session));
45 }
46
47 RaopService::RaopService(QObject *parent) :
48 QObject(parent),
49 m_dnssd(0),
50 m_raop(0)
51 {
52 /* This whole hack is required because QAudioOutput
53 * needs to be created in a QThread, threads created
54 * outside Qt are not allowed (they have no eventloop) */
55 m_handler.moveToThread(&m_thread);
56 }
57
58 RaopService::~RaopService()
59 {
60 this->stop();
61
62 dnssd_destroy(m_dnssd);
63 raop_destroy(m_raop);
64 }
65
66 bool RaopService::init()
67 {
68 const char hwaddr[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 };
69 raop_callbacks_t raop_cbs;
70 int error;
71
72 raop_cbs.cls = &m_handler;
73 raop_cbs.audio_init = audio_init;
74 raop_cbs.audio_set_volume = audio_set_volume;
75 raop_cbs.audio_process = audio_process;
76 raop_cbs.audio_flush = audio_flush;
77 raop_cbs.audio_destroy = audio_destroy;
78
79 QFile file("airport.key");
80 if (!file.exists()) {
81 // This is used when running from Qt Creator on Mac
82 file.setFileName("../../../../airport.key");
83 }
84 if (!file.exists()) {
85 // This is used when running from Qt Creator on Windows
86 file.setFileName("../airport.key");
87 }
88 if (!file.exists()) {
89 return false;
90 }
91 file.open(QIODevice::ReadOnly);
92 QByteArray array = file.read(file.size());
93 array.append('\0');
94
95 m_raop = raop_init(&raop_cbs, array.data(), hwaddr, sizeof(hwaddr));
96 if (!m_raop) {
97 return false;
98 }
99
100 m_dnssd = dnssd_init(hwaddr, sizeof(hwaddr), &error);
101 if (!m_dnssd) {
102 raop_destroy(m_raop);
103 m_raop = NULL;
104 return false;
105 }
106
107 return true;
108 }
109
110 bool RaopService::start(const QString & name, quint16 port)
111 {
112 if (!m_raop || !m_dnssd || m_thread.isRunning()) {
113 return false;
114 }
115
116 m_thread.start();
117 if (raop_start(m_raop, &port) < 0) {
118 m_thread.quit();
119 m_thread.wait();
120 return false;
121 }
122 if (dnssd_register_raop(m_dnssd, name.toUtf8(), port) < 0) {
123 raop_stop(m_raop);
124 m_thread.quit();
125 m_thread.wait();
126 return false;
127 }
128
129 return true;
130 }
131
132 void RaopService::stop()
133 {
134 if (m_dnssd) {
135 dnssd_unregister_raop(m_dnssd);
136 }
137 if (m_raop) {
138 raop_stop(m_raop);
139 }
140 if (m_thread.isRunning()) {
141 m_thread.quit();
142 m_thread.wait();
143 }
144 }
145