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"