File indexing completed on 2024-05-05 16:04:27

0001 /*
0002  * SPDX-FileCopyrightText: 2018 Manuel Weichselbaumer <mincequi@web.de>
0003  *
0004  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0005  */
0006 
0007 #include "mediaendpointconnector.h"
0008 
0009 #include <QCoreApplication>
0010 #include <QDBusObjectPath>
0011 #include <QDBusUnixFileDescriptor>
0012 #include <QDebug>
0013 
0014 #include "adapter.h"
0015 #include "device.h"
0016 #include "initmanagerjob.h"
0017 #include "media.h"
0018 #include "mediaendpoint.h"
0019 #include "mediatransport.h"
0020 #include "services.h"
0021 #include "tpendingcall.h"
0022 
0023 using namespace BluezQt;
0024 
0025 class NoInputNoOutputAgentPrivate
0026 {
0027 public:
0028     QStringList allowedUuids;
0029 };
0030 
0031 NoInputNoOutputAgent::NoInputNoOutputAgent(const QStringList &uuids, QObject *parent)
0032     : Agent(parent)
0033     , d(new NoInputNoOutputAgentPrivate)
0034 {
0035     d->allowedUuids = uuids;
0036 }
0037 
0038 NoInputNoOutputAgent::~NoInputNoOutputAgent()
0039 {
0040     delete d;
0041 }
0042 
0043 QDBusObjectPath NoInputNoOutputAgent::objectPath() const
0044 {
0045     return QDBusObjectPath("/Agent/NoInputNoOutput");
0046 }
0047 
0048 Agent::Capability NoInputNoOutputAgent::capability() const
0049 {
0050     return Agent::NoInputNoOutput;
0051 }
0052 
0053 void NoInputNoOutputAgent::authorizeService(DevicePtr device, const QString &uuid, const Request<> &request)
0054 {
0055     Q_UNUSED(device)
0056 
0057     d->allowedUuids.contains(uuid) ? request.accept() : request.reject();
0058     Q_EMIT serviceAuthorized(device, uuid, d->allowedUuids.contains(uuid));
0059 }
0060 
0061 MediaEndpointConnector::MediaEndpointConnector(Manager *manager, QObject *parent)
0062     : QObject(parent)
0063     , m_manager(manager)
0064 {
0065     connect(manager, &Manager::deviceChanged, [this](DevicePtr device) {
0066         connect(device.data(), &Device::mediaTransportChanged, this, &MediaEndpointConnector::onTransportChanged);
0067     });
0068 
0069     NoInputNoOutputAgent *agent = new NoInputNoOutputAgent({Services::AdvancedAudioDistribution, Services::AudioVideoRemoteControl});
0070     connect(agent, &NoInputNoOutputAgent::serviceAuthorized, this, &MediaEndpointConnector::onServiceAuthorized);
0071     manager->registerAgent(agent);
0072     manager->requestDefaultAgent(agent);
0073 
0074     MediaEndpoint *sbcSink = new MediaEndpoint({MediaEndpoint::Role::AudioSink, MediaEndpoint::Codec::Sbc}, manager);
0075     MediaEndpoint *aacSink = new MediaEndpoint({MediaEndpoint::Role::AudioSink, MediaEndpoint::Codec::Aac}, manager);
0076     connect(sbcSink, &MediaEndpoint::configurationSelected, this, &MediaEndpointConnector::onConfigurationSelected);
0077     connect(aacSink, &MediaEndpoint::configurationSelected, this, &MediaEndpointConnector::onConfigurationSelected);
0078     connect(sbcSink, &MediaEndpoint::configurationSet, this, &MediaEndpointConnector::onConfigurationSet);
0079     connect(aacSink, &MediaEndpoint::configurationSet, this, &MediaEndpointConnector::onConfigurationSet);
0080     connect(sbcSink, &MediaEndpoint::configurationCleared, this, &MediaEndpointConnector::onConfigurationCleared);
0081     connect(aacSink, &MediaEndpoint::configurationCleared, this, &MediaEndpointConnector::onConfigurationCleared);
0082     manager->usableAdapter()->media()->registerEndpoint(sbcSink);
0083     manager->usableAdapter()->media()->registerEndpoint(aacSink);
0084 }
0085 
0086 void MediaEndpointConnector::onTransportChanged(MediaTransportPtr transport)
0087 {
0088     if (!transport) {
0089         return;
0090     }
0091 
0092     connect(transport.data(), &MediaTransport::stateChanged, [transport](MediaTransport::State state) {
0093         qDebug() << "Transport state:" << state;
0094 
0095         if (state == MediaTransport::State::Pending) {
0096             TPendingCall<QDBusUnixFileDescriptor, uint16_t, uint16_t> *fd = transport->tryAcquire();
0097             connect(fd, &PendingCall::finished, [fd]() {
0098                 qDebug() << "fd: " << fd->valueAt<0>().fileDescriptor() << "mtu read:" << fd->valueAt<1>() << "mtu write:" << fd->valueAt<2>();
0099             });
0100         }
0101     });
0102     connect(transport.data(), &MediaTransport::volumeChanged, [](quint16 volume) {
0103         qDebug() << "Transport volume:" << volume;
0104     });
0105 }
0106 
0107 void MediaEndpointConnector::onServiceAuthorized(BluezQt::DevicePtr device, const QString &uuid, bool allowed)
0108 {
0109     qDebug() << (allowed ? "Accepted" : "Rejected") << "service:" << uuid << "from" << device->friendlyName();
0110 }
0111 
0112 void MediaEndpointConnector::onConfigurationSelected(const QByteArray &capabilities, const QByteArray &configuration)
0113 {
0114     if (configuration.isEmpty()) {
0115         qDebug() << "No usable configuration found for capabilities:" << capabilities;
0116     } else {
0117         qDebug() << "Selected configuration:" << configuration << "for capabilities:" << capabilities;
0118     }
0119 }
0120 
0121 void MediaEndpointConnector::onConfigurationSet(const QString &transportObjectPath, const QVariantMap &properties)
0122 {
0123     qDebug() << "Set configuration for transport:" << transportObjectPath << "to:" << properties;
0124 }
0125 
0126 void MediaEndpointConnector::onConfigurationCleared(const QString &transportObjectPath)
0127 {
0128     qDebug() << "Cleared configuration for transport:" << transportObjectPath;
0129 }
0130 
0131 int main(int argc, char **argv)
0132 {
0133     QCoreApplication app(argc, argv);
0134 
0135     qDebug() << "Waiting for bluetooth audio source to connect. Ctrl + C to cancel...";
0136 
0137     Manager *manager = new Manager();
0138     InitManagerJob *initJob = manager->init();
0139     initJob->exec();
0140     if (initJob->error()) {
0141         qWarning() << "Error initializing manager:" << initJob->errorText();
0142         return 1;
0143     }
0144 
0145     if (!manager->usableAdapter()) {
0146         qWarning() << "No usable adapter";
0147         return 2;
0148     }
0149 
0150     if (!manager->usableAdapter()->media()) {
0151         qWarning() << "No media interface";
0152         return 2;
0153     }
0154 
0155     new MediaEndpointConnector(manager);
0156 
0157     return app.exec();
0158 }
0159 
0160 #include "moc_mediaendpointconnector.cpp"