File indexing completed on 2024-04-28 03:52:07

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() = default;
0039 
0040 QDBusObjectPath NoInputNoOutputAgent::objectPath() const
0041 {
0042     return QDBusObjectPath("/Agent/NoInputNoOutput");
0043 }
0044 
0045 Agent::Capability NoInputNoOutputAgent::capability() const
0046 {
0047     return Agent::NoInputNoOutput;
0048 }
0049 
0050 void NoInputNoOutputAgent::authorizeService(DevicePtr device, const QString &uuid, const Request<> &request)
0051 {
0052     Q_UNUSED(device)
0053 
0054     d->allowedUuids.contains(uuid) ? request.accept() : request.reject();
0055     Q_EMIT serviceAuthorized(device, uuid, d->allowedUuids.contains(uuid));
0056 }
0057 
0058 MediaEndpointConnector::MediaEndpointConnector(Manager *manager, QObject *parent)
0059     : QObject(parent)
0060     , m_manager(manager)
0061 {
0062     connect(manager, &Manager::deviceChanged, [this](DevicePtr device) {
0063         connect(device.data(), &Device::mediaTransportChanged, this, &MediaEndpointConnector::onTransportChanged);
0064     });
0065 
0066     NoInputNoOutputAgent *agent = new NoInputNoOutputAgent({Services::AdvancedAudioDistribution, Services::AudioVideoRemoteControl});
0067     connect(agent, &NoInputNoOutputAgent::serviceAuthorized, this, &MediaEndpointConnector::onServiceAuthorized);
0068     manager->registerAgent(agent);
0069     manager->requestDefaultAgent(agent);
0070 
0071     MediaEndpoint *sbcSink = new MediaEndpoint({MediaEndpoint::Role::AudioSink, MediaEndpoint::Codec::Sbc}, manager);
0072     MediaEndpoint *aacSink = new MediaEndpoint({MediaEndpoint::Role::AudioSink, MediaEndpoint::Codec::Aac}, manager);
0073     connect(sbcSink, &MediaEndpoint::configurationSelected, this, &MediaEndpointConnector::onConfigurationSelected);
0074     connect(aacSink, &MediaEndpoint::configurationSelected, this, &MediaEndpointConnector::onConfigurationSelected);
0075     connect(sbcSink, &MediaEndpoint::configurationSet, this, &MediaEndpointConnector::onConfigurationSet);
0076     connect(aacSink, &MediaEndpoint::configurationSet, this, &MediaEndpointConnector::onConfigurationSet);
0077     connect(sbcSink, &MediaEndpoint::configurationCleared, this, &MediaEndpointConnector::onConfigurationCleared);
0078     connect(aacSink, &MediaEndpoint::configurationCleared, this, &MediaEndpointConnector::onConfigurationCleared);
0079     manager->usableAdapter()->media()->registerEndpoint(sbcSink);
0080     manager->usableAdapter()->media()->registerEndpoint(aacSink);
0081 }
0082 
0083 void MediaEndpointConnector::onTransportChanged(MediaTransportPtr transport)
0084 {
0085     if (!transport) {
0086         return;
0087     }
0088 
0089     connect(transport.data(), &MediaTransport::stateChanged, [transport](MediaTransport::State state) {
0090         qDebug() << "Transport state:" << state;
0091 
0092         if (state == MediaTransport::State::Pending) {
0093             TPendingCall<QDBusUnixFileDescriptor, uint16_t, uint16_t> *fd = transport->tryAcquire();
0094             connect(fd, &PendingCall::finished, [fd]() {
0095                 qDebug() << "fd: " << fd->valueAt<0>().fileDescriptor() << "mtu read:" << fd->valueAt<1>() << "mtu write:" << fd->valueAt<2>();
0096             });
0097         }
0098     });
0099     connect(transport.data(), &MediaTransport::volumeChanged, [](quint16 volume) {
0100         qDebug() << "Transport volume:" << volume;
0101     });
0102 }
0103 
0104 void MediaEndpointConnector::onServiceAuthorized(BluezQt::DevicePtr device, const QString &uuid, bool allowed)
0105 {
0106     qDebug() << (allowed ? "Accepted" : "Rejected") << "service:" << uuid << "from" << device->friendlyName();
0107 }
0108 
0109 void MediaEndpointConnector::onConfigurationSelected(const QByteArray &capabilities, const QByteArray &configuration)
0110 {
0111     if (configuration.isEmpty()) {
0112         qDebug() << "No usable configuration found for capabilities:" << capabilities;
0113     } else {
0114         qDebug() << "Selected configuration:" << configuration << "for capabilities:" << capabilities;
0115     }
0116 }
0117 
0118 void MediaEndpointConnector::onConfigurationSet(const QString &transportObjectPath, const QVariantMap &properties)
0119 {
0120     qDebug() << "Set configuration for transport:" << transportObjectPath << "to:" << properties;
0121 }
0122 
0123 void MediaEndpointConnector::onConfigurationCleared(const QString &transportObjectPath)
0124 {
0125     qDebug() << "Cleared configuration for transport:" << transportObjectPath;
0126 }
0127 
0128 int main(int argc, char **argv)
0129 {
0130     QCoreApplication app(argc, argv);
0131 
0132     qDebug() << "Waiting for bluetooth audio source to connect. Ctrl + C to cancel...";
0133 
0134     Manager *manager = new Manager();
0135     InitManagerJob *initJob = manager->init();
0136     initJob->exec();
0137     if (initJob->error()) {
0138         qWarning() << "Error initializing manager:" << initJob->errorText();
0139         return 1;
0140     }
0141 
0142     if (!manager->usableAdapter()) {
0143         qWarning() << "No usable adapter";
0144         return 2;
0145     }
0146 
0147     if (!manager->usableAdapter()->media()) {
0148         qWarning() << "No media interface";
0149         return 2;
0150     }
0151 
0152     new MediaEndpointConnector(manager);
0153 
0154     return app.exec();
0155 }
0156 
0157 #include "moc_mediaendpointconnector.cpp"