Implement pause/resume functionality to MediaRecorder
Bug: 20092236
Change-Id: Ia0e92ff246302fd5fdef53c4f961d6645cc26a86
diff --git a/include/media/IMediaRecorder.h b/include/media/IMediaRecorder.h
index 77ed5d3..caa6592 100644
--- a/include/media/IMediaRecorder.h
+++ b/include/media/IMediaRecorder.h
@@ -53,6 +53,8 @@
virtual status_t start() = 0;
virtual status_t stop() = 0;
virtual status_t reset() = 0;
+ virtual status_t pause() = 0;
+ virtual status_t resume() = 0;
virtual status_t init() = 0;
virtual status_t close() = 0;
virtual status_t release() = 0;
diff --git a/include/media/MediaRecorderBase.h b/include/media/MediaRecorderBase.h
index d6cc4bb..c05d782 100644
--- a/include/media/MediaRecorderBase.h
+++ b/include/media/MediaRecorderBase.h
@@ -53,6 +53,8 @@
virtual status_t prepare() = 0;
virtual status_t start() = 0;
virtual status_t stop() = 0;
+ virtual status_t pause() = 0;
+ virtual status_t resume() = 0;
virtual status_t close() = 0;
virtual status_t reset() = 0;
virtual status_t getMaxAmplitude(int *max) = 0;
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 15ff82d..c375dff 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -233,6 +233,8 @@
status_t start();
status_t stop();
status_t reset();
+ status_t pause();
+ status_t resume();
status_t init();
status_t close();
status_t release();
diff --git a/include/media/stagefright/MediaCodecSource.h b/include/media/stagefright/MediaCodecSource.h
index 71f58a9..14b0072 100644
--- a/include/media/stagefright/MediaCodecSource.h
+++ b/include/media/stagefright/MediaCodecSource.h
@@ -47,6 +47,7 @@
bool isVideo() const { return mIsVideo; }
sp<IGraphicBufferProducer> getGraphicBufferProducer();
+ void setInputBufferTimeOffset(int64_t timeOffsetUs);
// MediaSource
virtual status_t start(MetaData *params = NULL);
@@ -75,6 +76,7 @@
kWhatStart,
kWhatStop,
kWhatPause,
+ kWhatSetInputBufferTimeOffset,
};
MediaCodecSource(
@@ -117,6 +119,7 @@
List<MediaBuffer *> mInputBufferQueue;
List<size_t> mAvailEncoderInputIndices;
List<int64_t> mDecodingTimeQueue; // decoding time (us) for video
+ int64_t mInputBufferTimeOffsetUs;
// audio drift time
int64_t mFirstSampleTimeUs;
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index ee3b584..0eea820 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -54,7 +54,9 @@
SET_PREVIEW_SURFACE,
SET_CAMERA,
SET_LISTENER,
- SET_CLIENT_NAME
+ SET_CLIENT_NAME,
+ PAUSE,
+ RESUME
};
class BpMediaRecorder: public BpInterface<IMediaRecorder>
@@ -276,6 +278,24 @@
return reply.readInt32();
}
+ status_t pause()
+ {
+ ALOGV("pause");
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+ remote()->transact(PAUSE, data, &reply);
+ return reply.readInt32();
+ }
+
+ status_t resume()
+ {
+ ALOGV("resume");
+ Parcel data, reply;
+ data.writeInterfaceToken(IMediaRecorder::getInterfaceDescriptor());
+ remote()->transact(RESUME, data, &reply);
+ return reply.readInt32();
+ }
+
status_t close()
{
ALOGV("close");
@@ -340,6 +360,18 @@
reply->writeInt32(start());
return NO_ERROR;
} break;
+ case PAUSE: {
+ ALOGV("PAUSE");
+ CHECK_INTERFACE(IMediaRecorder, data, reply);
+ reply->writeInt32(pause());
+ return NO_ERROR;
+ } break;
+ case RESUME: {
+ ALOGV("RESUME");
+ CHECK_INTERFACE(IMediaRecorder, data, reply);
+ reply->writeInt32(resume());
+ return NO_ERROR;
+ } break;
case PREPARE: {
ALOGV("PREPARE");
CHECK_INTERFACE(IMediaRecorder, data, reply);
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 8bbd8f1..bfdf41d 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -561,6 +561,50 @@
return ret;
}
+status_t MediaRecorder::pause()
+{
+ ALOGV("pause");
+ if (mMediaRecorder == NULL) {
+ ALOGE("media recorder is not initialized yet");
+ return INVALID_OPERATION;
+ }
+ if (!(mCurrentState & MEDIA_RECORDER_RECORDING)) {
+ ALOGE("stop called in an invalid state: %d", mCurrentState);
+ return INVALID_OPERATION;
+ }
+
+ status_t ret = mMediaRecorder->pause();
+ if (OK != ret) {
+ ALOGE("pause failed: %d", ret);
+ mCurrentState = MEDIA_RECORDER_ERROR;
+ return ret;
+ }
+
+ return ret;
+}
+
+status_t MediaRecorder::resume()
+{
+ ALOGV("resume");
+ if (mMediaRecorder == NULL) {
+ ALOGE("media recorder is not initialized yet");
+ return INVALID_OPERATION;
+ }
+ if (!(mCurrentState & MEDIA_RECORDER_RECORDING)) {
+ ALOGE("resume called in an invalid state: %d", mCurrentState);
+ return INVALID_OPERATION;
+ }
+
+ status_t ret = mMediaRecorder->resume();
+ if (OK != ret) {
+ ALOGE("resume failed: %d", ret);
+ mCurrentState = MEDIA_RECORDER_ERROR;
+ return ret;
+ }
+
+ return ret;
+}
+
status_t MediaRecorder::close()
{
ALOGV("close");
diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp
index 08c7899..3b4e148 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.cpp
+++ b/media/libmediaplayerservice/MediaRecorderClient.cpp
@@ -250,6 +250,29 @@
return mRecorder->stop();
}
+status_t MediaRecorderClient::pause()
+{
+ ALOGV("pause");
+ Mutex::Autolock lock(mLock);
+ if (mRecorder == NULL) {
+ ALOGE("recorder is not initialized");
+ return NO_INIT;
+ }
+ return mRecorder->pause();
+
+}
+
+status_t MediaRecorderClient::resume()
+{
+ ALOGV("resume");
+ Mutex::Autolock lock(mLock);
+ if (mRecorder == NULL) {
+ ALOGE("recorder is not initialized");
+ return NO_INIT;
+ }
+ return mRecorder->resume();
+}
+
status_t MediaRecorderClient::init()
{
ALOGV("init");
diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h
index 05130d4..c0d9c4c 100644
--- a/media/libmediaplayerservice/MediaRecorderClient.h
+++ b/media/libmediaplayerservice/MediaRecorderClient.h
@@ -51,6 +51,8 @@
virtual status_t start();
virtual status_t stop();
virtual status_t reset();
+ virtual status_t pause();
+ virtual status_t resume();
virtual status_t init();
virtual status_t close();
virtual status_t release();
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 36fe800..73d07a0 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -907,7 +907,7 @@
return status;
}
-sp<MediaSource> StagefrightRecorder::createAudioSource() {
+sp<MediaCodecSource> StagefrightRecorder::createAudioSource() {
int32_t sourceSampleRate = mSampleRate;
if (mCaptureFpsEnable && mCaptureFps >= mFrameRate) {
@@ -982,7 +982,7 @@
}
format->setInt32("priority", 0 /* realtime */);
- sp<MediaSource> audioEncoder =
+ sp<MediaCodecSource> audioEncoder =
MediaCodecSource::Create(mLooper, format, audioSource);
mAudioSourceNode = audioSource;
@@ -1041,13 +1041,14 @@
return status;
}
- sp<MediaSource> audioEncoder = createAudioSource();
+ sp<MediaCodecSource> audioEncoder = createAudioSource();
if (audioEncoder == NULL) {
return UNKNOWN_ERROR;
}
CHECK(mWriter != 0);
mWriter->addSource(audioEncoder);
+ mAudioEncoderSource = audioEncoder;
if (mMaxFileDurationUs != 0) {
mWriter->setMaxFileDuration(mMaxFileDurationUs);
@@ -1075,10 +1076,11 @@
return BAD_VALUE;
}
- sp<MediaSource> source;
+ sp<MediaCodecSource> source;
if (mAudioSource != AUDIO_SOURCE_CNT) {
source = createAudioSource();
+ mAudioEncoderSource = source;
} else {
setDefaultVideoEncoderIfNecessary();
@@ -1092,6 +1094,7 @@
if (err != OK) {
return err;
}
+ mVideoEncoderSource = source;
}
mWriter = new ARTPWriter(mOutputFd);
@@ -1132,7 +1135,7 @@
return err;
}
- sp<MediaSource> encoder;
+ sp<MediaCodecSource> encoder;
err = setupVideoEncoder(mediaSource, &encoder);
if (err != OK) {
@@ -1140,6 +1143,7 @@
}
writer->addSource(encoder);
+ mVideoEncoderSource = encoder;
}
if (mMaxFileDurationUs != 0) {
@@ -1489,7 +1493,7 @@
status_t StagefrightRecorder::setupVideoEncoder(
sp<MediaSource> cameraSource,
- sp<MediaSource> *source) {
+ sp<MediaCodecSource> *source) {
source->clear();
sp<AMessage> format = new AMessage();
@@ -1618,12 +1622,13 @@
return UNKNOWN_ERROR;
}
- sp<MediaSource> audioEncoder = createAudioSource();
+ sp<MediaCodecSource> audioEncoder = createAudioSource();
if (audioEncoder == NULL) {
return UNKNOWN_ERROR;
}
writer->addSource(audioEncoder);
+ mAudioEncoderSource = audioEncoder;
return OK;
}
@@ -1649,13 +1654,14 @@
return err;
}
- sp<MediaSource> encoder;
+ sp<MediaCodecSource> encoder;
err = setupVideoEncoder(mediaSource, &encoder);
if (err != OK) {
return err;
}
writer->addSource(encoder);
+ mVideoEncoderSource = encoder;
mTotalBitRate += mVideoBitRate;
}
@@ -1726,25 +1732,49 @@
status_t StagefrightRecorder::pause() {
ALOGV("pause");
- if (mWriter == NULL) {
- return UNKNOWN_ERROR;
- }
- mWriter->pause();
-
- if (mStarted) {
- mStarted = false;
-
- uint32_t params = 0;
- if (mAudioSource != AUDIO_SOURCE_CNT) {
- params |= IMediaPlayerService::kBatteryDataTrackAudio;
- }
- if (mVideoSource != VIDEO_SOURCE_LIST_END) {
- params |= IMediaPlayerService::kBatteryDataTrackVideo;
- }
-
- addBatteryData(params);
+ if (!mStarted) {
+ return INVALID_OPERATION;
}
+ // Already paused --- no-op.
+ if (mPauseStartTimeUs != 0) {
+ return OK;
+ }
+
+ if (mAudioEncoderSource != NULL) {
+ mAudioEncoderSource->pause();
+ }
+ if (mVideoEncoderSource != NULL) {
+ mVideoEncoderSource->pause();
+ }
+
+ mPauseStartTimeUs = systemTime() / 1000;
+
+ return OK;
+}
+
+status_t StagefrightRecorder::resume() {
+ ALOGV("resume");
+ if (!mStarted) {
+ return INVALID_OPERATION;
+ }
+
+ // Not paused --- no-op.
+ if (mPauseStartTimeUs == 0) {
+ return OK;
+ }
+
+ // 30 ms buffer to avoid timestamp overlap
+ mTotalPausedDurationUs += (systemTime() / 1000) - mPauseStartTimeUs - 30000;
+ if (mAudioEncoderSource != NULL) {
+ mAudioEncoderSource->setInputBufferTimeOffset(-mTotalPausedDurationUs);
+ mAudioEncoderSource->start();
+ }
+ if (mVideoEncoderSource != NULL) {
+ mVideoEncoderSource->setInputBufferTimeOffset(-mTotalPausedDurationUs);
+ mVideoEncoderSource->start();
+ }
+ mPauseStartTimeUs = 0;
return OK;
}
@@ -1765,6 +1795,8 @@
mGraphicBufferProducer.clear();
mPersistentSurface.clear();
+ mAudioEncoderSource.clear();
+ mVideoEncoderSource.clear();
if (mOutputFd >= 0) {
::close(mOutputFd);
@@ -1840,6 +1872,8 @@
mTotalBitRate = 0;
mOutputFd = -1;
+ mPauseStartTimeUs = 0;
+ mTotalPausedDurationUs = 0;
return OK;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index da00bc7..a799e9d 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -30,6 +30,7 @@
class ICameraRecordingProxy;
class CameraSource;
class CameraSourceTimeLapse;
+struct MediaCodecSource;
struct MediaSource;
struct MediaWriter;
class MetaData;
@@ -62,6 +63,7 @@
virtual status_t prepare();
virtual status_t start();
virtual status_t pause();
+ virtual status_t resume();
virtual status_t stop();
virtual status_t close();
virtual status_t reset();
@@ -121,6 +123,11 @@
bool mIsMetaDataStoredInVideoBuffers;
MediaProfiles *mEncoderProfiles;
+ int64_t mPauseStartTimeUs;
+ int64_t mTotalPausedDurationUs;
+ sp<MediaCodecSource> mAudioEncoderSource;
+ sp<MediaCodecSource> mVideoEncoderSource;
+
bool mStarted;
// Needed when GLFrames are encoded.
// An <IGraphicBufferProducer> pointer
@@ -139,7 +146,7 @@
status_t setupRawAudioRecording();
status_t setupRTPRecording();
status_t setupMPEG2TSRecording();
- sp<MediaSource> createAudioSource();
+ sp<MediaCodecSource> createAudioSource();
status_t checkVideoEncoderCapabilities();
status_t checkAudioEncoderCapabilities();
// Generic MediaSource set-up. Returns the appropriate
@@ -148,7 +155,7 @@
status_t setupMediaSource(sp<MediaSource> *mediaSource);
status_t setupCameraSource(sp<CameraSource> *cameraSource);
status_t setupAudioEncoder(const sp<MediaWriter>& writer);
- status_t setupVideoEncoder(sp<MediaSource> cameraSource, sp<MediaSource> *source);
+ status_t setupVideoEncoder(sp<MediaSource> cameraSource, sp<MediaCodecSource> *source);
// Encoding parameter handling utilities
status_t setParameter(const String8 &key, const String8 &value);
diff --git a/media/libstagefright/MediaCodecSource.cpp b/media/libstagefright/MediaCodecSource.cpp
index 7f9f824..d579695 100644
--- a/media/libstagefright/MediaCodecSource.cpp
+++ b/media/libstagefright/MediaCodecSource.cpp
@@ -274,6 +274,12 @@
return NULL;
}
+void MediaCodecSource::setInputBufferTimeOffset(int64_t timeOffsetUs) {
+ sp<AMessage> msg = new AMessage(kWhatSetInputBufferTimeOffset, mReflector);
+ msg->setInt64("time-offset-us", timeOffsetUs);
+ postSynchronouslyAndReturnError(msg);
+}
+
status_t MediaCodecSource::start(MetaData* params) {
sp<AMessage> msg = new AMessage(kWhatStart, mReflector);
msg->setObject("meta", params);
@@ -348,6 +354,7 @@
mEncoderFormat(0),
mEncoderDataSpace(0),
mGraphicBufferConsumer(consumer),
+ mInputBufferTimeOffsetUs(0),
mFirstSampleTimeUs(-1ll),
mEncoderReachedEOS(false),
mErrorCode(OK) {
@@ -568,6 +575,7 @@
if (mbuf != NULL) {
CHECK(mbuf->meta_data()->findInt64(kKeyTime, &timeUs));
+ timeUs += mInputBufferTimeOffsetUs;
// push decoding time for video, or drift time for audio
if (mIsVideo) {
@@ -749,6 +757,9 @@
if (mIsVideo) {
int64_t decodingTimeUs;
if (mFlags & FLAG_USE_SURFACE_INPUT) {
+ // Time offset is not applied at
+ // feedEncoderInputBuffer() in surface input case.
+ timeUs += mInputBufferTimeOffsetUs;
// GraphicBufferSource is supposed to discard samples
// queued before start, and offset timeUs by start time
CHECK_GE(timeUs, 0ll);
@@ -852,7 +863,7 @@
}
case kWhatPause:
{
- if (mFlags && FLAG_USE_SURFACE_INPUT) {
+ if (mFlags & FLAG_USE_SURFACE_INPUT) {
suspend();
} else {
CHECK(mPuller != NULL);
@@ -860,6 +871,17 @@
}
break;
}
+ case kWhatSetInputBufferTimeOffset:
+ {
+ sp<AReplyToken> replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ CHECK(msg->findInt64("time-offset-us", &mInputBufferTimeOffsetUs));
+
+ sp<AMessage> response = new AMessage;
+ response->postReply(replyID);
+ break;
+ }
default:
TRESPASS();
}