Move hwaddress to raop_start instead of raop_init.
[deb_shairplay.git] / AirTV-Qt / raopservice.cpp
1 /**
2 * Copyright (C) 2011-2012 Juho Vähä-Herttua
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 */
14
15 #include "raopservice.h"
16
17 #include <QDebug>
18 #include <QFile>
19
20 static void
21 audio_init(void *cls, void **session, int bits, int channels, int samplerate)
22 {
23 QMetaObject::invokeMethod((QObject*)cls, "audioInit", Qt::BlockingQueuedConnection,
24 Q_ARG(void*, (void*)session),
25 Q_ARG(int, bits),
26 Q_ARG(int, channels),
27 Q_ARG(int, samplerate));
28 }
29
30 static void
31 audio_set_volume(void *cls, void *session, float volume)
32 {
33 QMetaObject::invokeMethod((QObject*)cls, "audioSetVolume", Qt::BlockingQueuedConnection,
34 Q_ARG(void*, session),
35 Q_ARG(float, volume));
36 }
37
38 static void
39 audio_process(void *cls, void *session, const void *buffer, int buflen)
40 {
41 QMetaObject::invokeMethod((QObject*)cls, "audioProcess", Qt::BlockingQueuedConnection,
42 Q_ARG(void*, session),
43 Q_ARG(void*, (void*)buffer),
44 Q_ARG(int, buflen));
45 }
46
47 static void
48 audio_flush(void *cls, void *session)
49 {
50 QMetaObject::invokeMethod((QObject*)cls, "audioFlush", Qt::BlockingQueuedConnection,
51 Q_ARG(void*, session));
52 }
53
54 static void
55 audio_destroy(void *cls, void *session)
56 {
57 QMetaObject::invokeMethod((QObject*)cls, "audioDestroy", Qt::BlockingQueuedConnection,
58 Q_ARG(void*, session));
59 }
60
61 RaopService::RaopService(QObject *parent) :
62 QObject(parent),
63 m_dnssd(0),
64 m_raop(0)
65 {
66 /* This whole hack is required because QAudioOutput
67 * needs to be created in a QThread, threads created
68 * outside Qt are not allowed (they have no eventloop) */
69 m_handler.moveToThread(&m_thread);
70 }
71
72 RaopService::~RaopService()
73 {
74 this->stop();
75
76 dnssd_destroy(m_dnssd);
77 raop_destroy(m_raop);
78 }
79
80 bool RaopService::init()
81 {
82 const char hwaddr[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 };
83
84 raop_callbacks_t raop_cbs;
85 int error;
86
87 raop_cbs.cls = &m_handler;
88 raop_cbs.audio_init = audio_init;
89 raop_cbs.audio_set_volume = audio_set_volume;
90 raop_cbs.audio_process = audio_process;
91 raop_cbs.audio_flush = audio_flush;
92 raop_cbs.audio_destroy = audio_destroy;
93
94 QFile file("airport.key");
95 if (!file.exists()) {
96 // This is used when running from Qt Creator on Mac
97 file.setFileName("../../../../airport.key");
98 }
99 if (!file.exists()) {
100 // This is used when running from Qt Creator on Windows
101 file.setFileName("../airport.key");
102 }
103 if (!file.exists()) {
104 return false;
105 }
106 file.open(QIODevice::ReadOnly);
107 QByteArray array = file.read(file.size());
108 array.append('\0');
109
110 m_raop = raop_init(&raop_cbs, array.data());
111 if (!m_raop) {
112 return false;
113 }
114
115 m_dnssd = dnssd_init(hwaddr, sizeof(hwaddr), &error);
116 if (!m_dnssd) {
117 raop_destroy(m_raop);
118 m_raop = NULL;
119 return false;
120 }
121
122 return true;
123 }
124
125 bool RaopService::start(const QString & name, quint16 port)
126 {
127 const char hwaddr[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 };
128
129 if (!m_raop || !m_dnssd || m_thread.isRunning()) {
130 return false;
131 }
132
133 m_thread.start();
134 if (raop_start(m_raop, &port, hwaddr, sizeof(hwaddr)) < 0) {
135 m_thread.quit();
136 m_thread.wait();
137 return false;
138 }
139 if (dnssd_register_raop(m_dnssd, name.toUtf8(), port) < 0) {
140 raop_stop(m_raop);
141 m_thread.quit();
142 m_thread.wait();
143 return false;
144 }
145
146 return true;
147 }
148
149 void RaopService::stop()
150 {
151 if (m_dnssd) {
152 dnssd_unregister_raop(m_dnssd);
153 }
154 if (m_raop) {
155 raop_stop(m_raop);
156 }
157 if (m_thread.isRunning()) {
158 m_thread.quit();
159 m_thread.wait();
160 }
161 }
162