From 9434b30cc94fe0ebbd58fa8a149f3c9475dc156d Mon Sep 17 00:00:00 2001 From: =?utf8?q?Juho=20V=C3=A4h=C3=A4-Herttua?= Date: Sat, 19 May 2012 11:22:01 +0300 Subject: [PATCH] Start writing the Qt4 bindings --- AirTV-Qt/AirTV.pro | 26 +++- AirTV-Qt/audiocallbacks.cpp | 44 ++++++ AirTV-Qt/audiocallbacks.h | 30 +++++ AirTV-Qt/audiooutput.cpp | 4 +- AirTV-Qt/audiooutput.h | 2 +- AirTV-Qt/mainapplication.cpp | 11 +- AirTV-Qt/mainapplication.h | 4 + AirTV-Qt/raopcallbackhandler.cpp | 59 --------- AirTV-Qt/raopcallbackhandler.h | 41 ------ AirTV-Qt/raopservice.cpp | 162 ----------------------- AirTV-Qt/raopservice.h | 50 ------- src/bindings/qt4/dnssdservice.cpp | 41 ++++++ src/bindings/qt4/dnssdservice.h | 32 +++++ src/bindings/qt4/raopcallbackhandler.cpp | 62 +++++++++ src/bindings/qt4/raopcallbackhandler.h | 30 +++++ src/bindings/qt4/raopcallbacks.h | 27 ++++ src/bindings/qt4/raopservice.cpp | 159 ++++++++++++++++++++++ src/bindings/qt4/raopservice.h | 35 +++++ 18 files changed, 497 insertions(+), 322 deletions(-) create mode 100644 AirTV-Qt/audiocallbacks.cpp create mode 100644 AirTV-Qt/audiocallbacks.h delete mode 100644 AirTV-Qt/raopcallbackhandler.cpp delete mode 100644 AirTV-Qt/raopcallbackhandler.h delete mode 100644 AirTV-Qt/raopservice.cpp delete mode 100644 AirTV-Qt/raopservice.h create mode 100644 src/bindings/qt4/dnssdservice.cpp create mode 100644 src/bindings/qt4/dnssdservice.h create mode 100644 src/bindings/qt4/raopcallbackhandler.cpp create mode 100644 src/bindings/qt4/raopcallbackhandler.h create mode 100644 src/bindings/qt4/raopcallbacks.h create mode 100644 src/bindings/qt4/raopservice.cpp create mode 100644 src/bindings/qt4/raopservice.h diff --git a/AirTV-Qt/AirTV.pro b/AirTV-Qt/AirTV.pro index 207743c..ef524ab 100644 --- a/AirTV-Qt/AirTV.pro +++ b/AirTV-Qt/AirTV.pro @@ -24,7 +24,7 @@ macx { QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.5 } -INCLUDEPATH += ../src/include/ +INCLUDEPATH += ../src/include/ ../src/bindings/qt4/ SOURCES += main.cpp\ ../src/lib/utils.c \ ../src/lib/sdp.c \ @@ -50,18 +50,34 @@ SOURCES += main.cpp\ ../src/lib/logger.c \ ../src/lib/digest.c \ audiooutput.cpp \ - raopservice.cpp \ mainapplication.cpp \ - raopcallbackhandler.cpp + audiocallbacks.cpp \ + ../src/bindings/qt4/raopservice.cpp \ + ../src/bindings/qt4/raopcallbackhandler.cpp \ + ../src/bindings/qt4/dnssdservice.cpp HEADERS += \ audiooutput.h \ - raopservice.h \ mainapplication.h \ - raopcallbackhandler.h + audiocallbacks.h \ + ../src/bindings/qt4/raopservice.h \ + ../src/bindings/qt4/raopcallbacks.h \ + ../src/bindings/qt4/raopcallbackhandler.h \ + ../src/bindings/qt4/dnssdservice.h FORMS += mainwindow.ui RESOURCES += \ AirTV.qrc + + + + + + + + + + + diff --git a/AirTV-Qt/audiocallbacks.cpp b/AirTV-Qt/audiocallbacks.cpp new file mode 100644 index 0000000..ac402b5 --- /dev/null +++ b/AirTV-Qt/audiocallbacks.cpp @@ -0,0 +1,44 @@ +#include "audiocallbacks.h" + +AudioCallbacks::AudioCallbacks(QObject *parent) : + RaopCallbacks(parent) +{ +} + +void * AudioCallbacks::audioInit(int bits, int channels, int samplerate) +{ + AudioOutput *audioOutput = new AudioOutput(0); + audioOutput->init(bits, channels, samplerate); + audioOutput->start(); + m_outputList.append(audioOutput); + return audioOutput; +} + +void AudioCallbacks::audioProcess(void *session, const QByteArray & buffer) +{ + AudioOutput *audioOutput = (AudioOutput*)session; + audioOutput->output(buffer); +} + +void AudioCallbacks::audioDestroy(void *session) +{ + AudioOutput *audioOutput = (AudioOutput*)session; + m_outputList.removeAll(audioOutput); + + audioOutput->stop(); + delete audioOutput; +} + + +void AudioCallbacks::audioFlush(void *session) +{ + AudioOutput *audioOutput = (AudioOutput*)session; + audioOutput->flush(); +} + +void AudioCallbacks::audioSetVolume(void *session, float volume) +{ + AudioOutput *audioOutput = (AudioOutput*)session; + audioOutput->setVolume(volume); +} + diff --git a/AirTV-Qt/audiocallbacks.h b/AirTV-Qt/audiocallbacks.h new file mode 100644 index 0000000..835dd46 --- /dev/null +++ b/AirTV-Qt/audiocallbacks.h @@ -0,0 +1,30 @@ +#ifndef AUDIOCALLBACKS_H +#define AUDIOCALLBACKS_H + +#include "raopcallbacks.h" + +#include "audiooutput.h" + +class AudioCallbacks : public RaopCallbacks +{ + Q_OBJECT +public: + explicit AudioCallbacks(QObject *parent = 0); + + virtual void * audioInit(int bits, int channels, int samplerate); + virtual void audioSetVolume(void *session, float volume); + virtual void audioProcess(void *session, const QByteArray &buffer); + virtual void audioFlush(void *session); + virtual void audioDestroy(void *session); + + +private: + QList m_outputList; + +signals: + +public slots: + +}; + +#endif // AUDIOCALLBACKS_H diff --git a/AirTV-Qt/audiooutput.cpp b/AirTV-Qt/audiooutput.cpp index 5b4bc2f..e2e2d48 100644 --- a/AirTV-Qt/audiooutput.cpp +++ b/AirTV-Qt/audiooutput.cpp @@ -96,11 +96,11 @@ void AudioOutput::setVolume(float volume) m_volume = volume; } -void AudioOutput::output(const char *data, int datalen) +void AudioOutput::output(const QByteArray & data) { if (m_output && m_output->state() != QAudio::StoppedState) { // Append input data to the end of buffer - m_buffer.append(data, datalen); + m_buffer.append(data); // Check if our buffer has grown too large if (m_buffer.length() > 2*BUFFER_SIZE) { diff --git a/AirTV-Qt/audiooutput.h b/AirTV-Qt/audiooutput.h index 8a6bdf7..8e61584 100644 --- a/AirTV-Qt/audiooutput.h +++ b/AirTV-Qt/audiooutput.h @@ -33,7 +33,7 @@ public: void start(); void setVolume(float volume); - void output(const char *data, int datalen); + void output(const QByteArray & data); void flush(); void stop(); diff --git a/AirTV-Qt/mainapplication.cpp b/AirTV-Qt/mainapplication.cpp index 477f782..62b3f62 100644 --- a/AirTV-Qt/mainapplication.cpp +++ b/AirTV-Qt/mainapplication.cpp @@ -6,10 +6,12 @@ MainApplication::MainApplication(QObject *parent) : QObject(parent) { raopService = new RaopService(0); + dnssdService = new DnssdService(0); trayIconMenu = new QMenu(0); // Initialize the service - raopService->init(); + raopService->init(10, &m_callbacks); + dnssdService->init(); quitAction = new QAction(tr("&Quit"), trayIconMenu); connect(quitAction, SIGNAL(triggered()), this, SIGNAL(quitRequested())); @@ -30,12 +32,17 @@ MainApplication::~MainApplication() void MainApplication::start() { - raopService->start(); + char chwaddr[] = { 0x01, 0x23, 0x45, 0x67, 0x89, 0xAB }; + QByteArray hwaddr(chwaddr, sizeof(chwaddr)); + + raopService->start(5000, hwaddr); + dnssdService->registerRaop("Shairplay", 5000, hwaddr); trayIcon->show(); } void MainApplication::stop() { + dnssdService->unregisterRaop(); raopService->stop(); trayIcon->hide(); } diff --git a/AirTV-Qt/mainapplication.h b/AirTV-Qt/mainapplication.h index 0e80c39..b24b791 100644 --- a/AirTV-Qt/mainapplication.h +++ b/AirTV-Qt/mainapplication.h @@ -21,6 +21,8 @@ #include #include "raopservice.h" +#include "dnssdservice.h" +#include "audiocallbacks.h" class MainApplication : public QObject { @@ -34,6 +36,8 @@ public: private: RaopService *raopService; + DnssdService *dnssdService; + AudioCallbacks m_callbacks; QSystemTrayIcon *trayIcon; QMenu *trayIconMenu; diff --git a/AirTV-Qt/raopcallbackhandler.cpp b/AirTV-Qt/raopcallbackhandler.cpp deleted file mode 100644 index 752e185..0000000 --- a/AirTV-Qt/raopcallbackhandler.cpp +++ /dev/null @@ -1,59 +0,0 @@ -/** - * Copyright (C) 2011-2012 Juho Vähä-Herttua - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "raopcallbackhandler.h" - -RaopCallbackHandler::RaopCallbackHandler(QObject *parent) : - QObject(parent) -{ -} - -void RaopCallbackHandler::audioInit(void *session, int bits, int channels, int samplerate) -{ - void **retval = (void**)session; - - AudioOutput *audioOutput = new AudioOutput(0); - audioOutput->init(bits, channels, samplerate); - audioOutput->start(); - *retval = audioOutput; - - m_outputList.append(audioOutput); -} - -void RaopCallbackHandler::audioSetVolume(void *session, float volume) -{ - AudioOutput *audioOutput = (AudioOutput*)session; - audioOutput->setVolume(volume); -} - -void RaopCallbackHandler::audioProcess(void *session, void *buffer, int buflen) -{ - AudioOutput *audioOutput = (AudioOutput*)session; - audioOutput->output((const char *)buffer, buflen); -} - -void RaopCallbackHandler::audioFlush(void *session) -{ - AudioOutput *audioOutput = (AudioOutput*)session; - audioOutput->flush(); -} - -void RaopCallbackHandler::audioDestroy(void *session) -{ - AudioOutput *audioOutput = (AudioOutput*)session; - m_outputList.removeAll(audioOutput); - - audioOutput->stop(); - delete audioOutput; -} diff --git a/AirTV-Qt/raopcallbackhandler.h b/AirTV-Qt/raopcallbackhandler.h deleted file mode 100644 index 0ff6f1f..0000000 --- a/AirTV-Qt/raopcallbackhandler.h +++ /dev/null @@ -1,41 +0,0 @@ -/** - * Copyright (C) 2011-2012 Juho Vähä-Herttua - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#ifndef RAOPCALLBACKHANDLER_H -#define RAOPCALLBACKHANDLER_H - -#include - -#include "audiooutput.h" - -class RaopCallbackHandler : public QObject -{ - Q_OBJECT -public: - explicit RaopCallbackHandler(QObject *parent = 0); - -private: - QList m_outputList; - -signals: - -public slots: - void audioInit(void *session, int bits, int channels, int samplerate); - void audioSetVolume(void *session, float volume); - void audioProcess(void *session, void *buffer, int buflen); - void audioFlush(void *session); - void audioDestroy(void *session); -}; - -#endif // RAOPCALLBACKHANDLER_H diff --git a/AirTV-Qt/raopservice.cpp b/AirTV-Qt/raopservice.cpp deleted file mode 100644 index 1c3519d..0000000 --- a/AirTV-Qt/raopservice.cpp +++ /dev/null @@ -1,162 +0,0 @@ -/** - * Copyright (C) 2011-2012 Juho Vähä-Herttua - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#include "raopservice.h" - -#include -#include - -static void* -audio_init(void *cls, int bits, int channels, int samplerate) -{ - void *session; - QMetaObject::invokeMethod((QObject*)cls, "audioInit", Qt::BlockingQueuedConnection, - Q_ARG(void*, (void*)&session), - Q_ARG(int, bits), - Q_ARG(int, channels), - Q_ARG(int, samplerate)); - return session; -} - -static void -audio_set_volume(void *cls, void *session, float volume) -{ - QMetaObject::invokeMethod((QObject*)cls, "audioSetVolume", Qt::BlockingQueuedConnection, - Q_ARG(void*, session), - Q_ARG(float, volume)); -} - -static void -audio_process(void *cls, void *session, const void *buffer, int buflen) -{ - QMetaObject::invokeMethod((QObject*)cls, "audioProcess", Qt::BlockingQueuedConnection, - Q_ARG(void*, session), - Q_ARG(void*, (void*)buffer), - Q_ARG(int, buflen)); -} - -static void -audio_flush(void *cls, void *session) -{ - QMetaObject::invokeMethod((QObject*)cls, "audioFlush", Qt::BlockingQueuedConnection, - Q_ARG(void*, session)); -} - -static void -audio_destroy(void *cls, void *session) -{ - QMetaObject::invokeMethod((QObject*)cls, "audioDestroy", Qt::BlockingQueuedConnection, - Q_ARG(void*, session)); -} - -RaopService::RaopService(QObject *parent) : - QObject(parent), - m_dnssd(0), - m_raop(0) -{ - /* This whole hack is required because QAudioOutput - * needs to be created in a QThread, threads created - * outside Qt are not allowed (they have no eventloop) */ - m_handler.moveToThread(&m_thread); -} - -RaopService::~RaopService() -{ - this->stop(); - - dnssd_destroy(m_dnssd); - raop_destroy(m_raop); -} - -bool RaopService::init() -{ - raop_callbacks_t raop_cbs; - int error; - - raop_cbs.cls = &m_handler; - raop_cbs.audio_init = audio_init; - raop_cbs.audio_set_volume = audio_set_volume; - raop_cbs.audio_process = audio_process; - raop_cbs.audio_flush = audio_flush; - raop_cbs.audio_destroy = audio_destroy; - - QFile file("airport.key"); - if (!file.exists()) { - // This is used when running from Qt Creator on Mac - file.setFileName("../../../../airport.key"); - } - if (!file.exists()) { - // This is used when running from Qt Creator on Windows - file.setFileName("../airport.key"); - } - if (!file.exists()) { - return false; - } - file.open(QIODevice::ReadOnly); - QByteArray array = file.read(file.size()); - array.append('\0'); - - m_raop = raop_init(&raop_cbs, array.data()); - if (!m_raop) { - return false; - } - - m_dnssd = dnssd_init(&error); - if (!m_dnssd) { - raop_destroy(m_raop); - m_raop = NULL; - return false; - } - - return true; -} - -bool RaopService::start(const QString & name, quint16 port) -{ - const char hwaddr[] = { 0x48, 0x5d, 0x60, 0x7c, 0xee, 0x22 }; - - if (!m_raop || !m_dnssd || m_thread.isRunning()) { - return false; - } - - m_thread.start(); - if (raop_start(m_raop, &port, hwaddr, sizeof(hwaddr), NULL) < 0) { - m_thread.quit(); - m_thread.wait(); - return false; - } - if (dnssd_register_raop(m_dnssd, name.toUtf8(), port, hwaddr, sizeof(hwaddr), 0) < 0) { - raop_stop(m_raop); - m_thread.quit(); - m_thread.wait(); - return false; - } - - return true; -} - -void RaopService::stop() -{ - if (m_dnssd) { - dnssd_unregister_raop(m_dnssd); - } - if (m_raop) { - raop_stop(m_raop); - } - if (m_thread.isRunning()) { - m_thread.quit(); - m_thread.wait(); - } -} - diff --git a/AirTV-Qt/raopservice.h b/AirTV-Qt/raopservice.h deleted file mode 100644 index 450cbb6..0000000 --- a/AirTV-Qt/raopservice.h +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Copyright (C) 2011-2012 Juho Vähä-Herttua - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - */ - -#ifndef RAOPSERVICE_H -#define RAOPSERVICE_H - -#include -#include - -#include "raopcallbackhandler.h" - -#include "dnssd.h" -#include "raop.h" - -class RaopService : public QObject -{ - Q_OBJECT -public: - explicit RaopService(QObject *parent = 0); - ~RaopService(); - - bool init(); - bool start(const QString & name=QString("AirTV"), quint16 port=5000); - void stop(); - -private: - dnssd_t * m_dnssd; - raop_t * m_raop; - - QThread m_thread; - RaopCallbackHandler m_handler; - -signals: - -public slots: - -}; - -#endif // RAOPSERVICE_H diff --git a/src/bindings/qt4/dnssdservice.cpp b/src/bindings/qt4/dnssdservice.cpp new file mode 100644 index 0000000..a079a44 --- /dev/null +++ b/src/bindings/qt4/dnssdservice.cpp @@ -0,0 +1,41 @@ +#include "dnssdservice.h" + +DnssdService::DnssdService(QObject *parent) : + QObject(parent) +{ +} + +bool DnssdService::init() +{ + int error; + m_dnssd = dnssd_init(&error); + if (!m_dnssd) { + return false; + } + return true; +} + +DnssdService::~DnssdService() +{ + dnssd_destroy(m_dnssd); +} + +void DnssdService::registerRaop(const QString & name, quint16 port, const QByteArray & hwaddr) +{ + dnssd_register_raop(m_dnssd, name.toUtf8().data(), port, hwaddr.data(), hwaddr.size(), 0); +} + +void DnssdService::unregisterRaop() +{ + dnssd_unregister_raop(m_dnssd); +} + +void DnssdService::registerAirplay(const QString &name, quint16 port, const QByteArray &hwaddr) +{ + dnssd_register_airplay(m_dnssd, name.toUtf8().data(), port, hwaddr.data(), hwaddr.size()); +} + +void DnssdService::unregisterAirplay() +{ + dnssd_unregister_airplay(m_dnssd); +} diff --git a/src/bindings/qt4/dnssdservice.h b/src/bindings/qt4/dnssdservice.h new file mode 100644 index 0000000..a2ee573 --- /dev/null +++ b/src/bindings/qt4/dnssdservice.h @@ -0,0 +1,32 @@ +#ifndef DNSSDSERVICE_H +#define DNSSDSERVICE_H + +#include + +#include "dnssd.h" + +class DnssdService : public QObject +{ + Q_OBJECT +public: + explicit DnssdService(QObject *parent = 0); + ~DnssdService(); + + bool init(); + + void registerRaop(const QString & name, quint16 port, const QByteArray & hwaddr); + void unregisterRaop(); + + void registerAirplay(const QString & name, quint16 port, const QByteArray & hwaddr); + void unregisterAirplay(); + +private: + dnssd_t * m_dnssd; + +signals: + +public slots: + +}; + +#endif // DNSSDSERVICE_H diff --git a/src/bindings/qt4/raopcallbackhandler.cpp b/src/bindings/qt4/raopcallbackhandler.cpp new file mode 100644 index 0000000..edb50db --- /dev/null +++ b/src/bindings/qt4/raopcallbackhandler.cpp @@ -0,0 +1,62 @@ +#include "raopcallbackhandler.h" + +RaopCallbackHandler::RaopCallbackHandler(QObject *parent) : + QObject(parent) +{ +} + +void RaopCallbackHandler::init(RaopCallbacks *callbacks) +{ + m_callbacks = callbacks; +} + +void RaopCallbackHandler::audioInit(void *session, int bits, int channels, int samplerate) +{ + void **retval = (void**)session; + if (m_callbacks) { + *retval = m_callbacks->audioInit(bits, channels, samplerate); + } +} + +void RaopCallbackHandler::audioProcess(void *session, void *buffer, int buflen) +{ + if (m_callbacks) { + m_callbacks->audioProcess(session, QByteArray((const char *)buffer, buflen)); + } +} + +void RaopCallbackHandler::audioDestroy(void *session) +{ + if (m_callbacks) { + m_callbacks->audioDestroy(session); + } +} + +void RaopCallbackHandler::audioFlush(void *session) +{ + if (m_callbacks) { + m_callbacks->audioFlush(session); + } +} + +void RaopCallbackHandler::audioSetVolume(void *session, float volume) +{ + if (m_callbacks) { + m_callbacks->audioSetVolume(session, volume); + } +} + +void RaopCallbackHandler::audioSetMetadata(void *session, void *buffer, int buflen) +{ + if (m_callbacks) { + m_callbacks->audioSetMetadata(session, QByteArray((const char *)buffer, buflen)); + } +} + +void RaopCallbackHandler::audioSetCoverart(void *session, void *buffer, int buflen) +{ + if (m_callbacks) { + m_callbacks->audioSetCoverart(session, QByteArray((const char *)buffer, buflen)); + } +} + diff --git a/src/bindings/qt4/raopcallbackhandler.h b/src/bindings/qt4/raopcallbackhandler.h new file mode 100644 index 0000000..cf91bdc --- /dev/null +++ b/src/bindings/qt4/raopcallbackhandler.h @@ -0,0 +1,30 @@ +#ifndef RAOPCALLBACKHANDLER_H +#define RAOPCALLBACKHANDLER_H + +#include + +#include "raopcallbacks.h" + +class RaopCallbackHandler : public QObject +{ + Q_OBJECT +public: + explicit RaopCallbackHandler(QObject *parent = 0); + void init(RaopCallbacks *callbacks); + +private: + RaopCallbacks * m_callbacks; + +signals: + +public slots: + void audioInit(void *session, int bits, int channels, int samplerate); + void audioProcess(void *session, void *buffer, int buflen); + void audioDestroy(void *session); + void audioFlush(void *session); + void audioSetVolume(void *session, float volume); + void audioSetMetadata(void *session, void *buffer, int buflen); + void audioSetCoverart(void *session, void *buffer, int buflen); +}; + +#endif // RAOPCALLBACKHANDLER_H diff --git a/src/bindings/qt4/raopcallbacks.h b/src/bindings/qt4/raopcallbacks.h new file mode 100644 index 0000000..53a4468 --- /dev/null +++ b/src/bindings/qt4/raopcallbacks.h @@ -0,0 +1,27 @@ +#ifndef RAOPCALLBACKS_H +#define RAOPCALLBACKS_H + +#include + +class RaopCallbacks : public QObject +{ + Q_OBJECT +public: + explicit RaopCallbacks(QObject *parent = 0) : QObject(parent) {} + + virtual void * audioInit(int bits, int channels, int samplerate) = 0; + virtual void audioProcess(void *session, const QByteArray & buffer) = 0; + virtual void audioDestroy(void *session) = 0; + + virtual void audioFlush(void *session) { Q_UNUSED(session) } + virtual void audioSetVolume(void *session, float volume) { Q_UNUSED(session) Q_UNUSED(volume) } + virtual void audioSetMetadata(void *session, const QByteArray & buffer) { Q_UNUSED(session) Q_UNUSED(buffer) } + virtual void audioSetCoverart(void *session, const QByteArray & buffer) { Q_UNUSED(session) Q_UNUSED(buffer) } + +signals: + +public slots: + +}; + +#endif // RAOPCALLBACKS_H diff --git a/src/bindings/qt4/raopservice.cpp b/src/bindings/qt4/raopservice.cpp new file mode 100644 index 0000000..f575aa2 --- /dev/null +++ b/src/bindings/qt4/raopservice.cpp @@ -0,0 +1,159 @@ +#include "raopservice.h" + +#include + +#include + +#define RSA_KEY \ +"-----BEGIN RSA PRIVATE KEY-----\n"\ +"MIIEpQIBAAKCAQEA59dE8qLieItsH1WgjrcFRKj6eUWqi+bGLOX1HL3U3GhC/j0Qg90u3sG/1CUt\n"\ +"wC5vOYvfDmFI6oSFXi5ELabWJmT2dKHzBJKa3k9ok+8t9ucRqMd6DZHJ2YCCLlDRKSKv6kDqnw4U\n"\ +"wPdpOMXziC/AMj3Z/lUVX1G7WSHCAWKf1zNS1eLvqr+boEjXuBOitnZ/bDzPHrTOZz0Dew0uowxf\n"\ +"/+sG+NCK3eQJVxqcaJ/vEHKIVd2M+5qL71yJQ+87X6oV3eaYvt3zWZYD6z5vYTcrtij2VZ9Zmni/\n"\ +"UAaHqn9JdsBWLUEpVviYnhimNVvYFZeCXg/IdTQ+x4IRdiXNv5hEewIDAQABAoIBAQDl8Axy9XfW\n"\ +"BLmkzkEiqoSwF0PsmVrPzH9KsnwLGH+QZlvjWd8SWYGN7u1507HvhF5N3drJoVU3O14nDY4TFQAa\n"\ +"LlJ9VM35AApXaLyY1ERrN7u9ALKd2LUwYhM7Km539O4yUFYikE2nIPscEsA5ltpxOgUGCY7b7ez5\n"\ +"NtD6nL1ZKauw7aNXmVAvmJTcuPxWmoktF3gDJKK2wxZuNGcJE0uFQEG4Z3BrWP7yoNuSK3dii2jm\n"\ +"lpPHr0O/KnPQtzI3eguhe0TwUem/eYSdyzMyVx/YpwkzwtYL3sR5k0o9rKQLtvLzfAqdBxBurciz\n"\ +"aaA/L0HIgAmOit1GJA2saMxTVPNhAoGBAPfgv1oeZxgxmotiCcMXFEQEWflzhWYTsXrhUIuz5jFu\n"\ +"a39GLS99ZEErhLdrwj8rDDViRVJ5skOp9zFvlYAHs0xh92ji1E7V/ysnKBfsMrPkk5KSKPrnjndM\n"\ +"oPdevWnVkgJ5jxFuNgxkOLMuG9i53B4yMvDTCRiIPMQ++N2iLDaRAoGBAO9v//mU8eVkQaoANf0Z\n"\ +"oMjW8CN4xwWA2cSEIHkd9AfFkftuv8oyLDCG3ZAf0vrhrrtkrfa7ef+AUb69DNggq4mHQAYBp7L+\n"\ +"k5DKzJrKuO0r+R0YbY9pZD1+/g9dVt91d6LQNepUE/yY2PP5CNoFmjedpLHMOPFdVgqDzDFxU8hL\n"\ +"AoGBANDrr7xAJbqBjHVwIzQ4To9pb4BNeqDndk5Qe7fT3+/H1njGaC0/rXE0Qb7q5ySgnsCb3DvA\n"\ +"cJyRM9SJ7OKlGt0FMSdJD5KG0XPIpAVNwgpXXH5MDJg09KHeh0kXo+QA6viFBi21y340NonnEfdf\n"\ +"54PX4ZGS/Xac1UK+pLkBB+zRAoGAf0AY3H3qKS2lMEI4bzEFoHeK3G895pDaK3TFBVmD7fV0Zhov\n"\ +"17fegFPMwOII8MisYm9ZfT2Z0s5Ro3s5rkt+nvLAdfC/PYPKzTLalpGSwomSNYJcB9HNMlmhkGzc\n"\ +"1JnLYT4iyUyx6pcZBmCd8bD0iwY/FzcgNDaUmbX9+XDvRA0CgYEAkE7pIPlE71qvfJQgoA9em0gI\n"\ +"LAuE4Pu13aKiJnfft7hIjbK+5kyb3TysZvoyDnb3HOKvInK7vXbKuU4ISgxB2bB3HcYzQMGsz1qJ\n"\ +"2gG0N5hvJpzwwhbhXqFKA4zaaSrw622wDniAK5MlIE0tIAKKP4yxNGjoD2QYjhBGuhvkWKY=\n"\ +"-----END RSA PRIVATE KEY-----\n" + + +static void* +audio_init_cb(void *cls, int bits, int channels, int samplerate) +{ + void *session; + QMetaObject::invokeMethod((QObject*)cls, "audioInit", Qt::BlockingQueuedConnection, + Q_ARG(void*, (void*)&session), + Q_ARG(int, bits), + Q_ARG(int, channels), + Q_ARG(int, samplerate)); + return session; +} + +static void +audio_process_cb(void *cls, void *session, const void *buffer, int buflen) +{ + QMetaObject::invokeMethod((QObject*)cls, "audioProcess", Qt::BlockingQueuedConnection, + Q_ARG(void*, session), + Q_ARG(void*, (void*)buffer), + Q_ARG(int, buflen)); +} + +static void +audio_destroy_cb(void *cls, void *session) +{ + QMetaObject::invokeMethod((QObject*)cls, "audioDestroy", Qt::BlockingQueuedConnection, + Q_ARG(void*, session)); +} + +static void +audio_flush_cb(void *cls, void *session) +{ + QMetaObject::invokeMethod((QObject*)cls, "audioFlush", Qt::BlockingQueuedConnection, + Q_ARG(void*, session)); +} + +static void +audio_set_volume_cb(void *cls, void *session, float volume) +{ + QMetaObject::invokeMethod((QObject*)cls, "audioSetVolume", Qt::BlockingQueuedConnection, + Q_ARG(void*, session), + Q_ARG(float, volume)); +} + +static void +audio_set_metadata_cb(void *cls, void *session, const void *buffer, int buflen) +{ + QMetaObject::invokeMethod((QObject*)cls, "audioSetVolume", Qt::BlockingQueuedConnection, + Q_ARG(void*, session), + Q_ARG(void*, (void*)buffer), + Q_ARG(int, buflen)); +} + +static void +audio_set_coverart_cb(void *cls, void *session, const void *buffer, int buflen) +{ + QMetaObject::invokeMethod((QObject*)cls, "audioSetVolume", Qt::BlockingQueuedConnection, + Q_ARG(void*, session), + Q_ARG(void*, (void*)buffer), + Q_ARG(int, buflen)); +} + +RaopService::RaopService(QObject *parent) : + QObject(parent), + m_raop(0) +{ + /* This whole hack is required because QAudioOutput + * needs to be created in a QThread, threads created + * outside Qt are not allowed (they have no eventloop) */ + m_handler.moveToThread(&m_thread); +} + +RaopService::~RaopService() +{ + this->stop(); + raop_destroy(m_raop); +} + +bool RaopService::init(int max_clients, RaopCallbacks *callbacks) +{ + raop_callbacks_t raop_cbs; + + m_handler.init(callbacks); + raop_cbs.cls = &m_handler; + raop_cbs.audio_init = &audio_init_cb; + raop_cbs.audio_process = &audio_process_cb; + raop_cbs.audio_destroy = &audio_destroy_cb; + raop_cbs.audio_flush = &audio_flush_cb; + raop_cbs.audio_set_volume = &audio_set_volume_cb; + raop_cbs.audio_set_metadata = &audio_set_metadata_cb; + raop_cbs.audio_set_coverart = &audio_set_coverart_cb; + + m_raop = raop_init(max_clients, &raop_cbs, RSA_KEY); + if (!m_raop) { + printf("Foobar\n"); + return false; + } + return true; +} + +bool RaopService::isRunning() +{ + return (raop_is_running(m_raop) != 0); +} + +bool RaopService::start(quint16 port, const QByteArray & hwaddr) +{ + int ret; + m_thread.start(); + ret = raop_start(m_raop, &port, hwaddr.data(), hwaddr.size(), 0); + if (ret < 0) { + m_thread.quit(); + m_thread.wait(); + return false; + } + return true; +} + +void RaopService::stop() +{ + if (m_raop) { + raop_stop(m_raop); + } + if (m_thread.isRunning()) { + m_thread.quit(); + m_thread.wait(); + } +} diff --git a/src/bindings/qt4/raopservice.h b/src/bindings/qt4/raopservice.h new file mode 100644 index 0000000..d01b734 --- /dev/null +++ b/src/bindings/qt4/raopservice.h @@ -0,0 +1,35 @@ +#ifndef RAOPSERVICE_H +#define RAOPSERVICE_H + +#include +#include + +#include "raopcallbackhandler.h" + +#include "raop.h" + +class RaopService : public QObject +{ + Q_OBJECT +public: + explicit RaopService(QObject *parent = 0); + ~RaopService(); + + bool init(int max_clients, RaopCallbacks *callbacks); + bool start(quint16 port, const QByteArray & hwaddr); + bool isRunning(); + void stop(); + +private: + raop_t * m_raop; + + QThread m_thread; + RaopCallbackHandler m_handler; + +signals: + +public slots: + +}; + +#endif // RAOPSERVICE_H -- 2.34.1