diff options
Diffstat (limited to 'media')
20 files changed, 589 insertions, 40 deletions
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp index 942aec3..bad84b7 100644 --- a/media/libmedia/IMediaPlayer.cpp +++ b/media/libmedia/IMediaPlayer.cpp @@ -67,6 +67,8 @@ enum { SET_RETRANSMIT_ENDPOINT, GET_RETRANSMIT_ENDPOINT, SET_NEXT_PLAYER, + SUSPEND, + RESUME, }; class BpMediaPlayer: public BpInterface<IMediaPlayer> @@ -419,6 +421,22 @@ public: return err; } + + status_t suspend() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(SUSPEND, data, &reply); + return reply.readInt32(); + } + + status_t resume() + { + Parcel data, reply; + data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor()); + remote()->transact(RESUME, data, &reply); + return reply.readInt32(); + } }; IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer"); @@ -682,6 +700,18 @@ status_t BnMediaPlayer::onTransact( return NO_ERROR; } break; + case SUSPEND: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + status_t ret = suspend(); + reply->writeInt32(ret); + return NO_ERROR; + } break; + case RESUME: { + CHECK_INTERFACE(IMediaPlayer, data, reply); + status_t ret = resume(); + reply->writeInt32(ret); + return NO_ERROR; + } break; default: return BBinder::onTransact(code, data, reply, flags); } diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp index ee3b584..4711c1b 100644 --- a/media/libmedia/IMediaRecorder.cpp +++ b/media/libmedia/IMediaRecorder.cpp @@ -39,6 +39,7 @@ enum { QUERY_SURFACE_MEDIASOURCE, RESET, STOP, + PAUSE, START, PREPARE, GET_MAX_AMPLITUDE, @@ -258,6 +259,15 @@ public: 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 stop() { ALOGV("stop"); @@ -334,6 +344,12 @@ status_t BnMediaRecorder::onTransact( reply->writeInt32(stop()); return NO_ERROR; } break; + case PAUSE: { + ALOGV("PAUSE"); + CHECK_INTERFACE(IMediaRecorder, data, reply); + reply->writeInt32(pause()); + return NO_ERROR; + } break; case START: { ALOGV("START"); CHECK_INTERFACE(IMediaRecorder, data, reply); diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp index 6c0606c..3c6bef3 100644 --- a/media/libmedia/mediaplayer.cpp +++ b/media/libmedia/mediaplayer.cpp @@ -333,6 +333,9 @@ status_t MediaPlayer::start() ALOGV("playback completed immediately following start()"); } } + } else if ( (mPlayer != 0) && (mCurrentState & MEDIA_PLAYER_SUSPENDED) ) { + ALOGV("start while suspended, so ignore this start"); + ret = NO_ERROR; } else { ALOGE("start called in state %d", mCurrentState); ret = INVALID_OPERATION; @@ -488,7 +491,8 @@ status_t MediaPlayer::getDuration_l(int *msec) { ALOGV("getDuration_l"); bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | - MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE)); + MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE | + MEDIA_PLAYER_SUSPENDED)); if (mPlayer != 0 && isValidState) { int durationMs; status_t ret = mPlayer->getDuration(&durationMs); @@ -518,7 +522,7 @@ status_t MediaPlayer::seekTo_l(int msec) { ALOGV("seekTo %d", msec); if ((mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PREPARED | - MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE) ) ) { + MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_SUSPENDED) ) ) { if ( msec < 0 ) { ALOGW("Attempt to seek to invalid position: %d", msec); msec = 0; @@ -939,4 +943,54 @@ status_t MediaPlayer::setNextMediaPlayer(const sp<MediaPlayer>& next) { return mPlayer->setNextPlayer(next == NULL ? NULL : next->mPlayer); } -} // namespace android +status_t MediaPlayer::suspend() { + ALOGV("MediaPlayer::suspend()"); + Mutex::Autolock _l(mLock); + if (mPlayer == NULL) { + ALOGE("mPlayer = NULL"); + return NO_INIT; + } + + bool isValidState = (mCurrentState & (MEDIA_PLAYER_PREPARED | MEDIA_PLAYER_STARTED | MEDIA_PLAYER_PAUSED | MEDIA_PLAYER_STOPPED | MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_SUSPENDED)); + + if (!isValidState) { + ALOGE("suspend while in a invalid state = %d", mCurrentState); + return UNKNOWN_ERROR; + } + + status_t ret = mPlayer->suspend(); + + if (OK != ret) { + ALOGE("MediaPlayer::suspend() return with error ret = %d", ret); + return ret; + } + mCurrentState = MEDIA_PLAYER_SUSPENDED; + return OK; +} + +status_t MediaPlayer::resume() { + ALOGV("MediaPlayer::resume()"); + Mutex::Autolock _l(mLock); + if (mPlayer == NULL) { + ALOGE("mPlayer == NULL"); + return NO_INIT; + } + + bool isValidState = (mCurrentState == MEDIA_PLAYER_SUSPENDED); + + if (!isValidState) { + ALOGE("resume while in a invalid state = %d", mCurrentState); + return UNKNOWN_ERROR; + } + + status_t ret = mPlayer->resume(); + + if (OK != ret) { + ALOGE("MediaPlayer::resume() return with error ret = %d", ret); + return ret; + } + mCurrentState = MEDIA_PLAYER_PREPARED; + return OK; +} + +}; // namespace android diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp index 8bbd8f1..8b7b171 100644..100755 --- a/media/libmedia/mediarecorder.cpp +++ b/media/libmedia/mediarecorder.cpp @@ -482,7 +482,7 @@ status_t MediaRecorder::start() ALOGE("media recorder is not initialized yet"); return INVALID_OPERATION; } - if (!(mCurrentState & MEDIA_RECORDER_PREPARED)) { + if (!(mCurrentState & (MEDIA_RECORDER_PREPARED | MEDIA_RECORDER_PAUSED))) { ALOGE("start called in an invalid state: %d", mCurrentState); return INVALID_OPERATION; } @@ -497,6 +497,29 @@ status_t MediaRecorder::start() 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("pause 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; + } + + mCurrentState = MEDIA_RECORDER_PAUSED; + return ret; +} + status_t MediaRecorder::stop() { ALOGV("stop"); @@ -504,7 +527,7 @@ status_t MediaRecorder::stop() ALOGE("media recorder is not initialized yet"); return INVALID_OPERATION; } - if (!(mCurrentState & MEDIA_RECORDER_RECORDING)) { + if (!(mCurrentState & (MEDIA_RECORDER_RECORDING | MEDIA_RECORDER_PAUSED))) { ALOGE("stop called in an invalid state: %d", mCurrentState); return INVALID_OPERATION; } @@ -540,6 +563,7 @@ status_t MediaRecorder::reset() ret = OK; break; + case MEDIA_RECORDER_PAUSED: case MEDIA_RECORDER_RECORDING: case MEDIA_RECORDER_DATASOURCE_CONFIGURED: case MEDIA_RECORDER_PREPARED: diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp index 6e104a4..de51b3c 100644 --- a/media/libmediaplayerservice/MediaPlayerService.cpp +++ b/media/libmediaplayerservice/MediaPlayerService.cpp @@ -1306,6 +1306,22 @@ void MediaPlayerService::Client::addNewMetadataUpdate(media::Metadata::Type meta } } +status_t MediaPlayerService::Client::suspend() +{ + ALOGV("[%d] suspend", mConnId); + sp<MediaPlayerBase> p = getPlayer(); + if (p == NULL) return NO_INIT; + return p->suspend(); +} + +status_t MediaPlayerService::Client::resume() +{ + ALOGV("[%d] resume", mConnId); + sp<MediaPlayerBase> p = getPlayer(); + if (p == NULL) return NO_INIT; + return p->resume(); +} + #if CALLBACK_ANTAGONIZER const int Antagonizer::interval = 10000; // 10 msecs diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h index 748b25f..ff8f550 100644 --- a/media/libmediaplayerservice/MediaPlayerService.h +++ b/media/libmediaplayerservice/MediaPlayerService.h @@ -334,6 +334,9 @@ private: int getAudioSessionId() { return mAudioSessionId; } + virtual status_t suspend(); + virtual status_t resume(); + private: friend class MediaPlayerService; Client( const sp<MediaPlayerService>& service, diff --git a/media/libmediaplayerservice/MediaRecorderClient.cpp b/media/libmediaplayerservice/MediaRecorderClient.cpp index 6f242e5..1e112c8 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.cpp +++ b/media/libmediaplayerservice/MediaRecorderClient.cpp @@ -244,6 +244,17 @@ status_t MediaRecorderClient::start() } +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::stop() { ALOGV("stop"); diff --git a/media/libmediaplayerservice/MediaRecorderClient.h b/media/libmediaplayerservice/MediaRecorderClient.h index 05130d4..2e77d21 100644 --- a/media/libmediaplayerservice/MediaRecorderClient.h +++ b/media/libmediaplayerservice/MediaRecorderClient.h @@ -49,6 +49,7 @@ public: virtual status_t prepare(); virtual status_t getMaxAmplitude(int* max); virtual status_t start(); + virtual status_t pause(); virtual status_t stop(); virtual status_t reset(); virtual status_t init(); diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index 4257b41..b1f0742 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -79,7 +79,8 @@ StagefrightRecorder::StagefrightRecorder(const String16 &opPackageName) mOutputFd(-1), mAudioSource(AUDIO_SOURCE_CNT), mVideoSource(VIDEO_SOURCE_LIST_END), - mStarted(false) { + mStarted(false), + mRecPaused(false) { ALOGV("Constructor"); reset(); @@ -846,6 +847,22 @@ status_t StagefrightRecorder::start() { return INVALID_OPERATION; } + if (mRecPaused == true) { + status_t err = mWriter->start(); + if (err != OK) { + ALOGE("Writer start in StagefrightRecorder pause failed"); + return err; + } + + err = setSourcePause(false); + if (err != OK) { + ALOGE("Source start after pause failed"); + return err; + } + + mRecPaused = false; + return OK; + } status_t status = OK; if (mVideoSource != VIDEO_SOURCE_SURFACE) { @@ -1010,6 +1027,7 @@ sp<MediaSource> StagefrightRecorder::createAudioSource() { ALOGE("Failed to create audio encoder"); } + mAudioEncoderOMX = audioEncoder; return audioEncoder; } @@ -1625,6 +1643,8 @@ status_t StagefrightRecorder::setupVideoEncoder( mGraphicBufferProducer = encoder->getGraphicBufferProducer(); } + mVideoSourceNode = cameraSource; + mVideoEncoderOMX = encoder; *source = encoder; return OK; @@ -1759,10 +1779,23 @@ void StagefrightRecorder::setupMPEG4orWEBMMetaData(sp<MetaData> *meta) { status_t StagefrightRecorder::pause() { ALOGV("pause"); + status_t err = OK; if (mWriter == NULL) { return UNKNOWN_ERROR; } - mWriter->pause(); + err = setSourcePause(true); + if (err != OK) { + ALOGE("StagefrightRecorder pause failed"); + return err; + } + + err = mWriter->pause(); + if (err != OK) { + ALOGE("Writer pause failed"); + return err; + } + + mRecPaused = true; if (mStarted) { mStarted = false; @@ -1791,6 +1824,16 @@ status_t StagefrightRecorder::stop() { mCameraSourceTimeLapse = NULL; } + if (mRecPaused) { + status_t err = setSourcePause(false); + if (err != OK) { + ALOGE("Source start after pause in StagefrightRecorder stop failed"); + return err; + } + + mRecPaused = false; + } + if (mWriter != NULL) { err = mWriter->stop(); mWriter.clear(); @@ -1960,4 +2003,68 @@ status_t StagefrightRecorder::dump( ::write(fd, result.string(), result.size()); return OK; } + +status_t StagefrightRecorder::setSourcePause(bool pause) { + status_t err = OK; + if (pause) { + if (mVideoEncoderOMX != NULL) { + err = mVideoEncoderOMX->pause(); + if (err != OK) { + ALOGE("OMX VideoEncoder pause failed"); + return err; + } + } + if (mAudioEncoderOMX != NULL) { + err = mAudioEncoderOMX->pause(); + if (err != OK) { + ALOGE("OMX AudioEncoder pause failed"); + return err; + } + } + if (mVideoSourceNode != NULL) { + err = mVideoSourceNode->pause(); + if (err != OK) { + ALOGE("OMX VideoSourceNode pause failed"); + return err; + } + } + if (mAudioSourceNode != NULL) { + err = mAudioSourceNode->pause(); + if (err != OK) { + ALOGE("OMX AudioSourceNode pause failed"); + return err; + } + } + } else { + if (mVideoSourceNode != NULL) { + err = mVideoSourceNode->start(); + if (err != OK) { + ALOGE("OMX VideoSourceNode start failed"); + return err; + } + } + if (mAudioSourceNode != NULL) { + err = mAudioSourceNode->start(); + if (err != OK) { + ALOGE("OMX AudioSourceNode start failed"); + return err; + } + } + if (mVideoEncoderOMX != NULL) { + err = mVideoEncoderOMX->start(); + if (err != OK) { + ALOGE("OMX VideoEncoder start failed"); + return err; + } + } + if (mAudioEncoderOMX != NULL) { + err = mAudioEncoderOMX->start(); + if (err != OK) { + ALOGE("OMX AudioEncoder start failed"); + return err; + } + } + } + return err; +} } // namespace android diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index d2ff62d..26c5582 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -60,6 +60,7 @@ struct StagefrightRecorder : public MediaRecorderBase { virtual status_t setParameters(const String8& params); virtual status_t setListener(const sp<IMediaRecorderClient>& listener); virtual status_t setClientName(const String16& clientName); + virtual status_t setSourcePause(bool pause); virtual status_t prepare(); virtual status_t start(); virtual status_t pause(); @@ -80,6 +81,9 @@ protected: String16 mClientName; uid_t mClientUid; sp<MediaWriter> mWriter; + sp<MediaSource> mVideoEncoderOMX; + sp<MediaSource> mAudioEncoderOMX; + sp<MediaSource> mVideoSourceNode; int mOutputFd; sp<AudioSource> mAudioSourceNode; @@ -123,6 +127,7 @@ protected: MediaProfiles *mEncoderProfiles; bool mStarted; + bool mRecPaused; // Needed when GLFrames are encoded. // An <IGraphicBufferProducer> pointer // will be sent to the client side using which the diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp index dd9d393..1ff5d4f 100644 --- a/media/libstagefright/AudioPlayer.cpp +++ b/media/libstagefright/AudioPlayer.cpp @@ -54,6 +54,7 @@ AudioPlayer::AudioPlayer( mFinalStatus(OK), mSeekTimeUs(0), mStarted(false), + mSourcePaused(false), mIsFirstBuffer(false), mFirstBufferResult(OK), mFirstBuffer(NULL), @@ -62,7 +63,8 @@ AudioPlayer::AudioPlayer( mPinnedTimeUs(-1ll), mPlaying(false), mStartPosUs(0), - mCreateFlags(flags) { + mCreateFlags(flags), + mPauseRequired(false) { } AudioPlayer::~AudioPlayer() { @@ -82,6 +84,7 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { status_t err; if (!sourceAlreadyStarted) { + mSourcePaused = false; err = mSource->start(); if (err != OK) { @@ -257,13 +260,16 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) { mStarted = true; mPlaying = true; mPinnedTimeUs = -1ll; - + const char *componentName; + if (!(format->findCString(kKeyDecoderComponent, &componentName))) { + componentName = "none"; + } + mPauseRequired = !strncmp(componentName, "OMX.qcom.", 9); return OK; } void AudioPlayer::pause(bool playPendingSamples) { CHECK(mStarted); - if (playPendingSamples) { if (mAudioSink.get() != NULL) { mAudioSink->stop(); @@ -284,10 +290,21 @@ void AudioPlayer::pause(bool playPendingSamples) { } mPlaying = false; + CHECK(mSource != NULL); + if (mPauseRequired) { + if (mSource->pause() == OK) { + mSourcePaused = true; + } + } } status_t AudioPlayer::resume() { CHECK(mStarted); + CHECK(mSource != NULL); + if (mSourcePaused == true) { + mSourcePaused = false; + mSource->start(); + } status_t err; if (mAudioSink.get() != NULL) { @@ -349,7 +366,7 @@ void AudioPlayer::reset() { mInputBuffer->release(); mInputBuffer = NULL; } - + mSourcePaused = false; mSource->stop(); // The following hack is necessary to ensure that the OMX @@ -379,6 +396,7 @@ void AudioPlayer::reset() { mStarted = false; mPlaying = false; mStartPosUs = 0; + mPauseRequired = false; } // static @@ -549,6 +567,10 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) { mIsFirstBuffer = false; } else { err = mSource->read(&mInputBuffer, &options); + if (err == OK && mInputBuffer == NULL && mSourcePaused) { + ALOGV("mSourcePaused, return 0 from fillBuffer"); + return 0; + } } CHECK((err == OK && mInputBuffer != NULL) diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp index dc9c37b..db08476 100644 --- a/media/libstagefright/AudioSource.cpp +++ b/media/libstagefright/AudioSource.cpp @@ -58,7 +58,8 @@ AudioSource::AudioSource( mPrevSampleTimeUs(0), mFirstSampleTimeUs(-1ll), mNumFramesReceived(0), - mNumClientOwnedBuffers(0) { + mNumClientOwnedBuffers(0), + mRecPaused(false) { ALOGV("sampleRate: %u, outSampleRate: %u, channelCount: %u", sampleRate, outSampleRate, channelCount); CHECK(channelCount == 1 || channelCount == 2 || channelCount == 6); @@ -109,6 +110,11 @@ status_t AudioSource::initCheck() const { status_t AudioSource::start(MetaData *params) { Mutex::Autolock autoLock(mLock); + if (mRecPaused) { + mRecPaused = false; + return OK; + } + if (mStarted) { return UNKNOWN_ERROR; } @@ -138,6 +144,12 @@ status_t AudioSource::start(MetaData *params) { return err; } +status_t AudioSource::pause() { + ALOGV("AudioSource::Pause"); + mRecPaused = true; + return OK; +} + void AudioSource::releaseQueuedFrames_l() { ALOGV("releaseQueuedFrames_l"); List<MediaBuffer *>::iterator it; @@ -368,6 +380,14 @@ status_t AudioSource::dataCallback(const AudioRecord::Buffer& audioBuffer) { } void AudioSource::queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs) { + if (mRecPaused) { + if (!mBuffersReceived.empty()) { + releaseQueuedFrames_l(); + } + buffer->release(); + return; + } + const size_t bufferSize = buffer->range_length(); const size_t frameSize = mRecord->frameSize(); const int64_t timestampUs = diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp index 3cd0b0e..778dfa5 100644 --- a/media/libstagefright/AwesomePlayer.cpp +++ b/media/libstagefright/AwesomePlayer.cpp @@ -216,7 +216,8 @@ AwesomePlayer::AwesomePlayer() mLastVideoTimeUs(-1), mTextDriver(NULL), mOffloadAudio(false), - mAudioTearDown(false) { + mAudioTearDown(false), + mIsFirstFrameAfterResume(false) { CHECK_EQ(mClient.connect(), (status_t)OK); DataSource::RegisterDefaultSniffers(); @@ -1804,11 +1805,18 @@ void AwesomePlayer::onVideoEvent() { if (mSeeking != NO_SEEK) { ALOGV("seeking to %" PRId64 " us (%.2f secs)", mSeekTimeUs, mSeekTimeUs / 1E6); + MediaSource::ReadOptions::SeekMode seekmode = (mSeeking == SEEK_VIDEO_ONLY) + ? MediaSource::ReadOptions::SEEK_NEXT_SYNC + : MediaSource::ReadOptions::SEEK_CLOSEST_SYNC; + // Seek to the next key-frame after resume for http streaming + if (mCachedSource != NULL && mIsFirstFrameAfterResume) { + seekmode = MediaSource::ReadOptions::SEEK_NEXT_SYNC; + mIsFirstFrameAfterResume = false; + } + options.setSeekTo( mSeekTimeUs, - mSeeking == SEEK_VIDEO_ONLY - ? MediaSource::ReadOptions::SEEK_NEXT_SYNC - : MediaSource::ReadOptions::SEEK_CLOSEST_SYNC); + seekmode); } for (;;) { status_t err = mVideoSource->read(&mVideoBuffer, &options); @@ -3044,4 +3052,86 @@ void AwesomePlayer::onAudioTearDownEvent() { beginPrepareAsync_l(); } +// suspend() will release the decoders, the renderers and the buffers allocated for decoders +// Releasing decoders eliminates draining power in suspended state. +status_t AwesomePlayer::suspend() { + ALOGV("suspend()"); + Mutex::Autolock autoLock(mLock); + + // Set PAUSE to DrmManagerClient which will be set START in play_l() + if (mDecryptHandle != NULL) { + mDrmManagerClient->setPlaybackStatus(mDecryptHandle, + Playback::PAUSE, 0); + } + + cancelPlayerEvents(); + if (mQueueStarted) { + mQueue.stop(); + mQueueStarted = false; + } + + // Shutdown audio decoder first + if ((mAudioPlayer == NULL || !(mFlags & AUDIOPLAYER_STARTED)) + && mAudioSource != NULL) { + mAudioSource->stop(); + } + mAudioSource.clear(); + mOmxSource.clear(); + delete mAudioPlayer; + mAudioPlayer = NULL; + modifyFlags(AUDIO_RUNNING | AUDIOPLAYER_STARTED, CLEAR); + + // Shutdown the video decoder + mVideoRenderer.clear(); + if (mVideoSource != NULL) { + shutdownVideoDecoder_l(); + } + modifyFlags(PLAYING, CLEAR); + mVideoRenderingStarted = false; + + // Disconnect the source + if (mCachedSource != NULL) { + status_t err = mCachedSource->disconnectWhileSuspend(); + if (err != OK) { + return err; + } + } + + return OK; +} + +status_t AwesomePlayer::resume() { + ALOGV("resume()"); + Mutex::Autolock autoLock(mLock); + + // Reconnect the source + status_t err = mCachedSource->connectWhileResume(); + if (err != OK) { + return err; + } + + if (mVideoTrack != NULL && mVideoSource == NULL) { + status_t err = initVideoDecoder(); + if (err != OK) { + return err; + } + } + + if (mAudioTrack != NULL && mAudioSource == NULL) { + status_t err = initAudioDecoder(); + if (err != OK) { + return err; + } + } + + mIsFirstFrameAfterResume = true; + + if (!mQueueStarted) { + mQueue.start(); + mQueueStarted = true; + } + + return OK; +} + } // namespace android diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp index aea0f13..7652fe2 100644 --- a/media/libstagefright/CameraSource.cpp +++ b/media/libstagefright/CameraSource.cpp @@ -199,7 +199,11 @@ CameraSource::CameraSource( mNumFramesDropped(0), mNumGlitches(0), mGlitchDurationThresholdUs(200000), - mCollectStats(false) { + mCollectStats(false), + mPauseAdjTimeUs(0), + mPauseStartTimeUs(0), + mPauseEndTimeUs(0), + mRecPause(false) { mVideoSize.width = -1; mVideoSize.height = -1; @@ -669,6 +673,14 @@ status_t CameraSource::startCameraRecording() { status_t CameraSource::start(MetaData *meta) { ALOGV("start"); + if(mRecPause) { + mRecPause = false; + mPauseAdjTimeUs = mPauseEndTimeUs - mPauseStartTimeUs; + ALOGV("resume : mPause Adj / End / Start : %lld / %lld / %lld us", + mPauseAdjTimeUs, mPauseEndTimeUs, mPauseStartTimeUs); + return OK; + } + CHECK(!mStarted); if (mInitCheck != OK) { ALOGE("CameraSource is not initialized yet"); @@ -682,6 +694,10 @@ status_t CameraSource::start(MetaData *meta) { } mStartTimeUs = 0; + mRecPause = false; + mPauseAdjTimeUs = 0; + mPauseStartTimeUs = 0; + mPauseEndTimeUs = 0; mNumInputBuffers = 0; mEncoderFormat = HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED; mEncoderDataSpace = HAL_DATASPACE_BT709; @@ -715,6 +731,16 @@ status_t CameraSource::start(MetaData *meta) { return err; } +status_t CameraSource::pause() { + mRecPause = true; + mPauseStartTimeUs = mLastFrameTimestampUs; + //record the end time too, or there is a risk the end time is 0 + mPauseEndTimeUs = mLastFrameTimestampUs; + ALOGV("pause : mPauseStart %lld us, #Queued Frames : %d", + mPauseStartTimeUs, mFramesReceived.size()); + return OK; +} + void CameraSource::stopCameraRecording() { ALOGV("stopCameraRecording"); if (mCameraFlags & FLAGS_HOT_CAMERA) { @@ -916,6 +942,19 @@ void CameraSource::dataCallbackTimestamp(int64_t timestampUs, return; } + if (mRecPause == true) { + if(!mFramesReceived.empty()) { + ALOGV("releaseQueuedFrames - #Queued Frames : %d", mFramesReceived.size()); + releaseQueuedFrames(); + } + ALOGV("release One Video Frame for Pause : %lld us", timestampUs); + releaseOneRecordingFrame(data); + mPauseEndTimeUs = timestampUs; + return; + } + timestampUs -= mPauseAdjTimeUs; + ALOGV("dataCallbackTimestamp: AdjTimestamp %lld us", timestampUs); + if (mNumFramesReceived > 0) { if (timestampUs <= mLastFrameTimestampUs) { ALOGW("Dropping frame with backward timestamp %lld (last %lld)", diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp index 926e95c..53815bd 100644 --- a/media/libstagefright/CameraSourceTimeLapse.cpp +++ b/media/libstagefright/CameraSourceTimeLapse.cpp @@ -78,6 +78,7 @@ CameraSourceTimeLapse::CameraSourceTimeLapse( storeMetaDataInVideoBuffers), mTimeBetweenTimeLapseVideoFramesUs(1E6/videoFrameRate), mLastTimeLapseFrameRealTimestampUs(0), + mLastTimeLapseFrameTimeStampUs(0), mSkipCurrentFrame(false) { mTimeBetweenFrameCaptureUs = timeBetweenFrameCaptureUs; @@ -252,6 +253,7 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) { ALOGV("dataCallbackTimestamp timelapse: initial frame"); mLastTimeLapseFrameRealTimestampUs = *timestampUs; + mLastTimeLapseFrameTimeStampUs = *timestampUs; return false; } @@ -263,8 +265,10 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) { if (mForceRead) { ALOGV("dataCallbackTimestamp timelapse: forced read"); mForceRead = false; + mLastTimeLapseFrameRealTimestampUs = *timestampUs; *timestampUs = - mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs; + mLastTimeLapseFrameTimeStampUs + mTimeBetweenTimeLapseVideoFramesUs; + mLastTimeLapseFrameTimeStampUs = *timestampUs; // Really make sure that this video recording frame will not be dropped. if (*timestampUs < mStartTimeUs) { @@ -294,7 +298,8 @@ bool CameraSourceTimeLapse::skipFrameAndModifyTimeStamp(int64_t *timestampUs) { ALOGV("dataCallbackTimestamp timelapse: got timelapse frame"); mLastTimeLapseFrameRealTimestampUs = *timestampUs; - *timestampUs = mLastFrameTimestampUs + mTimeBetweenTimeLapseVideoFramesUs; + *timestampUs = mLastTimeLapseFrameTimeStampUs + mTimeBetweenTimeLapseVideoFramesUs; + mLastTimeLapseFrameTimeStampUs = *timestampUs; // Update start-time once the captured-time reaches the expected start-time. // Not doing so will result in CameraSource always dropping frames since // updated-timestamp will never intersect start-timestamp diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index cb9df29..926833f 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -2341,19 +2341,6 @@ status_t MPEG4Writer::Track::threadEntry() { #if 0 if (mResumed) { - int64_t durExcludingEarlierPausesUs = timestampUs - previousPausedDurationUs; - if (WARN_UNLESS(durExcludingEarlierPausesUs >= 0ll, "for %s track", trackName)) { - copy->release(); - return ERROR_MALFORMED; - } - - int64_t pausedDurationUs = durExcludingEarlierPausesUs - mTrackDurationUs; - if (WARN_UNLESS(pausedDurationUs >= lastDurationUs, "for %s track", trackName)) { - copy->release(); - return ERROR_MALFORMED; - } - - previousPausedDurationUs += pausedDurationUs - lastDurationUs; mResumed = false; } #endif diff --git a/media/libstagefright/NuCachedSource2.cpp b/media/libstagefright/NuCachedSource2.cpp index d6255d6..f72acf7 100644 --- a/media/libstagefright/NuCachedSource2.cpp +++ b/media/libstagefright/NuCachedSource2.cpp @@ -197,7 +197,8 @@ NuCachedSource2::NuCachedSource2( mHighwaterThresholdBytes(kDefaultHighWaterThreshold), mLowwaterThresholdBytes(kDefaultLowWaterThreshold), mKeepAliveIntervalUs(kDefaultKeepAliveIntervalUs), - mDisconnectAtHighwatermark(disconnectAtHighwatermark) { + mDisconnectAtHighwatermark(disconnectAtHighwatermark), + mSuspended(false) { // We are NOT going to support disconnect-at-highwatermark indefinitely // and we are not guaranteeing support for client-specified cache // parameters. Both of these are temporary measures to solve a specific @@ -332,7 +333,7 @@ void NuCachedSource2::fetchInternal() { } } - if (reconnect) { + if (reconnect && !mSuspended) { status_t err = mSource->reconnectAtOffset(mCacheOffset + mCache->totalSize()); @@ -442,6 +443,13 @@ void NuCachedSource2::onFetch() { delayUs = 100000ll; } + if (mSuspended) { + static_cast<HTTPBase *>(mSource.get())->disconnect(); + mFinalStatus = -EAGAIN; + return; + } + + (new AMessage(kWhatFetchMore, mReflector))->post(delayUs); } @@ -771,4 +779,25 @@ void NuCachedSource2::RemoveCacheSpecificHeaders( } } +status_t NuCachedSource2::disconnectWhileSuspend() { + if (mSource != NULL) { + static_cast<HTTPBase *>(mSource.get())->disconnect(); + mFinalStatus = -EAGAIN; + mSuspended = true; + } else { + return ERROR_UNSUPPORTED; + } + + return OK; +} + +status_t NuCachedSource2::connectWhileResume() { + mSuspended = false; + + // Begin to connect again and fetch more data + (new AMessage(kWhatFetchMore, mReflector))->post(); + + return OK; +} + } // namespace android diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp index b2bfceb..3ec02d4 100644 --- a/media/libstagefright/OMXCodec.cpp +++ b/media/libstagefright/OMXCodec.cpp @@ -1614,6 +1614,8 @@ bool OMXCodec::isIntermediateState(State state) { return state == LOADED_TO_IDLE || state == IDLE_TO_EXECUTING || state == EXECUTING_TO_IDLE + || state == PAUSING + || state == FLUSHING || state == IDLE_TO_LOADED || state == RECONFIGURING; } @@ -2665,6 +2667,14 @@ void OMXCodec::onStateChange(OMX_STATETYPE newState) { break; } + case OMX_StatePause: + { + CODEC_LOGV("Now paused."); + CHECK_EQ((int)mState, (int)PAUSING); + setState(PAUSED); + break; + } + case OMX_StateInvalid: { setState(ERROR); @@ -2780,7 +2790,7 @@ void OMXCodec::onPortSettingsChanged(OMX_U32 portIndex) { bool OMXCodec::flushPortAsync(OMX_U32 portIndex) { CHECK(mState == EXECUTING || mState == RECONFIGURING - || mState == EXECUTING_TO_IDLE); + || mState == EXECUTING_TO_IDLE || mState == FLUSHING); CODEC_LOGV("flushPortAsync(%u): we own %zu out of %zu buffers already.", portIndex, countBuffersWeOwn(mPortBuffers[portIndex]), @@ -2830,7 +2840,7 @@ status_t OMXCodec::enablePortAsync(OMX_U32 portIndex) { } void OMXCodec::fillOutputBuffers() { - CHECK_EQ((int)mState, (int)EXECUTING); + CHECK(mState == EXECUTING || mState == FLUSHING); // This is a workaround for some decoders not properly reporting // end-of-output-stream. If we own all input buffers and also own @@ -2857,7 +2867,7 @@ void OMXCodec::fillOutputBuffers() { } void OMXCodec::drainInputBuffers() { - CHECK(mState == EXECUTING || mState == RECONFIGURING); + CHECK(mState == EXECUTING || mState == RECONFIGURING || mState == FLUSHING); if (mFlags & kUseSecureInputBuffers) { Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput]; @@ -3604,6 +3614,11 @@ void OMXCodec::clearCodecSpecificData() { status_t OMXCodec::start(MetaData *meta) { Mutex::Autolock autoLock(mLock); + if (mPaused) { + status_t err = resumeLocked(true); + return err; + } + if (mState != LOADED) { CODEC_LOGE("called start in the unexpected state: %d", mState); return UNKNOWN_ERROR; @@ -3714,6 +3729,7 @@ status_t OMXCodec::stopOmxComponent_l() { isError = true; } + case PAUSED: case EXECUTING: { setState(EXECUTING_TO_IDLE); @@ -3785,6 +3801,14 @@ status_t OMXCodec::read( Mutex::Autolock autoLock(mLock); + if (mPaused) { + err = resumeLocked(false); + if(err != OK) { + CODEC_LOGE("Failed to restart codec err= %d", err); + return err; + } + } + if (mState != EXECUTING && mState != RECONFIGURING) { return UNKNOWN_ERROR; } @@ -3841,6 +3865,8 @@ status_t OMXCodec::read( mFilledBuffers.clear(); CHECK_EQ((int)mState, (int)EXECUTING); + //DSP supports flushing of ports simultaneously. Flushing individual port is not supported. + setState(FLUSHING); bool emulateInputFlushCompletion = !flushPortAsync(kPortIndexInput); bool emulateOutputFlushCompletion = !flushPortAsync(kPortIndexOutput); @@ -3870,6 +3896,11 @@ status_t OMXCodec::read( return UNKNOWN_ERROR; } + if (seeking) { + CHECK_EQ((int)mState, (int)FLUSHING); + setState(EXECUTING); + } + if (mFilledBuffers.empty()) { return mSignalledEOS ? mFinalStatus : ERROR_END_OF_STREAM; } @@ -4303,11 +4334,60 @@ void OMXCodec::initOutputFormat(const sp<MetaData> &inputFormat) { } status_t OMXCodec::pause() { - Mutex::Autolock autoLock(mLock); + CODEC_LOGV("pause mState=%d", mState); + + Mutex::Autolock autoLock(mLock); + + if (mState != EXECUTING) { + return UNKNOWN_ERROR; + } + + while (isIntermediateState(mState)) { + mAsyncCompletion.wait(mLock); + } + if (!strncmp(mComponentName, "OMX.qcom.", 9)) { + status_t err = mOMX->sendCommand(mNode, + OMX_CommandStateSet, OMX_StatePause); + CHECK_EQ(err, (status_t)OK); + setState(PAUSING); + + mPaused = true; + while (mState != PAUSED && mState != ERROR) { + mAsyncCompletion.wait(mLock); + } + return mState == ERROR ? UNKNOWN_ERROR : OK; + } else { + mPaused = true; + return OK; + } - mPaused = true; +} - return OK; +status_t OMXCodec::resumeLocked(bool drainInputBuf) { + CODEC_LOGV("resume mState=%d", mState); + + if (!strncmp(mComponentName, "OMX.qcom.", 9)) { + while (isIntermediateState(mState)) { + mAsyncCompletion.wait(mLock); + } + CHECK_EQ(mState, (status_t)PAUSED); + status_t err = mOMX->sendCommand(mNode, + OMX_CommandStateSet, OMX_StateExecuting); + CHECK_EQ(err, (status_t)OK); + setState(IDLE_TO_EXECUTING); + mPaused = false; + while (mState != EXECUTING && mState != ERROR) { + mAsyncCompletion.wait(mLock); + } + if(drainInputBuf) + drainInputBuffers(); + return mState == ERROR ? UNKNOWN_ERROR : OK; + } else { // SW Codec + mPaused = false; + if(drainInputBuf) + drainInputBuffers(); + return OK; + } } //////////////////////////////////////////////////////////////////////////////// diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h index c72f9f6..1a8e6c8 100644 --- a/media/libstagefright/include/AwesomePlayer.h +++ b/media/libstagefright/include/AwesomePlayer.h @@ -109,6 +109,9 @@ struct AwesomePlayer { void postAudioTearDown(); status_t dump(int fd, const Vector<String16> &args) const; + status_t suspend(); + status_t resume(); + private: friend struct AwesomeEvent; friend struct PreviewPlayer; @@ -356,6 +359,8 @@ private: bool mAudioTearDownWasPlaying; int64_t mAudioTearDownPosition; + bool mIsFirstFrameAfterResume; + status_t setVideoScalingMode(int32_t mode); status_t setVideoScalingMode_l(int32_t mode); status_t getTrackInfo(Parcel* reply) const; diff --git a/media/libstagefright/include/NuCachedSource2.h b/media/libstagefright/include/NuCachedSource2.h index afa91ae..1f282ca 100644 --- a/media/libstagefright/include/NuCachedSource2.h +++ b/media/libstagefright/include/NuCachedSource2.h @@ -66,6 +66,9 @@ struct NuCachedSource2 : public DataSource { String8 *cacheConfig, bool *disconnectAtHighwatermark); + virtual status_t disconnectWhileSuspend(); + virtual status_t connectWhileResume(); + protected: virtual ~NuCachedSource2(); @@ -123,6 +126,8 @@ protected: bool mDisconnectAtHighwatermark; + bool mSuspended; + void onMessageReceived(const sp<AMessage> &msg); void onFetch(); void onRead(const sp<AMessage> &msg); |