Update the python bindings to the latest API version
[deb_shairplay.git] / AirTV-Qt / raopservice.cpp
CommitLineData
23e7e3ae
JVH
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
2340bcd3
JVH
15#include "raopservice.h"
16
17#include <QDebug>
18#include <QFile>
19
4fb2a472
JVH
20static void*
21audio_init(void *cls, int bits, int channels, int samplerate)
2340bcd3 22{
4fb2a472 23 void *session;
2340bcd3 24 QMetaObject::invokeMethod((QObject*)cls, "audioInit", Qt::BlockingQueuedConnection,
4fb2a472 25 Q_ARG(void*, (void*)&session),
2340bcd3
JVH
26 Q_ARG(int, bits),
27 Q_ARG(int, channels),
28 Q_ARG(int, samplerate));
4fb2a472 29 return session;
2340bcd3
JVH
30}
31
32static void
33audio_set_volume(void *cls, void *session, float volume)
34{
35 QMetaObject::invokeMethod((QObject*)cls, "audioSetVolume", Qt::BlockingQueuedConnection,
36 Q_ARG(void*, session),
37 Q_ARG(float, volume));
38}
39
40static void
41audio_process(void *cls, void *session, const void *buffer, int buflen)
42{
43 QMetaObject::invokeMethod((QObject*)cls, "audioProcess", Qt::BlockingQueuedConnection,
44 Q_ARG(void*, session),
45 Q_ARG(void*, (void*)buffer),
46 Q_ARG(int, buflen));
47}
48
49static void
50audio_flush(void *cls, void *session)
51{
52 QMetaObject::invokeMethod((QObject*)cls, "audioFlush", Qt::BlockingQueuedConnection,
53 Q_ARG(void*, session));
54}
55
56static void
57audio_destroy(void *cls, void *session)
58{
59 QMetaObject::invokeMethod((QObject*)cls, "audioDestroy", Qt::BlockingQueuedConnection,
60 Q_ARG(void*, session));
61}
62
63RaopService::RaopService(QObject *parent) :
64 QObject(parent),
65 m_dnssd(0),
66 m_raop(0)
67{
68 /* This whole hack is required because QAudioOutput
69 * needs to be created in a QThread, threads created
70 * outside Qt are not allowed (they have no eventloop) */
71 m_handler.moveToThread(&m_thread);
72}
73
74RaopService::~RaopService()
75{
76 this->stop();
77
78 dnssd_destroy(m_dnssd);
79 raop_destroy(m_raop);
80}
81
82bool RaopService::init()
83{
2340bcd3
JVH
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
406e9777 110 m_raop = raop_init(&raop_cbs, array.data());
2340bcd3
JVH
111 if (!m_raop) {
112 return false;
113 }
114
4fb2a472 115 m_dnssd = dnssd_init(&error);
2340bcd3
JVH
116 if (!m_dnssd) {
117 raop_destroy(m_raop);
118 m_raop = NULL;
119 return false;
120 }
121
122 return true;
123}
124
125bool RaopService::start(const QString & name, quint16 port)
126{
406e9777
JVH
127 const char hwaddr[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 };
128
2340bcd3
JVH
129 if (!m_raop || !m_dnssd || m_thread.isRunning()) {
130 return false;
131 }
132
133 m_thread.start();
a68fedbb 134 if (raop_start(m_raop, &port, hwaddr, sizeof(hwaddr), NULL) < 0) {
2340bcd3
JVH
135 m_thread.quit();
136 m_thread.wait();
137 return false;
138 }
a68fedbb 139 if (dnssd_register_raop(m_dnssd, name.toUtf8(), port, hwaddr, sizeof(hwaddr), 0) < 0) {
2340bcd3
JVH
140 raop_stop(m_raop);
141 m_thread.quit();
142 m_thread.wait();
143 return false;
144 }
145
146 return true;
147}
148
149void 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