1 /*
2  *  Kaidan - A user-friendly XMPP client for every device!
3  *
4  *  Copyright (C) 2016-2021 Kaidan developers and contributors
5  *  (see the LICENSE file for a full list of copyright authors)
6  *
7  *  Kaidan is free software: you can redistribute it and/or modify
8  *  it under the terms of the GNU General Public License as published by
9  *  the Free Software Foundation, either version 3 of the License, or
10  *  (at your option) any later version.
11  *
12  *  In addition, as a special exception, the author of Kaidan gives
13  *  permission to link the code of its release with the OpenSSL
14  *  project's "OpenSSL" library (or with modified versions of it that
15  *  use the same license as the "OpenSSL" library), and distribute the
16  *  linked executables. You must obey the GNU General Public License in
17  *  all respects for all of the code used other than "OpenSSL". If you
18  *  modify this file, you may extend this exception to your version of
19  *  the file, but you are not obligated to do so.  If you do not wish to
20  *  do so, delete this exception statement from your version.
21  *
22  *  Kaidan is distributed in the hope that it will be useful,
23  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
24  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
25  *  GNU General Public License for more details.
26  *
27  *  You should have received a copy of the GNU General Public License
28  *  along with Kaidan.  If not, see <http://www.gnu.org/licenses/>.
29  */
30 
31 #include "MediaRecorder.h"
32 #include "Kaidan.h"
33 
34 #include <QUrl>
35 #include <QFile>
36 #include <QSettings>
37 
38 /*
39  * NOTES: Codecs and containers supported list are available as soon as the object is created.
40  * Resolutions, frame rates etc are populated once the objects become *ready*.
41  */
42 
43 #define ENABLE_DEBUG false
44 
45 #define SETTING_USER_DEFAULT QStringLiteral("User Default")
46 #define SETTING_DEFAULT_CAMERA_DEVICE_NAME QStringLiteral("Camera Device Name")
47 #define SETTING_DEFAULT_AUDIO_INPUT_DEVICE_NAME QStringLiteral("Audio Input Device Name")
48 
connectCamera(QCamera * camera,MediaRecorder * receiver)49 static void connectCamera(QCamera *camera, MediaRecorder *receiver) {
50 	QObject::connect(camera, &QCamera::statusChanged, receiver, &MediaRecorder::readyChanged);
51 }
52 
connectImageCapturer(CameraImageCapture * capturer,MediaRecorder * receiver)53 static void connectImageCapturer(CameraImageCapture *capturer, MediaRecorder *receiver) {
54 	QObject::connect(capturer, &CameraImageCapture::availabilityChanged, receiver, &MediaRecorder::availabilityStatusChanged);
55 	QObject::connect(capturer, QOverload<int, QCameraImageCapture::Error, const QString&>::of(&CameraImageCapture::error), receiver, &MediaRecorder::errorChanged);
56 	QObject::connect(capturer, &CameraImageCapture::actualLocationChanged, receiver, &MediaRecorder::actualLocationChanged);
57 	QObject::connect(capturer, &CameraImageCapture::readyForCaptureChanged, receiver, &MediaRecorder::readyChanged);
58 }
59 
60 template <typename T>
connectMediaRecorder(T * recorder,MediaRecorder * receiver)61 static void connectMediaRecorder(T *recorder, MediaRecorder *receiver) {
62 	QObject::connect(recorder, QOverload<QMultimedia::AvailabilityStatus>::of(&T::availabilityChanged), receiver, &MediaRecorder::availabilityStatusChanged);
63 	QObject::connect(recorder, &T::stateChanged, receiver, &MediaRecorder::stateChanged);
64 	QObject::connect(recorder, &T::statusChanged, receiver, &MediaRecorder::statusChanged);
65 	QObject::connect(recorder, QOverload<QMediaRecorder::Error>::of(&T::error), receiver, &MediaRecorder::errorChanged);
66 	QObject::connect(recorder, &T::actualLocationChanged, receiver, &MediaRecorder::actualLocationChanged);
67 	QObject::connect(recorder, &T::durationChanged, receiver, &MediaRecorder::durationChanged);
68 	QObject::connect(recorder, &T::mutedChanged, receiver, &MediaRecorder::mutedChanged);
69 	QObject::connect(recorder, &T::volumeChanged, receiver, &MediaRecorder::volumeChanged);
70 	QObject::connect(recorder, QOverload<QMultimedia::AvailabilityStatus>::of(&T::availabilityChanged), receiver, &MediaRecorder::readyChanged);
71 	QObject::connect(recorder, &T::statusChanged, receiver, &MediaRecorder::readyChanged);
72 }
73 
74 template <>
connectMediaRecorder(QAudioRecorder * recorder,MediaRecorder * receiver)75 void connectMediaRecorder<QAudioRecorder>(QAudioRecorder *recorder, MediaRecorder *receiver) {
76 	connectMediaRecorder(qobject_cast<QMediaRecorder *>(recorder), receiver);
77 }
78 
79 template <typename F>
buildCodecList(const QStringList & codecs,F toString,MediaRecorder * recorder,const QString & startsWith=QString ())80 static QStringList buildCodecList(const QStringList &codecs, F toString,
81 	MediaRecorder *recorder, const QString &startsWith = QString()) {
82 	QStringList entries(codecs);
83 
84 	if (!startsWith.isEmpty()) {
85 		entries.erase(std::remove_if(entries.begin(), entries.end(), [&startsWith](const QString &entry) {
86 			return !entry.startsWith(startsWith, Qt::CaseInsensitive);
87 		}), entries.end());
88 	}
89 
90 	std::sort(entries.begin(), entries.end(), [&toString, recorder](const QString &left, const QString &right) {
91 		return toString(left, recorder).compare(toString(right, recorder), Qt::CaseInsensitive) < 0;
92 	});
93 
94 	entries.prepend(QString());
95 
96 	return entries;
97 }
98 
buildResolutionList(const QList<QSize> & resolutions)99 static QList<QSize> buildResolutionList(const QList<QSize> &resolutions) {
100 	QList<QSize> entries(resolutions);
101 
102 	std::sort(entries.begin(), entries.end(), [](const QSize &left, const QSize &right) {
103 		return left.width() == right.width()
104 			       ? left.height() < right.height()
105 			       : left.width() < right.width();
106 	});
107 
108 	entries.prepend(QSize());
109 
110 	return entries;
111 }
112 
buildQualityList()113 static QList<CommonEncoderSettings::EncodingQuality> buildQualityList() {
114 	const QList<CommonEncoderSettings::EncodingQuality> entries {
115 		CommonEncoderSettings::EncodingQuality::VeryLowQuality,
116 		CommonEncoderSettings::EncodingQuality::LowQuality,
117 		CommonEncoderSettings::EncodingQuality::NormalQuality,
118 		CommonEncoderSettings::EncodingQuality::HighQuality,
119 		CommonEncoderSettings::EncodingQuality::VeryHighQuality
120 	};
121 
122 	return entries;
123 }
124 
buildSampleRateList(const QList<int> & sampleRates)125 static QList<int> buildSampleRateList(const QList<int> &sampleRates) {
126 	QList<int> entries(sampleRates);
127 
128 	if (entries.isEmpty()) {
129 		entries = {
130 			8000,
131 			16000,
132 			22050,
133 			32000,
134 			37800,
135 			44100,
136 			48000,
137 			96000,
138 			192000
139 		};
140 	}
141 
142 	std::sort(entries.begin(), entries.end(), [](const int left, const int right) {
143 		return left < right;
144 	});
145 
146 	entries.prepend(-1);
147 
148 	return entries;
149 }
150 
buildFrameRateList(const QList<qreal> & frameRates)151 static QList<qreal> buildFrameRateList(const QList<qreal> &frameRates) {
152 	QList<qreal> entries(frameRates);
153 
154 	std::sort(entries.begin(), entries.end(), [](const qreal left, const qreal right) {
155 		return left < right;
156 	});
157 
158 	entries.prepend(0.0);
159 
160 	return entries;
161 }
162 
MediaRecorder(QObject * parent)163 MediaRecorder::MediaRecorder(QObject *parent)
164 	: QObject(parent)
165 	  , m_cameraModel(new CameraModel(this))
166 	  , m_audioDeviceModel(new AudioDeviceModel(this))
167 	  , m_containerModel(new MediaSettingsContainerModel(this, this))
168 	  , m_imageCodecModel(new MediaSettingsImageCodecModel(this, this))
169 	  , m_imageResolutionModel(new MediaSettingsResolutionModel(this, this))
170 	  , m_imageQualityModel(new MediaSettingsQualityModel(this, this))
171 	  , m_audioCodecModel(new MediaSettingsAudioCodecModel(this, this))
172 	  , m_audioSampleRateModel(new MediaSettingsAudioSampleRateModel(this, this))
173 	  , m_audioQualityModel(new MediaSettingsQualityModel(this, this))
174 	  , m_videoCodecModel(new MediaSettingsVideoCodecModel(this, this))
175 	  , m_videoResolutionModel(new MediaSettingsResolutionModel(this, this))
176 	  , m_videoFrameRateModel(new MediaSettingsVideoFrameRateModel(this, this))
177 	  , m_videoQualityModel(new MediaSettingsQualityModel(this, this))
178 {
179 	connect(this, &MediaRecorder::readyChanged, this, [this]() {
180 		if (!isReady()) {
181 			if (m_type == MediaRecorder::Type::Invalid) {
182 				m_cameraModel->setCurrentIndex(-1);
183 				m_audioDeviceModel->setCurrentIndex(-1);
184 				m_containerModel->clear();
185 
186 				m_imageCodecModel->clear();
187 				m_imageResolutionModel->clear();
188 				m_imageQualityModel->clear();
189 
190 				m_audioCodecModel->clear();
191 				m_audioSampleRateModel->clear();
192 				m_audioQualityModel->clear();
193 
194 				m_videoCodecModel->clear();
195 				m_videoResolutionModel->clear();
196 				m_videoFrameRateModel->clear();
197 				m_videoQualityModel->clear();
198 			}
199 
200 			return;
201 		}
202 
203 #if ENABLE_DEBUG
204 		qDebug("syncProperties Begin");
205 #endif
206 
207 		switch (m_type) {
208 		case MediaRecorder::Type::Invalid: {
209 			Q_UNREACHABLE();
210 			break;
211 		}
212 		case MediaRecorder::Type::Image: {
213 			m_cameraModel->setCurrentCamera(m_mediaSettings.camera);
214 			m_imageCodecModel->setValuesAndCurrentValue(buildCodecList(m_imageCapturer->supportedImageCodecs(), imageEncoderCodec, this),
215 				m_imageEncoderSettings.codec);
216 			m_imageResolutionModel->setValuesAndCurrentValue(buildResolutionList(m_imageCapturer->supportedResolutions()),
217 				m_imageEncoderSettings.resolution);
218 			m_imageQualityModel->setValuesAndCurrentValue(buildQualityList(),
219 				m_imageEncoderSettings.quality);
220 			break;
221 		}
222 		case MediaRecorder::Type::Audio: {
223 			m_audioDeviceModel->setCurrentAudioDevice(m_mediaSettings.audioInputDevice);
224 			m_containerModel->setValuesAndCurrentValue(buildCodecList(m_audioRecorder->supportedContainers(), encoderContainer, this, QStringLiteral("audio")),
225 				m_mediaSettings.container);
226 			m_audioCodecModel->setValuesAndCurrentValue(buildCodecList(m_audioRecorder->supportedAudioCodecs(), audioEncoderCodec, this),
227 				m_audioEncoderSettings.codec);
228 			m_audioSampleRateModel->setValuesAndCurrentValue(buildSampleRateList(m_audioRecorder->supportedAudioSampleRates()),
229 				m_audioEncoderSettings.sampleRate);
230 			m_audioQualityModel->setValuesAndCurrentValue(buildQualityList(),
231 				m_audioEncoderSettings.quality);
232 			break;
233 		}
234 		case MediaRecorder::Type::Video: {
235 			m_cameraModel->setCurrentCamera(m_mediaSettings.camera);
236 			m_containerModel->setValuesAndCurrentValue(buildCodecList(m_videoRecorder->supportedContainers(), encoderContainer, this, QStringLiteral("video")),
237 				m_mediaSettings.container);
238 			m_audioCodecModel->setValuesAndCurrentValue(buildCodecList(m_videoRecorder->supportedAudioCodecs(), audioEncoderCodec, this),
239 				m_audioEncoderSettings.codec);
240 			m_audioSampleRateModel->setValuesAndCurrentValue(buildSampleRateList(m_videoRecorder->supportedAudioSampleRates()),
241 				m_audioEncoderSettings.sampleRate);
242 			m_audioQualityModel->setValuesAndCurrentValue(buildQualityList(),
243 				m_audioEncoderSettings.quality);
244 			m_videoCodecModel->setValuesAndCurrentValue(buildCodecList(m_videoRecorder->supportedVideoCodecs(), videoEncoderCodec, this),
245 				m_videoEncoderSettings.codec);
246 			m_videoResolutionModel->setValuesAndCurrentValue(buildResolutionList(m_videoRecorder->supportedResolutions()),
247 				m_videoEncoderSettings.resolution);
248 			m_videoFrameRateModel->setValuesAndCurrentValue(buildFrameRateList(m_videoRecorder->supportedFrameRates()),
249 				m_videoEncoderSettings.frameRate);
250 			m_videoQualityModel->setValuesAndCurrentValue(buildQualityList(),
251 				m_videoEncoderSettings.quality);
252 			break;
253 		}
254 		}
255 
256 #if ENABLE_DEBUG
257 		qDebug("syncProperties End");
258 #endif
259 	});
260 }
261 
~MediaRecorder()262 MediaRecorder::~MediaRecorder()
263 {
264 	if (isAvailable() && !isReady()) {
265 		cancel();
266 	}
267 }
268 
type() const269 MediaRecorder::Type MediaRecorder::type() const
270 {
271 	return m_type;
272 }
273 
setType(MediaRecorder::Type type)274 void MediaRecorder::setType(MediaRecorder::Type type)
275 {
276 	if (m_type == type) {
277 		return;
278 	}
279 
280 	setupRecorder(type);
281 }
282 
mediaObject() const283 QMediaObject *MediaRecorder::mediaObject() const
284 {
285 	switch (m_type) {
286 	case MediaRecorder::Type::Image:
287 	case MediaRecorder::Type::Video:
288 		return m_camera;
289 	case MediaRecorder::Type::Invalid:
290 	case MediaRecorder::Type::Audio:
291 		break;
292 	}
293 
294 	return nullptr;
295 }
296 
currentSettingsKey() const297 QString MediaRecorder::currentSettingsKey() const
298 {
299 	switch (m_type) {
300 	case MediaRecorder::Type::Invalid:
301 		break;
302 	case MediaRecorder::Type::Image:
303 		Q_ASSERT(!m_mediaSettings.camera.isNull());
304 		return settingsKey(m_type, m_mediaSettings.camera.deviceName());
305 	case MediaRecorder::Type::Audio:
306 		Q_ASSERT(!m_mediaSettings.audioInputDevice.isNull());
307 		return settingsKey(m_type, m_mediaSettings.audioInputDevice.deviceName());
308 	case MediaRecorder::Type::Video:
309 		Q_ASSERT(!m_mediaSettings.camera.isNull());
310 		return settingsKey(m_type, m_mediaSettings.camera.deviceName());
311 	}
312 
313 	return { };
314 }
315 
availabilityStatus() const316 MediaRecorder::AvailabilityStatus MediaRecorder::availabilityStatus() const
317 {
318 	switch (m_type) {
319 	case MediaRecorder::Type::Invalid:
320 		break;
321 	case MediaRecorder::Type::Image:
322 		return static_cast<MediaRecorder::AvailabilityStatus>(m_imageCapturer->availability());
323 	case MediaRecorder::Type::Audio:
324 		return static_cast<MediaRecorder::AvailabilityStatus>(m_audioRecorder->availability());
325 	case MediaRecorder::Type::Video:
326 		return static_cast<MediaRecorder::AvailabilityStatus>(m_videoRecorder->availability());
327 	}
328 
329 	return MediaRecorder::AvailabilityStatus::ServiceMissing;
330 }
331 
state() const332 MediaRecorder::State MediaRecorder::state() const
333 {
334 	switch (m_type) {
335 	case MediaRecorder::Type::Invalid:
336 	case MediaRecorder::Type::Image:
337 		break;
338 	case MediaRecorder::Type::Audio:
339 		return static_cast<MediaRecorder::State>(m_audioRecorder->state());
340 	case MediaRecorder::Type::Video:
341 		return static_cast<MediaRecorder::State>(m_videoRecorder->state());
342 	}
343 
344 	return MediaRecorder::State::StoppedState;
345 }
346 
status() const347 MediaRecorder::Status MediaRecorder::status() const
348 {
349 	switch (m_type) {
350 	case MediaRecorder::Type::Invalid:
351 	case MediaRecorder::Type::Image:
352 		break;
353 	case MediaRecorder::Type::Audio:
354 		return static_cast<MediaRecorder::Status>(m_audioRecorder->status());
355 	case MediaRecorder::Type::Video:
356 		return static_cast<MediaRecorder::Status>(m_videoRecorder->status());
357 	}
358 
359 	return MediaRecorder::Status::UnavailableStatus;
360 }
361 
isAvailable() const362 bool MediaRecorder::isAvailable() const
363 {
364 	switch (m_type) {
365 	case MediaRecorder::Type::Invalid:
366 		break;
367 	case MediaRecorder::Type::Image:
368 		return m_imageCapturer->isAvailable();
369 	case MediaRecorder::Type::Audio:
370 		return m_audioRecorder->isAvailable();
371 	case MediaRecorder::Type::Video:
372 		return m_videoRecorder->isAvailable();
373 	}
374 
375 	return false;
376 }
377 
isReady() const378 bool MediaRecorder::isReady() const
379 {
380 	switch (m_type) {
381 	case MediaRecorder::Type::Invalid:
382 		break;
383 	case MediaRecorder::Type::Image:
384 		return m_imageCapturer->isReadyForCapture();
385 	case MediaRecorder::Type::Audio:
386 		return isAvailable()
387 		       && status() >= MediaRecorder::Status::UnloadedStatus
388 		       && status() <= MediaRecorder::Status::LoadedStatus;
389 	case MediaRecorder::Type::Video:
390 		return isAvailable()
391 		       && m_camera->status() == QCamera::Status::ActiveStatus
392 		       && status() >= MediaRecorder::Status::UnloadedStatus
393 		       && status() <= MediaRecorder::Status::LoadedStatus;
394 	}
395 
396 	return false;
397 }
398 
error() const399 MediaRecorder::Error MediaRecorder::error() const
400 {
401 	static const QMap<int, MediaRecorder::Error> capturerMapping = {
402 		{ QCameraImageCapture::Error::NoError, MediaRecorder::Error::NoError },
403 		{ QCameraImageCapture::Error::NotReadyError, MediaRecorder::Error::NotReadyError },
404 		{ QCameraImageCapture::Error::ResourceError, MediaRecorder::Error::ResourceError },
405 		{ QCameraImageCapture::Error::OutOfSpaceError, MediaRecorder::Error::OutOfSpaceError },
406 		{ QCameraImageCapture::Error::NotSupportedFeatureError, MediaRecorder::Error::NotSupportedFeatureError },
407 		{ QCameraImageCapture::Error::FormatError, MediaRecorder::Error::FormatError }
408 	};
409 	static const QMap<int, MediaRecorder::Error> recorderMapping = {
410 		{ QMediaRecorder::Error::NoError, MediaRecorder::Error::NoError },
411 		{ QMediaRecorder::Error::ResourceError, MediaRecorder::Error::ResourceError },
412 		{ QMediaRecorder::Error::FormatError, MediaRecorder::Error::FormatError },
413 		{ QMediaRecorder::Error::OutOfSpaceError, MediaRecorder::Error::OutOfSpaceError }
414 	};
415 
416 	switch (m_type) {
417 	case MediaRecorder::Type::Invalid:
418 		break;
419 	case MediaRecorder::Type::Image: {
420 		const auto it = capturerMapping.constFind(m_imageCapturer->error());
421 		Q_ASSERT(it != capturerMapping.constEnd());
422 		return it.value();
423 	}
424 	case MediaRecorder::Type::Audio: {
425 		const auto it = recorderMapping.constFind(m_audioRecorder->error());
426 		Q_ASSERT(it != recorderMapping.constEnd());
427 		return it.value();
428 	}
429 	case MediaRecorder::Type::Video: {
430 		const auto it = recorderMapping.constFind(m_videoRecorder->error());
431 		Q_ASSERT(it != recorderMapping.constEnd());
432 		return it.value();
433 	}
434 	}
435 
436 	return MediaRecorder::Error::NoError;
437 }
438 
errorString() const439 QString MediaRecorder::errorString() const
440 {
441 	switch (m_type) {
442 	case MediaRecorder::Type::Invalid:
443 		break;
444 	case MediaRecorder::Type::Image:
445 		return m_imageCapturer->errorString();
446 	case MediaRecorder::Type::Audio:
447 		return m_audioRecorder->errorString();
448 	case MediaRecorder::Type::Video:
449 		return m_videoRecorder->errorString();
450 	}
451 
452 	return { };
453 }
454 
actualLocation() const455 QUrl MediaRecorder::actualLocation() const
456 {
457 	static const auto urlExists = [](const QUrl &url) {
458 		if (url.isEmpty() || (!url.scheme().isEmpty() && !url.isLocalFile())) {
459 			return url;
460 		}
461 
462 		const QUrl u(url.isLocalFile() ? url : QUrl::fromLocalFile(url.toString()));
463 		return QFile::exists(u.toLocalFile()) ? u : QUrl();
464 	};
465 
466 	switch (m_type) {
467 	case MediaRecorder::Type::Invalid:
468 		break;
469 	case MediaRecorder::Type::Image:
470 		return urlExists(m_imageCapturer->actualLocation());
471 	case MediaRecorder::Type::Audio:
472 		return urlExists(m_audioRecorder->actualLocation());
473 	case MediaRecorder::Type::Video:
474 		return urlExists(m_videoRecorder->actualLocation());
475 	}
476 
477 	return { };
478 }
479 
duration() const480 qint64 MediaRecorder::duration() const
481 {
482 	switch (m_type) {
483 	case MediaRecorder::Type::Invalid:
484 	case MediaRecorder::Type::Image:
485 		break;
486 	case MediaRecorder::Type::Audio:
487 		return m_audioRecorder->duration();
488 	case MediaRecorder::Type::Video:
489 		return m_videoRecorder->duration();
490 	}
491 
492 	return 0;
493 }
494 
isMuted() const495 bool MediaRecorder::isMuted() const
496 {
497 	switch (m_type) {
498 	case MediaRecorder::Type::Invalid:
499 	case MediaRecorder::Type::Image:
500 		break;
501 	case MediaRecorder::Type::Audio:
502 		return m_audioRecorder->isMuted();
503 	case MediaRecorder::Type::Video:
504 		return m_videoRecorder->isMuted();
505 	}
506 
507 	return false;
508 }
509 
setMuted(bool muted)510 void MediaRecorder::setMuted(bool muted)
511 {
512 	switch (m_type) {
513 	case MediaRecorder::Type::Invalid:
514 	case MediaRecorder::Type::Image:
515 		Q_UNREACHABLE();
516 		break;
517 	case MediaRecorder::Type::Audio:
518 		m_audioRecorder->setMuted(muted);
519 		break;
520 	case MediaRecorder::Type::Video:
521 		m_videoRecorder->setMuted(muted);
522 		break;
523 	}
524 }
525 
volume() const526 qreal MediaRecorder::volume() const
527 {
528 	switch (m_type) {
529 	case MediaRecorder::Type::Invalid:
530 	case MediaRecorder::Type::Image:
531 		break;
532 	case MediaRecorder::Type::Audio:
533 		return m_audioRecorder->volume();
534 	case MediaRecorder::Type::Video:
535 		return m_videoRecorder->volume();
536 	}
537 
538 	return false;
539 }
540 
setVolume(qreal volume)541 void MediaRecorder::setVolume(qreal volume)
542 {
543 	switch (m_type) {
544 	case MediaRecorder::Type::Invalid:
545 	case MediaRecorder::Type::Image:
546 		Q_UNREACHABLE();
547 		break;
548 	case MediaRecorder::Type::Audio:
549 		m_audioRecorder->setVolume(volume);
550 		break;
551 	case MediaRecorder::Type::Video:
552 		m_videoRecorder->setVolume(volume);
553 		break;
554 	}
555 }
556 
mediaSettings() const557 MediaSettings MediaRecorder::mediaSettings() const
558 {
559 	return m_mediaSettings;
560 }
561 
setMediaSettings(const MediaSettings & settings)562 void MediaRecorder::setMediaSettings(const MediaSettings &settings)
563 {
564 #if ENABLE_DEBUG
565 	qDebug(Q_FUNC_INFO);
566 #endif
567 
568 	if (settings == m_mediaSettings) {
569 		return;
570 	}
571 
572 	const auto oldSettings = m_mediaSettings;
573 
574 #if ENABLE_DEBUG
575 	settings.dumpProperties(QStringLiteral("New"));
576 	oldSettings.dumpProperties(QStringLiteral("Old"));
577 #endif
578 
579 	m_mediaSettings = settings;
580 
581 	if (!m_initializing) {
582 		switch (m_type) {
583 		case MediaRecorder::Type::Invalid:
584 			Q_UNREACHABLE();
585 			break;
586 		case MediaRecorder::Type::Image:
587 			if (oldSettings.camera != settings.camera) {
588 				setupRecorder(m_type);
589 			}
590 
591 			break;
592 		case MediaRecorder::Type::Audio:
593 			if (oldSettings.audioInputDevice != settings.audioInputDevice) {
594 				setupRecorder(m_type);
595 			} else {
596 				m_audioRecorder->setContainerFormat(m_mediaSettings.container);
597 			}
598 
599 			break;
600 		case MediaRecorder::Type::Video:
601 			if (oldSettings.camera != settings.camera) {
602 				setupRecorder(m_type);
603 			}  else {
604 				m_videoRecorder->setContainerFormat(m_mediaSettings.container);
605 			}
606 
607 			break;
608 		}
609 
610 		emit mediaSettingsChanged();
611 	}
612 }
613 
imageEncoderSettings() const614 ImageEncoderSettings MediaRecorder::imageEncoderSettings() const
615 {
616 	return m_imageEncoderSettings;
617 }
618 
setImageEncoderSettings(const ImageEncoderSettings & settings)619 void MediaRecorder::setImageEncoderSettings(const ImageEncoderSettings &settings)
620 {
621 #if ENABLE_DEBUG
622 	qDebug(Q_FUNC_INFO);
623 #endif
624 
625 	switch (m_type) {
626 	case MediaRecorder::Type::Image: {
627 		if (settings != m_imageEncoderSettings) {
628 #if ENABLE_DEBUG
629 			settings.dumpProperties(QStringLiteral("New"));
630 			m_imageEncoderSettings.dumpProperties(QStringLiteral("Old"));
631 #endif
632 
633 			m_imageEncoderSettings = settings;
634 
635 			if (!m_initializing) {
636 				m_imageCapturer->setEncodingSettings(m_imageEncoderSettings.toQImageEncoderSettings());
637 				emit imageEncoderSettingsChanged();
638 			}
639 		}
640 
641 		break;
642 	}
643 	case MediaRecorder::Type::Invalid:
644 	case MediaRecorder::Type::Audio:
645 	case MediaRecorder::Type::Video:
646 		Q_UNREACHABLE();
647 		break;
648 	}
649 }
650 
audioEncoderSettings() const651 AudioEncoderSettings MediaRecorder::audioEncoderSettings() const
652 {
653 	return m_audioEncoderSettings;
654 }
655 
setAudioEncoderSettings(const AudioEncoderSettings & settings)656 void MediaRecorder::setAudioEncoderSettings(const AudioEncoderSettings &settings)
657 {
658 #if ENABLE_DEBUG
659 	qDebug(Q_FUNC_INFO);
660 #endif
661 
662 	switch (m_type) {
663 	case MediaRecorder::Type::Audio:
664 	case MediaRecorder::Type::Video: {
665 		if (settings != m_audioEncoderSettings) {
666 #if ENABLE_DEBUG
667 			settings.dumpProperties(QStringLiteral("New"));
668 			m_audioEncoderSettings.dumpProperties(QStringLiteral("Old"));
669 #endif
670 
671 			m_audioEncoderSettings = settings;
672 
673 			if (!m_initializing) {
674 				if (m_audioRecorder) {
675 					m_audioRecorder->setAudioSettings(m_audioEncoderSettings.toQAudioEncoderSettings());
676 				} else if (m_videoRecorder) {
677 					m_videoRecorder->setAudioSettings(m_audioEncoderSettings.toQAudioEncoderSettings());
678 				}
679 
680 				emit audioEncoderSettingsChanged();
681 
682 				// Changing audio settings does not trigger ready changed.
683 				if (m_type == MediaRecorder::Type::Audio) {
684 					emit readyChanged();
685 				}
686 			}
687 		}
688 
689 		break;
690 	}
691 	case MediaRecorder::Type::Image:
692 	case MediaRecorder::Type::Invalid:
693 		Q_UNREACHABLE();
694 		break;
695 	}
696 }
697 
videoEncoderSettings() const698 VideoEncoderSettings MediaRecorder::videoEncoderSettings() const
699 {
700 	return m_videoEncoderSettings;
701 }
702 
setVideoEncoderSettings(const VideoEncoderSettings & settings)703 void MediaRecorder::setVideoEncoderSettings(const VideoEncoderSettings &settings)
704 {
705 #if ENABLE_DEBUG
706 	qDebug(Q_FUNC_INFO);
707 #endif
708 
709 	switch (m_type) {
710 	case MediaRecorder::Type::Video: {
711 		if (settings != m_videoEncoderSettings) {
712 #if ENABLE_DEBUG
713 			settings.dumpProperties(QStringLiteral("New"));
714 			m_videoEncoderSettings.dumpProperties(QStringLiteral("Old"));
715 #endif
716 
717 			m_videoEncoderSettings = settings;
718 
719 			if (!m_initializing) {
720 				m_videoRecorder->setVideoSettings(m_videoEncoderSettings.toQVideoEncoderSettings());
721 				emit videoEncoderSettingsChanged();
722 			}
723 		}
724 
725 		break;
726 	}
727 	case MediaRecorder::Type::Image:
728 	case MediaRecorder::Type::Audio:
729 	case MediaRecorder::Type::Invalid:
730 		Q_UNREACHABLE();
731 		break;
732 	}
733 }
734 
resetSettingsToDefaults()735 void MediaRecorder::resetSettingsToDefaults()
736 {
737 #if ENABLE_DEBUG
738 	qDebug(Q_FUNC_INFO);
739 #endif
740 
741 	switch (m_type) {
742 	case MediaRecorder::Type::Invalid:
743 		Q_UNREACHABLE();
744 		break;
745 	case MediaRecorder::Type::Image:
746 		setMediaSettings(MediaSettings());
747 		setImageEncoderSettings(ImageEncoderSettings());
748 		break;
749 	case MediaRecorder::Type::Audio:
750 		setMediaSettings(MediaSettings());
751 		setAudioEncoderSettings(AudioEncoderSettings());
752 		break;
753 	case MediaRecorder::Type::Video:
754 		setMediaSettings(MediaSettings());
755 		setAudioEncoderSettings(AudioEncoderSettings());
756 		setVideoEncoderSettings(VideoEncoderSettings());
757 		break;
758 	}
759 }
760 
resetUserSettings()761 void MediaRecorder::resetUserSettings()
762 {
763 	resetSettings(userDefaultCamera(), userDefaultAudioInput());
764 }
765 
saveUserSettings()766 void MediaRecorder::saveUserSettings()
767 {
768 #if ENABLE_DEBUG
769 	qDebug(Q_FUNC_INFO);
770 #endif
771 
772 	switch (m_type) {
773 	case MediaRecorder::Type::Invalid:
774 		Q_UNREACHABLE();
775 		break;
776 	case MediaRecorder::Type::Image: {
777 		QSettings &settings(*Kaidan::instance()->settings());
778 
779 		settings.beginGroup(settingsKey(m_type, SETTING_USER_DEFAULT));
780 		settings.setValue(SETTING_DEFAULT_CAMERA_DEVICE_NAME, m_mediaSettings.camera.deviceName());
781 		settings.endGroup();
782 
783 		if (!m_mediaSettings.camera.isNull() || !m_mediaSettings.audioInputDevice.isNull()) {
784 			settings.beginGroup(currentSettingsKey());
785 			m_mediaSettings.saveSettings(&settings);
786 			m_imageEncoderSettings.saveSettings(&settings);
787 			settings.endGroup();
788 		}
789 		break;
790 	}
791 	case MediaRecorder::Type::Audio: {
792 		QSettings &settings(*Kaidan::instance()->settings());
793 
794 		settings.beginGroup(settingsKey(m_type, SETTING_USER_DEFAULT));
795 		settings.setValue(SETTING_DEFAULT_AUDIO_INPUT_DEVICE_NAME, m_mediaSettings.audioInputDevice.deviceName());
796 		settings.endGroup();
797 
798 		settings.beginGroup(currentSettingsKey());
799 		m_mediaSettings.saveSettings(&settings);
800 		m_audioEncoderSettings.saveSettings(&settings);
801 		settings.endGroup();
802 		break;
803 	}
804 	case MediaRecorder::Type::Video: {
805 		QSettings &settings(*Kaidan::instance()->settings());
806 
807 		settings.beginGroup(settingsKey(m_type, SETTING_USER_DEFAULT));
808 		settings.setValue(SETTING_DEFAULT_CAMERA_DEVICE_NAME, m_mediaSettings.camera.deviceName());
809 		settings.endGroup();
810 
811 		settings.beginGroup(currentSettingsKey());
812 		m_mediaSettings.saveSettings(&settings);
813 		m_audioEncoderSettings.saveSettings(&settings);
814 		m_videoEncoderSettings.saveSettings(&settings);
815 		settings.endGroup();
816 		break;
817 	}
818 	}
819 }
820 
record()821 void MediaRecorder::record()
822 {
823 	switch (m_type) {
824 	case MediaRecorder::Type::Invalid:
825 		Q_UNREACHABLE();
826 		break;
827 	case MediaRecorder::Type::Image:
828 #if ENABLE_DEBUG
829 		m_mediaSettings.dumpProperties("Capture");
830 		m_imageEncoderSettings.dumpProperties("Capture");
831 #endif
832 		m_imageCapturer->capture();
833 		break;
834 	case MediaRecorder::Type::Audio:
835 #if ENABLE_DEBUG
836 		m_mediaSettings.dumpProperties("Capture");
837 		m_audioEncoderSettings.dumpProperties("Capture");
838 #endif
839 		m_audioRecorder->record();
840 		break;
841 	case MediaRecorder::Type::Video:
842 #if ENABLE_DEBUG
843 		m_mediaSettings.dumpProperties("Capture");
844 		m_audioEncoderSettings.dumpProperties("Capture");
845 		m_videoEncoderSettings.dumpProperties("Capture");
846 #endif
847 		m_videoRecorder->record();
848 		break;
849 	}
850 }
851 
pause()852 void MediaRecorder::pause()
853 {
854 	switch (m_type) {
855 	case MediaRecorder::Type::Invalid:
856 	case MediaRecorder::Type::Image:
857 		Q_UNREACHABLE();
858 		break;
859 	case MediaRecorder::Type::Audio:
860 		m_audioRecorder->pause();
861 		break;
862 	case MediaRecorder::Type::Video:
863 		m_videoRecorder->pause();
864 		break;
865 	}
866 }
867 
stop()868 void MediaRecorder::stop()
869 {
870 	switch (m_type) {
871 	case MediaRecorder::Type::Invalid:
872 	case MediaRecorder::Type::Image:
873 		Q_UNREACHABLE();
874 		break;
875 	case MediaRecorder::Type::Audio:
876 		m_audioRecorder->stop();
877 		break;
878 	case MediaRecorder::Type::Video:
879 		m_videoRecorder->stop();
880 		break;
881 	}
882 }
883 
cancel()884 void MediaRecorder::cancel()
885 {
886 	switch (m_type) {
887 	case MediaRecorder::Type::Invalid:
888 		Q_UNREACHABLE();
889 		break;
890 	case MediaRecorder::Type::Image:
891 		m_imageCapturer->cancelCapture();
892 		deleteActualLocation();
893 		break;
894 	case MediaRecorder::Type::Audio:
895 	case MediaRecorder::Type::Video: {
896 		if (state() == MediaRecorder::State::RecordingState) {
897 			stop();
898 		}
899 
900 		deleteActualLocation();
901 		break;
902 	}
903 	}
904 }
905 
setMediaObject(QMediaObject * object)906 bool MediaRecorder::setMediaObject(QMediaObject *object)
907 {
908 	Q_UNUSED(object);
909 	return false;
910 }
911 
settingsKey(MediaRecorder::Type type,const QString & deviceName) const912 QString MediaRecorder::settingsKey(MediaRecorder::Type type, const QString &deviceName) const
913 {
914 	Q_ASSERT(type != MediaRecorder::Type::Invalid);
915 
916 	const QString deviceKey = QString(deviceName)
917 					  .replace(QLatin1Char('/'), QLatin1Char(' '))
918 					  .replace(QLatin1Char('\\'), QLatin1Char(' '))
919 					  .replace(QLatin1Char('('), QLatin1Char(' '))
920 					  .replace(QLatin1Char(')'), QLatin1Char(' '))
921 					  .replace(QLatin1Char('='), QLatin1Char(' '))
922 					  .replace(QLatin1Char(':'), QLatin1Char(' '))
923 					  .replace(QLatin1Char('\n'), QLatin1Char(' '))
924 					  .simplified();
925 	return QString::fromLatin1("Multimedia/%1/%2").arg(Enums::toString(type), deviceKey);
926 }
927 
userDefaultCamera() const928 CameraInfo MediaRecorder::userDefaultCamera() const
929 {
930 	if (m_type == MediaRecorder::Type::Invalid) {
931 		return CameraInfo();
932 	}
933 
934 	QSettings &settings(*Kaidan::instance()->settings());
935 	CameraInfo cameraInfo = m_cameraModel->defaultCamera();
936 
937 	settings.beginGroup(settingsKey(m_type, SETTING_USER_DEFAULT));
938 
939 	if (settings.contains(SETTING_DEFAULT_CAMERA_DEVICE_NAME)) {
940 		const CameraInfo userCamera(settings.value(SETTING_DEFAULT_CAMERA_DEVICE_NAME).toString());
941 
942 		if (!userCamera.isNull()) {
943 			cameraInfo = userCamera;
944 		}
945 	}
946 
947 	settings.endGroup();
948 
949 	return cameraInfo;
950 }
951 
userDefaultAudioInput() const952 AudioDeviceInfo MediaRecorder::userDefaultAudioInput() const
953 {
954 	if (m_type == MediaRecorder::Type::Invalid) {
955 		return AudioDeviceInfo();
956 	}
957 
958 	QSettings &settings(*Kaidan::instance()->settings());
959 	AudioDeviceInfo audioInput = m_audioDeviceModel->defaultAudioInputDevice();
960 
961 	settings.beginGroup(settingsKey(m_type, SETTING_USER_DEFAULT));
962 
963 	if (settings.contains(SETTING_DEFAULT_AUDIO_INPUT_DEVICE_NAME)) {
964 		const AudioDeviceInfo userAudioInput(AudioDeviceModel::audioInputDevice(settings.value(SETTING_DEFAULT_AUDIO_INPUT_DEVICE_NAME).toString()));
965 
966 		if (!userAudioInput.isNull()) {
967 			audioInput = userAudioInput;
968 		}
969 	}
970 
971 	settings.endGroup();
972 
973 	return audioInput;
974 }
975 
resetSettings(const CameraInfo & camera,const AudioDeviceInfo & audioInput)976 void MediaRecorder::resetSettings(const CameraInfo &camera, const AudioDeviceInfo &audioInput)
977 {
978 #if ENABLE_DEBUG
979 	qDebug(Q_FUNC_INFO);
980 #endif
981 
982 	switch (m_type) {
983 	case MediaRecorder::Type::Invalid:
984 		m_mediaSettings = MediaSettings();
985 		m_imageEncoderSettings = ImageEncoderSettings();
986 		m_audioEncoderSettings = AudioEncoderSettings();
987 		m_videoEncoderSettings = VideoEncoderSettings();
988 		break;
989 	case MediaRecorder::Type::Image: {
990 		QSettings &settings(*Kaidan::instance()->settings());
991 		MediaSettings mediaSettings(camera, AudioDeviceInfo());
992 		ImageEncoderSettings imageSettings;
993 
994 		settings.beginGroup(settingsKey(m_type, mediaSettings.camera.deviceName()));
995 		mediaSettings.loadSettings(&settings);
996 		imageSettings.loadSettings(&settings);
997 		settings.endGroup();
998 
999 		setMediaSettings(mediaSettings);
1000 		setImageEncoderSettings(imageSettings);
1001 		break;
1002 	}
1003 	case MediaRecorder::Type::Audio: {
1004 		QSettings &settings(*Kaidan::instance()->settings());
1005 		MediaSettings mediaSettings(CameraInfo(), audioInput);
1006 		AudioEncoderSettings audioSettings;
1007 
1008 		settings.beginGroup(settingsKey(m_type, mediaSettings.audioInputDevice.deviceName()));
1009 		mediaSettings.loadSettings(&settings);
1010 		audioSettings.loadSettings(&settings);
1011 		settings.endGroup();
1012 
1013 		setMediaSettings(mediaSettings);
1014 		setAudioEncoderSettings(audioSettings);
1015 		break;
1016 	}
1017 	case MediaRecorder::Type::Video: {
1018 		QSettings &settings(*Kaidan::instance()->settings());
1019 		MediaSettings mediaSettings(camera, AudioDeviceInfo());
1020 		AudioEncoderSettings audioSettings;
1021 		VideoEncoderSettings videoSettings;
1022 
1023 		settings.beginGroup(settingsKey(m_type, mediaSettings.camera.deviceName()));
1024 		mediaSettings.loadSettings(&settings);
1025 		audioSettings.loadSettings(&settings);
1026 		videoSettings.loadSettings(&settings);
1027 		settings.endGroup();
1028 
1029 		setMediaSettings(mediaSettings);
1030 		setAudioEncoderSettings(audioSettings);
1031 		setVideoEncoderSettings(videoSettings);
1032 		break;
1033 	}
1034 	}
1035 }
1036 
setupCamera()1037 void MediaRecorder::setupCamera()
1038 {
1039 	// If there is no camera, there is no need to work any further
1040 	if (m_mediaSettings.camera.isNull())
1041 		return;
1042 
1043 	m_camera = new QCamera(m_mediaSettings.camera, this);
1044 
1045 	switch (m_type) {
1046 	case MediaRecorder::Type::Invalid:
1047 	case MediaRecorder::Type::Audio:
1048 		Q_UNREACHABLE();
1049 		break;
1050 	case MediaRecorder::Type::Image:
1051 		m_camera->setCaptureMode(QCamera::CaptureMode::CaptureStillImage);
1052 		m_camera->imageProcessing()->setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode::WhiteBalanceFlash);
1053 		m_camera->exposure()->setExposureCompensation(-1.0);
1054 		m_camera->exposure()->setExposureMode(QCameraExposure::ExposureMode::ExposurePortrait);
1055 		m_camera->exposure()->setFlashMode(QCameraExposure::FlashMode::FlashRedEyeReduction);
1056 		break;
1057 	case MediaRecorder::Type::Video:
1058 		m_camera->setCaptureMode(QCamera::CaptureMode::CaptureVideo);
1059 		m_camera->imageProcessing()->setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode::WhiteBalanceAuto);
1060 		m_camera->exposure()->setExposureCompensation(0);
1061 		m_camera->exposure()->setExposureMode(QCameraExposure::ExposureMode::ExposureAuto);
1062 		m_camera->exposure()->setFlashMode(QCameraExposure::FlashMode::FlashOff);
1063 		break;
1064 	}
1065 
1066 	connectCamera(m_camera, this);
1067 }
1068 
setupCapturer()1069 void MediaRecorder::setupCapturer()
1070 {
1071 	if (m_camera)
1072 		m_imageCapturer = new CameraImageCapture(m_camera, this);
1073 	else
1074 		m_imageCapturer = new CameraImageCapture({});
1075 
1076 	m_imageCapturer->setEncodingSettings(m_imageEncoderSettings.toQImageEncoderSettings());
1077 
1078 	connectImageCapturer(m_imageCapturer, this);
1079 }
1080 
setupAudioRecorder()1081 void MediaRecorder::setupAudioRecorder()
1082 {
1083 	m_audioRecorder = new QAudioRecorder(this);
1084 	m_audioRecorder->setAudioInput(m_mediaSettings.audioInputDevice.deviceName());
1085 	m_audioRecorder->setContainerFormat(m_mediaSettings.container);
1086 	m_audioRecorder->setAudioSettings(m_audioEncoderSettings.toQAudioEncoderSettings());
1087 
1088 	connectMediaRecorder(m_audioRecorder, this);
1089 }
1090 
setupVideoRecorder()1091 void MediaRecorder::setupVideoRecorder()
1092 {
1093 	if (m_camera)
1094 		m_videoRecorder = new QMediaRecorder(m_camera, this);
1095 	else
1096 		m_videoRecorder = new QMediaRecorder({}, this);
1097 
1098 	m_videoRecorder->setContainerFormat(m_mediaSettings.container);
1099 	m_videoRecorder->setAudioSettings(m_audioEncoderSettings.toQAudioEncoderSettings());
1100 	m_videoRecorder->setVideoSettings(m_videoEncoderSettings.toQVideoEncoderSettings());
1101 
1102 	connectMediaRecorder(m_videoRecorder, this);
1103 }
1104 
setupRecorder(MediaRecorder::Type type)1105 void MediaRecorder::setupRecorder(MediaRecorder::Type type)
1106 {
1107 	if (isAvailable() && !isReady()) {
1108 		cancel();
1109 	}
1110 
1111 	delete m_imageCapturer; m_imageCapturer = nullptr;
1112 	delete m_audioRecorder; m_audioRecorder = nullptr;
1113 	delete m_videoRecorder; m_videoRecorder = nullptr;
1114 	delete m_camera; m_camera = nullptr;
1115 
1116 	if (m_type != type) {
1117 		m_type = type;
1118 		m_initializing = true;
1119 		resetUserSettings();
1120 		m_initializing = false;
1121 	} else {
1122 		m_initializing = true;
1123 		resetSettings(m_mediaSettings.camera, m_mediaSettings.audioInputDevice);
1124 		m_initializing = false;
1125 	}
1126 
1127 	switch (m_type) {
1128 	case MediaRecorder::Type::Invalid:
1129 		emit imageEncoderSettingsChanged();
1130 		emit audioEncoderSettingsChanged();
1131 		emit videoEncoderSettingsChanged();
1132 		break;
1133 	case MediaRecorder::Type::Image:
1134 		setupCamera();
1135 		setupCapturer();
1136 		break;
1137 	case MediaRecorder::Type::Audio:
1138 		setupAudioRecorder();
1139 		break;
1140 	case MediaRecorder::Type::Video:
1141 		setupCamera();
1142 		setupVideoRecorder();
1143 		break;
1144 	}
1145 
1146 	emit typeChanged();
1147 	emit availabilityStatusChanged();
1148 	emit stateChanged();
1149 	emit statusChanged();
1150 	emit readyChanged();
1151 	emit errorChanged();
1152 	emit actualLocationChanged();
1153 	emit durationChanged();
1154 	emit mutedChanged();
1155 	emit volumeChanged();
1156 
1157 	if (m_camera && m_camera->state() != QCamera::State::ActiveState) {
1158 		m_camera->start();
1159 	}
1160 }
1161 
deleteActualLocation()1162 void MediaRecorder::deleteActualLocation()
1163 {
1164 	const QUrl url(actualLocation());
1165 
1166 	if (!url.isEmpty() && url.isLocalFile()) {
1167 		const QString filePath(url.toLocalFile());
1168 		QFile file(filePath);
1169 
1170 		if (file.exists()) {
1171 			if (file.remove()) {
1172 				emit actualLocationChanged();
1173 			} else {
1174 				qWarning("Can not delete record '%s'", qUtf8Printable(filePath));
1175 			}
1176 		}
1177 	}
1178 }
1179 
encoderContainer(const QString & container,const void * userData)1180 QString MediaRecorder::encoderContainer(const QString &container, const void *userData)
1181 {
1182 	const auto *recorder = static_cast<const MediaRecorder *>(userData);
1183 	const auto *mediaRecorder = recorder->m_audioRecorder ? recorder->m_audioRecorder : recorder->m_videoRecorder;
1184 	return container.isEmpty()
1185 		       ? tr("Default")
1186 		       : QString::fromLatin1("%1 (%2)").arg(mediaRecorder->containerDescription(container), container);
1187 }
1188 
encoderResolution(const QSize & size,const void * userData)1189 QString MediaRecorder::encoderResolution(const QSize &size, const void *userData)
1190 {
1191 	Q_UNUSED(userData);
1192 	return size.isEmpty()
1193 		       ? tr("Default")
1194 		       : QString::fromLatin1("%1x%2").arg(QString::number(size.width()), QString::number(size.height()));
1195 }
1196 
encoderQuality(const CommonEncoderSettings::EncodingQuality quality,const void * userData)1197 QString MediaRecorder::encoderQuality(const CommonEncoderSettings::EncodingQuality quality, const void *userData)
1198 {
1199 	Q_UNUSED(userData);
1200 	return CommonEncoderSettings::toString(quality);
1201 }
1202 
imageEncoderCodec(const QString & codec,const void * userData)1203 QString MediaRecorder::imageEncoderCodec(const QString &codec, const void *userData)
1204 {
1205 	const auto *recorder = static_cast<const MediaRecorder *>(userData);
1206 	const auto *capturer = recorder->m_imageCapturer;
1207 	return codec.isEmpty()
1208 		       ? tr("Default")
1209 		       : QString::fromLatin1("%1 (%2)").arg(capturer->imageCodecDescription(codec), codec);
1210 }
1211 
audioEncoderCodec(const QString & codec,const void * userData)1212 QString MediaRecorder::audioEncoderCodec(const QString &codec, const void *userData)
1213 {
1214 	const auto *recorder = static_cast<const MediaRecorder *>(userData);
1215 	const auto *mediaRecorder = recorder->m_audioRecorder ? recorder->m_audioRecorder : recorder->m_videoRecorder;
1216 	return codec.isEmpty()
1217 		       ? tr("Default")
1218 		       : QString::fromLatin1("%1 (%2)").arg(mediaRecorder->audioCodecDescription(codec), codec);
1219 }
1220 
audioEncoderSampleRate(const int sampleRate,const void * userData)1221 QString MediaRecorder::audioEncoderSampleRate(const int sampleRate, const void *userData)
1222 {
1223 	Q_UNUSED(userData);
1224 	return sampleRate == -1
1225 		       ? tr("Default")
1226 		       : QString::fromLatin1("%1 Hz").arg(sampleRate);
1227 }
1228 
videoEncoderCodec(const QString & codec,const void * userData)1229 QString MediaRecorder::videoEncoderCodec(const QString &codec, const void *userData)
1230 {
1231 	const auto *recorder = static_cast<const MediaRecorder *>(userData);
1232 	const auto *videoRecorder = recorder->m_videoRecorder;
1233 	return codec.isEmpty()
1234 		       ? tr("Default")
1235 		       : QString::fromLatin1("%1 (%2)").arg(videoRecorder->videoCodecDescription(codec), codec);
1236 }
1237 
videoEncoderFrameRate(const qreal frameRate,const void * userData)1238 QString MediaRecorder::videoEncoderFrameRate(const qreal frameRate, const void *userData)
1239 {
1240 	Q_UNUSED(userData);
1241 	return qIsNull(frameRate)
1242 		       ? tr("Default")
1243 		       : QString::fromLatin1("%1 FPS").arg(frameRate);
1244 }
1245