1// Copyright (C) 2022 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 "qmediacapturesession.h"
5#include "qmediacapturesession_p.h"
6#include "qaudiodevice.h"
7#include "qcamera.h"
8#include "qmediarecorder.h"
9#include "qimagecapture.h"
10#include "qvideosink.h"
11#include "qscreencapture.h"
12#include "qwindowcapture.h"
13#include "qvideoframeinput.h"
14
15#include "qplatformmediaintegration_p.h"
16#include "qplatformmediacapture_p.h"
17#include "qaudioinput.h"
18#include "qaudiobufferinput.h"
19#include "qaudiooutput.h"
20
21QT_BEGIN_NAMESPACE
22
23void QMediaCaptureSessionPrivate::setVideoSink(QVideoSink *sink)
24{
25 Q_Q(QMediaCaptureSession);
26
27 if (sink == videoSink)
28 return;
29 if (videoSink)
30 videoSink->setSource(nullptr);
31 videoSink = sink;
32 if (sink)
33 sink->setSource(q);
34 if (captureSession)
35 captureSession->setVideoPreview(sink);
36 emit q->videoOutputChanged();
37}
38
39/*!
40 \class QMediaCaptureSession
41
42 \brief The QMediaCaptureSession class allows capturing of audio and video content.
43 \inmodule QtMultimedia
44 \ingroup multimedia
45 \ingroup multimedia_video
46 \ingroup multimedia_audio
47
48 The QMediaCaptureSession is the central class that manages capturing of media on the local
49 device.
50
51 You can connect a video input to QMediaCaptureSession using setCamera(),
52 setScreenCapture(), setWindowCapture() or setVideoFrameInput().
53 A preview of the captured media can be seen by setting a QVideoWidget or QGraphicsVideoItem
54 using setVideoOutput().
55
56 You can connect a microphone to QMediaCaptureSession using setAudioInput(), or set your
57 custom audio input using setAudioBufferInput().
58 The captured sound can be heard by routing the audio to an output device using setAudioOutput().
59
60 You can capture still images from a camera by setting a QImageCapture object on the capture
61 session, and record audio/video using a QMediaRecorder.
62
63 \sa QCamera, QAudioDevice, QMediaRecorder, QImageCapture, QScreenCapture, QWindowCapture,
64 QVideoFrameInput, QMediaRecorder, QGraphicsVideoItem
65*/
66
67/*!
68 \qmltype CaptureSession
69 \since 6.2
70 \nativetype QMediaCaptureSession
71 \brief Allows capturing of audio and video content.
72
73 \inqmlmodule QtMultimedia
74 \ingroup multimedia_qml
75 \ingroup multimedia_audio_qml
76 \ingroup multimedia_video_qml
77
78 This is the central type that manages capturing of media on the local device.
79
80 Connect a camera and a microphone to a CaptureSession by assigning Camera
81 and AudioInput objects to the relevant properties.
82
83 Capture a screen by connecting a ScreenCapture object to
84 the screenCapture property.
85
86 Capture a window by connecting a WindowCapture object to
87 the windowCapture property.
88
89 Enable a preview of the captured media by assigning a VideoOutput element to
90 the videoOutput property.
91
92 Route audio to an output device by assigning an AudioOutput object
93 to the audioOutput property.
94
95 Capture still images from a camera by assigning an ImageCapture to the
96 imageCapture property.
97
98 Record audio/video by assigning a MediaRecorder to the recorder property.
99
100\qml
101 CaptureSession {
102 id: captureSession
103 camera: Camera {
104 id: camera
105 }
106 imageCapture: ImageCapture {
107 id: imageCapture
108 }
109
110 recorder: MediaRecorder {
111 id: recorder
112 }
113 videoOutput: preview
114 }
115\endqml
116
117 \sa Camera, MediaDevices, MediaRecorder, ImageCapture, ScreenCapture, WindowCapture, AudioInput, VideoOutput
118*/
119
120/*!
121 Creates a session for media capture from the \a parent object.
122 */
123QMediaCaptureSession::QMediaCaptureSession(QObject *parent)
124 : QObject{ *new QMediaCaptureSessionPrivate, parent }
125{
126 QT6_ONLY(Q_UNUSED(unused))
127
128 Q_D(QMediaCaptureSession);
129
130 auto maybeCaptureSession = QPlatformMediaIntegration::instance()->createCaptureSession();
131 if (maybeCaptureSession) {
132 d->captureSession.reset(p: maybeCaptureSession.value());
133 d->captureSession->setCaptureSession(this);
134 } else {
135 qWarning() << "Failed to initialize QMediaCaptureSession" << maybeCaptureSession.error();
136 }
137}
138
139/*!
140 Destroys the session.
141 */
142QMediaCaptureSession::~QMediaCaptureSession()
143{
144 Q_D(QMediaCaptureSession);
145
146 setCamera(nullptr);
147 setRecorder(nullptr);
148 setImageCapture(nullptr);
149 setScreenCapture(nullptr);
150 setWindowCapture(nullptr);
151 setVideoFrameInput(nullptr);
152 setAudioBufferInput(nullptr);
153 setAudioInput(nullptr);
154 setAudioOutput(nullptr);
155 d->setVideoSink(nullptr);
156 d->captureSession.reset();
157}
158/*!
159 \qmlproperty AudioInput QtMultimedia::CaptureSession::audioInput
160
161 This property holds the audio input that is being used to capture audio.
162*/
163
164/*!
165 \property QMediaCaptureSession::audioInput
166
167 Returns the device that is being used to capture audio.
168*/
169QAudioInput *QMediaCaptureSession::audioInput() const
170{
171 Q_D(const QMediaCaptureSession);
172 return d->audioInput;
173}
174
175/*!
176 Sets the audio input device to \a input. If setting it to an empty
177 QAudioDevice the capture session will use the default input as
178 defined by the operating system.
179*/
180void QMediaCaptureSession::setAudioInput(QAudioInput *input)
181{
182 Q_D(QMediaCaptureSession);
183
184 QAudioInput *oldInput = d->audioInput;
185 if (oldInput == input)
186 return;
187
188 // To avoid double emit of audioInputChanged
189 // from recursive setAudioInput(nullptr) call.
190 d->audioInput = nullptr;
191
192 if (d->captureSession)
193 d->captureSession->setAudioInput(nullptr);
194 if (oldInput)
195 oldInput->setDisconnectFunction({});
196 if (input) {
197 input->setDisconnectFunction([this](){ setAudioInput(nullptr); });
198 if (d->captureSession)
199 d->captureSession->setAudioInput(input->handle());
200 }
201 d->audioInput = input;
202 emit audioInputChanged();
203}
204
205/*!
206 \property QMediaCaptureSession::audioBufferInput
207 \since 6.8
208
209 \brief The object used to send custom audio buffers to \l QMediaRecorder.
210*/
211QAudioBufferInput *QMediaCaptureSession::audioBufferInput() const
212{
213 Q_D(const QMediaCaptureSession);
214
215 return d->audioBufferInput;
216}
217
218void QMediaCaptureSession::setAudioBufferInput(QAudioBufferInput *input)
219{
220 Q_D(QMediaCaptureSession);
221
222 // TODO: come up with an unification of the captures setup
223 QAudioBufferInput *oldInput = d->audioBufferInput;
224 if (oldInput == input)
225 return;
226 d->audioBufferInput = input;
227 if (d->captureSession)
228 d->captureSession->setAudioBufferInput(nullptr);
229 if (oldInput) {
230 if (oldInput->captureSession() && oldInput->captureSession() != this)
231 oldInput->captureSession()->setAudioBufferInput(nullptr);
232 oldInput->setCaptureSession(nullptr);
233 }
234 if (input) {
235 if (input->captureSession())
236 input->captureSession()->setAudioBufferInput(nullptr);
237 if (d->captureSession)
238 d->captureSession->setAudioBufferInput(input->platformAudioBufferInput());
239 input->setCaptureSession(this);
240 }
241 emit audioBufferInputChanged();
242}
243
244/*!
245 \qmlproperty Camera QtMultimedia::CaptureSession::camera
246
247 \brief The camera used to capture video.
248
249 Record video or take images by adding a camera to the capture session using
250 this property.
251*/
252
253/*!
254 \property QMediaCaptureSession::camera
255
256 \brief The camera used to capture video.
257
258 Record video or take images by adding a camera to the capture session
259 using this property.
260*/
261QCamera *QMediaCaptureSession::camera() const
262{
263 Q_D(const QMediaCaptureSession);
264
265 return d->camera;
266}
267
268void QMediaCaptureSession::setCamera(QCamera *camera)
269{
270 Q_D(QMediaCaptureSession);
271
272 // TODO: come up with an unification of the captures setup
273 QCamera *oldCamera = d->camera;
274 if (oldCamera == camera)
275 return;
276 d->camera = camera;
277 if (d->captureSession)
278 d->captureSession->setCamera(nullptr);
279 if (oldCamera) {
280 if (oldCamera->captureSession() && oldCamera->captureSession() != this)
281 oldCamera->captureSession()->setCamera(nullptr);
282 oldCamera->setCaptureSession(nullptr);
283 }
284 if (camera) {
285 if (camera->captureSession())
286 camera->captureSession()->setCamera(nullptr);
287 if (d->captureSession)
288 d->captureSession->setCamera(camera->platformCamera());
289 camera->setCaptureSession(this);
290 }
291 emit cameraChanged();
292}
293
294/*!
295 \qmlproperty ScreenCapture QtMultimedia::CaptureSession::screenCapture
296 \since 6.5
297
298 \brief The object used to capture a screen.
299
300 Record a screen by adding a screen capture objet
301 to the capture session using this property.
302*/
303
304/*!
305 \property QMediaCaptureSession::screenCapture
306 \since 6.5
307
308 \brief The object used to capture a screen.
309
310 Record a screen by adding a screen capture object
311 to the capture session using this property.
312*/
313QScreenCapture *QMediaCaptureSession::screenCapture()
314{
315 Q_D(QMediaCaptureSession);
316
317 return d->screenCapture;
318}
319
320void QMediaCaptureSession::setScreenCapture(QScreenCapture *screenCapture)
321{
322 Q_D(QMediaCaptureSession);
323
324 // TODO: come up with an unification of the captures setup
325 QScreenCapture *oldScreenCapture = d->screenCapture;
326 if (oldScreenCapture == screenCapture)
327 return;
328 d->screenCapture = screenCapture;
329 if (d->captureSession)
330 d->captureSession->setScreenCapture(nullptr);
331 if (oldScreenCapture) {
332 if (oldScreenCapture->captureSession() && oldScreenCapture->captureSession() != this)
333 oldScreenCapture->captureSession()->setScreenCapture(nullptr);
334 oldScreenCapture->setCaptureSession(nullptr);
335 }
336 if (screenCapture) {
337 if (screenCapture->captureSession())
338 screenCapture->captureSession()->setScreenCapture(nullptr);
339 if (d->captureSession)
340 d->captureSession->setScreenCapture(screenCapture->platformScreenCapture());
341 screenCapture->setCaptureSession(this);
342 }
343 emit screenCaptureChanged();
344}
345
346/*!
347 \qmlproperty WindowCapture QtMultimedia::CaptureSession::windowCapture
348 \since 6.6
349
350 \brief The object used to capture a window.
351
352 Record a window by adding a window capture object
353 to the capture session using this property.
354*/
355
356/*!
357 \property QMediaCaptureSession::windowCapture
358 \since 6.6
359
360 \brief The object used to capture a window.
361
362 Record a window by adding a window capture objet
363 to the capture session using this property.
364*/
365QWindowCapture *QMediaCaptureSession::windowCapture()
366{
367 Q_D(QMediaCaptureSession);
368 return d->windowCapture;
369}
370
371void QMediaCaptureSession::setWindowCapture(QWindowCapture *windowCapture)
372{
373 Q_D(QMediaCaptureSession);
374
375 // TODO: come up with an unification of the captures setup
376 QWindowCapture *oldCapture = d->windowCapture;
377 if (oldCapture == windowCapture)
378 return;
379 d->windowCapture = windowCapture;
380 if (d->captureSession)
381 d->captureSession->setWindowCapture(nullptr);
382 if (oldCapture) {
383 if (oldCapture->captureSession() && oldCapture->captureSession() != this)
384 oldCapture->captureSession()->setWindowCapture(nullptr);
385 oldCapture->setCaptureSession(nullptr);
386 }
387 if (windowCapture) {
388 if (windowCapture->captureSession())
389 windowCapture->captureSession()->setWindowCapture(nullptr);
390 if (d->captureSession)
391 d->captureSession->setWindowCapture(windowCapture->platformWindowCapture());
392 windowCapture->setCaptureSession(this);
393 }
394 emit windowCaptureChanged();
395}
396
397/*!
398 \property QMediaCaptureSession::videoFrameInput
399 \since 6.8
400
401 \brief The object used to send custom video frames to
402 \l QMediaRecorder or a video output.
403*/
404QVideoFrameInput *QMediaCaptureSession::videoFrameInput() const
405{
406 Q_D(const QMediaCaptureSession);
407 return d->videoFrameInput;
408}
409
410void QMediaCaptureSession::setVideoFrameInput(QVideoFrameInput *input)
411{
412 Q_D(QMediaCaptureSession);
413 // TODO: come up with an unification of the captures setup
414 QVideoFrameInput *oldInput = d->videoFrameInput;
415 if (oldInput == input)
416 return;
417 d->videoFrameInput = input;
418 if (d->captureSession)
419 d->captureSession->setVideoFrameInput(nullptr);
420 if (oldInput) {
421 if (oldInput->captureSession() && oldInput->captureSession() != this)
422 oldInput->captureSession()->setVideoFrameInput(nullptr);
423 oldInput->setCaptureSession(nullptr);
424 }
425 if (input) {
426 if (input->captureSession())
427 input->captureSession()->setVideoFrameInput(nullptr);
428 if (d->captureSession)
429 d->captureSession->setVideoFrameInput(input->platformVideoFrameInput());
430 input->setCaptureSession(this);
431 }
432 emit videoFrameInputChanged();
433}
434
435/*!
436 \qmlproperty ImageCapture QtMultimedia::CaptureSession::imageCapture
437
438 \brief The object used to capture still images.
439
440 Add an ImageCapture interface to the capture session to enable
441 capturing of still images from the camera.
442*/
443/*!
444 \property QMediaCaptureSession::imageCapture
445
446 \brief the object used to capture still images.
447
448 Add a QImageCapture object to the capture session to enable
449 capturing of still images from the camera.
450*/
451QImageCapture *QMediaCaptureSession::imageCapture()
452{
453 Q_D(QMediaCaptureSession);
454
455 return d->imageCapture;
456}
457
458void QMediaCaptureSession::setImageCapture(QImageCapture *imageCapture)
459{
460 Q_D(QMediaCaptureSession);
461
462 // TODO: come up with an unification of the captures setup
463 QImageCapture *oldImageCapture = d->imageCapture;
464 if (oldImageCapture == imageCapture)
465 return;
466 d->imageCapture = imageCapture;
467 if (d->captureSession)
468 d->captureSession->setImageCapture(nullptr);
469 if (oldImageCapture) {
470 if (oldImageCapture->captureSession() && oldImageCapture->captureSession() != this)
471 oldImageCapture->captureSession()->setImageCapture(nullptr);
472 oldImageCapture->setCaptureSession(nullptr);
473 }
474 if (imageCapture) {
475 if (imageCapture->captureSession())
476 imageCapture->captureSession()->setImageCapture(nullptr);
477 if (d->captureSession)
478 d->captureSession->setImageCapture(imageCapture->platformImageCapture());
479 imageCapture->setCaptureSession(this);
480 }
481 emit imageCaptureChanged();
482}
483/*!
484 \qmlproperty MediaRecorder QtMultimedia::CaptureSession::recorder
485
486 \brief The recorder object used to capture audio/video.
487
488 Add a MediaRcorder object to the capture session to enable
489 recording of audio and/or video from the capture session.
490*/
491/*!
492 \property QMediaCaptureSession::recorder
493
494 \brief The recorder object used to capture audio/video.
495
496 Add a QMediaRecorder object to the capture session to enable
497 recording of audio and/or video from the capture session.
498*/
499
500QMediaRecorder *QMediaCaptureSession::recorder()
501{
502 Q_D(QMediaCaptureSession);
503 return d->recorder;
504}
505
506void QMediaCaptureSession::setRecorder(QMediaRecorder *recorder)
507{
508 Q_D(QMediaCaptureSession);
509 QMediaRecorder *oldRecorder = d->recorder;
510 if (oldRecorder == recorder)
511 return;
512 d->recorder = recorder;
513 if (d->captureSession)
514 d->captureSession->setMediaRecorder(nullptr);
515 if (oldRecorder) {
516 if (oldRecorder->captureSession() && oldRecorder->captureSession() != this)
517 oldRecorder->captureSession()->setRecorder(nullptr);
518 oldRecorder->setCaptureSession(nullptr);
519 }
520 if (recorder) {
521 if (recorder->captureSession())
522 recorder->captureSession()->setRecorder(nullptr);
523 if (d->captureSession)
524 d->captureSession->setMediaRecorder(recorder->platformRecoder());
525 recorder->setCaptureSession(this);
526 }
527 emit recorderChanged();
528}
529/*!
530 \qmlproperty VideoOutput QtMultimedia::CaptureSession::videoOutput
531
532 \brief The VideoOutput that is the video preview for the capture session.
533
534 A VideoOutput based preview is expected to have an invokable videoSink()
535 method that returns a QVideoSink.
536
537 The previously set preview is detached.
538
539*/
540/*!
541 \property QMediaCaptureSession::videoOutput
542
543 Returns the video output for the session.
544*/
545QObject *QMediaCaptureSession::videoOutput() const
546{
547 Q_D(const QMediaCaptureSession);
548 return d->videoOutput;
549}
550/*!
551 Sets a QObject, (\a output), to a video preview for the capture session.
552
553 A QObject based preview is expected to have an invokable videoSink()
554 method that returns a QVideoSink.
555
556 The previously set preview is detached.
557*/
558void QMediaCaptureSession::setVideoOutput(QObject *output)
559{
560 Q_D(QMediaCaptureSession);
561 if (d->videoOutput == output)
562 return;
563 QVideoSink *sink = qobject_cast<QVideoSink *>(object: output);
564 if (!sink && output) {
565 auto *mo = output->metaObject();
566 mo->invokeMethod(obj: output, member: "videoSink", Q_RETURN_ARG(QVideoSink *, sink));
567 }
568 d->videoOutput = output;
569 d->setVideoSink(sink);
570}
571
572/*!
573 Sets a QVideoSink, (\a sink), to a video preview for the capture session.
574
575 A QObject based preview is expected to have an invokable videoSink()
576 method that returns a QVideoSink.
577
578 The previously set preview is detached.
579*/
580void QMediaCaptureSession::setVideoSink(QVideoSink *sink)
581{
582 Q_D(QMediaCaptureSession);
583 d->videoOutput = nullptr;
584 d->setVideoSink(sink);
585}
586
587/*!
588 Returns the QVideoSink for the session.
589*/
590QVideoSink *QMediaCaptureSession::videoSink() const
591{
592 Q_D(const QMediaCaptureSession);
593 return d->videoSink;
594}
595/*!
596 Sets the audio output device to \a{output}.
597
598 Setting an audio output device enables audio routing from an audio input device.
599*/
600void QMediaCaptureSession::setAudioOutput(QAudioOutput *output)
601{
602 Q_D(QMediaCaptureSession);
603
604 QAudioOutput *oldOutput = d->audioOutput;
605 if (oldOutput == output)
606 return;
607
608 // We don't want to end up with signal emitted
609 // twice (from recursive call setAudioInput(nullptr)
610 // from oldOutput->setDisconnectFunction():
611 d->audioOutput = nullptr;
612
613 if (d->captureSession)
614 d->captureSession->setAudioOutput(nullptr);
615 if (oldOutput)
616 oldOutput->setDisconnectFunction({});
617 if (output) {
618 output->setDisconnectFunction([this](){ setAudioOutput(nullptr); });
619 if (d->captureSession)
620 d->captureSession->setAudioOutput(output->handle());
621 }
622 d->audioOutput = output;
623 emit audioOutputChanged();
624}
625/*!
626 \qmlproperty AudioOutput QtMultimedia::CaptureSession::audioOutput
627 \brief The audio output device for the capture session.
628
629 Add an AudioOutput device to the capture session to enable
630 audio routing from an AudioInput device.
631*/
632/*!
633 \property QMediaCaptureSession::audioOutput
634
635 Returns the audio output for the session.
636*/
637QAudioOutput *QMediaCaptureSession::audioOutput() const
638{
639 Q_D(const QMediaCaptureSession);
640 return d->audioOutput;
641}
642
643/*!
644 \internal
645*/
646QPlatformMediaCaptureSession *QMediaCaptureSession::platformSession() const
647{
648 Q_D(const QMediaCaptureSession);
649 return d->captureSession.get();
650}
651/*!
652 \qmlsignal QtMultimedia::CaptureSession::audioInputChanged()
653 This signal is emitted when an audio input has changed.
654 \sa CaptureSession::audioInput
655*/
656
657/*!
658 \qmlsignal QtMultimedia::CaptureSession::cameraChanged()
659 This signal is emitted when the selected camera has changed.
660 \sa CaptureSession::camera
661*/
662
663/*!
664 \qmlsignal QtMultimedia::CaptureSession::imageCaptureChanged()
665 This signal is emitted when the selected interface has changed.
666 \sa CaptureSession::camera
667*/
668
669/*!
670 \qmlsignal QtMultimedia::CaptureSession::recorderChanged()
671 This signal is emitted when the selected recorder has changed.
672 \sa CaptureSession::recorder
673*/
674
675/*!
676 \qmlsignal QtMultimedia::CaptureSession::videoOutputChanged()
677 This signal is emitted when the selected video output has changed.
678 \sa CaptureSession::videoOutput
679*/
680
681/*!
682 \qmlsignal QtMultimedia::CaptureSession::audioOutputChanged()
683 This signal is emitted when the selected audio output has changed.
684 \sa CaptureSession::audioOutput
685*/
686QT_END_NAMESPACE
687
688#include "moc_qmediacapturesession.cpp"
689

source code of qtmultimedia/src/multimedia/recording/qmediacapturesession.cpp