43689c3e7118c42a54e2f28d5d1332db57a2f1e1
[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 raop_callbacks_t raop_cbs;
84 int error;
85
86 raop_cbs.cls = &m_handler;
87 raop_cbs.audio_init = audio_init;
88 raop_cbs.audio_set_volume = audio_set_volume;
89 raop_cbs.audio_process = audio_process;
90 raop_cbs.audio_flush = audio_flush;
91 raop_cbs.audio_destroy = audio_destroy;
92
93 QFile file("airport.key");
94 if (!file.exists()) {
95 // This is used when running from Qt Creator on Mac
96 file.setFileName("../../../../airport.key");
97 }
98 if (!file.exists()) {
99 // This is used when running from Qt Creator on Windows
100 file.setFileName("../airport.key");
101 }
102 if (!file.exists()) {
103 return false;
104 }
105 file.open(QIODevice::ReadOnly);
106 QByteArray array = file.read(file.size());
107 array.append('\0');
108
109 m_raop = raop_init(&raop_cbs, array.data(), hwaddr, sizeof(hwaddr));
110 if (!m_raop) {
111 return false;
112 }
113
114 m_dnssd = dnssd_init(hwaddr, sizeof(hwaddr), &error);
115 if (!m_dnssd) {
116 raop_destroy(m_raop);
117 m_raop = NULL;
118 return false;
119 }
120
121 return true;
122 }
123
124 bool RaopService::start(const QString & name, quint16 port)
125 {
126 if (!m_raop || !m_dnssd || m_thread.isRunning()) {
127 return false;
128 }
129
130 m_thread.start();
131 if (raop_start(m_raop, &port) < 0) {
132 m_thread.quit();
133 m_thread.wait();
134 return false;
135 }
136 if (dnssd_register_raop(m_dnssd, name.toUtf8(), port) < 0) {
137 raop_stop(m_raop);
138 m_thread.quit();
139 m_thread.wait();
140 return false;
141 }
142
143 return true;
144 }
145
146 void RaopService::stop()
147 {
148 if (m_dnssd) {
149 dnssd_unregister_raop(m_dnssd);
150 }
151 if (m_raop) {
152 raop_stop(m_raop);
153 }
154 if (m_thread.isRunning()) {
155 m_thread.quit();
156 m_thread.wait();
157 }
158 }
159