| 1 | /* |
| 2 | * BluezQt - Asynchronous Bluez wrapper library |
| 3 | * |
| 4 | * SPDX-FileCopyrightText: 2019 Manuel Weichselbaumer <mincequi@web.de> |
| 5 | * |
| 6 | * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL |
| 7 | */ |
| 8 | |
| 9 | #include "mediatransport_p.h" |
| 10 | #include "a2dp-codecs.h" |
| 11 | #include "macros.h" |
| 12 | #include "utils.h" |
| 13 | |
| 14 | namespace BluezQt |
| 15 | { |
| 16 | static MediaTransport::State stringToState(const QString &state) |
| 17 | { |
| 18 | if (state == QLatin1String("pending" )) { |
| 19 | return MediaTransport::State::Pending; |
| 20 | } else if (state == QLatin1String("active" )) { |
| 21 | return MediaTransport::State::Active; |
| 22 | } |
| 23 | return MediaTransport::State::Idle; |
| 24 | } |
| 25 | |
| 26 | static AudioCodec intToCodec(int value) |
| 27 | { |
| 28 | switch (value) { |
| 29 | case A2DP_CODEC_SBC: |
| 30 | return AudioCodec::Sbc; |
| 31 | break; |
| 32 | case A2DP_CODEC_MPEG24: |
| 33 | return AudioCodec::Aac; |
| 34 | break; |
| 35 | } |
| 36 | |
| 37 | return AudioCodec::Invalid; |
| 38 | } |
| 39 | |
| 40 | static AudioSampleRate byteArrayToSampleRate(AudioCodec codec, const QByteArray &buffer) |
| 41 | { |
| 42 | switch (codec) { |
| 43 | case AudioCodec::Sbc: { |
| 44 | if (buffer.size() != sizeof(a2dp_sbc_t)) { |
| 45 | return AudioSampleRate::Invalid; |
| 46 | } |
| 47 | |
| 48 | a2dp_sbc_t sbcConfig = *reinterpret_cast<const a2dp_sbc_t *>(buffer.constData()); |
| 49 | switch (sbcConfig.frequency) { |
| 50 | case SBC_SAMPLING_FREQ_44100: |
| 51 | return AudioSampleRate::Rate44100; |
| 52 | break; |
| 53 | case SBC_SAMPLING_FREQ_48000: |
| 54 | return AudioSampleRate::Rate48000; |
| 55 | break; |
| 56 | } |
| 57 | break; |
| 58 | } |
| 59 | case AudioCodec::Aac: { |
| 60 | if (buffer.size() != sizeof(a2dp_aac_t)) { |
| 61 | return AudioSampleRate::Invalid; |
| 62 | } |
| 63 | |
| 64 | a2dp_aac_t aacConfig = *reinterpret_cast<const a2dp_aac_t *>(buffer.constData()); |
| 65 | switch (AAC_GET_FREQUENCY(aacConfig)) { |
| 66 | case AAC_SAMPLING_FREQ_44100: |
| 67 | return AudioSampleRate::Rate44100; |
| 68 | break; |
| 69 | case AAC_SAMPLING_FREQ_48000: |
| 70 | return AudioSampleRate::Rate48000; |
| 71 | break; |
| 72 | } |
| 73 | break; |
| 74 | } |
| 75 | default: |
| 76 | break; |
| 77 | } |
| 78 | |
| 79 | return AudioSampleRate::Invalid; |
| 80 | } |
| 81 | |
| 82 | MediaTransportPrivate::MediaTransportPrivate(const QString &path, const QVariantMap &properties) |
| 83 | : QObject() |
| 84 | , m_dbusInterface(Strings::orgBluez(), path, DBusConnection::orgBluez()) |
| 85 | , m_dbusProperties(nullptr) |
| 86 | , m_path(path) |
| 87 | { |
| 88 | DBusConnection::orgBluez().connect(service: Strings::orgBluez(), |
| 89 | path, |
| 90 | interface: Strings::orgFreedesktopDBusProperties(), |
| 91 | QStringLiteral("PropertiesChanged" ), |
| 92 | receiver: this, |
| 93 | SLOT(onPropertiesChanged(QString, QVariantMap, QStringList))); |
| 94 | init(properties); |
| 95 | } |
| 96 | |
| 97 | void MediaTransportPrivate::init(const QVariantMap &properties) |
| 98 | { |
| 99 | m_dbusProperties = new DBusProperties(Strings::orgBluez(), m_path, DBusConnection::orgBluez(), this); |
| 100 | |
| 101 | m_volume = properties.value(QStringLiteral("Volume" )).toUInt(); |
| 102 | m_state = stringToState(state: properties.value(QStringLiteral("State" )).toString()); |
| 103 | m_configuration.codec = intToCodec(value: properties.value(QStringLiteral("Codec" )).toInt()); |
| 104 | m_configuration.sampleRate = byteArrayToSampleRate(codec: m_configuration.codec, buffer: properties.value(QStringLiteral("Configuration" )).toByteArray()); |
| 105 | } |
| 106 | |
| 107 | QDBusPendingReply<> MediaTransportPrivate::setDBusProperty(const QString &name, const QVariant &value) |
| 108 | { |
| 109 | return m_dbusProperties->Set(interface_name: Strings::orgBluezMediaTransport1(), property_name: name, value: QDBusVariant(value)); |
| 110 | } |
| 111 | |
| 112 | void MediaTransportPrivate::onPropertiesChanged(const QString &interface, const QVariantMap &changed, const QStringList &invalidated) |
| 113 | { |
| 114 | if (interface != Strings::orgBluezMediaTransport1()) { |
| 115 | return; |
| 116 | } |
| 117 | |
| 118 | for (auto it = changed.constBegin(); it != changed.constEnd(); ++it) { |
| 119 | const QString &key = it.key(); |
| 120 | const QVariant &value = it.value(); |
| 121 | |
| 122 | if (key == QLatin1String("Volume" )) { |
| 123 | m_volume = value.toUInt(); |
| 124 | Q_EMIT q.lock()->volumeChanged(volume: m_volume); |
| 125 | } else if (key == QLatin1String("State" )) { |
| 126 | m_state = stringToState(state: value.toString()); |
| 127 | Q_EMIT q.lock()->stateChanged(state: m_state); |
| 128 | } |
| 129 | } |
| 130 | |
| 131 | for (const QString &property : invalidated) { |
| 132 | if (property == QLatin1String("Volume" )) { |
| 133 | m_volume = 0; |
| 134 | Q_EMIT q.lock()->volumeChanged(volume: m_volume); |
| 135 | } else if (property == QLatin1String("State" )) { |
| 136 | PROPERTY_INVALIDATED(m_state, MediaTransport::State::Idle, stateChanged); |
| 137 | } |
| 138 | } |
| 139 | } |
| 140 | |
| 141 | } // namespace BluezQt |
| 142 | |
| 143 | #include "moc_mediatransport_p.cpp" |
| 144 | |