1 | // Copyright (C) 2024 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 "qaudiobufferinput.h" |
5 | #include "qplatformaudiobufferinput_p.h" |
6 | #include "qmediainputencoderinterface_p.h" |
7 | #include "qmediaframeinput_p.h" |
8 | |
9 | QT_BEGIN_NAMESPACE |
10 | |
11 | class QAudioBufferInputPrivate : public QMediaFrameInputPrivate |
12 | { |
13 | public: |
14 | QAudioBufferInputPrivate(QAudioBufferInput *q) : q(q) { } |
15 | |
16 | bool sendAudioBuffer(const QAudioBuffer &audioBuffer) |
17 | { |
18 | return sendMediaFrame( |
19 | sender: [&]() { emit m_platfromAudioBufferInput->newAudioBuffer(buffer: audioBuffer); }); |
20 | } |
21 | |
22 | void initialize(const QAudioFormat &format = {}) |
23 | { |
24 | m_platfromAudioBufferInput = std::make_unique<QPlatformAudioBufferInput>(args: format); |
25 | addUpdateSignal(sender: m_platfromAudioBufferInput.get(), |
26 | signal: &QPlatformAudioBufferInput::encoderUpdated); |
27 | } |
28 | |
29 | void uninitialize() |
30 | { |
31 | m_platfromAudioBufferInput.reset(); |
32 | |
33 | if (captureSession()) |
34 | captureSession()->setAudioBufferInput(nullptr); |
35 | } |
36 | |
37 | QMediaCaptureSession *session() const { return m_captureSession; } |
38 | |
39 | QPlatformAudioBufferInput *platfromAudioBufferInput() const |
40 | { |
41 | return m_platfromAudioBufferInput.get(); |
42 | } |
43 | |
44 | private: |
45 | void updateCaptureSessionConnections(QMediaCaptureSession *prevSession, |
46 | QMediaCaptureSession *newSession) override |
47 | { |
48 | if (prevSession) |
49 | removeUpdateSignal(sender: prevSession, signal: &QMediaCaptureSession::audioOutputChanged); |
50 | |
51 | if (newSession) |
52 | addUpdateSignal(sender: newSession, signal: &QMediaCaptureSession::audioOutputChanged); |
53 | } |
54 | |
55 | bool checkIfCanSendMediaFrame() const override |
56 | { |
57 | if (auto encoderInterface = m_platfromAudioBufferInput->encoderInterface()) |
58 | return encoderInterface->canPushFrame(); |
59 | |
60 | // Not implemented yet |
61 | // return captureSession()->audioOutput() != nullptr; |
62 | return false; |
63 | } |
64 | |
65 | void emitReadyToSendMediaFrame() override { emit q->readyToSendAudioBuffer(); } |
66 | |
67 | private: |
68 | QAudioBufferInput *q = nullptr; |
69 | QMediaCaptureSession *m_captureSession = nullptr; |
70 | std::unique_ptr<QPlatformAudioBufferInput> m_platfromAudioBufferInput; |
71 | }; |
72 | |
73 | /*! |
74 | \class QAudioBufferInput |
75 | \inmodule QtMultimedia |
76 | \ingroup multimedia |
77 | \ingroup multimedia_audio |
78 | \since 6.8 |
79 | |
80 | \brief The QAudioBufferInput class is used for providing custom audio buffers |
81 | to \l QMediaRecorder through \l QMediaCaptureSession. |
82 | |
83 | QAudioBufferInput is only supported with the FFmpeg backend. |
84 | |
85 | Custom audio buffers can be recorded by connecting a \l QAudioBufferInput and a |
86 | \l QMediaRecorder to a \l QMediaCaptureSession. For a pull mode implementation, |
87 | call \l sendAudioBuffer() in response to the \l readyToSendAudioBuffer() signal. |
88 | In the snippet below this is done by connecting the signal to a slot in a custom |
89 | media generator class. The slot function emits another signal with a new audio |
90 | buffer, which is connected to \l sendAudioBuffer(): |
91 | |
92 | \snippet custommediainputsnippets/custommediainputsnippets.cpp QAudioBufferInput setup |
93 | |
94 | Here's a minimal implementation of the slot function that provides audio buffers: |
95 | |
96 | \snippet custommediainputsnippets/custommediainputsnippets.cpp nextAudioBuffer() |
97 | |
98 | For more details see readyToSendAudioBuffer() and sendAudioBuffer(). |
99 | |
100 | \sa QMediaRecorder and QMediaCaptureSession. |
101 | */ |
102 | |
103 | /*! |
104 | Constructs a new QAudioBufferInput object with \a parent. |
105 | */ |
106 | QAudioBufferInput::QAudioBufferInput(QObject *parent) : QAudioBufferInput({}, parent) { } |
107 | |
108 | /*! |
109 | Constructs a new QAudioBufferInput object with audio \a format and \a parent. |
110 | |
111 | The specified \a format will work as a hint for the initialization of the matching |
112 | audio encoder upon invoking \l QMediaRecorder::record(). |
113 | If the format is not specified or not valid, the audio encoder will be initialized |
114 | upon sending the first audio buffer. |
115 | |
116 | We recommend specifying the format if you know in advance what kind of audio buffers |
117 | you're going to send. |
118 | */ |
119 | QAudioBufferInput::QAudioBufferInput(const QAudioFormat &format, QObject *parent) |
120 | : QObject(*new QAudioBufferInputPrivate(this), parent) |
121 | { |
122 | Q_D(QAudioBufferInput); |
123 | d->initialize(format); |
124 | } |
125 | |
126 | /*! |
127 | Destroys the object. |
128 | */ |
129 | QAudioBufferInput::~QAudioBufferInput() |
130 | { |
131 | Q_D(QAudioBufferInput); |
132 | d->uninitialize(); |
133 | } |
134 | |
135 | /*! |
136 | Sends \l QAudioBuffer to \l QMediaRecorder through \l QMediaCaptureSession. |
137 | |
138 | Returns \c true if the specified \a audioBuffer has been sent successfully |
139 | to the destination. Returns \c false, if the buffer hasn't been sent, |
140 | which can happen if the instance is not assigned to |
141 | \l QMediaCaptureSession, the session doesn't have a media recorder, |
142 | the media recorder is not started or its queue is full. |
143 | The \l readyToSendAudioBuffer() signal will be emitted as soon as |
144 | the destination is able to handle a new audio buffer. |
145 | |
146 | Sending of an empty audio buffer is treated by \l QMediaRecorder |
147 | as an end of the input stream. QMediaRecorder stops the recording |
148 | automatically if \l QMediaRecorder::autoStop is \c true and |
149 | all the inputs have reported the end of the stream. |
150 | */ |
151 | bool QAudioBufferInput::sendAudioBuffer(const QAudioBuffer &audioBuffer) |
152 | { |
153 | Q_D(QAudioBufferInput); |
154 | return d->sendAudioBuffer(audioBuffer); |
155 | } |
156 | |
157 | /*! |
158 | Returns the audio format that was specified upon construction of the audio buffer input. |
159 | */ |
160 | QAudioFormat QAudioBufferInput::format() const |
161 | { |
162 | Q_D(const QAudioBufferInput); |
163 | return d->platfromAudioBufferInput()->audioFormat(); |
164 | } |
165 | |
166 | /*! |
167 | Returns the capture session this audio buffer input is connected to, or |
168 | a \c nullptr if the audio buffer input is not connected to a capture session. |
169 | |
170 | Use QMediaCaptureSession::setAudioBufferInput() to connect |
171 | the audio buffer input to a session. |
172 | */ |
173 | QMediaCaptureSession *QAudioBufferInput::captureSession() const |
174 | { |
175 | Q_D(const QAudioBufferInput); |
176 | return d->captureSession(); |
177 | } |
178 | |
179 | void QAudioBufferInput::setCaptureSession(QMediaCaptureSession *captureSession) |
180 | { |
181 | Q_D(QAudioBufferInput); |
182 | d->setCaptureSession(captureSession); |
183 | } |
184 | |
185 | QPlatformAudioBufferInput *QAudioBufferInput::platformAudioBufferInput() const |
186 | { |
187 | Q_D(const QAudioBufferInput); |
188 | return d->platfromAudioBufferInput(); |
189 | } |
190 | |
191 | /*! |
192 | \fn void QAudioBufferInput::readyToSendAudioBuffer() |
193 | |
194 | Signals that a new audio buffer can be sent to the audio buffer input. |
195 | After receiving the signal, if you have audio date to be sent, invoke \l sendAudioBuffer |
196 | once or in a loop until it returns \c false. |
197 | |
198 | \sa sendAudioBuffer() |
199 | */ |
200 | |
201 | QT_END_NAMESPACE |
202 |
Definitions
- QAudioBufferInputPrivate
- QAudioBufferInputPrivate
- sendAudioBuffer
- initialize
- uninitialize
- session
- platfromAudioBufferInput
- updateCaptureSessionConnections
- checkIfCanSendMediaFrame
- emitReadyToSendMediaFrame
- QAudioBufferInput
- QAudioBufferInput
- ~QAudioBufferInput
- sendAudioBuffer
- format
- captureSession
- setCaptureSession
Start learning QML with our Intro Training
Find out more