15#include <private/qplatformaudioinput_p.h>
16#include <private/qplatformaudiooutput_p.h>
17#include <private/qmediarecorder_p.h>
18#include <private/qmediastoragelocation_p.h>
19#include <QtMultimedia/private/qmultimedia_ranges_p.h>
24namespace ranges = QtMultimediaPrivate::ranges;
31 , m_state(QMediaRecorder::StoppedState)
33 , m_audioEncoder(AndroidMediaRecorder::DefaultAudioEncoder)
36 m_notifyTimer.setInterval(1000);
37 connect(&m_notifyTimer, &QTimer::timeout,
this, &QAndroidCaptureSession::updateDuration);
43 m_mediaRecorder =
nullptr;
44 if (m_audioInput && m_audioOutput)
50 if (m_cameraSession) {
51 disconnect(m_connOpenCamera);
52 disconnect(m_connActiveChangedCamera);
55 m_cameraSession = cameraSession;
56 if (m_cameraSession) {
57 m_connOpenCamera = connect(cameraSession, &QAndroidCameraSession::opened,
58 this, &QAndroidCaptureSession::onCameraOpened);
59 m_connActiveChangedCamera = connect(cameraSession, &QAndroidCameraSession::activeChanged,
60 this, [
this](
bool isActive) {
69 if (m_audioInput == input)
73 disconnect(m_audioInputChanged);
79 m_audioInputChanged = connect(m_audioInput->q, &QAudioInput::deviceChanged,
this, [
this]() {
80 if (m_state == QMediaRecorder::RecordingState)
81 m_mediaRecorder->setAudioInput(m_audioInput->device.id());
82 updateStreamingState();
85 updateStreamingState();
90 if (m_audioOutput == output)
94 disconnect(m_audioOutputChanged);
96 m_audioOutput = output;
99 m_audioOutputChanged = connect(m_audioOutput->q, &QAudioOutput::deviceChanged,
this,
101 AndroidMediaPlayer::setAudioOutput(m_audioOutput->device.id());
102 updateStreamingState();
104 AndroidMediaPlayer::setAudioOutput(m_audioOutput->device.id());
106 updateStreamingState();
111 if (m_audioInput && m_audioOutput) {
113 m_audioOutput->device.id().toInt());
133 if (m_state == QMediaRecorder::RecordingState)
136 if (!m_cameraSession && !m_audioInput) {
137 updateError(QMediaRecorder::ResourceError, QLatin1String(
"No devices are set"));
143 const bool validCameraSession = m_cameraSession && m_cameraSession
->camera();
146 updateError(QMediaRecorder::ResourceError, QLatin1String(
"Camera permission denied."));
151 if (m_audioInput && !qt_androidCheckMicrophonePermission()) {
152 updateError(QMediaRecorder::ResourceError, QLatin1String(
"Microphone permission denied."));
157 m_mediaRecorder = std::make_shared<AndroidMediaRecorder>();
158 connect(m_mediaRecorder.get(), &AndroidMediaRecorder::error,
this,
163 applySettings(settings);
166 if (validCameraSession) {
170 m_mediaRecorder->setCamera(m_cameraSession->camera());
171 m_mediaRecorder->setVideoSource(AndroidMediaRecorder::Camera);
175 m_mediaRecorder->setAudioInput(m_audioInput->device.id());
176 if (!m_mediaRecorder->isAudioSourceSet())
177 m_mediaRecorder->setAudioSource(AndroidMediaRecorder::DefaultAudioSource);
181 m_mediaRecorder->setOutputFormat(m_outputFormat);
184 if (validCameraSession) {
185 m_mediaRecorder->setVideoSize(settings.videoResolution());
186 m_mediaRecorder->setVideoFrameRate(qRound(settings.videoFrameRate()));
187 m_mediaRecorder->setVideoEncodingBitRate(settings.videoBitRate());
188 m_mediaRecorder->setVideoEncoder(m_videoEncoder);
192 if (m_cameraSession->camera()->getFacing() == AndroidCamera::CameraFacingFront)
193 rotation = (360 - rotation) % 360;
195 m_mediaRecorder->setOrientationHint(rotation);
200 m_mediaRecorder->setAudioChannels(settings.audioChannelCount());
201 m_mediaRecorder->setAudioEncodingBitRate(settings.audioBitRate());
202 m_mediaRecorder->setAudioSamplingRate(settings.audioSampleRate());
203 m_mediaRecorder->setAudioEncoder(m_audioEncoder);
206 QString extension = settings.preferredSuffix();
208 auto location = outputLocation.toString(QUrl::PreferLocalFile);
209 QString filePath = location;
210 if (QUrl(filePath).scheme() != QLatin1String(
"content")) {
211 filePath = QMediaStorageLocation::generateFileName(
212 location, m_cameraSession ? QStandardPaths::MoviesLocation
213 : QStandardPaths::MusicLocation, extension);
216 m_usedOutputLocation = QUrl::fromLocalFile(filePath);
217 m_outputLocationIsStandard = location.isEmpty() || QFileInfo(location).isRelative();
218 m_mediaRecorder->setOutputFile(filePath);
220 if (validCameraSession) {
224 if (!m_mediaRecorder->prepare()) {
225 updateError(QMediaRecorder::FormatError,
226 QLatin1String(
"Unable to prepare the media recorder."));
232 if (!m_mediaRecorder->start()) {
233 updateError(QMediaRecorder::FormatError, QMediaRecorderPrivate::msgFailedStartRecording());
239 m_elapsedTime.start();
240 m_notifyTimer.start();
243 if (validCameraSession) {
251 m_state = QMediaRecorder::RecordingState;
252 emit stateChanged(m_state);
257 if (m_state == QMediaRecorder::StoppedState || m_mediaRecorder ==
nullptr)
260 m_mediaRecorder->stop();
261 m_notifyTimer.stop();
263 m_elapsedTime.invalidate();
265 m_mediaRecorder =
nullptr;
276 if (m_outputLocationIsStandard)
277 AndroidMultimediaUtils::registerMediaFile(m_usedOutputLocation.toLocalFile());
279 emit actualLocationChanged(m_usedOutputLocation);
282 m_state = QMediaRecorder::StoppedState;
283 emit stateChanged(m_state);
294 auto fileFormat = settings.mediaFormat().fileFormat();
295 if (!m_cameraSession && fileFormat == QMediaFormat::AAC) {
297 }
else if (fileFormat == QMediaFormat::Ogg) {
299 }
else if (fileFormat == QMediaFormat::WebM) {
309 if (settings.audioChannelCount() <= 0)
310 settings.setAudioChannelCount(m_defaultSettings.audioChannels);
311 if (settings.audioBitRate() <= 0)
312 settings.setAudioBitRate(m_defaultSettings.audioBitRate);
313 if (settings.audioSampleRate() <= 0)
314 settings.setAudioSampleRate(m_defaultSettings.audioSampleRate);
316 if (settings.audioCodec() == QMediaFormat::AudioCodec::AAC)
317 m_audioEncoder = AndroidMediaRecorder::AAC;
318 else if (settings.audioCodec() == QMediaFormat::AudioCodec::Opus)
319 m_audioEncoder = AndroidMediaRecorder::OPUS;
320 else if (settings.audioCodec() == QMediaFormat::AudioCodec::Vorbis)
321 m_audioEncoder = AndroidMediaRecorder::VORBIS;
323 m_audioEncoder = m_defaultSettings.audioEncoder;
327 if (m_cameraSession && m_cameraSession
->camera()) {
328 if (settings.videoResolution().isEmpty()) {
329 settings.setVideoResolution(m_defaultSettings.videoResolution);
330 }
else if (!m_supportedResolutions.contains(settings.videoResolution())) {
332 QSize reqSize = settings.videoResolution();
333 int reqPixelCount = reqSize.width() * reqSize.height();
334 QList<
int> supportedPixelCounts;
335 for (
int i = 0; i < m_supportedResolutions.size(); ++i) {
336 const QSize &s = m_supportedResolutions.at(i);
337 supportedPixelCounts.append(s.width() * s.height());
339 int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
340 settings.setVideoResolution(m_supportedResolutions.at(closestIndex));
343 if (settings.videoFrameRate() <= 0)
344 settings.setVideoFrameRate(m_defaultSettings.videoFrameRate);
345 if (settings.videoBitRate() <= 0)
346 settings.setVideoBitRate(m_defaultSettings.videoBitRate);
348 if (settings.videoCodec() == QMediaFormat::VideoCodec::H264)
350 else if (settings.videoCodec() == QMediaFormat::VideoCodec::H265)
352 else if (settings.videoCodec() == QMediaFormat::VideoCodec::MPEG4)
355 m_videoEncoder = m_defaultSettings.videoEncoder;
365 if (!m_cameraSession)
368 if (m_cameraSession && m_cameraSession
->camera()) {
376 m_mediaRecorder =
nullptr;
381 if (m_elapsedTime.isValid())
382 m_duration = m_elapsedTime.elapsed();
384 emit durationChanged(m_duration);
389 m_supportedResolutions.clear();
390 m_supportedFramerates.clear();
393 for (
int i = 0; i < 8; ++i) {
394 CaptureProfile profile = getProfile(i);
395 if (!profile.isNull) {
396 if (i == AndroidCamcorderProfile::QUALITY_HIGH)
397 m_defaultSettings = profile;
399 if (!m_supportedResolutions.contains(profile.videoResolution))
400 m_supportedResolutions.append(profile.videoResolution);
401 if (!m_supportedFramerates.contains(profile.videoFrameRate))
402 m_supportedFramerates.append(profile.videoFrameRate);
406 ranges::sort(m_supportedResolutions, qt_sizeLessThan);
407 ranges::sort(m_supportedFramerates);
409 QMediaEncoderSettings defaultSettings;
410 applySettings(defaultSettings);
411 m_cameraSession->applyResolution(defaultSettings.videoResolution());
416 CaptureProfile profile;
425 profile.audioEncoder = AndroidMediaRecorder::AudioEncoder(camProfile.getValue(AndroidCamcorderProfile::audioCodec));
436 profile.outputFileExtension = QStringLiteral(
"mp4");
438 profile.outputFileExtension = QStringLiteral(
"3gp");
440 profile.outputFileExtension = QStringLiteral(
"amr");
442 profile.outputFileExtension = QStringLiteral(
"awb");
444 profile.isNull =
false;
455 updateError(QMediaRecorder::ResourceError, QLatin1String(
"Unknown error."));
464 updateError(QMediaRecorder::OutOfSpaceError, QLatin1String(
"Maximum duration reached."));
465 }
else if (what == 801) {
468 updateError(QMediaRecorder::OutOfSpaceError, QLatin1String(
"Maximum file size reached."));
474#include "moc_qandroidcapturesession_p.cpp"
int getValue(Field field) const
void setupPreviewFrameCallback()
void stopPreviewSynchronous()
int currentCameraRotation() const
AndroidCamera * camera() const
void setKeepAlive(bool keepAlive)
void setReadyForCapture(bool ready)
void start(QMediaEncoderSettings &settings, const QUrl &outputLocation)
void stop(bool error=false)
~QAndroidCaptureSession()
void setCameraSession(QAndroidCameraSession *cameraSession=0)