Merge "Camera2: improve ZSL candidate selection logic" into lmp-dev
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index a3cc396..72e51f9 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -430,7 +430,7 @@
* - NO_ERROR: successful operation
* - BAD_VALUE: position is NULL
*/
- status_t getPosition(uint32_t *position) const;
+ status_t getPosition(uint32_t *position);
/* For static buffer mode only, this returns the current playback position in frames
* relative to start of buffer. It is analogous to the position units used by
@@ -581,6 +581,7 @@
* if you need a high resolution mapping between frame position and presentation time,
* consider implementing that at application level, based on the low resolution timestamps.
* Returns NO_ERROR if timestamp is valid.
+ * The timestamp parameter is undefined on return, if status is not NO_ERROR.
*/
status_t getTimestamp(AudioTimestamp& timestamp);
@@ -639,7 +640,7 @@
// caller must hold lock on mLock for all _l methods
- status_t createTrack_l(size_t epoch);
+ status_t createTrack_l();
// can only be called when mState != STATE_ACTIVE
void flush_l();
@@ -659,6 +660,9 @@
bool isDirect_l() const
{ return (mFlags & AUDIO_OUTPUT_FLAG_DIRECT) != 0; }
+ // increment mPosition by the delta of mServer, and return new value of mPosition
+ uint32_t updateAndGetPosition_l();
+
// Next 4 fields may be changed if IAudioTrack is re-created, but always != 0
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
@@ -731,6 +735,18 @@
bool mMarkerReached;
uint32_t mNewPosition; // in frames
uint32_t mUpdatePeriod; // in frames, zero means no EVENT_NEW_POS
+ uint32_t mServer; // in frames, last known mProxy->getPosition()
+ // which is count of frames consumed by server,
+ // reset by new IAudioTrack,
+ // whether it is reset by stop() is TBD
+ uint32_t mPosition; // in frames, like mServer except continues
+ // monotonically after new IAudioTrack,
+ // and could be easily widened to uint64_t
+ uint32_t mReleased; // in frames, count of frames released to server
+ // but not necessarily consumed by server,
+ // reset by stop() but continues monotonically
+ // after new IAudioTrack to restore mPosition,
+ // and could be easily widened to uint64_t
audio_output_flags_t mFlags;
// const after set(), except for bits AUDIO_OUTPUT_FLAG_FAST and AUDIO_OUTPUT_FLAG_OFFLOAD.
diff --git a/include/media/IAudioTrack.h b/include/media/IAudioTrack.h
index 5c8a484..619ac78 100644
--- a/include/media/IAudioTrack.h
+++ b/include/media/IAudioTrack.h
@@ -88,7 +88,7 @@
/* Send parameters to the audio hardware */
virtual status_t setParameters(const String8& keyValuePairs) = 0;
- /* Return NO_ERROR if timestamp is valid */
+ /* Return NO_ERROR if timestamp is valid. timestamp is undefined otherwise. */
virtual status_t getTimestamp(AudioTimestamp& timestamp) = 0;
/* Signal the playback thread for a change in control block */
diff --git a/include/media/nbaio/NBAIO.h b/include/media/nbaio/NBAIO.h
index be0c15b..d422576 100644
--- a/include/media/nbaio/NBAIO.h
+++ b/include/media/nbaio/NBAIO.h
@@ -227,7 +227,7 @@
// Returns NO_ERROR if a timestamp is available. The timestamp includes the total number
// of frames presented to an external observer, together with the value of CLOCK_MONOTONIC
- // as of this presentation count.
+ // as of this presentation count. The timestamp parameter is undefined if error is returned.
virtual status_t getTimestamp(AudioTimestamp& timestamp) { return INVALID_OPERATION; }
protected:
diff --git a/include/media/stagefright/foundation/AMessage.h b/include/media/stagefright/foundation/AMessage.h
index 5846d6b..a9e235b 100644
--- a/include/media/stagefright/foundation/AMessage.h
+++ b/include/media/stagefright/foundation/AMessage.h
@@ -137,7 +137,9 @@
Rect rectValue;
} u;
const char *mName;
+ size_t mNameLength;
Type mType;
+ void setName(const char *name, size_t len);
};
enum {
@@ -147,12 +149,14 @@
size_t mNumItems;
Item *allocateItem(const char *name);
- void freeItem(Item *item);
+ void freeItemValue(Item *item);
const Item *findItem(const char *name, Type type) const;
void setObjectInternal(
const char *name, const sp<RefBase> &obj, Type type);
+ size_t findItemIndex(const char *name, size_t len) const;
+
DISALLOW_EVIL_CONSTRUCTORS(AMessage);
};
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index d87e6f5..ff7da83 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -398,7 +398,7 @@
}
// create the IAudioTrack
- status = createTrack_l(0 /*epoch*/);
+ status = createTrack_l();
if (status != NO_ERROR) {
if (mAudioTrackThread != 0) {
@@ -417,6 +417,9 @@
mMarkerReached = false;
mNewPosition = 0;
mUpdatePeriod = 0;
+ mServer = 0;
+ mPosition = 0;
+ mReleased = 0;
AudioSystem::acquireAudioSessionId(mSessionId, mClientPid);
mSequence = 1;
mObservedSequence = mSequence;
@@ -443,14 +446,16 @@
} else {
mState = STATE_ACTIVE;
}
+ (void) updateAndGetPosition_l();
if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
// reset current position as seen by client to 0
- mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
+ mPosition = 0;
+ mReleased = 0;
// force refresh of remaining frames by processAudioBuffer() as last
// write before stop could be partial.
mRefreshRemaining = true;
}
- mNewPosition = mProxy->getPosition() + mUpdatePeriod;
+ mNewPosition = mPosition + mUpdatePeriod;
int32_t flags = android_atomic_and(~CBLK_DISABLED, &mCblk->mFlags);
sp<AudioTrackThread> t = mAudioTrackThread;
@@ -709,7 +714,7 @@
{
// FIXME If setting a loop also sets position to start of loop, then
// this is correct. Otherwise it should be removed.
- mNewPosition = mProxy->getPosition() + mUpdatePeriod;
+ mNewPosition = updateAndGetPosition_l() + mUpdatePeriod;
mLoopPeriod = loopCount != 0 ? loopEnd - loopStart : 0;
mStaticProxy->setLoop(loopStart, loopEnd, loopCount);
}
@@ -751,7 +756,7 @@
}
AutoMutex lock(mLock);
- mNewPosition = mProxy->getPosition() + updatePeriod;
+ mNewPosition = updateAndGetPosition_l() + updatePeriod;
mUpdatePeriod = updatePeriod;
return NO_ERROR;
@@ -791,7 +796,7 @@
if (mState == STATE_ACTIVE) {
return INVALID_OPERATION;
}
- mNewPosition = mProxy->getPosition() + mUpdatePeriod;
+ mNewPosition = updateAndGetPosition_l() + mUpdatePeriod;
mLoopPeriod = 0;
// FIXME Check whether loops and setting position are incompatible in old code.
// If we use setLoop for both purposes we lose the capability to set the position while looping.
@@ -800,7 +805,7 @@
return NO_ERROR;
}
-status_t AudioTrack::getPosition(uint32_t *position) const
+status_t AudioTrack::getPosition(uint32_t *position)
{
if (position == NULL) {
return BAD_VALUE;
@@ -823,8 +828,8 @@
*position = dspFrames;
} else {
// IAudioTrack::stop() isn't synchronous; we don't know when presentation completes
- *position = (mState == STATE_STOPPED || mState == STATE_FLUSHED) ? 0 :
- mProxy->getPosition();
+ *position = (mState == STATE_STOPPED || mState == STATE_FLUSHED) ?
+ 0 : updateAndGetPosition_l();
}
return NO_ERROR;
}
@@ -881,7 +886,7 @@
// -------------------------------------------------------------------------
// must be called with mLock held
-status_t AudioTrack::createTrack_l(size_t epoch)
+status_t AudioTrack::createTrack_l()
{
status_t status;
const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
@@ -1184,7 +1189,6 @@
mProxy->setVolumeLR(GAIN_MINIFLOAT_PACKED_UNITY);
mProxy->setSendLevel(mSendLevel);
mProxy->setSampleRate(mSampleRate);
- mProxy->setEpoch(epoch);
mProxy->setMinimum(mNotificationFramesAct);
mDeathNotifier = new DeathNotifier(this);
@@ -1319,6 +1323,7 @@
buffer.mRaw = audioBuffer->raw;
AutoMutex lock(mLock);
+ mReleased += stepCount;
mInUnderrun = false;
mProxy->releaseBuffer(&buffer);
@@ -1531,7 +1536,7 @@
}
// Get current position of server
- size_t position = mProxy->getPosition();
+ size_t position = updateAndGetPosition_l();
// Manage marker callback
bool markerReached = false;
@@ -1796,14 +1801,18 @@
return DEAD_OBJECT;
}
- // if the new IAudioTrack is created, createTrack_l() will modify the
+ // save the old static buffer position
+ size_t bufferPosition = mStaticProxy != NULL ? mStaticProxy->getBufferPosition() : 0;
+
+ // If a new IAudioTrack is successfully created, createTrack_l() will modify the
// following member variables: mAudioTrack, mCblkMemory and mCblk.
- // It will also delete the strong references on previous IAudioTrack and IMemory
+ // It will also delete the strong references on previous IAudioTrack and IMemory.
+ // If a new IAudioTrack cannot be created, the previous (dead) instance will be left intact.
+ result = createTrack_l();
// take the frames that will be lost by track recreation into account in saved position
- size_t position = mProxy->getPosition() + mProxy->getFramesFilled();
- size_t bufferPosition = mStaticProxy != NULL ? mStaticProxy->getBufferPosition() : 0;
- result = createTrack_l(position /*epoch*/);
+ (void) updateAndGetPosition_l();
+ mPosition = mReleased;
if (result == NO_ERROR) {
// continue playback from last known position, but
@@ -1838,6 +1847,27 @@
return result;
}
+uint32_t AudioTrack::updateAndGetPosition_l()
+{
+ // This is the sole place to read server consumed frames
+ uint32_t newServer = mProxy->getPosition();
+ int32_t delta = newServer - mServer;
+ mServer = newServer;
+ // TODO There is controversy about whether there can be "negative jitter" in server position.
+ // This should be investigated further, and if possible, it should be addressed.
+ // A more definite failure mode is infrequent polling by client.
+ // One could call (void)getPosition_l() in releaseBuffer(),
+ // so mReleased and mPosition are always lock-step as best possible.
+ // That should ensure delta never goes negative for infrequent polling
+ // unless the server has more than 2^31 frames in its buffer,
+ // in which case the use of uint32_t for these counters has bigger issues.
+ if (delta < 0) {
+ ALOGE("detected illegal retrograde motion by the server: mServer advanced by %d", delta);
+ delta = 0;
+ }
+ return mPosition += (uint32_t) delta;
+}
+
status_t AudioTrack::setParameters(const String8& keyValuePairs)
{
AutoMutex lock(mLock);
@@ -1854,9 +1884,34 @@
if (mState != STATE_ACTIVE && mState != STATE_PAUSED) {
return INVALID_OPERATION;
}
+ // The presented frame count must always lag behind the consumed frame count.
+ // To avoid a race, read the presented frames first. This ensures that presented <= consumed.
status_t status = mAudioTrack->getTimestamp(timestamp);
if (status == NO_ERROR) {
- timestamp.mPosition += mProxy->getEpoch();
+ // Update the mapping between local consumed (mPosition) and server consumed (mServer)
+ (void) updateAndGetPosition_l();
+ // Server consumed (mServer) and presented both use the same server time base,
+ // and server consumed is always >= presented.
+ // The delta between these represents the number of frames in the buffer pipeline.
+ // If this delta between these is greater than the client position, it means that
+ // actually presented is still stuck at the starting line (figuratively speaking),
+ // waiting for the first frame to go by. So we can't report a valid timestamp yet.
+ if ((uint32_t) (mServer - timestamp.mPosition) > mPosition) {
+ return INVALID_OPERATION;
+ }
+ // Convert timestamp position from server time base to client time base.
+ // TODO The following code should work OK now because timestamp.mPosition is 32-bit.
+ // But if we change it to 64-bit then this could fail.
+ // If (mPosition - mServer) can be negative then should use:
+ // (int32_t)(mPosition - mServer)
+ timestamp.mPosition += mPosition - mServer;
+ // Immediately after a call to getPosition_l(), mPosition and
+ // mServer both represent the same frame position. mPosition is
+ // in client's point of view, and mServer is in server's point of
+ // view. So the difference between them is the "fudge factor"
+ // between client and server views due to stop() and/or new
+ // IAudioTrack. And timestamp.mPosition is initially in server's
+ // point of view, so we need to apply the same fudge factor to it.
}
return status;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index e2bcb1e..b904aa8 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -183,11 +183,7 @@
return BAD_VALUE;
}
- if (ve == VIDEO_ENCODER_DEFAULT) {
- mVideoEncoder = VIDEO_ENCODER_H263;
- } else {
- mVideoEncoder = ve;
- }
+ mVideoEncoder = ve;
return OK;
}
@@ -1033,6 +1029,7 @@
if (mAudioSource != AUDIO_SOURCE_CNT) {
source = createAudioSource();
} else {
+ setDefaultVideoEncoderIfNecessary();
sp<MediaSource> mediaSource;
status_t err = setupMediaSource(&mediaSource);
@@ -1074,6 +1071,7 @@
if (mVideoSource < VIDEO_SOURCE_LIST_END) {
if (mVideoEncoder != VIDEO_ENCODER_H264) {
+ ALOGE("MPEG2TS recording only supports H.264 encoding!");
return ERROR_UNSUPPORTED;
}
@@ -1108,6 +1106,12 @@
void StagefrightRecorder::clipVideoFrameRate() {
ALOGV("clipVideoFrameRate: encoder %d", mVideoEncoder);
+ if (mFrameRate == -1) {
+ mFrameRate = mEncoderProfiles->getCamcorderProfileParamByName(
+ "vid.fps", mCameraId, CAMCORDER_QUALITY_LOW);
+ ALOGW("Using default video fps %d", mFrameRate);
+ }
+
int minFrameRate = mEncoderProfiles->getVideoEncoderParamByName(
"enc.vid.fps.min", mVideoEncoder);
int maxFrameRate = mEncoderProfiles->getVideoEncoderParamByName(
@@ -1243,6 +1247,27 @@
}
}
+void StagefrightRecorder::setDefaultVideoEncoderIfNecessary() {
+ if (mVideoEncoder == VIDEO_ENCODER_DEFAULT) {
+ if (mOutputFormat == OUTPUT_FORMAT_WEBM) {
+ // default to VP8 for WEBM recording
+ mVideoEncoder = VIDEO_ENCODER_VP8;
+ } else {
+ // pick the default encoder for CAMCORDER_QUALITY_LOW
+ int videoCodec = mEncoderProfiles->getCamcorderProfileParamByName(
+ "vid.codec", mCameraId, CAMCORDER_QUALITY_LOW);
+
+ if (videoCodec > VIDEO_ENCODER_DEFAULT &&
+ videoCodec < VIDEO_ENCODER_LIST_END) {
+ mVideoEncoder = (video_encoder)videoCodec;
+ } else {
+ // default to H.264 if camcorder profile not available
+ mVideoEncoder = VIDEO_ENCODER_H264;
+ }
+ }
+ }
+}
+
status_t StagefrightRecorder::checkAudioEncoderCapabilities() {
clipAudioBitRate();
clipAudioSampleRate();
@@ -1562,6 +1587,7 @@
}
if (mVideoSource < VIDEO_SOURCE_LIST_END) {
+ setDefaultVideoEncoderIfNecessary();
sp<MediaSource> mediaSource;
err = setupMediaSource(&mediaSource);
@@ -1721,7 +1747,7 @@
// Default parameters
mOutputFormat = OUTPUT_FORMAT_THREE_GPP;
mAudioEncoder = AUDIO_ENCODER_AMR_NB;
- mVideoEncoder = VIDEO_ENCODER_H263;
+ mVideoEncoder = VIDEO_ENCODER_DEFAULT;
mVideoWidth = 176;
mVideoHeight = 144;
mFrameRate = -1;
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 9062f30..54c38d3 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -178,6 +178,7 @@
void clipAudioSampleRate();
void clipNumberOfAudioChannels();
void setDefaultProfileIfNecessary();
+ void setDefaultVideoEncoderIfNecessary();
StagefrightRecorder(const StagefrightRecorder &);
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index 8e1987a..ec1a9a0 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -54,7 +54,8 @@
mDrmManagerClient(NULL),
mMetaDataSize(-1ll),
mBitrate(-1ll),
- mPollBufferingGeneration(0) {
+ mPollBufferingGeneration(0),
+ mPendingReadBufferTypes(0) {
resetDataSource();
DataSource::RegisterDefaultSniffers();
}
@@ -1148,15 +1149,27 @@
}
void NuPlayer::GenericSource::postReadBuffer(media_track_type trackType) {
- sp<AMessage> msg = new AMessage(kWhatReadBuffer, id());
- msg->setInt32("trackType", trackType);
- msg->post();
+ Mutex::Autolock _l(mReadBufferLock);
+
+ if ((mPendingReadBufferTypes & (1 << trackType)) == 0) {
+ mPendingReadBufferTypes |= (1 << trackType);
+ sp<AMessage> msg = new AMessage(kWhatReadBuffer, id());
+ msg->setInt32("trackType", trackType);
+ msg->post();
+ }
}
void NuPlayer::GenericSource::onReadBuffer(sp<AMessage> msg) {
int32_t tmpType;
CHECK(msg->findInt32("trackType", &tmpType));
media_track_type trackType = (media_track_type)tmpType;
+ {
+ // only protect the variable change, as readBuffer may
+ // take considerable time. This may result in one extra
+ // read being processed, but that is benign.
+ Mutex::Autolock _l(mReadBufferLock);
+ mPendingReadBufferTypes &= ~(1 << trackType);
+ }
readBuffer(trackType);
}
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.h b/media/libmediaplayerservice/nuplayer/GenericSource.h
index 50ff98a..c70c48e 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.h
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.h
@@ -131,6 +131,8 @@
off64_t mMetaDataSize;
int64_t mBitrate;
int32_t mPollBufferingGeneration;
+ uint32_t mPendingReadBufferTypes;
+ mutable Mutex mReadBufferLock;
sp<ALooper> mLooper;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 8ce7baf..163a0b5 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -595,7 +595,18 @@
{
if (!isStaleReply(msg)) {
onInputBufferFilled(msg);
+ } else {
+ /* release any MediaBuffer passed in the stale buffer */
+ sp<ABuffer> buffer;
+ MediaBuffer *mediaBuffer = NULL;
+ if (msg->findBuffer("buffer", &buffer) &&
+ buffer->meta()->findPointer(
+ "mediaBuffer", (void **)&mediaBuffer) &&
+ mediaBuffer != NULL) {
+ mediaBuffer->release();
+ }
}
+
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index c9be0dd..ab7906a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -115,9 +115,12 @@
notify->post();
mPendingBuffers++;
- sp<AMessage> message = new AMessage(kWhatRequestABuffer, id());
- message->setInt32("generation", mBufferGeneration);
- message->post();
+ // pending buffers will already result in requestABuffer
+ if (mPendingBuffers < kMaxPendingBuffers) {
+ sp<AMessage> message = new AMessage(kWhatRequestABuffer, id());
+ message->setInt32("generation", mBufferGeneration);
+ message->post();
+ }
return;
}
@@ -155,9 +158,7 @@
void NuPlayer::DecoderPassThrough::onBufferConsumed(int32_t size) {
mPendingBuffers--;
mCachedBytes -= size;
- sp<AMessage> message = new AMessage(kWhatRequestABuffer, id());
- message->setInt32("generation", mBufferGeneration);
- message->post();
+ requestABuffer();
}
void NuPlayer::DecoderPassThrough::onFlush() {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 35cd514..7dd54c1 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -626,12 +626,15 @@
switch (msg) {
case MEDIA_PLAYBACK_COMPLETE:
{
- if (mLooping && mState != STATE_RESET_IN_PROGRESS) {
- mPlayer->seekToAsync(0);
- break;
+ if (mState != STATE_RESET_IN_PROGRESS) {
+ if (mLooping) {
+ mPlayer->seekToAsync(0);
+ break;
+ }
+
+ mPlayer->pause();
+ mState = STATE_PAUSED;
}
- mPlayer->pause();
- mState = STATE_PAUSED;
// fall through
}
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index fc2dd30..0bfc6e4 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -270,7 +270,20 @@
}
sp<AMessage> response;
- return PostAndAwaitResponse(msg, &response);
+ status_t err = PostAndAwaitResponse(msg, &response);
+
+ if (err != OK && err != INVALID_OPERATION) {
+ // MediaCodec now set state to UNINITIALIZED upon any fatal error.
+ // To maintain backward-compatibility, do a reset() to put codec
+ // back into INITIALIZED state.
+ // But don't reset if the err is INVALID_OPERATION, which means
+ // the configure failure is due to wrong state.
+
+ ALOGE("configure failed with err 0x%08x, resetting...", err);
+ reset();
+ }
+
+ return err;
}
status_t MediaCodec::createInputSurface(
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 6dd9b92..8b4dd6f 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-//#define LOG_NDEBUG 0
#define LOG_TAG "SoftAAC2"
+//#define LOG_NDEBUG 0
#include <utils/Log.h>
#include "SoftAAC2.h"
@@ -68,6 +68,7 @@
mOutputBufferCount(0),
mSignalledError(false),
mLastInHeader(NULL),
+ mCurrentInputTime(0),
mOutputPortSettingsChange(NONE) {
initPorts();
CHECK_EQ(initDecoder(), (status_t)OK);
@@ -609,24 +610,9 @@
notify(OMX_EventError, OMX_ErrorStreamCorrupt, ERROR_MALFORMED, NULL);
return;
}
-
- // insert buffer size and time stamp
- mBufferSizes.add(inBufferLength[0]);
- if (mLastInHeader != inHeader) {
- mBufferTimestamps.add(inHeader->nTimeStamp);
- mLastInHeader = inHeader;
- } else {
- int64_t currentTime = mBufferTimestamps.top();
- currentTime += mStreamInfo->aacSamplesPerFrame *
- 1000000ll / mStreamInfo->sampleRate;
- mBufferTimestamps.add(currentTime);
- }
} else {
inBuffer[0] = inHeader->pBuffer + inHeader->nOffset;
inBufferLength[0] = inHeader->nFilledLen;
- mLastInHeader = inHeader;
- mBufferTimestamps.add(inHeader->nTimeStamp);
- mBufferSizes.add(inHeader->nFilledLen);
}
// Fill and decode
@@ -635,130 +621,136 @@
INT prevSampleRate = mStreamInfo->sampleRate;
INT prevNumChannels = mStreamInfo->numChannels;
+ if (inHeader != mLastInHeader) {
+ mLastInHeader = inHeader;
+ mCurrentInputTime = inHeader->nTimeStamp;
+ } else {
+ if (mStreamInfo->sampleRate) {
+ mCurrentInputTime += mStreamInfo->aacSamplesPerFrame *
+ 1000000ll / mStreamInfo->sampleRate;
+ } else {
+ ALOGW("no sample rate yet");
+ }
+ }
+ mAnchorTimes.add(mCurrentInputTime);
aacDecoder_Fill(mAACDecoder,
inBuffer,
inBufferLength,
bytesValid);
- // run DRC check
- mDrcWrap.submitStreamData(mStreamInfo);
- mDrcWrap.update();
+ // run DRC check
+ mDrcWrap.submitStreamData(mStreamInfo);
+ mDrcWrap.update();
- UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
- inHeader->nFilledLen -= inBufferUsedLength;
- inHeader->nOffset += inBufferUsedLength;
+ AAC_DECODER_ERROR decoderErr =
+ aacDecoder_DecodeFrame(mAACDecoder,
+ tmpOutBuffer,
+ 2048 * MAX_CHANNEL_COUNT,
+ 0 /* flags */);
- AAC_DECODER_ERROR decoderErr;
- do {
- int numconsumed = mStreamInfo->numTotalBytes + mStreamInfo->numBadBytes;
- decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
- tmpOutBuffer,
- 2048 * MAX_CHANNEL_COUNT,
- 0 /* flags */);
+ if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+ }
- numconsumed = (mStreamInfo->numTotalBytes + mStreamInfo->numBadBytes) - numconsumed;
- if (numconsumed != 0) {
- mDecodedSizes.add(numconsumed);
- }
+ if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
+ ALOGE("AAC_DEC_NOT_ENOUGH_BITS should never happen");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
- if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
- break;
- }
+ if (bytesValid[0] != 0) {
+ ALOGE("bytesValid[0] != 0 should never happen");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
+ }
- if (decoderErr != AAC_DEC_OK) {
- ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
- }
+ size_t numOutBytes =
+ mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
- if (bytesValid[0] != 0) {
- ALOGE("bytesValid[0] != 0 should never happen");
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
- return;
- }
-
- size_t numOutBytes =
- mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
-
- if (decoderErr == AAC_DEC_OK) {
- if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
- mStreamInfo->frameSize * mStreamInfo->numChannels)) {
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
- return;
- }
- } else {
- ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
-
- memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
-
- if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
- mStreamInfo->frameSize * mStreamInfo->numChannels)) {
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
- return;
- }
-
- // Discard input buffer.
- inHeader->nFilledLen = 0;
-
- aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
-
- // fall through
- }
-
- /*
- * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
- * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
- * rate system and the sampling rate in the final output is actually
- * doubled compared with the core AAC decoder sampling rate.
- *
- * Explicit signalling is done by explicitly defining SBR audio object
- * type in the bitstream. Implicit signalling is done by embedding
- * SBR content in AAC extension payload specific to SBR, and hence
- * requires an AAC decoder to perform pre-checks on actual audio frames.
- *
- * Thus, we could not say for sure whether a stream is
- * AAC+/eAAC+ until the first data frame is decoded.
- */
- if (mInputBufferCount <= 2 || mOutputBufferCount > 1) { // TODO: <= 1
- if (mStreamInfo->sampleRate != prevSampleRate ||
- mStreamInfo->numChannels != prevNumChannels) {
- ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
- prevSampleRate, mStreamInfo->sampleRate,
- prevNumChannels, mStreamInfo->numChannels);
-
- notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
- mOutputPortSettingsChange = AWAITING_DISABLED;
-
- if (inHeader->nFilledLen == 0) {
- inInfo->mOwnedByUs = false;
- mInputBufferCount++;
- inQueue.erase(inQueue.begin());
- mLastInHeader = NULL;
- inInfo = NULL;
- notifyEmptyBufferDone(inHeader);
- inHeader = NULL;
- }
- return;
- }
- } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
- ALOGW("Invalid AAC stream");
+ if (decoderErr == AAC_DEC_OK) {
+ if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+ mStreamInfo->frameSize * mStreamInfo->numChannels)) {
mSignalledError = true;
notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
return;
}
- if (inHeader && inHeader->nFilledLen == 0) {
- inInfo->mOwnedByUs = false;
- mInputBufferCount++;
- inQueue.erase(inQueue.begin());
- mLastInHeader = NULL;
- inInfo = NULL;
- notifyEmptyBufferDone(inHeader);
- inHeader = NULL;
- } else {
- ALOGV("inHeader->nFilledLen = %d", inHeader ? inHeader->nFilledLen : 0);
+ UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
+ inHeader->nFilledLen -= inBufferUsedLength;
+ inHeader->nOffset += inBufferUsedLength;
+ } else {
+ ALOGW("AAC decoder returned error 0x%4.4x, substituting silence", decoderErr);
+
+ memset(tmpOutBuffer, 0, numOutBytes); // TODO: check for overflow
+
+ if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
+ mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
}
- } while (decoderErr == AAC_DEC_OK);
+
+ // Discard input buffer.
+ inHeader->nFilledLen = 0;
+
+ aacDecoder_SetParam(mAACDecoder, AAC_TPDEC_CLEAR_BUFFER, 1);
+
+ // fall through
+ }
+
+ /*
+ * AAC+/eAAC+ streams can be signalled in two ways: either explicitly
+ * or implicitly, according to MPEG4 spec. AAC+/eAAC+ is a dual
+ * rate system and the sampling rate in the final output is actually
+ * doubled compared with the core AAC decoder sampling rate.
+ *
+ * Explicit signalling is done by explicitly defining SBR audio object
+ * type in the bitstream. Implicit signalling is done by embedding
+ * SBR content in AAC extension payload specific to SBR, and hence
+ * requires an AAC decoder to perform pre-checks on actual audio frames.
+ *
+ * Thus, we could not say for sure whether a stream is
+ * AAC+/eAAC+ until the first data frame is decoded.
+ */
+ if (mInputBufferCount <= 2 || mOutputBufferCount > 1) { // TODO: <= 1
+ if (mStreamInfo->sampleRate != prevSampleRate ||
+ mStreamInfo->numChannels != prevNumChannels) {
+ ALOGI("Reconfiguring decoder: %d->%d Hz, %d->%d channels",
+ prevSampleRate, mStreamInfo->sampleRate,
+ prevNumChannels, mStreamInfo->numChannels);
+
+ notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ mInputBufferCount++;
+ inQueue.erase(inQueue.begin());
+ mLastInHeader = NULL;
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ }
+ return;
+ }
+ } else if (!mStreamInfo->sampleRate || !mStreamInfo->numChannels) {
+ ALOGW("Invalid AAC stream");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ return;
+ }
+ if (inHeader->nFilledLen == 0) {
+ inInfo->mOwnedByUs = false;
+ mInputBufferCount++;
+ inQueue.erase(inQueue.begin());
+ mLastInHeader = NULL;
+ inInfo = NULL;
+ notifyEmptyBufferDone(inHeader);
+ inHeader = NULL;
+ } else {
+ ALOGV("inHeader->nFilledLen = %d", inHeader->nFilledLen);
+ }
}
int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
@@ -817,9 +809,8 @@
INT_PCM *outBuffer =
reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset);
- int samplesize = mStreamInfo->numChannels * sizeof(int16_t);
if (outHeader->nOffset
- + mStreamInfo->frameSize * samplesize
+ + mStreamInfo->frameSize * mStreamInfo->numChannels * sizeof(int16_t)
> outHeader->nAllocLen) {
ALOGE("buffer overflow");
mSignalledError = true;
@@ -827,67 +818,17 @@
return;
}
-
- int available = outputDelayRingBufferSamplesAvailable();
- int numSamples = outHeader->nAllocLen / samplesize;
- if (numSamples > available) {
- numSamples = available;
- }
- int64_t currentTime = 0;
- if (available) {
-
- int numFrames = numSamples / (mStreamInfo->frameSize * mStreamInfo->numChannels);
- numSamples = numFrames * (mStreamInfo->frameSize * mStreamInfo->numChannels);
-
- ALOGV("%d samples available (%d), or %d frames",
- numSamples, available, numFrames);
- int64_t *nextTimeStamp = &mBufferTimestamps.editItemAt(0);
- currentTime = *nextTimeStamp;
- int32_t *currentBufLeft = &mBufferSizes.editItemAt(0);
- for (int i = 0; i < numFrames; i++) {
- int32_t decodedSize = mDecodedSizes.itemAt(0);
- mDecodedSizes.removeAt(0);
- ALOGV("decoded %d of %d", decodedSize, *currentBufLeft);
- if (*currentBufLeft > decodedSize) {
- // adjust/interpolate next time stamp
- *currentBufLeft -= decodedSize;
- *nextTimeStamp += mStreamInfo->aacSamplesPerFrame *
- 1000000ll / mStreamInfo->sampleRate;
- ALOGV("adjusted nextTimeStamp/size to %lld/%d",
- *nextTimeStamp, *currentBufLeft);
- } else {
- // move to next timestamp in list
- if (mBufferTimestamps.size() > 0) {
- mBufferTimestamps.removeAt(0);
- nextTimeStamp = &mBufferTimestamps.editItemAt(0);
- mBufferSizes.removeAt(0);
- currentBufLeft = &mBufferSizes.editItemAt(0);
- ALOGV("moved to next time/size: %lld/%d",
- *nextTimeStamp, *currentBufLeft);
- }
- // try to limit output buffer size to match input buffers
- // (e.g when an input buffer contained 4 "sub" frames, output
- // at most 4 decoded units in the corresponding output buffer)
- // This is optional. Remove the next three lines to fill the output
- // buffer with as many units as available.
- numFrames = i + 1;
- numSamples = numFrames * mStreamInfo->frameSize * mStreamInfo->numChannels;
- break;
- }
- }
-
- ALOGV("getting %d from ringbuffer", numSamples);
- int32_t ns = outputDelayRingBufferGetSamples(outBuffer, numSamples);
- if (ns != numSamples) {
- ALOGE("not a complete frame of samples available");
- mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
- return;
- }
+ int32_t ns = outputDelayRingBufferGetSamples(outBuffer,
+ mStreamInfo->frameSize * mStreamInfo->numChannels); // TODO: check for overflow
+ if (ns != mStreamInfo->frameSize * mStreamInfo->numChannels) {
+ ALOGE("not a complete frame of samples available");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
+ return;
}
- outHeader->nFilledLen = numSamples * sizeof(int16_t);
-
+ outHeader->nFilledLen = mStreamInfo->frameSize * mStreamInfo->numChannels
+ * sizeof(int16_t);
if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) {
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
mEndOfOutput = true;
@@ -895,13 +836,13 @@
outHeader->nFlags = 0;
}
- outHeader->nTimeStamp = currentTime;
+ outHeader->nTimeStamp = mAnchorTimes.isEmpty() ? 0 : mAnchorTimes.itemAt(0);
+ mAnchorTimes.removeAt(0);
mOutputBufferCount++;
outInfo->mOwnedByUs = false;
outQueue.erase(outQueue.begin());
outInfo = NULL;
- ALOGV("out timestamp %lld / %d", outHeader->nTimeStamp, outHeader->nFilledLen);
notifyFillBufferDone(outHeader);
outHeader = NULL;
}
@@ -936,10 +877,8 @@
outHeader->nFilledLen = 0;
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
- outHeader->nTimeStamp = mBufferTimestamps.itemAt(0);
- mBufferTimestamps.clear();
- mBufferSizes.clear();
- mDecodedSizes.clear();
+ outHeader->nTimeStamp = mAnchorTimes.itemAt(0);
+ mAnchorTimes.removeAt(0);
mOutputBufferCount++;
outInfo->mOwnedByUs = false;
@@ -960,9 +899,7 @@
// depend on fragments from the last one decoded.
// drain all existing data
drainDecoder();
- mBufferTimestamps.clear();
- mBufferSizes.clear();
- mDecodedSizes.clear();
+ mAnchorTimes.clear();
mLastInHeader = NULL;
} else {
while (outputDelayRingBufferSamplesAvailable() > 0) {
@@ -1018,9 +955,7 @@
mOutputDelayRingBufferReadPos = 0;
mEndOfInput = false;
mEndOfOutput = false;
- mBufferTimestamps.clear();
- mBufferSizes.clear();
- mDecodedSizes.clear();
+ mAnchorTimes.clear();
mLastInHeader = NULL;
// To make the codec behave the same before and after a reset, we need to invalidate the
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.h b/media/libstagefright/codecs/aacdec/SoftAAC2.h
index 9fcb598..865bd15 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -59,9 +59,8 @@
size_t mOutputBufferCount;
bool mSignalledError;
OMX_BUFFERHEADERTYPE *mLastInHeader;
- Vector<int32_t> mBufferSizes;
- Vector<int32_t> mDecodedSizes;
- Vector<int64_t> mBufferTimestamps;
+ int64_t mCurrentInputTime;
+ Vector<int64_t> mAnchorTimes;
CDrcPresModeWrapper mDrcWrap;
diff --git a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
index 2f63bdd..828577a 100644
--- a/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
+++ b/media/libstagefright/codecs/on2/dec/SoftVPX.cpp
@@ -137,29 +137,10 @@
uint32_t width = mImg->d_w;
uint32_t height = mImg->d_h;
-
- if (width != mWidth || height != mHeight) {
- mWidth = width;
- mHeight = height;
-
- if (!mIsAdaptive || width > mAdaptiveMaxWidth || height > mAdaptiveMaxHeight) {
- if (mIsAdaptive) {
- if (width > mAdaptiveMaxWidth) {
- mAdaptiveMaxWidth = width;
- }
- if (height > mAdaptiveMaxHeight) {
- mAdaptiveMaxHeight = height;
- }
- }
- updatePortDefinitions();
- notify(OMX_EventPortSettingsChanged, kOutputPortIndex, 0, NULL);
- mOutputPortSettingsChange = AWAITING_DISABLED;
- return;
- } else {
- updatePortDefinitions();
- notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
- OMX_IndexConfigCommonOutputCrop, NULL);
- }
+ bool portWillReset = false;
+ handlePortSettingsChange(&portWillReset, width, height);
+ if (portWillReset) {
+ return;
}
outHeader->nOffset = 0;
@@ -167,36 +148,14 @@
outHeader->nFlags = EOSseen ? OMX_BUFFERFLAG_EOS : 0;
outHeader->nTimeStamp = inHeader->nTimeStamp;
- uint32_t buffer_stride = mIsAdaptive ? mAdaptiveMaxWidth : mWidth;
- uint32_t buffer_height = mIsAdaptive ? mAdaptiveMaxHeight : mHeight;
-
- const uint8_t *srcLine = (const uint8_t *)mImg->planes[PLANE_Y];
uint8_t *dst = outHeader->pBuffer;
- for (size_t i = 0; i < buffer_height; ++i) {
- if (i < mImg->d_h) {
- memcpy(dst, srcLine, mImg->d_w);
- srcLine += mImg->stride[PLANE_Y];
- }
- dst += buffer_stride;
- }
-
- srcLine = (const uint8_t *)mImg->planes[PLANE_U];
- for (size_t i = 0; i < buffer_height / 2; ++i) {
- if (i < mImg->d_h / 2) {
- memcpy(dst, srcLine, mImg->d_w / 2);
- srcLine += mImg->stride[PLANE_U];
- }
- dst += buffer_stride / 2;
- }
-
- srcLine = (const uint8_t *)mImg->planes[PLANE_V];
- for (size_t i = 0; i < buffer_height / 2; ++i) {
- if (i < mImg->d_h / 2) {
- memcpy(dst, srcLine, mImg->d_w / 2);
- srcLine += mImg->stride[PLANE_V];
- }
- dst += buffer_stride / 2;
- }
+ const uint8_t *srcY = (const uint8_t *)mImg->planes[PLANE_Y];
+ const uint8_t *srcU = (const uint8_t *)mImg->planes[PLANE_U];
+ const uint8_t *srcV = (const uint8_t *)mImg->planes[PLANE_V];
+ size_t srcYStride = mImg->stride[PLANE_Y];
+ size_t srcUStride = mImg->stride[PLANE_U];
+ size_t srcVStride = mImg->stride[PLANE_V];
+ copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride);
mImg = NULL;
outInfo->mOwnedByUs = false;
diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
index a7bde97..cf3c3e3 100644
--- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
+++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.cpp
@@ -58,7 +58,6 @@
320 /* width */, 240 /* height */, callbacks, appData, component),
mHandle(NULL),
mInputBufferCount(0),
- mPictureSize(mWidth * mHeight * 3 / 2),
mFirstPicture(NULL),
mFirstPictureId(-1),
mPicId(0),
@@ -118,7 +117,7 @@
}
H264SwDecRet ret = H264SWDEC_PIC_RDY;
- bool portSettingsChanged = false;
+ bool portWillReset = false;
while ((mEOSStatus != INPUT_DATA_AVAILABLE || !inQueue.empty())
&& outQueue.size() == kNumOutputBuffers) {
@@ -161,17 +160,13 @@
H264SwDecInfo decoderInfo;
CHECK(H264SwDecGetInfo(mHandle, &decoderInfo) == H264SWDEC_OK);
- if (handlePortSettingChangeEvent(&decoderInfo)) {
- portSettingsChanged = true;
- }
-
- if (decoderInfo.croppingFlag &&
- handleCropRectEvent(&decoderInfo.cropParams)) {
- portSettingsChanged = true;
- }
+ bool cropChanged = handleCropChange(decoderInfo);
+ handlePortSettingsChange(
+ &portWillReset, decoderInfo.picWidth, decoderInfo.picHeight,
+ cropChanged);
}
} else {
- if (portSettingsChanged) {
+ if (portWillReset) {
if (H264SwDecNextPicture(mHandle, &decodedPicture, 0)
== H264SWDEC_PIC_RDY) {
@@ -199,8 +194,7 @@
inInfo->mOwnedByUs = false;
notifyEmptyBufferDone(inHeader);
- if (portSettingsChanged) {
- portSettingsChanged = false;
+ if (portWillReset) {
return;
}
@@ -215,44 +209,33 @@
}
}
-bool SoftAVC::handlePortSettingChangeEvent(const H264SwDecInfo *info) {
- if (mWidth != info->picWidth || mHeight != info->picHeight) {
- mWidth = info->picWidth;
- mHeight = info->picHeight;
- mPictureSize = mWidth * mHeight * 3 / 2;
- updatePortDefinitions();
- notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
- mOutputPortSettingsChange = AWAITING_DISABLED;
- return true;
+bool SoftAVC::handleCropChange(const H264SwDecInfo& decInfo) {
+ if (!decInfo.croppingFlag) {
+ return false;
}
- return false;
-}
-
-bool SoftAVC::handleCropRectEvent(const CropParams *crop) {
- if (mCropLeft != crop->cropLeftOffset ||
- mCropTop != crop->cropTopOffset ||
- mCropWidth != crop->cropOutWidth ||
- mCropHeight != crop->cropOutHeight) {
- mCropLeft = crop->cropLeftOffset;
- mCropTop = crop->cropTopOffset;
- mCropWidth = crop->cropOutWidth;
- mCropHeight = crop->cropOutHeight;
-
- notify(OMX_EventPortSettingsChanged, 1,
- OMX_IndexConfigCommonOutputCrop, NULL);
-
- return true;
+ const CropParams& crop = decInfo.cropParams;
+ if (mCropLeft == crop.cropLeftOffset &&
+ mCropTop == crop.cropTopOffset &&
+ mCropWidth == crop.cropOutWidth &&
+ mCropHeight == crop.cropOutHeight) {
+ return false;
}
- return false;
+
+ mCropLeft = crop.cropLeftOffset;
+ mCropTop = crop.cropTopOffset;
+ mCropWidth = crop.cropOutWidth;
+ mCropHeight = crop.cropOutHeight;
+ return true;
}
void SoftAVC::saveFirstOutputBuffer(int32_t picId, uint8_t *data) {
CHECK(mFirstPicture == NULL);
mFirstPictureId = picId;
- mFirstPicture = new uint8_t[mPictureSize];
- memcpy(mFirstPicture, data, mPictureSize);
+ uint32_t pictureSize = mWidth * mHeight * 3 / 2;
+ mFirstPicture = new uint8_t[pictureSize];
+ memcpy(mFirstPicture, data, pictureSize);
}
void SoftAVC::drainOneOutputBuffer(int32_t picId, uint8_t* data) {
@@ -263,9 +246,17 @@
OMX_BUFFERHEADERTYPE *header = mPicToHeaderMap.valueFor(picId);
outHeader->nTimeStamp = header->nTimeStamp;
outHeader->nFlags = header->nFlags;
- outHeader->nFilledLen = mPictureSize;
- memcpy(outHeader->pBuffer + outHeader->nOffset,
- data, mPictureSize);
+ outHeader->nFilledLen = mWidth * mHeight * 3 / 2;
+
+ uint8_t *dst = outHeader->pBuffer + outHeader->nOffset;
+ const uint8_t *srcY = data;
+ const uint8_t *srcU = srcY + mWidth * mHeight;
+ const uint8_t *srcV = srcU + mWidth * mHeight / 4;
+ size_t srcYStride = mWidth;
+ size_t srcUStride = mWidth / 2;
+ size_t srcVStride = srcUStride;
+ copyYV12FrameToOutputBuffer(dst, srcY, srcU, srcV, srcYStride, srcUStride, srcVStride);
+
mPicToHeaderMap.removeItem(picId);
delete header;
outInfo->mOwnedByUs = false;
diff --git a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
index ee69926..253a406 100644
--- a/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
+++ b/media/libstagefright/codecs/on2/h264dec/SoftAVC.h
@@ -55,8 +55,6 @@
size_t mInputBufferCount;
- uint32_t mPictureSize;
-
uint8_t *mFirstPicture;
int32_t mFirstPictureId;
@@ -75,8 +73,7 @@
void drainAllOutputBuffers(bool eos);
void drainOneOutputBuffer(int32_t picId, uint8_t *data);
void saveFirstOutputBuffer(int32_t pidId, uint8_t *data);
- bool handleCropRectEvent(const CropParams* crop);
- bool handlePortSettingChangeEvent(const H264SwDecInfo *info);
+ bool handleCropChange(const H264SwDecInfo& decInfo);
DISALLOW_EVIL_CONSTRUCTORS(SoftAVC);
};
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index d268aa4..bc3e3fb 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -14,6 +14,11 @@
* limitations under the License.
*/
+#define LOG_TAG "AMessage"
+//#define LOG_NDEBUG 0
+//#define DUMP_STATS
+#include <cutils/log.h>
+
#include "AMessage.h"
#include <ctype.h>
@@ -60,12 +65,14 @@
void AMessage::clear() {
for (size_t i = 0; i < mNumItems; ++i) {
Item *item = &mItems[i];
- freeItem(item);
+ delete[] item->mName;
+ item->mName = NULL;
+ freeItemValue(item);
}
mNumItems = 0;
}
-void AMessage::freeItem(Item *item) {
+void AMessage::freeItemValue(Item *item) {
switch (item->mType) {
case kTypeString:
{
@@ -88,25 +95,85 @@
}
}
-AMessage::Item *AMessage::allocateItem(const char *name) {
- name = AAtomizer::Atomize(name);
+#ifdef DUMP_STATS
+#include <utils/Mutex.h>
- size_t i = 0;
- while (i < mNumItems && mItems[i].mName != name) {
- ++i;
+Mutex gLock;
+static int32_t gFindItemCalls = 1;
+static int32_t gDupCalls = 1;
+static int32_t gAverageNumItems = 0;
+static int32_t gAverageNumChecks = 0;
+static int32_t gAverageNumMemChecks = 0;
+static int32_t gAverageDupItems = 0;
+static int32_t gLastChecked = -1;
+
+static void reportStats() {
+ int32_t time = (ALooper::GetNowUs() / 1000);
+ if (time / 1000 != gLastChecked / 1000) {
+ gLastChecked = time;
+ ALOGI("called findItemIx %zu times (for len=%.1f i=%.1f/%.1f mem) dup %zu times (for len=%.1f)",
+ gFindItemCalls,
+ gAverageNumItems / (float)gFindItemCalls,
+ gAverageNumChecks / (float)gFindItemCalls,
+ gAverageNumMemChecks / (float)gFindItemCalls,
+ gDupCalls,
+ gAverageDupItems / (float)gDupCalls);
+ gFindItemCalls = gDupCalls = 1;
+ gAverageNumItems = gAverageNumChecks = gAverageNumMemChecks = gAverageDupItems = 0;
+ gLastChecked = time;
}
+}
+#endif
+inline size_t AMessage::findItemIndex(const char *name, size_t len) const {
+#ifdef DUMP_STATS
+ size_t memchecks = 0;
+#endif
+ size_t i = 0;
+ for (; i < mNumItems; i++) {
+ if (len != mItems[i].mNameLength) {
+ continue;
+ }
+#ifdef DUMP_STATS
+ ++memchecks;
+#endif
+ if (!memcmp(mItems[i].mName, name, len)) {
+ break;
+ }
+ }
+#ifdef DUMP_STATS
+ {
+ Mutex::Autolock _l(gLock);
+ ++gFindItemCalls;
+ gAverageNumItems += mNumItems;
+ gAverageNumMemChecks += memchecks;
+ gAverageNumChecks += i;
+ reportStats();
+ }
+#endif
+ return i;
+}
+
+// assumes item's name was uninitialized or NULL
+void AMessage::Item::setName(const char *name, size_t len) {
+ mNameLength = len;
+ mName = new char[len + 1];
+ memcpy((void*)mName, name, len + 1);
+}
+
+AMessage::Item *AMessage::allocateItem(const char *name) {
+ size_t len = strlen(name);
+ size_t i = findItemIndex(name, len);
Item *item;
if (i < mNumItems) {
item = &mItems[i];
- freeItem(item);
+ freeItemValue(item);
} else {
CHECK(mNumItems < kMaxNumItems);
i = mNumItems++;
item = &mItems[i];
-
- item->mName = name;
+ item->setName(name, len);
}
return item;
@@ -114,31 +181,18 @@
const AMessage::Item *AMessage::findItem(
const char *name, Type type) const {
- name = AAtomizer::Atomize(name);
-
- for (size_t i = 0; i < mNumItems; ++i) {
+ size_t i = findItemIndex(name, strlen(name));
+ if (i < mNumItems) {
const Item *item = &mItems[i];
+ return item->mType == type ? item : NULL;
- if (item->mName == name) {
- return item->mType == type ? item : NULL;
- }
}
-
return NULL;
}
bool AMessage::contains(const char *name) const {
- name = AAtomizer::Atomize(name);
-
- for (size_t i = 0; i < mNumItems; ++i) {
- const Item *item = &mItems[i];
-
- if (item->mName == name) {
- return true;
- }
- }
-
- return false;
+ size_t i = findItemIndex(name, strlen(name));
+ return i < mNumItems;
}
#define BASIC_TYPE(NAME,FIELDNAME,TYPENAME) \
@@ -297,11 +351,20 @@
sp<AMessage> msg = new AMessage(mWhat, mTarget);
msg->mNumItems = mNumItems;
+#ifdef DUMP_STATS
+ {
+ Mutex::Autolock _l(gLock);
+ ++gDupCalls;
+ gAverageDupItems += mNumItems;
+ reportStats();
+ }
+#endif
+
for (size_t i = 0; i < mNumItems; ++i) {
const Item *from = &mItems[i];
Item *to = &msg->mItems[i];
- to->mName = from->mName;
+ to->setName(from->mName, from->mNameLength);
to->mType = from->mType;
switch (from->mType) {
@@ -472,11 +535,11 @@
sp<AMessage> msg = new AMessage(what);
msg->mNumItems = static_cast<size_t>(parcel.readInt32());
-
for (size_t i = 0; i < msg->mNumItems; ++i) {
Item *item = &msg->mItems[i];
- item->mName = AAtomizer::Atomize(parcel.readCString());
+ const char *name = parcel.readCString();
+ item->setName(name, strlen(name));
item->mType = static_cast<Type>(parcel.readInt32());
switch (item->mType) {
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 7b18348..02ebfc7 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -498,7 +498,7 @@
break;
}
- onCheckBandwidth();
+ onCheckBandwidth(msg);
break;
}
@@ -919,14 +919,22 @@
}
}
- // Consider only 80% of the available bandwidth usable.
- bandwidthBps = (bandwidthBps * 8) / 10;
-
// Pick the highest bandwidth stream below or equal to estimated bandwidth.
index = mBandwidthItems.size() - 1;
- while (index > 0 && mBandwidthItems.itemAt(index).mBandwidth
- > (size_t)bandwidthBps) {
+ while (index > 0) {
+ // consider only 80% of the available bandwidth, but if we are switching up,
+ // be even more conservative (70%) to avoid overestimating and immediately
+ // switching back.
+ size_t adjustedBandwidthBps = bandwidthBps;
+ if (index > mCurBandwidthIndex) {
+ adjustedBandwidthBps = adjustedBandwidthBps * 7 / 10;
+ } else {
+ adjustedBandwidthBps = adjustedBandwidthBps * 8 / 10;
+ }
+ if (mBandwidthItems.itemAt(index).mBandwidth <= adjustedBandwidthBps) {
+ break;
+ }
--index;
}
}
@@ -1394,6 +1402,7 @@
// All fetchers have now been started, the configuration change
// has completed.
+ cancelCheckBandwidthEvent();
scheduleCheckBandwidthEvent();
ALOGV("XXX configuration change completed.");
@@ -1492,20 +1501,16 @@
}
}
-void LiveSession::onCheckBandwidth() {
+void LiveSession::onCheckBandwidth(const sp<AMessage> &msg) {
size_t bandwidthIndex = getBandwidthIndex();
if (canSwitchBandwidthTo(bandwidthIndex)) {
changeConfiguration(-1ll /* timeUs */, bandwidthIndex);
} else {
- scheduleCheckBandwidthEvent();
+ // Come back and check again 10 seconds later in case there is nothing to do now.
+ // If we DO change configuration, once that completes it'll schedule a new
+ // check bandwidth event with an incremented mCheckBandwidthGeneration.
+ msg->post(10000000ll);
}
-
- // Handling the kWhatCheckBandwidth even here does _not_ automatically
- // schedule another one on return, only an explicit call to
- // scheduleCheckBandwidthEvent will do that.
- // This ensures that only one configuration change is ongoing at any
- // one time, once that completes it'll schedule another check bandwidth
- // event.
}
void LiveSession::postPrepared(status_t err) {
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index 5423f0f..aa06a3f 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -257,7 +257,7 @@
void cancelBandwidthSwitch();
bool canSwitchBandwidthTo(size_t bandwidthIndex);
- void onCheckBandwidth();
+ void onCheckBandwidth(const sp<AMessage> &msg);
void finishDisconnect();
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 82a4c39..3ef0f06 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -737,12 +737,6 @@
const int32_t lastSeqNumberInPlaylist =
firstSeqNumberInPlaylist + (int32_t)mPlaylist->size() - 1;
- if (mStartup && mSeqNumber >= 0
- && (mSeqNumber < firstSeqNumberInPlaylist || mSeqNumber > lastSeqNumberInPlaylist)) {
- // in case we guessed wrong during reconfiguration, try fetching the latest content.
- mSeqNumber = lastSeqNumberInPlaylist;
- }
-
if (mDiscontinuitySeq < 0) {
mDiscontinuitySeq = mPlaylist->getDiscontinuitySeq();
}
diff --git a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
index ee553d9..4a6ab63 100644
--- a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
+++ b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
@@ -63,7 +63,14 @@
OMX_U32 numOutputBuffers,
const char *mimeType);
- virtual void updatePortDefinitions();
+ virtual void updatePortDefinitions(bool updateCrop = true);
+
+ void handlePortSettingsChange(
+ bool *portWillReset, uint32_t width, uint32_t height, bool cropChanged = false);
+
+ void copyYV12FrameToOutputBuffer(
+ uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
+ size_t srcYStride, size_t srcUStride, size_t srcVStride);
enum {
kInputPortIndex = 0,
diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
index 69b572e..e533fdd 100644
--- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
@@ -123,13 +123,15 @@
updatePortDefinitions();
}
-void SoftVideoDecoderOMXComponent::updatePortDefinitions() {
+void SoftVideoDecoderOMXComponent::updatePortDefinitions(bool updateCrop) {
OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kInputPortIndex)->mDef;
def->format.video.nFrameWidth = mWidth;
def->format.video.nFrameHeight = mHeight;
def->format.video.nStride = def->format.video.nFrameWidth;
def->format.video.nSliceHeight = def->format.video.nFrameHeight;
+ def->nBufferSize = def->format.video.nFrameWidth * def->format.video.nFrameHeight * 3 / 2;
+
def = &editPortInfo(kOutputPortIndex)->mDef;
def->format.video.nFrameWidth = mIsAdaptive ? mAdaptiveMaxWidth : mWidth;
def->format.video.nFrameHeight = mIsAdaptive ? mAdaptiveMaxHeight : mHeight;
@@ -140,10 +142,77 @@
(def->format.video.nFrameWidth *
def->format.video.nFrameHeight * 3) / 2;
- mCropLeft = 0;
- mCropTop = 0;
- mCropWidth = mWidth;
- mCropHeight = mHeight;
+ if (updateCrop) {
+ mCropLeft = 0;
+ mCropTop = 0;
+ mCropWidth = mWidth;
+ mCropHeight = mHeight;
+ }
+}
+
+void SoftVideoDecoderOMXComponent::handlePortSettingsChange(
+ bool *portWillReset, uint32_t width, uint32_t height, bool cropChanged) {
+ *portWillReset = false;
+ bool sizeChanged = (width != mWidth || height != mHeight);
+
+ if (sizeChanged || cropChanged) {
+ mWidth = width;
+ mHeight = height;
+
+ bool updateCrop = !cropChanged;
+ if ((sizeChanged && !mIsAdaptive)
+ || width > mAdaptiveMaxWidth
+ || height > mAdaptiveMaxHeight) {
+ if (mIsAdaptive) {
+ if (width > mAdaptiveMaxWidth) {
+ mAdaptiveMaxWidth = width;
+ }
+ if (height > mAdaptiveMaxHeight) {
+ mAdaptiveMaxHeight = height;
+ }
+ }
+ updatePortDefinitions(updateCrop);
+ notify(OMX_EventPortSettingsChanged, kOutputPortIndex, 0, NULL);
+ mOutputPortSettingsChange = AWAITING_DISABLED;
+ *portWillReset = true;
+ } else {
+ updatePortDefinitions(updateCrop);
+ notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
+ OMX_IndexConfigCommonOutputCrop, NULL);
+ }
+ }
+}
+
+void SoftVideoDecoderOMXComponent::copyYV12FrameToOutputBuffer(
+ uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
+ size_t srcYStride, size_t srcUStride, size_t srcVStride) {
+ size_t dstYStride = mIsAdaptive ? mAdaptiveMaxWidth : mWidth;
+ size_t dstUVStride = dstYStride / 2;
+ size_t dstHeight = mIsAdaptive ? mAdaptiveMaxHeight : mHeight;
+
+ for (size_t i = 0; i < dstHeight; ++i) {
+ if (i < mHeight) {
+ memcpy(dst, srcY, mWidth);
+ srcY += srcYStride;
+ }
+ dst += dstYStride;
+ }
+
+ for (size_t i = 0; i < dstHeight / 2; ++i) {
+ if (i < mHeight / 2) {
+ memcpy(dst, srcU, mWidth / 2);
+ srcU += srcUStride;
+ }
+ dst += dstUVStride;
+ }
+
+ for (size_t i = 0; i < dstHeight / 2; ++i) {
+ if (i < mHeight / 2) {
+ memcpy(dst, srcV, mWidth / 2);
+ srcV += srcVStride;
+ }
+ dst += dstUVStride;
+ }
}
OMX_ERRORTYPE SoftVideoDecoderOMXComponent::internalGetParameter(
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index 6adcde4..22c4e04 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -64,6 +64,7 @@
const StringToEnum sDeviceNameToEnumTable[] = {
STRING_TO_ENUM(AUDIO_DEVICE_OUT_EARPIECE),
STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER),
+ STRING_TO_ENUM(AUDIO_DEVICE_OUT_SPEAKER_SAFE),
STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADSET),
STRING_TO_ENUM(AUDIO_DEVICE_OUT_WIRED_HEADPHONE),
STRING_TO_ENUM(AUDIO_DEVICE_OUT_BLUETOOTH_SCO),
@@ -3824,6 +3825,14 @@
break;
}
}
+
+ /*Filter SPEAKER_SAFE out of results, as AudioService doesn't know about it
+ and doesn't really need to.*/
+ if (devices & AUDIO_DEVICE_OUT_SPEAKER_SAFE) {
+ devices |= AUDIO_DEVICE_OUT_SPEAKER;
+ devices &= ~AUDIO_DEVICE_OUT_SPEAKER_SAFE;
+ }
+
return devices;
}
@@ -3926,12 +3935,20 @@
// the isStreamActive() method only informs about the activity of a stream, not
// if it's for local playback. Note also that we use the same delay between both tests
device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/);
+ //user "safe" speaker if available instead of normal speaker to avoid triggering
+ //other acoustic safety mechanisms for notification
+ if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER_SAFE))
+ device = AUDIO_DEVICE_OUT_SPEAKER_SAFE;
} else if (isStreamActive(AUDIO_STREAM_MUSIC, SONIFICATION_RESPECTFUL_AFTER_MUSIC_DELAY)) {
// while media is playing (or has recently played), use the same device
device = getDeviceForStrategy(STRATEGY_MEDIA, false /*fromCache*/);
} else {
// when media is not playing anymore, fall back on the sonification behavior
device = getDeviceForStrategy(STRATEGY_SONIFICATION, false /*fromCache*/);
+ //user "safe" speaker if available instead of normal speaker to avoid triggering
+ //other acoustic safety mechanisms for notification
+ if (device == AUDIO_DEVICE_OUT_SPEAKER && (availableOutputDeviceTypes & AUDIO_DEVICE_OUT_SPEAKER_SAFE))
+ device = AUDIO_DEVICE_OUT_SPEAKER_SAFE;
}
break;
@@ -4668,6 +4685,10 @@
}
}
+ /*SPEAKER_SAFE is an alias of SPEAKER for purposes of volume control*/
+ if (device == AUDIO_DEVICE_OUT_SPEAKER_SAFE)
+ device = AUDIO_DEVICE_OUT_SPEAKER;
+
ALOGW_IF(popcount(device) != 1,
"getDeviceForVolume() invalid device combination: %08x",
device);
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
index 608550b..fa65b74 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
@@ -44,6 +44,7 @@
sp<Camera2Client> client,
wp<CaptureSequencer> sequencer):
Thread(false),
+ mLatestClearedBufferTimestamp(0),
mState(RUNNING),
mClient(client),
mSequencer(sequencer),
@@ -107,7 +108,6 @@
ALOGE("%s: metadata doesn't have timestamp, skip this result", __FUNCTION__);
return;
}
- (void)timestamp;
entry = result.mMetadata.find(ANDROID_REQUEST_FRAME_COUNT);
if (entry.count == 0) {
@@ -120,6 +120,9 @@
if (mState != RUNNING) return;
+ // Corresponding buffer has been cleared. No need to push into mFrameList
+ if (timestamp <= mLatestClearedBufferTimestamp) return;
+
mFrameList.editItemAt(mFrameListHead) = result.mMetadata;
mFrameListHead = (mFrameListHead + 1) % mFrameListDepth;
}
@@ -392,7 +395,7 @@
if (mZslStream != 0) {
// clear result metadata list first.
clearZslResultQueueLocked();
- return mZslStream->clearInputRingBuffer();
+ return mZslStream->clearInputRingBuffer(&mLatestClearedBufferTimestamp);
}
return OK;
}
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
index 76707fa..2975f7c 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
@@ -82,6 +82,7 @@
private:
static const nsecs_t kWaitDuration = 10000000; // 10 ms
+ nsecs_t mLatestClearedBufferTimestamp;
enum {
RUNNING,
diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
index 92bf81b..81330ea 100644
--- a/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3ZslStream.cpp
@@ -315,20 +315,24 @@
return OK;
}
-status_t Camera3ZslStream::clearInputRingBuffer() {
+status_t Camera3ZslStream::clearInputRingBuffer(nsecs_t* latestTimestamp) {
Mutex::Autolock l(mLock);
- return clearInputRingBufferLocked();
+ return clearInputRingBufferLocked(latestTimestamp);
}
-status_t Camera3ZslStream::clearInputRingBufferLocked() {
+status_t Camera3ZslStream::clearInputRingBufferLocked(nsecs_t* latestTimestamp) {
+
+ if (latestTimestamp) {
+ *latestTimestamp = mProducer->getLatestTimestamp();
+ }
mInputBufferQueue.clear();
return mProducer->clear();
}
status_t Camera3ZslStream::disconnectLocked() {
- clearInputRingBufferLocked();
+ clearInputRingBufferLocked(NULL);
return Camera3OutputStream::disconnectLocked();
}
diff --git a/services/camera/libcameraservice/device3/Camera3ZslStream.h b/services/camera/libcameraservice/device3/Camera3ZslStream.h
index d89c38d..5323a49 100644
--- a/services/camera/libcameraservice/device3/Camera3ZslStream.h
+++ b/services/camera/libcameraservice/device3/Camera3ZslStream.h
@@ -59,8 +59,10 @@
/**
* Clears the buffers that can be used by enqueueInputBufferByTimestamp
+ * latestTimestamp will be filled with the largest timestamp of buffers
+ * being cleared, 0 if there is no buffer being clear.
*/
- status_t clearInputRingBuffer();
+ status_t clearInputRingBuffer(nsecs_t* latestTimestamp);
protected:
@@ -100,7 +102,7 @@
// Disconnet the Camera3ZslStream specific bufferQueues.
virtual status_t disconnectLocked();
- status_t clearInputRingBufferLocked();
+ status_t clearInputRingBufferLocked(nsecs_t* latestTimestamp);
}; // class Camera3ZslStream
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
index e4ec5fd..f8562ec 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.cpp
@@ -41,7 +41,8 @@
uint32_t consumerUsage,
int bufferCount) :
ConsumerBase(consumer),
- mBufferCount(bufferCount)
+ mBufferCount(bufferCount),
+ mLatestTimestamp(0)
{
mConsumer->setConsumerUsageBits(consumerUsage);
mConsumer->setMaxAcquiredBufferCount(bufferCount);
@@ -152,6 +153,14 @@
return OK;
}
+nsecs_t RingBufferConsumer::getLatestTimestamp() {
+ Mutex::Autolock _l(mMutex);
+ if (mBufferItemList.size() == 0) {
+ return 0;
+ }
+ return mLatestTimestamp;
+}
+
void RingBufferConsumer::pinBufferLocked(const BufferItem& item) {
List<RingBufferItem>::iterator it, end;
@@ -302,6 +311,13 @@
item.mTimestamp,
mBufferItemList.size(), mBufferCount);
+ if (item.mTimestamp < mLatestTimestamp) {
+ BI_LOGE("Timestamp decreases from %" PRId64 " to %" PRId64,
+ mLatestTimestamp, item.mTimestamp);
+ }
+
+ mLatestTimestamp = item.mTimestamp;
+
item.mGraphicBuffer = mSlots[item.mBuf].mGraphicBuffer;
} // end of mMutex lock
diff --git a/services/camera/libcameraservice/gui/RingBufferConsumer.h b/services/camera/libcameraservice/gui/RingBufferConsumer.h
index a03736d..da97a11 100644
--- a/services/camera/libcameraservice/gui/RingBufferConsumer.h
+++ b/services/camera/libcameraservice/gui/RingBufferConsumer.h
@@ -159,6 +159,9 @@
// Release all the non-pinned buffers in the ring buffer
status_t clear();
+ // Return 0 if RingBuffer is empty, otherwise return timestamp of latest buffer.
+ nsecs_t getLatestTimestamp();
+
private:
// Override ConsumerBase::onFrameAvailable
@@ -180,6 +183,9 @@
// List of acquired buffers in our ring buffer
List<RingBufferItem> mBufferItemList;
const int mBufferCount;
+
+ // Timestamp of latest buffer
+ nsecs_t mLatestTimestamp;
};
} // namespace android