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
qohaudiostream.cpp
Go to the documentation of this file.
1// Copyright (C) 2026 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
5
6#include <QtMultimedia/private/qmultimedia_enum_to_string_converter_p.h>
7
8#include <QtCore/qloggingcategory.h>
9#include <QtCore/qmutex.h>
10
12
13// clang-format off
14QT_MM_MAKE_STRING_RESOLVER(OH_AudioStream_Result, QtMultimediaPrivate::EnumName,
15 (AUDIOSTREAM_SUCCESS, "AUDIOSTREAM_SUCCESS")
16 (AUDIOSTREAM_ERROR_INVALID_PARAM, "AUDIOSTREAM_ERROR_INVALID_PARAM")
17 (AUDIOSTREAM_ERROR_ILLEGAL_STATE, "AUDIOSTREAM_ERROR_ILLEGAL_STATE")
18 (AUDIOSTREAM_ERROR_SYSTEM, "AUDIOSTREAM_ERROR_SYSTEM")
19 (AUDIOSTREAM_ERROR_UNSUPPORTED_FORMAT, "AUDIOSTREAM_ERROR_UNSUPPORTED_FORMAT")
20 );
21// clang-format on
22
24
25namespace QtOHAudio {
26
27Q_LOGGING_CATEGORY(qLcOHAudioStream, "qt.multimedia.ohos.audiostream")
28
29namespace {
30// OH_AudioStreamBuilder is not thread-safe: concurrent Create/Destroy and
31// GenerateRenderer/GenerateCapturer calls from multiple threads crash inside
32// the audio service. Serialize all builder lifecycle operations.
33Q_CONSTINIT QBasicMutex g_streamBuilderMutex;
34}
35
36OH_AudioStream_SampleFormat toOHSampleFormat(QAudioFormat::SampleFormat fmt)
37{
38 switch (fmt) {
39 case QAudioFormat::UInt8:
40 return AUDIOSTREAM_SAMPLE_U8;
41 case QAudioFormat::Int16:
42 return AUDIOSTREAM_SAMPLE_S16LE;
43 case QAudioFormat::Int32:
44 return AUDIOSTREAM_SAMPLE_S32LE;
45 case QAudioFormat::Float:
46 return AUDIOSTREAM_SAMPLE_F32LE;
47 case QAudioFormat::Unknown:
48 case QAudioFormat::NSampleFormats:
49 break;
50 }
51 return AUDIOSTREAM_SAMPLE_S16LE;
52}
53
54QAudioFormat::SampleFormat fromOHSampleFormat(OH_AudioStream_SampleFormat fmt)
55{
56 switch (fmt) {
57 case AUDIOSTREAM_SAMPLE_U8:
58 return QAudioFormat::UInt8;
59 case AUDIOSTREAM_SAMPLE_S16LE:
60 return QAudioFormat::Int16;
61 case AUDIOSTREAM_SAMPLE_S24LE:
62 return QAudioFormat::Unknown;
63 case AUDIOSTREAM_SAMPLE_S32LE:
64 return QAudioFormat::Int32;
65 case AUDIOSTREAM_SAMPLE_F32LE:
66 return QAudioFormat::Float;
67 }
68 return QAudioFormat::Unknown;
69}
70
71QAudioFormat::SampleFormat preferredCompatibleSampleFormat(QAudioFormat::SampleFormat requested)
72{
73 switch (requested) {
74 case QAudioFormat::UInt8:
75 case QAudioFormat::Int16:
76 case QAudioFormat::Int32:
77 case QAudioFormat::Float:
78 return requested;
79 case QAudioFormat::Unknown:
80 case QAudioFormat::NSampleFormats:
81 break;
82 }
83 return QAudioFormat::Int16;
84}
85
86StreamBuilder::StreamBuilder(QAudioFormat fmt, OH_AudioStream_Type direction)
87 : format{ fmt }
88{
89 params.direction = direction;
90 QMutexLocker lock{ &g_streamBuilderMutex };
91 OH_AudioStream_Result result = OH_AudioStreamBuilder_Create(&m_builder, direction);
92 if (result != AUDIOSTREAM_SUCCESS)
93 qCWarning(qLcOHAudioStream)
94 << "Failed to create stream builder:" << result;
95}
96
98{
99 QMutexLocker lock{ &g_streamBuilderMutex };
100 if (m_builder)
101 OH_AudioStreamBuilder_Destroy(m_builder);
102}
103
105{
106 if (!m_builder)
107 return;
108
109 OH_AudioStreamBuilder_SetSamplingRate(m_builder, format.sampleRate());
110 OH_AudioStreamBuilder_SetChannelCount(m_builder, format.channelCount());
111 OH_AudioStreamBuilder_SetSampleFormat(m_builder, toOHSampleFormat(format.sampleFormat()));
112 OH_AudioStreamBuilder_SetEncodingType(m_builder, AUDIOSTREAM_ENCODING_TYPE_RAW);
113 OH_AudioStreamBuilder_SetLatencyMode(m_builder, params.latencyMode);
114
115 // TODO: register the OH_AudioRenderer_Callbacks / OH_AudioCapturer_Callbacks
116 // error and interrupt-event handlers so the backend can react to focus loss,
117 // device changes and stream errors. See:
118 // https://developer.huawei.com/consumer/en/doc/harmonyos-references/capi-ohaudio-oh-audiorenderer-callbacks-struct
119 if (params.direction == AUDIOSTREAM_TYPE_RENDERER) {
120 OH_AudioStreamBuilder_SetRendererInfo(m_builder, params.outputUsage);
121 if (writeCallback) {
122 OH_AudioStreamBuilder_SetRendererWriteDataCallback(m_builder, writeCallback,
123 userData);
124 }
125 } else {
126 OH_AudioStreamBuilder_SetCapturerInfo(m_builder, params.inputSourceType);
127 if (readCallback) {
128 OH_AudioCapturer_Callbacks callbacks{};
129 callbacks.OH_AudioCapturer_OnReadData = readCallback;
130 OH_AudioStreamBuilder_SetCapturerCallback(m_builder, callbacks, userData);
131 }
132 }
133}
134
137{
138 if (!builder.m_builder) {
139 qCWarning(qLcOHAudioStream) << "Builder is invalid";
140 return;
141 }
142
143 OH_AudioStream_Result result = AUDIOSTREAM_SUCCESS;
144 {
145 QMutexLocker lock{ &g_streamBuilderMutex };
146 if (m_streamType == AUDIOSTREAM_TYPE_RENDERER) {
147 result = OH_AudioStreamBuilder_GenerateRenderer(builder.m_builder, &m_renderer);
148 } else {
149 result = OH_AudioStreamBuilder_GenerateCapturer(builder.m_builder, &m_capturer);
150 }
151 }
152
153 if (result != AUDIOSTREAM_SUCCESS) {
154 qCWarning(qLcOHAudioStream)
155 << "Failed to generate stream:" << result;
156 return;
157 }
158
159 m_areStreamParametersRespected = true;
160}
161
163{
164 if (isOpen())
165 close();
166}
167
168bool Stream::start()
169{
170 OH_AudioStream_Result result = AUDIOSTREAM_SUCCESS;
171 if (m_streamType == AUDIOSTREAM_TYPE_RENDERER)
172 result = OH_AudioRenderer_Start(m_renderer);
173 else
174 result = OH_AudioCapturer_Start(m_capturer);
175
176 if (result != AUDIOSTREAM_SUCCESS)
177 qCWarning(qLcOHAudioStream) << "Failed to start stream:" << result;
178
179 return result == AUDIOSTREAM_SUCCESS;
180}
181
183{
184 if (m_streamType == AUDIOSTREAM_TYPE_RENDERER)
185 OH_AudioRenderer_Stop(m_renderer);
186 else
187 OH_AudioCapturer_Stop(m_capturer);
188}
189
191{
192 OH_AudioStream_Result result = AUDIOSTREAM_SUCCESS;
193 if (m_streamType == AUDIOSTREAM_TYPE_RENDERER)
194 result = OH_AudioRenderer_Pause(m_renderer);
195 else
196 result = OH_AudioCapturer_Pause(m_capturer);
197 if (result != AUDIOSTREAM_SUCCESS)
198 qCWarning(qLcOHAudioStream)
199 << "Failed to pause stream:" << result;
200}
201
203{
204 if (m_streamType == AUDIOSTREAM_TYPE_RENDERER)
205 OH_AudioRenderer_Flush(m_renderer);
206}
207
208bool Stream::isOpen() const
209{
210 return m_renderer != nullptr || m_capturer != nullptr;
211}
212
214{
215 return m_areStreamParametersRespected;
216}
217
218void Stream::close()
219{
220 if (m_streamType == AUDIOSTREAM_TYPE_RENDERER && m_renderer) {
221 OH_AudioRenderer_Release(m_renderer);
222 m_renderer = nullptr;
223 } else if (m_capturer) {
224 OH_AudioCapturer_Release(m_capturer);
225 m_capturer = nullptr;
226 }
227}
228
229} // namespace QtOHAudio
230
231QT_END_NAMESPACE
Combined button and popup list for selecting options.
QAudioFormat::SampleFormat fromOHSampleFormat(OH_AudioStream_SampleFormat fmt)
OH_AudioStream_SampleFormat toOHSampleFormat(QAudioFormat::SampleFormat fmt)
QAudioFormat::SampleFormat preferredCompatibleSampleFormat(QAudioFormat::SampleFormat requested)
QT_MM_DEFINE_QDEBUG_ENUM(OH_AudioStream_Result)
StreamBuilder(QAudioFormat format, OH_AudioStream_Type direction)
bool areStreamParametersRespected() const
Stream(StreamBuilder &builder)