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"