diff --git a/src/imports/multimedia/qdeclarativecamerarecorder.cpp b/src/imports/multimedia/qdeclarativecamerarecorder.cpp index 2739d19e..8bfb2ca9 100644 --- a/src/imports/multimedia/qdeclarativecamerarecorder.cpp +++ b/src/imports/multimedia/qdeclarativecamerarecorder.cpp @@ -465,6 +465,16 @@ void QDeclarativeCameraRecorder::stop() setRecorderState(StoppedState); } +/*! + \qmlmethod QtMultimedia::CameraRecorder::pause() + + Pauses recording. +*/ +void QDeclarativeCameraRecorder::pause() +{ + setRecorderState(PausedState); +} + void QDeclarativeCameraRecorder::setRecorderState(QDeclarativeCameraRecorder::RecorderState state) { if (!m_recorder) @@ -477,6 +487,9 @@ void QDeclarativeCameraRecorder::setRecorderState(QDeclarativeCameraRecorder::Re case QDeclarativeCameraRecorder::StoppedState: m_recorder->stop(); break; + case QDeclarativeCameraRecorder::PausedState: + m_recorder->pause(); + break; } } /*! diff --git a/src/imports/multimedia/qdeclarativecamerarecorder_p.h b/src/imports/multimedia/qdeclarativecamerarecorder_p.h index be30f48f..6e1251e5 100644 --- a/src/imports/multimedia/qdeclarativecamerarecorder_p.h +++ b/src/imports/multimedia/qdeclarativecamerarecorder_p.h @@ -95,7 +95,8 @@ public: enum RecorderState { StoppedState = QMediaRecorder::StoppedState, - RecordingState = QMediaRecorder::RecordingState + RecordingState = QMediaRecorder::RecordingState, + PausedState = QMediaRecorder::PausedState }; enum RecorderStatus @@ -158,6 +159,7 @@ public Q_SLOTS: void record(); void stop(); + void pause(); void setRecorderState(QDeclarativeCameraRecorder::RecorderState state); void setMuted(bool muted); diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp index 7cc3ad61..28d19477 100644 --- a/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp +++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.cpp @@ -188,106 +188,113 @@ void QAndroidCaptureSession::setState(QMediaRecorder::State state) start(); break; case QMediaRecorder::PausedState: - // Not supported by Android API - qWarning("QMediaRecorder::PausedState is not supported on Android"); + pause(); break; } } void QAndroidCaptureSession::start() { - if (m_state == QMediaRecorder::RecordingState || m_status != QMediaRecorder::LoadedStatus) + if (m_state == QMediaRecorder::RecordingState ||(m_status != QMediaRecorder::LoadedStatus && m_status != QMediaRecorder::PausedStatus)) return; - setStatus(QMediaRecorder::StartingStatus); - if (m_mediaRecorder) { - m_mediaRecorder->release(); - delete m_mediaRecorder; - } - - const bool granted = m_cameraSession - ? m_cameraSession->requestRecordingPermission() - : qt_androidRequestRecordingPermission(); - if (!granted) { - setStatus(QMediaRecorder::UnavailableStatus); - Q_EMIT error(QMediaRecorder::ResourceError, QLatin1String("Permission denied.")); - return; - } - - m_mediaRecorder = new AndroidMediaRecorder; - connect(m_mediaRecorder, SIGNAL(error(int,int)), this, SLOT(onError(int,int))); - connect(m_mediaRecorder, SIGNAL(info(int,int)), this, SLOT(onInfo(int,int))); + if (m_state != QMediaRecorder::PausedState) { + if (m_mediaRecorder) { + m_mediaRecorder->release(); + delete m_mediaRecorder; + } - // Set audio/video sources - if (m_cameraSession) { - updateViewfinder(); - m_cameraSession->camera()->unlock(); - m_mediaRecorder->setCamera(m_cameraSession->camera()); - m_mediaRecorder->setAudioSource(AndroidMediaRecorder::Camcorder); - m_mediaRecorder->setVideoSource(AndroidMediaRecorder::Camera); - } else { - m_mediaRecorder->setAudioSource(m_audioSource); - } + const bool granted = m_cameraSession + ? m_cameraSession->requestRecordingPermission() + : qt_androidRequestRecordingPermission(); + if (!granted) { + setStatus(QMediaRecorder::UnavailableStatus); + Q_EMIT error(QMediaRecorder::ResourceError, QLatin1String("Permission denied.")); + return; + } - // Set output format - m_mediaRecorder->setOutputFormat(m_outputFormat); + m_mediaRecorder = new AndroidMediaRecorder; + connect(m_mediaRecorder, SIGNAL(error(int,int)), this, SLOT(onError(int,int))); + connect(m_mediaRecorder, SIGNAL(info(int,int)), this, SLOT(onInfo(int,int))); + + // Set audio/video sources + if (m_cameraSession) { + updateViewfinder(); + m_cameraSession->camera()->unlock(); + m_mediaRecorder->setCamera(m_cameraSession->camera()); + m_mediaRecorder->setAudioSource(AndroidMediaRecorder::Camcorder); + m_mediaRecorder->setVideoSource(AndroidMediaRecorder::Camera); + } else { + m_mediaRecorder->setAudioSource(m_audioSource); + } - // Set audio encoder settings - m_mediaRecorder->setAudioChannels(m_audioSettings.channelCount()); - m_mediaRecorder->setAudioEncodingBitRate(m_audioSettings.bitRate()); - m_mediaRecorder->setAudioSamplingRate(m_audioSettings.sampleRate()); - m_mediaRecorder->setAudioEncoder(m_audioEncoder); + // Set output format + m_mediaRecorder->setOutputFormat(m_outputFormat); - // Set video encoder settings - if (m_cameraSession) { - m_mediaRecorder->setVideoSize(m_videoSettings.resolution()); - m_mediaRecorder->setVideoFrameRate(qRound(m_videoSettings.frameRate())); - m_mediaRecorder->setVideoEncodingBitRate(m_videoSettings.bitRate()); - m_mediaRecorder->setVideoEncoder(m_videoEncoder); + // Set audio encoder settings + m_mediaRecorder->setAudioChannels(m_audioSettings.channelCount()); + m_mediaRecorder->setAudioEncodingBitRate(m_audioSettings.bitRate()); + m_mediaRecorder->setAudioSamplingRate(m_audioSettings.sampleRate()); + m_mediaRecorder->setAudioEncoder(m_audioEncoder); - m_mediaRecorder->setOrientationHint(m_cameraSession->currentCameraRotation()); - } + // Set video encoder settings + if (m_cameraSession) { + m_mediaRecorder->setVideoSize(m_videoSettings.resolution()); + m_mediaRecorder->setVideoFrameRate(qRound(m_videoSettings.frameRate())); + m_mediaRecorder->setVideoEncodingBitRate(m_videoSettings.bitRate()); + m_mediaRecorder->setVideoEncoder(m_videoEncoder); - // Set output file - QString filePath = m_mediaStorageLocation.generateFileName( - m_requestedOutputLocation.isLocalFile() ? m_requestedOutputLocation.toLocalFile() - : m_requestedOutputLocation.toString(), - m_cameraSession ? QMediaStorageLocation::Movies - : QMediaStorageLocation::Sounds, - m_cameraSession ? QLatin1String("VID_") - : QLatin1String("REC_"), - m_containerFormat); - - m_usedOutputLocation = QUrl::fromLocalFile(filePath); - m_mediaRecorder->setOutputFile(filePath); - - // Even though the Android doc explicitly says that calling MediaRecorder.setPreviewDisplay() - // is not necessary when the Camera already has a Surface, it doesn't actually work on some - // devices. For example on the Samsung Galaxy Tab 2, the camera server dies after prepare() - // and start() if MediaRecorder.setPreviewDispaly() is not called. - if (m_cameraSession) { - // When using a SurfaceTexture, we need to pass a new one to the MediaRecorder, not the same - // one that is set on the Camera or it will crash, hence the reset(). - m_cameraSession->videoOutput()->reset(); - if (m_cameraSession->videoOutput()->surfaceTexture()) - m_mediaRecorder->setSurfaceTexture(m_cameraSession->videoOutput()->surfaceTexture()); - else if (m_cameraSession->videoOutput()->surfaceHolder()) - m_mediaRecorder->setSurfaceHolder(m_cameraSession->videoOutput()->surfaceHolder()); - } + m_mediaRecorder->setOrientationHint(m_cameraSession->currentCameraRotation()); + } - if (!m_mediaRecorder->prepare()) { - emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder.")); - if (m_cameraSession) - restartViewfinder(); - return; - } + // Set output file + QString filePath = m_mediaStorageLocation.generateFileName( + m_requestedOutputLocation.isLocalFile() ? m_requestedOutputLocation.toLocalFile() + : m_requestedOutputLocation.toString(), + m_cameraSession ? QMediaStorageLocation::Movies + : QMediaStorageLocation::Sounds, + m_cameraSession ? QLatin1String("VID_") + : QLatin1String("REC_"), + m_containerFormat); + + m_usedOutputLocation = QUrl::fromLocalFile(filePath); + m_mediaRecorder->setOutputFile(filePath); + + // Even though the Android doc explicitly says that calling MediaRecorder.setPreviewDisplay() + // is not necessary when the Camera already has a Surface, it doesn't actually work on some + // devices. For example on the Samsung Galaxy Tab 2, the camera server dies after prepare() + // and start() if MediaRecorder.setPreviewDispaly() is not called. + if (m_cameraSession) { + // When using a SurfaceTexture, we need to pass a new one to the MediaRecorder, not the same + // one that is set on the Camera or it will crash, hence the reset(). + m_cameraSession->videoOutput()->reset(); + if (m_cameraSession->videoOutput()->surfaceTexture()) + m_mediaRecorder->setSurfaceTexture(m_cameraSession->videoOutput()->surfaceTexture()); + else if (m_cameraSession->videoOutput()->surfaceHolder()) + m_mediaRecorder->setSurfaceHolder(m_cameraSession->videoOutput()->surfaceHolder()); + } - if (!m_mediaRecorder->start()) { - emit error(QMediaRecorder::FormatError, QLatin1String("Unable to start the media recorder.")); - if (m_cameraSession) - restartViewfinder(); - return; + if (!m_mediaRecorder->prepare()) { + emit error(QMediaRecorder::FormatError, QLatin1String("Unable to prepare the media recorder.")); + if (m_cameraSession) + restartViewfinder(); + return; + } + if (!m_mediaRecorder->start()) { + emit error(QMediaRecorder::FormatError, QLatin1String("Unable to start the media recorder.")); + if (m_cameraSession) + restartViewfinder(); + return; + } + } else { + m_previousElapsedTime += m_duration; + if (!m_mediaRecorder->resume()) { + emit error(QMediaRecorder::FormatError, QLatin1String("Unable to resume the media recorder.")); + if (m_cameraSession) + restartViewfinder(); + return; + } } m_elapsedTime.start(); @@ -347,6 +354,22 @@ void QAndroidCaptureSession::stop(bool error) setStatus(QMediaRecorder::LoadedStatus); } +void QAndroidCaptureSession::pause() +{ + if (m_state == QMediaRecorder::PausedState || m_mediaRecorder == 0) + return; + + setStatus(QMediaRecorder::PausedStatus); + + m_mediaRecorder->pause(); + m_notifyTimer.stop(); + updateDuration(); + m_elapsedTime.invalidate(); + + m_state = QMediaRecorder::PausedState; + emit stateChanged(m_state); +} + void QAndroidCaptureSession::setStatus(QMediaRecorder::Status status) { if (m_status == status) @@ -503,7 +526,7 @@ void QAndroidCaptureSession::restartViewfinder() void QAndroidCaptureSession::updateDuration() { if (m_elapsedTime.isValid()) - m_duration = m_elapsedTime.elapsed(); + m_duration = m_elapsedTime.elapsed() + m_previousElapsedTime; emit durationChanged(m_duration); } diff --git a/src/plugins/android/src/mediacapture/qandroidcapturesession.h b/src/plugins/android/src/mediacapture/qandroidcapturesession.h index 8cfb9ad2..40844bd4 100644 --- a/src/plugins/android/src/mediacapture/qandroidcapturesession.h +++ b/src/plugins/android/src/mediacapture/qandroidcapturesession.h @@ -137,6 +137,7 @@ private: void start(); void stop(bool error = false); + void pause(); void setStatus(QMediaRecorder::Status status); @@ -154,6 +155,7 @@ private: QElapsedTimer m_elapsedTime; QTimer m_notifyTimer; qint64 m_duration; + qint64 m_previousElapsedTime = 0; QMediaRecorder::State m_state; QMediaRecorder::Status m_status; diff --git a/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp b/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp index d607ab80..10508a35 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp +++ b/src/plugins/android/src/wrappers/jni/androidmediarecorder.cpp @@ -213,6 +213,32 @@ void AndroidMediaRecorder::stop() } } +void AndroidMediaRecorder::pause() +{ + QJNIEnvironmentPrivate env; + m_mediaRecorder.callMethod("pause"); + if (env->ExceptionCheck()) { +#ifdef QT_DEBUG + env->ExceptionDescribe(); +#endif + env->ExceptionClear(); + } +} + +bool AndroidMediaRecorder::resume() +{ + QJNIEnvironmentPrivate env; + m_mediaRecorder.callMethod("resume"); + if (env->ExceptionCheck()) { +#ifdef QT_DEBUG + env->ExceptionDescribe(); +#endif + env->ExceptionClear(); + return false; + } + return true; +} + void AndroidMediaRecorder::setAudioChannels(int numChannels) { m_mediaRecorder.callMethod("setAudioChannels", "(I)V", numChannels); diff --git a/src/plugins/android/src/wrappers/jni/androidmediarecorder.h b/src/plugins/android/src/wrappers/jni/androidmediarecorder.h index e4b3a80e..55b370cf 100644 --- a/src/plugins/android/src/wrappers/jni/androidmediarecorder.h +++ b/src/plugins/android/src/wrappers/jni/androidmediarecorder.h @@ -138,6 +138,8 @@ public: bool start(); void stop(); + void pause(); + bool resume(); void setAudioChannels(int numChannels); void setAudioEncoder(AudioEncoder encoder);