Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qandroidaudiodevices.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include <QtMultimedia/private/qandroidaudiodevices_p.h>
5
6#include <QtMultimedia/private/qandroidaudiodevice_p.h>
7#include <QtMultimedia/private/qandroidaudiojnitypes_p.h>
8#include <QtMultimedia/private/qandroidaudiosink_p.h>
9#include <QtMultimedia/private/qandroidaudiosource_p.h>
10
11#include <QtMultimedia/private/qplatformmediaintegration_p.h>
12
13#include <QtCore/qjniobject.h>
14
15QT_BEGIN_NAMESPACE
16
17using namespace QtJniTypes;
18
19namespace {
20
21QAudioFormat preferredFormatForDevice(const QtJniTypes::AudioDeviceInfo &deviceInfo)
22{
23 QAudioFormat preferredFormat;
24
25 // Set preferred channel count based on what device reports, with default set to stereo (2)
26 QJniArray<jint> channelCounts = deviceInfo.callMethod<QJniArray<jint>>("getChannelCounts");
27 if (channelCounts.isEmpty()) {
28 preferredFormat.setChannelConfig(QAudioFormat::ChannelConfigStereo);
29 } else {
30 const auto [minIt, maxIt] = std::minmax_element(channelCounts.begin(), channelCounts.end());
31 const int channelCount = std::clamp(2, *minIt, *maxIt);
32 preferredFormat.setChannelConfig(
33 QAudioFormat::defaultChannelConfigForChannelCount(channelCount));
34 }
35
36 // Get optimal sample rate from AudioManager
37 preferredFormat.setSampleRate(
38 QtAudioDeviceManager::callStaticMethod<jint>("getDefaultSampleRate"));
39
40 // Using Float avoids conversions for processing, so we should prefer that instead of whatever
41 // the device uses natively
42 preferredFormat.setSampleFormat(QAudioFormat::Float);
43
44 return preferredFormat;
45};
46
47QList<QAudioDevice> availableDevices(QAudioDevice::Mode mode)
48{
49 if (mode == QAudioDevice::Null)
50 return {};
51
52 QList<QAudioDevice> devices;
53 const char *getMethod =
54 mode == QAudioDevice::Input ? "getAudioInputDevices" : "getAudioOutputDevices";
55 auto deviceInfos =
56 QtAudioDeviceManager::callStaticMethod<QJniArray<AudioDeviceInfo>>(getMethod);
57
58 if (!deviceInfos.isValid())
59 return {};
60
61 for (int i = 0; i < deviceInfos.size(); ++i) {
62 AudioDeviceInfo deviceInfo = deviceInfos.at(i);
63 int id = deviceInfo.callMethod<jint>("getId");
64 jint deviceType = deviceInfo.callMethod<jint>("getType");
65 auto description = QtAudioDeviceManager::callStaticMethod<QString>(
66 "audioDeviceTypeToString", deviceType);
67 bool isBluetoothDevice =
68 QtAudioDeviceManager::callStaticMethod<jboolean>("isBluetoothDevice", deviceInfo);
69 devices << QAudioDevicePrivate::createQAudioDevice(std::make_unique<QAndroidAudioDevice>(
70 QString::number(id).toUtf8(), description, mode,
71 preferredFormatForDevice(deviceInfo), isBluetoothDevice, i == 0));
72 }
73
74 return devices;
75}
76
77} // namespace
78
79// Called by any C++ thread
80QAndroidAudioDevices::QAndroidAudioDevices() : QPlatformAudioDevices()
81{
82 QtAudioDeviceManager::callStaticMethod<void>(
83 "qAndroidAudioDevicesConstructed",
84 static_cast<jlong>(reinterpret_cast<size_t>(this)));
85}
86
87QAndroidAudioDevices::~QAndroidAudioDevices()
88{
89 // Performs a blocking call to unregister QAndroidAudioDevices from receiving
90 // any more callbacks, and flushes remaining callbacks.
91 QtAudioDeviceManager::callStaticMethod<void>("qAndroidAudioDevicesDestroyed");
92}
93
94// Called by any C++ thread
95QList<QAudioDevice> QAndroidAudioDevices::findAudioInputs() const
96{
97 return availableDevices(QAudioDevice::Input);
98}
99
100// Called by any C++ thread
101QList<QAudioDevice> QAndroidAudioDevices::findAudioOutputs() const
102{
103 return availableDevices(QAudioDevice::Output);
104}
105
106// Called by any C++ thread
107QPlatformAudioSource *QAndroidAudioDevices::createAudioSource(const QAudioDevice &deviceInfo,
108 const QAudioFormat &fmt,
109 QObject *parent)
110{
111 return new QtAAudio::QAndroidAudioSource(deviceInfo, fmt, parent);
112}
113
114// Called by any C++ thread
115QPlatformAudioSink *QAndroidAudioDevices::createAudioSink(const QAudioDevice &deviceInfo,
116 const QAudioFormat &fmt, QObject *parent)
117{
118 return new QtAAudio::QAndroidAudioSink(deviceInfo, fmt, parent);
119}
120
121// Invoked by background Java Handler thread
123 JNIEnv * /*env*/,
124 jobject /*thiz*/,
125 jlong nativePtr)
126{
127 auto *audioDevices = reinterpret_cast<QAndroidAudioDevices*>(static_cast<size_t>(nativePtr));
128 Q_ASSERT(!audioDevices->thread()->isCurrentThread());
129 audioDevices->onAudioInputsChanged();
130}
131Q_DECLARE_JNI_NATIVE_METHOD(onAudioInputDevicesUpdated)
132
133// Invoked by background Java Handler thread
135 JNIEnv * /*env*/,
136 jobject /*thiz*/,
137 jlong nativePtr)
138{
139 auto *audioDevices = reinterpret_cast<QAndroidAudioDevices*>(static_cast<size_t>(nativePtr));
140 Q_ASSERT(!audioDevices->thread()->isCurrentThread());
141 audioDevices->onAudioOutputsChanged();
142}
143Q_DECLARE_JNI_NATIVE_METHOD(onAudioOutputDevicesUpdated)
144
145bool QAndroidAudioDevices::registerNativeMethods()
146{
147 static const bool registered = []{
148 const auto context = QNativeInterface::QAndroidApplication::context();
149 QtAudioDeviceManager::callStaticMethod<void>("setContext", context);
150
151 return QtJniTypes::QtAudioDeviceManager::registerNativeMethods({
152 Q_JNI_NATIVE_METHOD(onAudioInputDevicesUpdated),
153 Q_JNI_NATIVE_METHOD(onAudioOutputDevicesUpdated),
154 });
155 }();
156 return registered;
157}
158
159QT_END_NAMESPACE
160
161extern "C" Q_DECL_EXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void * /*reserved*/)
162{
163 static bool initialized = false;
164 if (initialized)
165 return JNI_VERSION_1_6;
166 initialized = true;
167
168 QT_USE_NAMESPACE
169 typedef union {
170 JNIEnv *nativeEnvironment;
171 void *venv;
172 } UnionJNIEnvToVoid;
173
174 UnionJNIEnvToVoid uenv;
175 uenv.venv = NULL;
176
177 if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK)
178 return JNI_ERR;
179
180 if (!QAndroidAudioDevices::registerNativeMethods())
181 return JNI_ERR;
182
183 return JNI_VERSION_1_6;
184}
static void onAudioInputDevicesUpdated(JNIEnv *, jobject, jlong nativePtr)
static void onAudioOutputDevicesUpdated(JNIEnv *, jobject, jlong nativePtr)