15#include <private/qplatformaudioinput_p.h>
16#include <private/qplatformaudiooutput_p.h>
17#include <private/qmediarecorder_p.h>
18#include <private/qmediastoragelocation_p.h>
24QAndroidCaptureSession::QAndroidCaptureSession()
29 , m_state(QMediaRecorder::StoppedState)
30 , m_outputFormat(AndroidMediaRecorder::DefaultOutputFormat)
31 , m_audioEncoder(AndroidMediaRecorder::DefaultAudioEncoder)
32 , m_videoEncoder(AndroidMediaRecorder::DefaultVideoEncoder)
34 m_notifyTimer.setInterval(1000);
35 connect(&m_notifyTimer, &QTimer::timeout,
this, &QAndroidCaptureSession::updateDuration);
38QAndroidCaptureSession::~QAndroidCaptureSession()
41 m_mediaRecorder =
nullptr;
42 if (m_audioInput && m_audioOutput)
43 AndroidMediaPlayer::stopSoundStreaming();
46void QAndroidCaptureSession::setCameraSession(QAndroidCameraSession *cameraSession)
48 if (m_cameraSession) {
49 disconnect(m_connOpenCamera);
50 disconnect(m_connActiveChangedCamera);
53 m_cameraSession = cameraSession;
54 if (m_cameraSession) {
55 m_connOpenCamera = connect(cameraSession, &QAndroidCameraSession::opened,
56 this, &QAndroidCaptureSession::onCameraOpened);
57 m_connActiveChangedCamera = connect(cameraSession, &QAndroidCameraSession::activeChanged,
58 this, [
this](
bool isActive) {
65void QAndroidCaptureSession::setAudioInput(QPlatformAudioInput *input)
67 if (m_audioInput == input)
71 disconnect(m_audioInputChanged);
77 m_audioInputChanged = connect(m_audioInput->q, &QAudioInput::deviceChanged,
this, [
this]() {
78 if (m_state == QMediaRecorder::RecordingState)
79 m_mediaRecorder->setAudioInput(m_audioInput->device.id());
80 updateStreamingState();
83 updateStreamingState();
86void QAndroidCaptureSession::setAudioOutput(QPlatformAudioOutput *output)
88 if (m_audioOutput == output)
92 disconnect(m_audioOutputChanged);
94 m_audioOutput = output;
97 m_audioOutputChanged = connect(m_audioOutput->q, &QAudioOutput::deviceChanged,
this,
99 AndroidMediaPlayer::setAudioOutput(m_audioOutput->device.id());
100 updateStreamingState();
102 AndroidMediaPlayer::setAudioOutput(m_audioOutput->device.id());
104 updateStreamingState();
107void QAndroidCaptureSession::updateStreamingState()
109 if (m_audioInput && m_audioOutput) {
110 AndroidMediaPlayer::startSoundStreaming(m_audioInput->device.id().toInt(),
111 m_audioOutput->device.id().toInt());
113 AndroidMediaPlayer::stopSoundStreaming();
117QMediaRecorder::RecorderState QAndroidCaptureSession::state()
const
122void QAndroidCaptureSession::setKeepAlive(
bool keepAlive)
125 m_cameraSession->setKeepAlive(keepAlive);
129void QAndroidCaptureSession::start(QMediaEncoderSettings &settings,
const QUrl &outputLocation)
131 if (m_state == QMediaRecorder::RecordingState)
134 if (!m_cameraSession && !m_audioInput) {
135 updateError(QMediaRecorder::ResourceError, QLatin1String(
"No devices are set"));
141 const bool validCameraSession = m_cameraSession && m_cameraSession->camera();
143 if (validCameraSession && !qt_androidCheckCameraPermission()) {
144 updateError(QMediaRecorder::ResourceError, QLatin1String(
"Camera permission denied."));
149 if (m_audioInput && !qt_androidCheckMicrophonePermission()) {
150 updateError(QMediaRecorder::ResourceError, QLatin1String(
"Microphone permission denied."));
155 m_mediaRecorder = std::make_shared<AndroidMediaRecorder>();
156 connect(m_mediaRecorder.get(), &AndroidMediaRecorder::error,
this,
157 &QAndroidCaptureSession::onError);
158 connect(m_mediaRecorder.get(), &AndroidMediaRecorder::info,
this,
159 &QAndroidCaptureSession::onInfo);
161 applySettings(settings);
164 if (validCameraSession) {
165 m_cameraSession->camera()->stopPreviewSynchronous();
166 m_cameraSession->camera()->unlock();
168 m_mediaRecorder->setCamera(m_cameraSession->camera());
169 m_mediaRecorder->setVideoSource(AndroidMediaRecorder::Camera);
173 m_mediaRecorder->setAudioInput(m_audioInput->device.id());
174 if (!m_mediaRecorder->isAudioSourceSet())
175 m_mediaRecorder->setAudioSource(AndroidMediaRecorder::DefaultAudioSource);
179 m_mediaRecorder->setOutputFormat(m_outputFormat);
182 if (validCameraSession) {
183 m_mediaRecorder->setVideoSize(settings.videoResolution());
184 m_mediaRecorder->setVideoFrameRate(qRound(settings.videoFrameRate()));
185 m_mediaRecorder->setVideoEncodingBitRate(settings.videoBitRate());
186 m_mediaRecorder->setVideoEncoder(m_videoEncoder);
189 auto rotation = m_cameraSession->currentCameraRotation();
190 if (m_cameraSession->camera()->getFacing() == AndroidCamera::CameraFacingFront)
191 rotation = (360 - rotation) % 360;
193 m_mediaRecorder->setOrientationHint(rotation);
198 m_mediaRecorder->setAudioChannels(settings.audioChannelCount());
199 m_mediaRecorder->setAudioEncodingBitRate(settings.audioBitRate());
200 m_mediaRecorder->setAudioSamplingRate(settings.audioSampleRate());
201 m_mediaRecorder->setAudioEncoder(m_audioEncoder);
204 QString extension = settings.preferredSuffix();
206 auto location = outputLocation.toString(QUrl::PreferLocalFile);
207 QString filePath = location;
208 if (QUrl(filePath).scheme() != QLatin1String(
"content")) {
209 filePath = QMediaStorageLocation::generateFileName(
210 location, m_cameraSession ? QStandardPaths::MoviesLocation
211 : QStandardPaths::MusicLocation, extension);
214 m_usedOutputLocation = QUrl::fromLocalFile(filePath);
215 m_outputLocationIsStandard = location.isEmpty() || QFileInfo(location).isRelative();
216 m_mediaRecorder->setOutputFile(filePath);
218 if (validCameraSession) {
219 m_cameraSession->disableRotation();
222 if (!m_mediaRecorder->prepare()) {
223 updateError(QMediaRecorder::FormatError,
224 QLatin1String(
"Unable to prepare the media recorder."));
230 if (!m_mediaRecorder->start()) {
231 updateError(QMediaRecorder::FormatError, QMediaRecorderPrivate::msgFailedStartRecording());
237 m_elapsedTime.start();
238 m_notifyTimer.start();
241 if (validCameraSession) {
242 m_cameraSession->setReadyForCapture(
false);
246 m_cameraSession->camera()->setupPreviewFrameCallback();
249 m_state = QMediaRecorder::RecordingState;
250 emit stateChanged(m_state);
253void QAndroidCaptureSession::stop(
bool error)
255 if (m_state == QMediaRecorder::StoppedState || m_mediaRecorder ==
nullptr)
258 m_mediaRecorder->stop();
259 m_notifyTimer.stop();
261 m_elapsedTime.invalidate();
263 m_mediaRecorder =
nullptr;
265 if (m_cameraSession && m_cameraSession->isActive()) {
274 if (m_outputLocationIsStandard)
275 AndroidMultimediaUtils::registerMediaFile(m_usedOutputLocation.toLocalFile());
277 emit actualLocationChanged(m_usedOutputLocation);
280 m_state = QMediaRecorder::StoppedState;
281 emit stateChanged(m_state);
284qint64 QAndroidCaptureSession::duration()
const
289void QAndroidCaptureSession::applySettings(QMediaEncoderSettings &settings)
292 auto fileFormat = settings.mediaFormat().fileFormat();
293 if (!m_cameraSession && fileFormat == QMediaFormat::AAC) {
294 m_outputFormat = AndroidMediaRecorder::AAC_ADTS;
295 }
else if (fileFormat == QMediaFormat::Ogg) {
296 m_outputFormat = AndroidMediaRecorder::OGG;
297 }
else if (fileFormat == QMediaFormat::WebM) {
298 m_outputFormat = AndroidMediaRecorder::WEBM;
303 m_outputFormat = AndroidMediaRecorder::MPEG_4;
307 if (settings.audioChannelCount() <= 0)
308 settings.setAudioChannelCount(m_defaultSettings.audioChannels);
309 if (settings.audioBitRate() <= 0)
310 settings.setAudioBitRate(m_defaultSettings.audioBitRate);
311 if (settings.audioSampleRate() <= 0)
312 settings.setAudioSampleRate(m_defaultSettings.audioSampleRate);
314 if (settings.audioCodec() == QMediaFormat::AudioCodec::AAC)
315 m_audioEncoder = AndroidMediaRecorder::AAC;
316 else if (settings.audioCodec() == QMediaFormat::AudioCodec::Opus)
317 m_audioEncoder = AndroidMediaRecorder::OPUS;
318 else if (settings.audioCodec() == QMediaFormat::AudioCodec::Vorbis)
319 m_audioEncoder = AndroidMediaRecorder::VORBIS;
321 m_audioEncoder = m_defaultSettings.audioEncoder;
325 if (m_cameraSession && m_cameraSession->camera()) {
326 if (settings.videoResolution().isEmpty()) {
327 settings.setVideoResolution(m_defaultSettings.videoResolution);
328 }
else if (!m_supportedResolutions.contains(settings.videoResolution())) {
330 QSize reqSize = settings.videoResolution();
331 int reqPixelCount = reqSize.width() * reqSize.height();
332 QList<
int> supportedPixelCounts;
333 for (
int i = 0; i < m_supportedResolutions.size(); ++i) {
334 const QSize &s = m_supportedResolutions.at(i);
335 supportedPixelCounts.append(s.width() * s.height());
337 int closestIndex = qt_findClosestValue(supportedPixelCounts, reqPixelCount);
338 settings.setVideoResolution(m_supportedResolutions.at(closestIndex));
341 if (settings.videoFrameRate() <= 0)
342 settings.setVideoFrameRate(m_defaultSettings.videoFrameRate);
343 if (settings.videoBitRate() <= 0)
344 settings.setVideoBitRate(m_defaultSettings.videoBitRate);
346 if (settings.videoCodec() == QMediaFormat::VideoCodec::H264)
347 m_videoEncoder = AndroidMediaRecorder::H264;
348 else if (settings.videoCodec() == QMediaFormat::VideoCodec::H265)
349 m_videoEncoder = AndroidMediaRecorder::HEVC;
350 else if (settings.videoCodec() == QMediaFormat::VideoCodec::MPEG4)
351 m_videoEncoder = AndroidMediaRecorder::MPEG_4_SP;
353 m_videoEncoder = m_defaultSettings.videoEncoder;
358void QAndroidCaptureSession::restartViewfinder()
363 if (!m_cameraSession)
366 if (m_cameraSession && m_cameraSession->camera()) {
367 m_cameraSession->camera()->reconnect();
368 m_cameraSession->camera()->stopPreviewSynchronous();
369 m_cameraSession->camera()->startPreview();
370 m_cameraSession->setReadyForCapture(
true);
371 m_cameraSession->enableRotation();
374 m_mediaRecorder =
nullptr;
377void QAndroidCaptureSession::updateDuration()
379 if (m_elapsedTime.isValid())
380 m_duration = m_elapsedTime.elapsed();
382 emit durationChanged(m_duration);
385void QAndroidCaptureSession::onCameraOpened()
387 m_supportedResolutions.clear();
388 m_supportedFramerates.clear();
391 for (
int i = 0; i < 8; ++i) {
392 CaptureProfile profile = getProfile(i);
393 if (!profile.isNull) {
394 if (i == AndroidCamcorderProfile::QUALITY_HIGH)
395 m_defaultSettings = profile;
397 if (!m_supportedResolutions.contains(profile.videoResolution))
398 m_supportedResolutions.append(profile.videoResolution);
399 if (!m_supportedFramerates.contains(profile.videoFrameRate))
400 m_supportedFramerates.append(profile.videoFrameRate);
404 std::sort(m_supportedResolutions.begin(), m_supportedResolutions.end(), qt_sizeLessThan);
405 std::sort(m_supportedFramerates.begin(), m_supportedFramerates.end());
407 QMediaEncoderSettings defaultSettings;
408 applySettings(defaultSettings);
409 m_cameraSession->applyResolution(defaultSettings.videoResolution());
412QAndroidCaptureSession::CaptureProfile QAndroidCaptureSession::getProfile(
int id)
414 CaptureProfile profile;
415 const bool hasProfile = AndroidCamcorderProfile::hasProfile(m_cameraSession->camera()->cameraId(),
416 AndroidCamcorderProfile::Quality(id));
419 AndroidCamcorderProfile camProfile = AndroidCamcorderProfile::get(m_cameraSession->camera()->cameraId(),
420 AndroidCamcorderProfile::Quality(id));
422 profile.outputFormat = AndroidMediaRecorder::OutputFormat(camProfile.getValue(AndroidCamcorderProfile::fileFormat));
423 profile.audioEncoder = AndroidMediaRecorder::AudioEncoder(camProfile.getValue(AndroidCamcorderProfile::audioCodec));
424 profile.audioBitRate = camProfile.getValue(AndroidCamcorderProfile::audioBitRate);
425 profile.audioChannels = camProfile.getValue(AndroidCamcorderProfile::audioChannels);
426 profile.audioSampleRate = camProfile.getValue(AndroidCamcorderProfile::audioSampleRate);
427 profile.videoEncoder = AndroidMediaRecorder::VideoEncoder(camProfile.getValue(AndroidCamcorderProfile::videoCodec));
428 profile.videoBitRate = camProfile.getValue(AndroidCamcorderProfile::videoBitRate);
429 profile.videoFrameRate = camProfile.getValue(AndroidCamcorderProfile::videoFrameRate);
430 profile.videoResolution = QSize(camProfile.getValue(AndroidCamcorderProfile::videoFrameWidth),
431 camProfile.getValue(AndroidCamcorderProfile::videoFrameHeight));
433 if (profile.outputFormat == AndroidMediaRecorder::MPEG_4)
434 profile.outputFileExtension = QStringLiteral(
"mp4");
435 else if (profile.outputFormat == AndroidMediaRecorder::THREE_GPP)
436 profile.outputFileExtension = QStringLiteral(
"3gp");
437 else if (profile.outputFormat == AndroidMediaRecorder::AMR_NB_Format)
438 profile.outputFileExtension = QStringLiteral(
"amr");
439 else if (profile.outputFormat == AndroidMediaRecorder::AMR_WB_Format)
440 profile.outputFileExtension = QStringLiteral(
"awb");
442 profile.isNull =
false;
448void QAndroidCaptureSession::onError(
int what,
int extra)
453 updateError(QMediaRecorder::ResourceError, QLatin1String(
"Unknown error."));
456void QAndroidCaptureSession::onInfo(
int what,
int extra)
462 updateError(QMediaRecorder::OutOfSpaceError, QLatin1String(
"Maximum duration reached."));
463 }
else if (what == 801) {
466 updateError(QMediaRecorder::OutOfSpaceError, QLatin1String(
"Maximum file size reached."));
472#include "moc_qandroidcapturesession_p.cpp"