Initial commit to the repository
[deb_shairplay.git] / AirTV-Qt / raopservice.cpp
CommitLineData
2340bcd3
JVH
1#include "raopservice.h"
2
3#include <QDebug>
4#include <QFile>
5
6static void
7audio_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
16static void
17audio_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
24static void
25audio_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
33static void
34audio_flush(void *cls, void *session)
35{
36 QMetaObject::invokeMethod((QObject*)cls, "audioFlush", Qt::BlockingQueuedConnection,
37 Q_ARG(void*, session));
38}
39
40static void
41audio_destroy(void *cls, void *session)
42{
43 QMetaObject::invokeMethod((QObject*)cls, "audioDestroy", Qt::BlockingQueuedConnection,
44 Q_ARG(void*, session));
45}
46
47RaopService::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
58RaopService::~RaopService()
59{
60 this->stop();
61
62 dnssd_destroy(m_dnssd);
63 raop_destroy(m_raop);
64}
65
66bool 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
110bool 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
132void 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