Start writing the Qt4 bindings
authorJuho Vähä-Herttua <juhovh@iki.fi>
Sat, 19 May 2012 08:22:01 +0000 (11:22 +0300)
committerJuho Vähä-Herttua <juhovh@iki.fi>
Sat, 19 May 2012 08:22:01 +0000 (11:22 +0300)
18 files changed:
AirTV-Qt/AirTV.pro
AirTV-Qt/audiocallbacks.cpp [new file with mode: 0644]
AirTV-Qt/audiocallbacks.h [new file with mode: 0644]
AirTV-Qt/audiooutput.cpp
AirTV-Qt/audiooutput.h
AirTV-Qt/mainapplication.cpp
AirTV-Qt/mainapplication.h
AirTV-Qt/raopcallbackhandler.cpp [deleted file]
AirTV-Qt/raopcallbackhandler.h [deleted file]
AirTV-Qt/raopservice.cpp [deleted file]
AirTV-Qt/raopservice.h [deleted file]
src/bindings/qt4/dnssdservice.cpp [new file with mode: 0644]
src/bindings/qt4/dnssdservice.h [new file with mode: 0644]
src/bindings/qt4/raopcallbackhandler.cpp [new file with mode: 0644]
src/bindings/qt4/raopcallbackhandler.h [new file with mode: 0644]
src/bindings/qt4/raopcallbacks.h [new file with mode: 0644]
src/bindings/qt4/raopservice.cpp [new file with mode: 0644]
src/bindings/qt4/raopservice.h [new file with mode: 0644]

index 207743cef9f3e126d97f5774ffcc01abe1a2084f..ef524ab4d939b60dd76cc1f5bc7feed4e1ec93c4 100644 (file)
@@ -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 (file)
index 0000000..ac402b5
--- /dev/null
@@ -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 (file)
index 0000000..835dd46
--- /dev/null
@@ -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<AudioOutput*>  m_outputList;
+
+signals:
+
+public slots:
+
+};
+
+#endif // AUDIOCALLBACKS_H
index 5b4bc2f8682f7dc944cd7f335be50afec9c999e5..e2e2d4835f16ad1e36d22546defebe9cb86b31e0 100644 (file)
@@ -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) {
index 8a6bdf76ad317fe3115046bd0e8289efad1b3972..8e61584f3b18b25c4c6f9ad4d267c27d8fdaf74b 100644 (file)
@@ -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();
 
index 477f7824a863d6cf993dd36d11fbbc544cec6af9..62b3f62857f2c39b46f91189f9fef765525428ce 100644 (file)
@@ -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();
 }
index 0e80c396d60895a3fdf1f355f39590e40cd20497..b24b791680ce4ae40c1ff662ab772cd5730d4c0f 100644 (file)
@@ -21,6 +21,8 @@
 #include <QAction>
 
 #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 (file)
index 752e185..0000000
+++ /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 (file)
index 0ff6f1f..0000000
+++ /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 <QObject>
-
-#include "audiooutput.h"
-
-class RaopCallbackHandler : public QObject
-{
-    Q_OBJECT
-public:
-    explicit RaopCallbackHandler(QObject *parent = 0);
-
-private:
-    QList<AudioOutput*>  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 (file)
index 1c3519d..0000000
+++ /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 <QDebug>
-#include <QFile>
-
-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 (file)
index 450cbb6..0000000
+++ /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 <QObject>
-#include <QThread>
-
-#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 (file)
index 0000000..a079a44
--- /dev/null
@@ -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 (file)
index 0000000..a2ee573
--- /dev/null
@@ -0,0 +1,32 @@
+#ifndef DNSSDSERVICE_H
+#define DNSSDSERVICE_H
+
+#include <QObject>
+
+#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 (file)
index 0000000..edb50db
--- /dev/null
@@ -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 (file)
index 0000000..cf91bdc
--- /dev/null
@@ -0,0 +1,30 @@
+#ifndef RAOPCALLBACKHANDLER_H
+#define RAOPCALLBACKHANDLER_H
+
+#include <QObject>
+
+#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 (file)
index 0000000..53a4468
--- /dev/null
@@ -0,0 +1,27 @@
+#ifndef RAOPCALLBACKS_H
+#define RAOPCALLBACKS_H
+
+#include <QObject>
+
+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 (file)
index 0000000..f575aa2
--- /dev/null
@@ -0,0 +1,159 @@
+#include "raopservice.h"
+
+#include <QDebug>
+
+#include <shairplay/raop.h>
+
+#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 (file)
index 0000000..d01b734
--- /dev/null
@@ -0,0 +1,35 @@
+#ifndef RAOPSERVICE_H
+#define RAOPSERVICE_H
+
+#include <QObject>
+#include <QThread>
+
+#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