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