Merge "Optimize the YUV buffer copy a little bit to skip unnecessary operation." into lmp-dev
diff --git a/media/libmediaplayerservice/nuplayer/GenericSource.cpp b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
index ec1a9a0..511871d 100644
--- a/media/libmediaplayerservice/nuplayer/GenericSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/GenericSource.cpp
@@ -170,6 +170,8 @@
if (mAudioTrack.mSource == NULL) {
mAudioTrack.mIndex = i;
mAudioTrack.mSource = track;
+ mAudioTrack.mPackets =
+ new AnotherPacketSource(mAudioTrack.mSource->getFormat());
if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
mAudioIsVorbis = true;
@@ -181,6 +183,8 @@
if (mVideoTrack.mSource == NULL) {
mVideoTrack.mIndex = i;
mVideoTrack.mSource = track;
+ mVideoTrack.mPackets =
+ new AnotherPacketSource(mVideoTrack.mSource->getFormat());
// check if the source requires secure buffers
int32_t secure;
@@ -428,16 +432,12 @@
if (mAudioTrack.mSource != NULL) {
CHECK_EQ(mAudioTrack.mSource->start(), (status_t)OK);
- mAudioTrack.mPackets =
- new AnotherPacketSource(mAudioTrack.mSource->getFormat());
postReadBuffer(MEDIA_TRACK_TYPE_AUDIO);
}
if (mVideoTrack.mSource != NULL) {
CHECK_EQ(mVideoTrack.mSource->start(), (status_t)OK);
- mVideoTrack.mPackets =
- new AnotherPacketSource(mVideoTrack.mSource->getFormat());
postReadBuffer(MEDIA_TRACK_TYPE_VIDEO);
}
@@ -1176,12 +1176,14 @@
void NuPlayer::GenericSource::readBuffer(
media_track_type trackType, int64_t seekTimeUs, int64_t *actualTimeUs, bool formatChange) {
Track *track;
+ size_t maxBuffers = 1;
switch (trackType) {
case MEDIA_TRACK_TYPE_VIDEO:
track = &mVideoTrack;
break;
case MEDIA_TRACK_TYPE_AUDIO:
track = &mAudioTrack;
+ maxBuffers = 64;
break;
case MEDIA_TRACK_TYPE_SUBTITLE:
track = &mSubtitleTrack;
@@ -1214,7 +1216,7 @@
options.setNonBlocking();
}
- for (;;) {
+ for (size_t numBuffers = 0; numBuffers < maxBuffers; ) {
MediaBuffer *mbuf;
status_t err = track->mSource->read(&mbuf, &options);
@@ -1245,7 +1247,7 @@
sp<ABuffer> buffer = mediaBufferToABuffer(mbuf, trackType, actualTimeUs);
track->mPackets->queueAccessUnit(buffer);
- break;
+ ++numBuffers;
} else if (err == WOULD_BLOCK) {
break;
} else if (err == INFO_FORMAT_CHANGED) {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index df3e992..9020a8d 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -50,6 +50,10 @@
namespace android {
+// TODO optimize buffer size for power consumption
+// The offload read buffer size is 32 KB but 24 KB uses less power.
+const size_t NuPlayer::kAggregateBufferSizeBytes = 24 * 1024;
+
struct NuPlayer::Action : public RefBase {
Action() {}
@@ -730,7 +734,7 @@
if (err == -EWOULDBLOCK) {
if (mSource->feedMoreTSData() == OK) {
- msg->post(10000ll);
+ msg->post(10 * 1000ll);
}
}
} else if (what == Decoder::kWhatEOS) {
@@ -995,6 +999,7 @@
ALOGV("both audio and video are flushed now.");
mPendingAudioAccessUnit.clear();
+ mAggregateBuffer.clear();
if (mTimeDiscontinuityPending) {
mRenderer->signalTimeDiscontinuity();
@@ -1256,14 +1261,8 @@
// Aggregate smaller buffers into a larger buffer.
// The goal is to reduce power consumption.
// Unfortunately this does not work with the software AAC decoder.
- // TODO optimize buffer size for power consumption
- // The offload read buffer size is 32 KB but 24 KB uses less power.
- const int kAudioBigBufferSizeBytes = 24 * 1024;
- bool doBufferAggregation = (audio && mOffloadAudio);
- sp<ABuffer> biggerBuffer;
+ bool doBufferAggregation = (audio && mOffloadAudio);;
bool needMoreData = false;
- int numSmallBuffers = 0;
- bool gotTime = false;
bool dropAccessUnit;
do {
@@ -1279,14 +1278,10 @@
}
if (err == -EWOULDBLOCK) {
- if (biggerBuffer == NULL) {
- return err;
- } else {
- break; // Reply with data that we already have.
- }
+ return err;
} else if (err != OK) {
if (err == INFO_DISCONTINUITY) {
- if (biggerBuffer != NULL) {
+ if (mAggregateBuffer != NULL) {
// We already have some data so save this for later.
mPendingAudioErr = err;
mPendingAudioAccessUnit = accessUnit;
@@ -1401,46 +1396,45 @@
size_t smallSize = accessUnit->size();
needMoreData = false;
- if (doBufferAggregation && (biggerBuffer == NULL)
+ if (doBufferAggregation && (mAggregateBuffer == NULL)
// Don't bother if only room for a few small buffers.
- && (smallSize < (kAudioBigBufferSizeBytes / 3))) {
+ && (smallSize < (kAggregateBufferSizeBytes / 3))) {
// Create a larger buffer for combining smaller buffers from the extractor.
- biggerBuffer = new ABuffer(kAudioBigBufferSizeBytes);
- biggerBuffer->setRange(0, 0); // start empty
+ mAggregateBuffer = new ABuffer(kAggregateBufferSizeBytes);
+ mAggregateBuffer->setRange(0, 0); // start empty
}
- if (biggerBuffer != NULL) {
+ if (mAggregateBuffer != NULL) {
int64_t timeUs;
+ int64_t dummy;
bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs);
+ bool bigTimestampValid = mAggregateBuffer->meta()->findInt64("timeUs", &dummy);
// Will the smaller buffer fit?
- size_t bigSize = biggerBuffer->size();
- size_t roomLeft = biggerBuffer->capacity() - bigSize;
+ size_t bigSize = mAggregateBuffer->size();
+ size_t roomLeft = mAggregateBuffer->capacity() - bigSize;
// Should we save this small buffer for the next big buffer?
// If the first small buffer did not have a timestamp then save
// any buffer that does have a timestamp until the next big buffer.
if ((smallSize > roomLeft)
- || (!gotTime && (numSmallBuffers > 0) && smallTimestampValid)) {
+ || (!bigTimestampValid && (bigSize > 0) && smallTimestampValid)) {
mPendingAudioErr = err;
mPendingAudioAccessUnit = accessUnit;
accessUnit.clear();
} else {
+ // Grab time from first small buffer if available.
+ if ((bigSize == 0) && smallTimestampValid) {
+ mAggregateBuffer->meta()->setInt64("timeUs", timeUs);
+ }
// Append small buffer to the bigger buffer.
- memcpy(biggerBuffer->base() + bigSize, accessUnit->data(), smallSize);
+ memcpy(mAggregateBuffer->base() + bigSize, accessUnit->data(), smallSize);
bigSize += smallSize;
- biggerBuffer->setRange(0, bigSize);
+ mAggregateBuffer->setRange(0, bigSize);
- // Keep looping until we run out of room in the biggerBuffer.
+ // Keep looping until we run out of room in the mAggregateBuffer.
needMoreData = true;
- // Grab time from first small buffer if available.
- if ((numSmallBuffers == 0) && smallTimestampValid) {
- biggerBuffer->meta()->setInt64("timeUs", timeUs);
- gotTime = true;
- }
-
- ALOGV("feedDecoderInputData() #%d, smallSize = %zu, bigSize = %zu, capacity = %zu",
- numSmallBuffers, smallSize, bigSize, biggerBuffer->capacity());
- numSmallBuffers++;
+ ALOGV("feedDecoderInputData() smallSize = %zu, bigSize = %zu, capacity = %zu",
+ smallSize, bigSize, mAggregateBuffer->capacity());
}
}
} while (dropAccessUnit || needMoreData);
@@ -1459,9 +1453,11 @@
mCCDecoder->decode(accessUnit);
}
- if (biggerBuffer != NULL) {
- ALOGV("feedDecoderInputData() reply with aggregated buffer, %d", numSmallBuffers);
- reply->setBuffer("buffer", biggerBuffer);
+ if (mAggregateBuffer != NULL) {
+ ALOGV("feedDecoderInputData() reply with aggregated buffer, %zu",
+ mAggregateBuffer->size());
+ reply->setBuffer("buffer", mAggregateBuffer);
+ mAggregateBuffer.clear();
} else {
reply->setBuffer("buffer", accessUnit);
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 89ae11c..2e951bd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -67,6 +67,8 @@
status_t getSelectedTrack(int32_t type, Parcel* reply) const;
status_t selectTrack(size_t trackIndex, bool select);
+ static const size_t kAggregateBufferSizeBytes;
+
protected:
virtual ~NuPlayer();
@@ -158,8 +160,11 @@
// notion of time has changed.
bool mTimeDiscontinuityPending;
+ // Used by feedDecoderInputData to aggregate small buffers into
+ // one large buffer.
sp<ABuffer> mPendingAudioAccessUnit;
status_t mPendingAudioErr;
+ sp<ABuffer> mAggregateBuffer;
FlushStatus mFlushingAudio;
FlushStatus mFlushingVideo;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 163a0b5..87f85e7 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -122,14 +122,17 @@
mCodec->getName(&mComponentName);
+ status_t err;
if (mNativeWindow != NULL) {
// disconnect from surface as MediaCodec will reconnect
- CHECK_EQ((int)NO_ERROR,
- native_window_api_disconnect(
- surface.get(),
- NATIVE_WINDOW_API_MEDIA));
+ err = native_window_api_disconnect(
+ surface.get(), NATIVE_WINDOW_API_MEDIA);
+ // We treat this as a warning, as this is a preparatory step.
+ // Codec will try to connect to the surface, which is where
+ // any error signaling will occur.
+ ALOGW_IF(err != OK, "failed to disconnect from surface: %d", err);
}
- status_t err = mCodec->configure(
+ err = mCodec->configure(
format, surface, NULL /* crypto */, 0 /* flags */);
if (err != OK) {
ALOGE("Failed to configure %s decoder (err=%d)", mComponentName.c_str(), err);
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
index ab7906a..f7aacdd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.cpp
@@ -30,8 +30,10 @@
namespace android {
-static const int kMaxPendingBuffers = 10;
-static const int kMaxCachedBytes = 200000;
+static const size_t kMaxCachedBytes = 200000;
+// The buffers will contain a bit less than kAggregateBufferSizeBytes.
+// So we can start off with just enough buffers to keep the cache full.
+static const size_t kMaxPendingBuffers = 1 + (kMaxCachedBytes / NuPlayer::kAggregateBufferSizeBytes);
NuPlayer::DecoderPassThrough::DecoderPassThrough(
const sp<AMessage> ¬ify)
@@ -39,7 +41,8 @@
mNotify(notify),
mBufferGeneration(0),
mReachedEOS(true),
- mPendingBuffers(0),
+ mPendingBuffersToFill(0),
+ mPendingBuffersToDrain(0),
mCachedBytes(0),
mComponentName("pass through decoder") {
mDecoderLooper = new ALooper;
@@ -79,12 +82,13 @@
void NuPlayer::DecoderPassThrough::onConfigure(const sp<AMessage> &format) {
ALOGV("[%s] onConfigure", mComponentName.c_str());
- mPendingBuffers = 0;
mCachedBytes = 0;
+ mPendingBuffersToFill = 0;
+ mPendingBuffersToDrain = 0;
mReachedEOS = false;
++mBufferGeneration;
- requestABuffer();
+ requestMaxBuffers();
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatOutputFormatChanged);
@@ -98,12 +102,15 @@
return generation != mBufferGeneration;
}
-void NuPlayer::DecoderPassThrough::requestABuffer() {
- if (mCachedBytes >= kMaxCachedBytes || mReachedEOS) {
- ALOGV("[%s] mReachedEOS=%d, max pending buffers(%d:%d)",
- mComponentName.c_str(), (mReachedEOS ? 1 : 0),
- mPendingBuffers, kMaxPendingBuffers);
- return;
+bool NuPlayer::DecoderPassThrough::requestABuffer() {
+ if (mCachedBytes >= kMaxCachedBytes) {
+ ALOGV("[%s] mCachedBytes = %zu",
+ mComponentName.c_str(), mCachedBytes);
+ return false;
+ }
+ if (mReachedEOS) {
+ ALOGV("[%s] reached EOS", mComponentName.c_str());
+ return false;
}
sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, id());
@@ -113,19 +120,16 @@
notify->setInt32("what", kWhatFillThisBuffer);
notify->setMessage("reply", reply);
notify->post();
- mPendingBuffers++;
+ mPendingBuffersToFill++;
+ ALOGV("requestABuffer: #ToFill = %zu, #ToDrain = %zu", mPendingBuffersToFill,
+ mPendingBuffersToDrain);
- // pending buffers will already result in requestABuffer
- if (mPendingBuffers < kMaxPendingBuffers) {
- sp<AMessage> message = new AMessage(kWhatRequestABuffer, id());
- message->setInt32("generation", mBufferGeneration);
- message->post();
- }
- return;
+ return true;
}
void android::NuPlayer::DecoderPassThrough::onInputBufferFilled(
const sp<AMessage> &msg) {
+ --mPendingBuffersToFill;
if (mReachedEOS) {
return;
}
@@ -153,11 +157,16 @@
notify->setBuffer("buffer", buffer);
notify->setMessage("reply", reply);
notify->post();
+ ++mPendingBuffersToDrain;
+ ALOGV("onInputBufferFilled: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu",
+ mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes);
}
void NuPlayer::DecoderPassThrough::onBufferConsumed(int32_t size) {
- mPendingBuffers--;
+ --mPendingBuffersToDrain;
mCachedBytes -= size;
+ ALOGV("onBufferConsumed: #ToFill = %zu, #ToDrain = %zu, cachedBytes = %zu",
+ mPendingBuffersToFill, mPendingBuffersToDrain, mCachedBytes);
requestABuffer();
}
@@ -167,11 +176,20 @@
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kWhatFlushCompleted);
notify->post();
- mPendingBuffers = 0;
+ mPendingBuffersToFill = 0;
+ mPendingBuffersToDrain = 0;
mCachedBytes = 0;
mReachedEOS = false;
}
+void NuPlayer::DecoderPassThrough::requestMaxBuffers() {
+ for (size_t i = 0; i < kMaxPendingBuffers; i++) {
+ if (!requestABuffer()) {
+ break;
+ }
+ }
+}
+
void NuPlayer::DecoderPassThrough::onShutdown() {
++mBufferGeneration;
@@ -229,7 +247,7 @@
case kWhatResume:
{
- requestABuffer();
+ requestMaxBuffers();
break;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
index 8590856..fb20257 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoderPassThrough.h
@@ -55,19 +55,26 @@
sp<AMessage> mNotify;
sp<ALooper> mDecoderLooper;
- void requestABuffer();
+ /** Returns true if a buffer was requested.
+ * Returns false if at EOS or cache already full.
+ */
+ bool requestABuffer();
bool isStaleReply(const sp<AMessage> &msg);
void onConfigure(const sp<AMessage> &format);
void onFlush();
void onInputBufferFilled(const sp<AMessage> &msg);
void onBufferConsumed(int32_t size);
+ void requestMaxBuffers();
void onShutdown();
int32_t mBufferGeneration;
- bool mReachedEOS;
- int32_t mPendingBuffers;
- int32_t mCachedBytes;
+ bool mReachedEOS;
+ // TODO mPendingBuffersToFill and mPendingBuffersToDrain are only for
+ // debugging. They can be removed when the power investigation is done.
+ size_t mPendingBuffersToFill;
+ size_t mPendingBuffersToDrain;
+ size_t mCachedBytes;
AString mComponentName;
DISALLOW_EVIL_CONSTRUCTORS(DecoderPassThrough);
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 8b4dd6f..4569c1c 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_TAG "SoftAAC2"
//#define LOG_NDEBUG 0
+#define LOG_TAG "SoftAAC2"
#include <utils/Log.h>
#include "SoftAAC2.h"
@@ -68,7 +68,6 @@
mOutputBufferCount(0),
mSignalledError(false),
mLastInHeader(NULL),
- mCurrentInputTime(0),
mOutputPortSettingsChange(NONE) {
initPorts();
CHECK_EQ(initDecoder(), (status_t)OK);
@@ -610,9 +609,24 @@
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
@@ -621,136 +635,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();
- AAC_DECODER_ERROR decoderErr =
- aacDecoder_DecodeFrame(mAACDecoder,
- tmpOutBuffer,
- 2048 * MAX_CHANNEL_COUNT,
- 0 /* flags */);
+ UINT inBufferUsedLength = inBufferLength[0] - bytesValid[0];
+ inHeader->nFilledLen -= inBufferUsedLength;
+ inHeader->nOffset += inBufferUsedLength;
- if (decoderErr != AAC_DEC_OK) {
- ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
- }
-
- 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 (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;
+ AAC_DECODER_ERROR decoderErr;
+ do {
+ if (outputDelayRingBufferSamplesLeft() <
+ (mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+ ALOGV("skipping decode: not enough space left in ringbuffer");
+ break;
}
- 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
+ int numconsumed = mStreamInfo->numTotalBytes + mStreamInfo->numBadBytes;
+ decoderErr = aacDecoder_DecodeFrame(mAACDecoder,
+ tmpOutBuffer,
+ 2048 * MAX_CHANNEL_COUNT,
+ 0 /* flags */);
- if (!outputDelayRingBufferPutSamples(tmpOutBuffer,
- mStreamInfo->frameSize * mStreamInfo->numChannels)) {
+ numconsumed = (mStreamInfo->numTotalBytes + mStreamInfo->numBadBytes) - numconsumed;
+ if (numconsumed != 0) {
+ mDecodedSizes.add(numconsumed);
+ }
+
+ if (decoderErr == AAC_DEC_NOT_ENOUGH_BITS) {
+ break;
+ }
+
+ if (decoderErr != AAC_DEC_OK) {
+ ALOGW("aacDecoder_DecodeFrame decoderErr = 0x%4.4x", decoderErr);
+ }
+
+ if (bytesValid[0] != 0) {
+ ALOGE("bytesValid[0] != 0 should never happen");
mSignalledError = true;
- notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, NULL);
+ notify(OMX_EventError, OMX_ErrorUndefined, 0, NULL);
return;
}
- // Discard input buffer.
- inHeader->nFilledLen = 0;
+ size_t numOutBytes =
+ mStreamInfo->frameSize * sizeof(int16_t) * mStreamInfo->numChannels;
- 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;
+ 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");
+ mSignalledError = true;
+ notify(OMX_EventError, OMX_ErrorUndefined, decoderErr, 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);
- }
+ 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);
+ }
+ } while (decoderErr == AAC_DEC_OK);
}
int32_t outputDelay = mStreamInfo->outputDelay * mStreamInfo->numChannels;
@@ -809,8 +823,9 @@
INT_PCM *outBuffer =
reinterpret_cast<INT_PCM *>(outHeader->pBuffer + outHeader->nOffset);
+ int samplesize = mStreamInfo->numChannels * sizeof(int16_t);
if (outHeader->nOffset
- + mStreamInfo->frameSize * mStreamInfo->numChannels * sizeof(int16_t)
+ + mStreamInfo->frameSize * samplesize
> outHeader->nAllocLen) {
ALOGE("buffer overflow");
mSignalledError = true;
@@ -818,17 +833,67 @@
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;
+
+ int available = outputDelayRingBufferSamplesAvailable();
+ int numSamples = outHeader->nAllocLen / sizeof(int16_t);
+ 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;
+ }
}
- outHeader->nFilledLen = mStreamInfo->frameSize * mStreamInfo->numChannels
- * sizeof(int16_t);
+ outHeader->nFilledLen = numSamples * sizeof(int16_t);
+
if (mEndOfInput && !outQueue.empty() && outputDelayRingBufferSamplesAvailable() == 0) {
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
mEndOfOutput = true;
@@ -836,13 +901,13 @@
outHeader->nFlags = 0;
}
- outHeader->nTimeStamp = mAnchorTimes.isEmpty() ? 0 : mAnchorTimes.itemAt(0);
- mAnchorTimes.removeAt(0);
+ outHeader->nTimeStamp = currentTime;
mOutputBufferCount++;
outInfo->mOwnedByUs = false;
outQueue.erase(outQueue.begin());
outInfo = NULL;
+ ALOGV("out timestamp %lld / %d", outHeader->nTimeStamp, outHeader->nFilledLen);
notifyFillBufferDone(outHeader);
outHeader = NULL;
}
@@ -877,8 +942,10 @@
outHeader->nFilledLen = 0;
outHeader->nFlags = OMX_BUFFERFLAG_EOS;
- outHeader->nTimeStamp = mAnchorTimes.itemAt(0);
- mAnchorTimes.removeAt(0);
+ outHeader->nTimeStamp = mBufferTimestamps.itemAt(0);
+ mBufferTimestamps.clear();
+ mBufferSizes.clear();
+ mDecodedSizes.clear();
mOutputBufferCount++;
outInfo->mOwnedByUs = false;
@@ -899,7 +966,9 @@
// depend on fragments from the last one decoded.
// drain all existing data
drainDecoder();
- mAnchorTimes.clear();
+ mBufferTimestamps.clear();
+ mBufferSizes.clear();
+ mDecodedSizes.clear();
mLastInHeader = NULL;
} else {
while (outputDelayRingBufferSamplesAvailable() > 0) {
@@ -955,7 +1024,9 @@
mOutputDelayRingBufferReadPos = 0;
mEndOfInput = false;
mEndOfOutput = false;
- mAnchorTimes.clear();
+ mBufferTimestamps.clear();
+ mBufferSizes.clear();
+ mDecodedSizes.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 865bd15..9fcb598 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.h
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.h
@@ -59,8 +59,9 @@
size_t mOutputBufferCount;
bool mSignalledError;
OMX_BUFFERHEADERTYPE *mLastInHeader;
- int64_t mCurrentInputTime;
- Vector<int64_t> mAnchorTimes;
+ Vector<int32_t> mBufferSizes;
+ Vector<int32_t> mDecodedSizes;
+ Vector<int64_t> mBufferTimestamps;
CDrcPresModeWrapper mDrcWrap;
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
index 0d1ab71..5b2ab84 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.cpp
@@ -134,6 +134,12 @@
}
uint8_t *bitstream = inHeader->pBuffer + inHeader->nOffset;
+ uint32_t *start_code = (uint32_t *)bitstream;
+ bool volHeader = *start_code == 0xB0010000;
+ if (volHeader) {
+ PVCleanUpVideoDecoder(mHandle);
+ mInitialized = false;
+ }
if (!mInitialized) {
uint8_t *vol_data[1];
@@ -141,7 +147,7 @@
vol_data[0] = NULL;
- if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ if ((inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) || volHeader) {
vol_data[0] = bitstream;
vol_size = inHeader->nFilledLen;
}
@@ -169,21 +175,26 @@
PVSetPostProcType((VideoDecControls *) mHandle, 0);
+ bool hasFrameData = false;
if (inHeader->nFlags & OMX_BUFFERFLAG_CODECCONFIG) {
inInfo->mOwnedByUs = false;
inQueue.erase(inQueue.begin());
inInfo = NULL;
notifyEmptyBufferDone(inHeader);
inHeader = NULL;
+ } else if (volHeader) {
+ hasFrameData = true;
}
mInitialized = true;
- if (mode == MPEG4_MODE && portSettingsChanged()) {
+ if (mode == MPEG4_MODE && handlePortSettingsChange()) {
return;
}
- continue;
+ if (!hasFrameData) {
+ continue;
+ }
}
if (!mFramesConfigured) {
@@ -223,7 +234,9 @@
return;
}
- if (portSettingsChanged()) {
+ // H263 doesn't have VOL header, the frame size information is in short header, i.e. the
+ // decoder may detect size change after PVDecodeVideoFrame.
+ if (handlePortSettingsChange()) {
return;
}
@@ -269,7 +282,7 @@
}
}
-bool SoftMPEG4::portSettingsChanged() {
+bool SoftMPEG4::handlePortSettingsChange() {
uint32_t disp_width, disp_height;
PVGetVideoDimensions(mHandle, (int32 *)&disp_width, (int32 *)&disp_height);
@@ -282,25 +295,20 @@
ALOGV("disp_width = %d, disp_height = %d, buf_width = %d, buf_height = %d",
disp_width, disp_height, buf_width, buf_height);
- if (mCropWidth != disp_width
- || mCropHeight != disp_height) {
+ bool cropChanged = false;
+ if (mCropWidth != disp_width || mCropHeight != disp_height) {
mCropLeft = 0;
mCropTop = 0;
mCropWidth = disp_width;
mCropHeight = disp_height;
-
- notify(OMX_EventPortSettingsChanged,
- 1,
- OMX_IndexConfigCommonOutputCrop,
- NULL);
+ cropChanged = true;
}
- if (buf_width != mWidth || buf_height != mHeight) {
- mWidth = buf_width;
- mHeight = buf_height;
-
- updatePortDefinitions();
-
+ bool portWillReset = false;
+ const bool fakeStride = true;
+ SoftVideoDecoderOMXComponent::handlePortSettingsChange(
+ &portWillReset, buf_width, buf_height, cropChanged, fakeStride);
+ if (portWillReset) {
if (mMode == MODE_H263) {
PVCleanUpVideoDecoder(mHandle);
@@ -318,13 +326,9 @@
}
mFramesConfigured = false;
-
- notify(OMX_EventPortSettingsChanged, 1, 0, NULL);
- mOutputPortSettingsChange = AWAITING_DISABLED;
- return true;
}
- return false;
+ return portWillReset;
}
void SoftMPEG4::onPortFlushCompleted(OMX_U32 portIndex) {
diff --git a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h
index de14aaf..8a06a00 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h
+++ b/media/libstagefright/codecs/m4v_h263/dec/SoftMPEG4.h
@@ -67,7 +67,7 @@
status_t initDecoder();
virtual void updatePortDefinitions();
- bool portSettingsChanged();
+ bool handlePortSettingsChange();
DISALLOW_EVIL_CONSTRUCTORS(SoftMPEG4);
};
diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
index b3c350f..b03ec8c 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
@@ -1426,7 +1426,7 @@
video->nBitsForMBID = CalcNumBits((uint)video->nTotalMB - 1); /* otherwise calculate above */
}
size = (int32)video->width * video->height;
- if (video->currVop->predictionType == P_VOP && size > video->videoDecControls->size)
+ if (currVop->predictionType == P_VOP && size > video->videoDecControls->size)
{
status = PV_FAIL;
goto return_point;
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index cc98da0..1899b40 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -65,8 +65,8 @@
CHECK(format->findInt32("color-format", &colorFormatNew));
int32_t widthNew, heightNew;
- CHECK(format->findInt32("width", &widthNew));
- CHECK(format->findInt32("height", &heightNew));
+ CHECK(format->findInt32("stride", &widthNew));
+ CHECK(format->findInt32("slice-height", &heightNew));
int32_t cropLeftNew, cropTopNew, cropRightNew, cropBottomNew;
if (!format->findRect(
diff --git a/media/libstagefright/httplive/LiveSession.cpp b/media/libstagefright/httplive/LiveSession.cpp
index 02ebfc7..3720085 100644
--- a/media/libstagefright/httplive/LiveSession.cpp
+++ b/media/libstagefright/httplive/LiveSession.cpp
@@ -81,6 +81,7 @@
mDiscontinuities.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
mPacketSources.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
mPacketSources2.add(indexToType(i), new AnotherPacketSource(NULL /* meta */));
+ mBuffering[i] = false;
}
}
@@ -133,8 +134,26 @@
sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(stream);
+ ssize_t idx = typeToIndex(stream);
if (!packetSource->hasBufferAvailable(&finalResult)) {
- return finalResult == OK ? -EAGAIN : finalResult;
+ if (finalResult == OK) {
+ mBuffering[idx] = true;
+ return -EAGAIN;
+ } else {
+ return finalResult;
+ }
+ }
+
+ if (mBuffering[idx]) {
+ if (mSwitchInProgress
+ || packetSource->isFinished(0)
+ || packetSource->getEstimatedDurationUs() > 10000000ll) {
+ mBuffering[idx] = false;
+ }
+ }
+
+ if (mBuffering[idx]) {
+ return -EAGAIN;
}
// wait for counterpart
@@ -531,6 +550,19 @@
onSwapped(msg);
break;
}
+
+ case kWhatCheckSwitchDown:
+ {
+ onCheckSwitchDown();
+ break;
+ }
+
+ case kWhatSwitchDown:
+ {
+ onSwitchDown();
+ break;
+ }
+
default:
TRESPASS();
break;
@@ -554,6 +586,21 @@
return (StreamType)(1 << idx);
}
+// static
+ssize_t LiveSession::typeToIndex(int32_t type) {
+ switch (type) {
+ case STREAMTYPE_AUDIO:
+ return 0;
+ case STREAMTYPE_VIDEO:
+ return 1;
+ case STREAMTYPE_SUBTITLES:
+ return 2;
+ default:
+ return -1;
+ };
+ return -1;
+}
+
void LiveSession::onConnect(const sp<AMessage> &msg) {
AString url;
CHECK(msg->findString("url", &url));
@@ -643,6 +690,9 @@
// (finishDisconnect, onFinishDisconnect2)
cancelBandwidthSwitch();
+ // cancel switch down monitor
+ mSwitchDownMonitor.clear();
+
for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
mFetcherInfos.valueAt(i).mFetcher->stopAsync();
}
@@ -1236,12 +1286,6 @@
CHECK(msg->findInt32("streamMask", (int32_t *)&streamMask));
CHECK(msg->findInt32("resumeMask", (int32_t *)&resumeMask));
- for (size_t i = 0; i < kMaxStreams; ++i) {
- if (streamMask & indexToType(i)) {
- CHECK(msg->findString(mStreams[i].uriKey().c_str(), &mStreams[i].mUri));
- }
- }
-
int64_t timeUs;
int32_t pickTrack;
bool switching = false;
@@ -1257,7 +1301,20 @@
mRealTimeBaseUs = ALooper::GetNowUs() - timeUs;
}
+ for (size_t i = 0; i < kMaxStreams; ++i) {
+ if (streamMask & indexToType(i)) {
+ if (switching) {
+ CHECK(msg->findString(mStreams[i].uriKey().c_str(), &mStreams[i].mNewUri));
+ } else {
+ CHECK(msg->findString(mStreams[i].uriKey().c_str(), &mStreams[i].mUri));
+ }
+ }
+ }
+
mNewStreamMask = streamMask | resumeMask;
+ if (switching) {
+ mSwapMask = mStreamMask & ~resumeMask;
+ }
// Of all existing fetchers:
// * Resume fetchers that are still needed and assign them original packet sources.
@@ -1307,7 +1364,7 @@
}
AString uri;
- uri = mStreams[i].mUri;
+ uri = switching ? mStreams[i].mNewUri : mStreams[i].mUri;
sp<PlaylistFetcher> fetcher = addFetcher(uri.c_str());
CHECK(fetcher != NULL);
@@ -1320,7 +1377,8 @@
// TRICKY: looping from i as earlier streams are already removed from streamMask
for (size_t j = i; j < kMaxStreams; ++j) {
- if ((streamMask & indexToType(j)) && uri == mStreams[j].mUri) {
+ const AString &streamUri = switching ? mStreams[j].mNewUri : mStreams[j].mUri;
+ if ((streamMask & indexToType(j)) && uri == streamUri) {
sources[j] = mPacketSources.valueFor(indexToType(j));
if (timeUs >= 0) {
@@ -1409,7 +1467,6 @@
mReconfigurationInProgress = false;
if (switching) {
mSwitchInProgress = true;
- mSwapMask = streamMask;
} else {
mStreamMask = mNewStreamMask;
}
@@ -1428,6 +1485,15 @@
int32_t stream;
CHECK(msg->findInt32("stream", &stream));
+
+ ssize_t idx = typeToIndex(stream);
+ CHECK(idx >= 0);
+ if ((mNewStreamMask & stream) && mStreams[idx].mNewUri.empty()) {
+ ALOGW("swapping stream type %d %s to empty stream", stream, mStreams[idx].mUri.c_str());
+ }
+ mStreams[idx].mUri = mStreams[idx].mNewUri;
+ mStreams[idx].mNewUri.clear();
+
mSwapMask &= ~stream;
if (mSwapMask != 0) {
return;
@@ -1439,11 +1505,58 @@
StreamType extraStream = (StreamType) (extraStreams & ~(extraStreams - 1));
swapPacketSource(extraStream);
extraStreams &= ~extraStream;
+
+ idx = typeToIndex(extraStream);
+ CHECK(idx >= 0);
+ if (mStreams[idx].mNewUri.empty()) {
+ ALOGW("swapping extra stream type %d %s to empty stream",
+ extraStream, mStreams[idx].mUri.c_str());
+ }
+ mStreams[idx].mUri = mStreams[idx].mNewUri;
+ mStreams[idx].mNewUri.clear();
}
tryToFinishBandwidthSwitch();
}
+void LiveSession::onCheckSwitchDown() {
+ if (mSwitchDownMonitor == NULL) {
+ return;
+ }
+
+ for (size_t i = 0; i < kMaxStreams; ++i) {
+ int32_t targetDuration;
+ sp<AnotherPacketSource> packetSource = mPacketSources.valueFor(indexToType(i));
+ sp<AMessage> meta = packetSource->getLatestDequeuedMeta();
+
+ if (meta != NULL && meta->findInt32("targetDuration", &targetDuration) ) {
+ int64_t bufferedDurationUs = packetSource->getEstimatedDurationUs();
+ int64_t targetDurationUs = targetDuration * 1000000ll;
+
+ if (bufferedDurationUs < targetDurationUs / 3) {
+ (new AMessage(kWhatSwitchDown, id()))->post();
+ break;
+ }
+ }
+ }
+
+ mSwitchDownMonitor->post(1000000ll);
+}
+
+void LiveSession::onSwitchDown() {
+ if (mReconfigurationInProgress || mSwitchInProgress || mCurBandwidthIndex == 0) {
+ return;
+ }
+
+ ssize_t bandwidthIndex = getBandwidthIndex();
+ if (bandwidthIndex < mCurBandwidthIndex) {
+ changeConfiguration(-1, bandwidthIndex, false);
+ return;
+ }
+
+ changeConfiguration(-1, mCurBandwidthIndex - 1, false);
+}
+
// Mark switch done when:
// 1. all old buffers are swapped out
void LiveSession::tryToFinishBandwidthSwitch() {
@@ -1481,6 +1594,28 @@
mSwitchGeneration++;
mSwitchInProgress = false;
mSwapMask = 0;
+
+ for (size_t i = 0; i < mFetcherInfos.size(); ++i) {
+ FetcherInfo& info = mFetcherInfos.editValueAt(i);
+ if (info.mToBeRemoved) {
+ info.mToBeRemoved = false;
+ }
+ }
+
+ for (size_t i = 0; i < kMaxStreams; ++i) {
+ if (!mStreams[i].mNewUri.empty()) {
+ ssize_t j = mFetcherInfos.indexOfKey(mStreams[i].mNewUri);
+ if (j < 0) {
+ mStreams[i].mNewUri.clear();
+ continue;
+ }
+
+ const FetcherInfo &info = mFetcherInfos.valueAt(j);
+ info.mFetcher->stopAsync();
+ mFetcherInfos.removeItemsAt(j);
+ mStreams[i].mNewUri.clear();
+ }
+ }
}
bool LiveSession::canSwitchBandwidthTo(size_t bandwidthIndex) {
@@ -1527,6 +1662,9 @@
notify->post();
mInPreparationPhase = false;
+
+ mSwitchDownMonitor = new AMessage(kWhatCheckSwitchDown, id());
+ mSwitchDownMonitor->post();
}
} // namespace android
diff --git a/media/libstagefright/httplive/LiveSession.h b/media/libstagefright/httplive/LiveSession.h
index aa06a3f..6be86cf 100644
--- a/media/libstagefright/httplive/LiveSession.h
+++ b/media/libstagefright/httplive/LiveSession.h
@@ -108,6 +108,8 @@
kWhatChangeConfiguration3 = 'chC3',
kWhatFinishDisconnect2 = 'fin2',
kWhatSwapped = 'swap',
+ kWhatCheckSwitchDown = 'ckSD',
+ kWhatSwitchDown = 'sDwn',
};
struct BandwidthItem {
@@ -124,7 +126,7 @@
struct StreamItem {
const char *mType;
- AString mUri;
+ AString mUri, mNewUri;
size_t mCurDiscontinuitySeq;
int64_t mLastDequeuedTimeUs;
int64_t mLastSampleDurationUs;
@@ -151,6 +153,7 @@
sp<IMediaHTTPService> mHTTPService;
bool mInPreparationPhase;
+ bool mBuffering[kMaxStreams];
sp<HTTPBase> mHTTPDataSource;
KeyedVector<String8, String8> mExtraHeaders;
@@ -202,6 +205,7 @@
bool mFirstTimeUsValid;
int64_t mFirstTimeUs;
int64_t mLastSeekTimeUs;
+ sp<AMessage> mSwitchDownMonitor;
KeyedVector<size_t, int64_t> mDiscontinuityAbsStartTimesUs;
KeyedVector<size_t, int64_t> mDiscontinuityOffsetTimesUs;
@@ -239,6 +243,7 @@
static int SortByBandwidth(const BandwidthItem *, const BandwidthItem *);
static StreamType indexToType(int idx);
+ static ssize_t typeToIndex(int32_t type);
void changeConfiguration(
int64_t timeUs, size_t bandwidthIndex, bool pickTrack = false);
@@ -246,6 +251,8 @@
void onChangeConfiguration2(const sp<AMessage> &msg);
void onChangeConfiguration3(const sp<AMessage> &msg);
void onSwapped(const sp<AMessage> &msg);
+ void onCheckSwitchDown();
+ void onSwitchDown();
void tryToFinishBandwidthSwitch();
void scheduleCheckBandwidthEvent();
diff --git a/media/libstagefright/httplive/PlaylistFetcher.cpp b/media/libstagefright/httplive/PlaylistFetcher.cpp
index 3ef0f06..1166762 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.cpp
+++ b/media/libstagefright/httplive/PlaylistFetcher.cpp
@@ -945,7 +945,7 @@
}
if (err == -EAGAIN) {
- // starting sequence number too low
+ // starting sequence number too low/high
mTSParser.clear();
postMonitorQueue();
return;
@@ -1017,12 +1017,39 @@
return;
}
- mStartup = false;
++mSeqNumber;
postMonitorQueue();
}
+int32_t PlaylistFetcher::getSeqNumberWithAnchorTime(int64_t anchorTimeUs) const {
+ int32_t firstSeqNumberInPlaylist, lastSeqNumberInPlaylist;
+ if (mPlaylist->meta() == NULL
+ || !mPlaylist->meta()->findInt32("media-sequence", &firstSeqNumberInPlaylist)) {
+ firstSeqNumberInPlaylist = 0;
+ }
+ lastSeqNumberInPlaylist = firstSeqNumberInPlaylist + mPlaylist->size() - 1;
+
+ int32_t index = mSeqNumber - firstSeqNumberInPlaylist - 1;
+ while (index >= 0 && anchorTimeUs > mStartTimeUs) {
+ sp<AMessage> itemMeta;
+ CHECK(mPlaylist->itemAt(index, NULL /* uri */, &itemMeta));
+
+ int64_t itemDurationUs;
+ CHECK(itemMeta->findInt64("durationUs", &itemDurationUs));
+
+ anchorTimeUs -= itemDurationUs;
+ --index;
+ }
+
+ int32_t newSeqNumber = firstSeqNumberInPlaylist + index + 1;
+ if (newSeqNumber <= lastSeqNumberInPlaylist) {
+ return newSeqNumber;
+ } else {
+ return lastSeqNumberInPlaylist;
+ }
+}
+
int32_t PlaylistFetcher::getSeqNumberForDiscontinuity(size_t discontinuitySeq) const {
int32_t firstSeqNumberInPlaylist;
if (mPlaylist->meta() == NULL
@@ -1192,60 +1219,84 @@
if (timeUs < 0) {
timeUs = 0;
}
- } else if (mAdaptive && timeUs > mStartTimeUs) {
- int32_t seq;
- if (mStartTimeUsNotify != NULL
- && !mStartTimeUsNotify->findInt32("discontinuitySeq", &seq)) {
- mStartTimeUsNotify->setInt32("discontinuitySeq", mDiscontinuitySeq);
- }
- int64_t startTimeUs;
- if (mStartTimeUsNotify != NULL
- && !mStartTimeUsNotify->findInt64(key, &startTimeUs)) {
- mStartTimeUsNotify->setInt64(key, timeUs);
-
- uint32_t streamMask = 0;
- mStartTimeUsNotify->findInt32("streamMask", (int32_t *) &streamMask);
- streamMask |= mPacketSources.keyAt(i);
- mStartTimeUsNotify->setInt32("streamMask", streamMask);
-
- if (streamMask == mStreamTypeMask) {
- mStartTimeUsNotify->post();
- mStartTimeUsNotify.clear();
- }
- }
}
if (timeUs < mStartTimeUs) {
- if (mAdaptive) {
- int32_t targetDuration;
- mPlaylist->meta()->findInt32("target-duration", &targetDuration);
- int32_t incr = (mStartTimeUs - timeUs) / 1000000 / targetDuration;
- if (incr == 0) {
- // increment mSeqNumber by at least one
- incr = 1;
- }
- mSeqNumber += incr;
- err = -EAGAIN;
- break;
- } else {
- // buffer up to the closest preceding IDR frame
- ALOGV("timeUs %" PRId64 " us < mStartTimeUs %" PRId64 " us",
- timeUs, mStartTimeUs);
- const char *mime;
- sp<MetaData> format = source->getFormat();
- bool isAvc = false;
- if (format != NULL && format->findCString(kKeyMIMEType, &mime)
- && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
- isAvc = true;
- }
- if (isAvc && IsIDR(accessUnit)) {
- mVideoBuffer->clear();
- }
- if (isAvc) {
- mVideoBuffer->queueAccessUnit(accessUnit);
- }
+ // buffer up to the closest preceding IDR frame
+ ALOGV("timeUs %" PRId64 " us < mStartTimeUs %" PRId64 " us",
+ timeUs, mStartTimeUs);
+ const char *mime;
+ sp<MetaData> format = source->getFormat();
+ bool isAvc = false;
+ if (format != NULL && format->findCString(kKeyMIMEType, &mime)
+ && !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+ isAvc = true;
+ }
+ if (isAvc && IsIDR(accessUnit)) {
+ mVideoBuffer->clear();
+ }
+ if (isAvc) {
+ mVideoBuffer->queueAccessUnit(accessUnit);
+ }
- continue;
+ continue;
+ }
+ }
+
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+ if (mStartTimeUsNotify != NULL && timeUs > mStartTimeUs) {
+
+ int32_t targetDurationSecs;
+ CHECK(mPlaylist->meta()->findInt32("target-duration", &targetDurationSecs));
+ int64_t targetDurationUs = targetDurationSecs * 1000000ll;
+ // mStartup
+ // mStartup is true until we have queued a packet for all the streams
+ // we are fetching. We queue packets whose timestamps are greater than
+ // mStartTimeUs.
+ // mSegmentStartTimeUs >= 0
+ // mSegmentStartTimeUs is non-negative when adapting or switching tracks
+ // timeUs - mStartTimeUs > targetDurationUs:
+ // This and the 2 above conditions should only happen when adapting in a live
+ // stream; the old fetcher has already fetched to mStartTimeUs; the new fetcher
+ // would start fetching after timeUs, which should be greater than mStartTimeUs;
+ // the old fetcher would then continue fetching data until timeUs. We don't want
+ // timeUs to be too far ahead of mStartTimeUs because we want the old fetcher to
+ // stop as early as possible. The definition of being "too far ahead" is
+ // arbitrary; here we use targetDurationUs as threshold.
+ if (mStartup && mSegmentStartTimeUs >= 0
+ && timeUs - mStartTimeUs > targetDurationUs) {
+ // we just guessed a starting timestamp that is too high when adapting in a
+ // live stream; re-adjust based on the actual timestamp extracted from the
+ // media segment; if we didn't move backward after the re-adjustment
+ // (newSeqNumber), start at least 1 segment prior.
+ int32_t newSeqNumber = getSeqNumberWithAnchorTime(timeUs);
+ if (newSeqNumber >= mSeqNumber) {
+ --mSeqNumber;
+ } else {
+ mSeqNumber = newSeqNumber;
+ }
+ mStartTimeUsNotify = mNotify->dup();
+ mStartTimeUsNotify->setInt32("what", kWhatStartedAt);
+ return -EAGAIN;
+ }
+
+ int32_t seq;
+ if (!mStartTimeUsNotify->findInt32("discontinuitySeq", &seq)) {
+ mStartTimeUsNotify->setInt32("discontinuitySeq", mDiscontinuitySeq);
+ }
+ int64_t startTimeUs;
+ if (!mStartTimeUsNotify->findInt64(key, &startTimeUs)) {
+ mStartTimeUsNotify->setInt64(key, timeUs);
+
+ uint32_t streamMask = 0;
+ mStartTimeUsNotify->findInt32("streamMask", (int32_t *) &streamMask);
+ streamMask |= mPacketSources.keyAt(i);
+ mStartTimeUsNotify->setInt32("streamMask", streamMask);
+
+ if (streamMask == mStreamTypeMask) {
+ mStartup = false;
+ mStartTimeUsNotify->post();
+ mStartTimeUsNotify.clear();
}
}
}
diff --git a/media/libstagefright/httplive/PlaylistFetcher.h b/media/libstagefright/httplive/PlaylistFetcher.h
index daefb26..4ba37fa 100644
--- a/media/libstagefright/httplive/PlaylistFetcher.h
+++ b/media/libstagefright/httplive/PlaylistFetcher.h
@@ -104,7 +104,12 @@
uint32_t mStreamTypeMask;
int64_t mStartTimeUs;
+
+ // Start time relative to the beginning of the first segment in the initial
+ // playlist. It's value is initialized to a non-negative value only when we are
+ // adapting or switching tracks.
int64_t mSegmentStartTimeUs;
+
ssize_t mDiscontinuitySeq;
bool mStartTimeUsRelative;
sp<AMessage> mStopParams; // message containing the latest timestamps we should fetch.
diff --git a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
index 4a6ab63..8cb8ed7 100644
--- a/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
+++ b/media/libstagefright/include/SoftVideoDecoderOMXComponent.h
@@ -66,7 +66,8 @@
virtual void updatePortDefinitions(bool updateCrop = true);
void handlePortSettingsChange(
- bool *portWillReset, uint32_t width, uint32_t height, bool cropChanged = false);
+ bool *portWillReset, uint32_t width, uint32_t height,
+ bool cropChanged = false, bool fakeStride = false);
void copyYV12FrameToOutputBuffer(
uint8_t *dst, const uint8_t *srcY, const uint8_t *srcU, const uint8_t *srcV,
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
index 010063f..c74c3e7 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
@@ -42,7 +42,8 @@
mLastQueuedTimeUs(0),
mEOSResult(OK),
mLatestEnqueuedMeta(NULL),
- mLatestDequeuedMeta(NULL) {
+ mLatestDequeuedMeta(NULL),
+ mQueuedDiscontinuityCount(0) {
setFormat(meta);
}
@@ -122,6 +123,7 @@
mFormat.clear();
}
+ --mQueuedDiscontinuityCount;
return INFO_DISCONTINUITY;
}
@@ -210,6 +212,11 @@
mBuffers.push_back(buffer);
mCondition.signal();
+ int32_t discontinuity;
+ if (buffer->meta()->findInt32("discontinuity", &discontinuity)) {
+ ++mQueuedDiscontinuityCount;
+ }
+
if (mLatestEnqueuedMeta == NULL) {
mLatestEnqueuedMeta = buffer->meta();
} else {
@@ -226,6 +233,7 @@
mBuffers.clear();
mEOSResult = OK;
+ mQueuedDiscontinuityCount = 0;
mFormat = NULL;
mLatestEnqueuedMeta = NULL;
@@ -262,6 +270,7 @@
mEOSResult = OK;
mLastQueuedTimeUs = 0;
mLatestEnqueuedMeta = NULL;
+ ++mQueuedDiscontinuityCount;
sp<ABuffer> buffer = new ABuffer(0);
buffer->meta()->setInt32("discontinuity", static_cast<int32_t>(type));
@@ -291,7 +300,10 @@
int64_t AnotherPacketSource::getBufferedDurationUs(status_t *finalResult) {
Mutex::Autolock autoLock(mLock);
+ return getBufferedDurationUs_l(finalResult);
+}
+int64_t AnotherPacketSource::getBufferedDurationUs_l(status_t *finalResult) {
*finalResult = mEOSResult;
if (mBuffers.empty()) {
@@ -300,6 +312,7 @@
int64_t time1 = -1;
int64_t time2 = -1;
+ int64_t durationUs = 0;
List<sp<ABuffer> >::iterator it = mBuffers.begin();
while (it != mBuffers.end()) {
@@ -307,20 +320,64 @@
int64_t timeUs;
if (buffer->meta()->findInt64("timeUs", &timeUs)) {
- if (time1 < 0) {
+ if (time1 < 0 || timeUs < time1) {
time1 = timeUs;
}
- time2 = timeUs;
+ if (time2 < 0 || timeUs > time2) {
+ time2 = timeUs;
+ }
} else {
// This is a discontinuity, reset everything.
+ durationUs += time2 - time1;
time1 = time2 = -1;
}
++it;
}
- return time2 - time1;
+ return durationUs + (time2 - time1);
+}
+
+// A cheaper but less precise version of getBufferedDurationUs that we would like to use in
+// LiveSession::dequeueAccessUnit to trigger downwards adaptation.
+int64_t AnotherPacketSource::getEstimatedDurationUs() {
+ Mutex::Autolock autoLock(mLock);
+ if (mBuffers.empty()) {
+ return 0;
+ }
+
+ if (mQueuedDiscontinuityCount > 0) {
+ status_t finalResult;
+ return getBufferedDurationUs_l(&finalResult);
+ }
+
+ List<sp<ABuffer> >::iterator it = mBuffers.begin();
+ sp<ABuffer> buffer = *it;
+
+ int64_t startTimeUs;
+ buffer->meta()->findInt64("timeUs", &startTimeUs);
+ if (startTimeUs < 0) {
+ return 0;
+ }
+
+ it = mBuffers.end();
+ --it;
+ buffer = *it;
+
+ int64_t endTimeUs;
+ buffer->meta()->findInt64("timeUs", &endTimeUs);
+ if (endTimeUs < 0) {
+ return 0;
+ }
+
+ int64_t diffUs;
+ if (endTimeUs > startTimeUs) {
+ diffUs = endTimeUs - startTimeUs;
+ } else {
+ diffUs = startTimeUs - endTimeUs;
+ }
+ return diffUs;
}
status_t AnotherPacketSource::nextBufferTime(int64_t *timeUs) {
diff --git a/media/libstagefright/mpeg2ts/AnotherPacketSource.h b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
index 0c717d7..809a858 100644
--- a/media/libstagefright/mpeg2ts/AnotherPacketSource.h
+++ b/media/libstagefright/mpeg2ts/AnotherPacketSource.h
@@ -49,6 +49,8 @@
// presentation timestamps since the last discontinuity (if any).
int64_t getBufferedDurationUs(status_t *finalResult);
+ int64_t getEstimatedDurationUs();
+
status_t nextBufferTime(int64_t *timeUs);
void queueAccessUnit(const sp<ABuffer> &buffer);
@@ -83,7 +85,10 @@
sp<AMessage> mLatestEnqueuedMeta;
sp<AMessage> mLatestDequeuedMeta;
+ size_t mQueuedDiscontinuityCount;
+
bool wasFormatChange(int32_t discontinuityType) const;
+ int64_t getBufferedDurationUs_l(status_t *finalResult);
DISALLOW_EVIL_CONSTRUCTORS(AnotherPacketSource);
};
diff --git a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
index 37535ce..1cb1859 100644
--- a/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
+++ b/media/libstagefright/omx/SoftVideoDecoderOMXComponent.cpp
@@ -151,7 +151,7 @@
}
void SoftVideoDecoderOMXComponent::handlePortSettingsChange(
- bool *portWillReset, uint32_t width, uint32_t height, bool cropChanged) {
+ bool *portWillReset, uint32_t width, uint32_t height, bool cropChanged, bool fakeStride) {
*portWillReset = false;
bool sizeChanged = (width != mWidth || height != mHeight);
@@ -177,6 +177,19 @@
*portWillReset = true;
} else {
updatePortDefinitions(updateCrop);
+
+ if (fakeStride) {
+ // MAJOR HACK that is not pretty, it's just to fool the renderer to read the correct
+ // data.
+ // Some software decoders (e.g. SoftMPEG4) fill decoded frame directly to output
+ // buffer without considering the output buffer stride and slice height. So this is
+ // used to signal how the buffer is arranged. The alternative is to re-arrange the
+ // output buffer in SoftMPEG4, but that results in memcopies.
+ OMX_PARAM_PORTDEFINITIONTYPE *def = &editPortInfo(kOutputPortIndex)->mDef;
+ def->format.video.nStride = mWidth;
+ def->format.video.nSliceHeight = mHeight;
+ }
+
notify(OMX_EventPortSettingsChanged, kOutputPortIndex,
OMX_IndexConfigCommonOutputCrop, NULL);
}
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 1843722..e200857 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -418,6 +418,13 @@
mRecordThreads.valueAt(i)->dump(fd, args);
}
+ // dump orphan effect chains
+ if (mOrphanEffectChains.size() != 0) {
+ write(fd, " Orphan Effect Chains\n", strlen(" Orphan Effect Chains\n"));
+ for (size_t i = 0; i < mOrphanEffectChains.size(); i++) {
+ mOrphanEffectChains.valueAt(i)->dump(fd, args);
+ }
+ }
// dump all hardware devs
for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice();
@@ -796,9 +803,14 @@
}
AutoMutex lock(mHardwareLock);
- audio_hw_device_t *dev = mPrimaryHardwareDev->hwDevice();
mHardwareStatus = AUDIO_HW_SET_MIC_MUTE;
- ret = dev->set_mic_mute(dev, state);
+ for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
+ audio_hw_device_t *dev = mAudioHwDevs.valueAt(i)->hwDevice();
+ status_t result = dev->set_mic_mute(dev, state);
+ if (result != NO_ERROR) {
+ ret = result;
+ }
+ }
mHardwareStatus = AUDIO_HW_IDLE;
return ret;
}
@@ -1416,7 +1428,7 @@
*sessionId = lSessionId;
}
}
- ALOGV("openRecord() lSessionId: %d", lSessionId);
+ ALOGV("openRecord() lSessionId: %d input %d", lSessionId, input);
// TODO: the uid should be passed in as a parameter to openRecord
recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
@@ -2022,6 +2034,16 @@
}
ALOGV("closeInput() %d", input);
+ {
+ // If we still have effect chains, it means that a client still holds a handle
+ // on at least one effect. We must keep the chain alive in case a new record
+ // thread is opened for a new capture on the same session
+ Mutex::Autolock _sl(thread->mLock);
+ Vector< sp<EffectChain> > effectChains = thread->getEffectChains_l();
+ for (size_t i = 0; i < effectChains.size(); i++) {
+ putOrphanEffectChain_l(effectChains[i]);
+ }
+ }
audioConfigChanged(AudioSystem::INPUT_CLOSED, input, NULL);
mRecordThreads.removeItem(input);
}
@@ -2451,6 +2473,13 @@
lStatus = BAD_VALUE;
goto Exit;
}
+ } else {
+ // Check if one effect chain was awaiting for an effect to be created on this
+ // session and used it instead of creating a new one.
+ sp<EffectChain> chain = getOrphanEffectChain_l((audio_session_t)sessionId);
+ if (chain != 0) {
+ thread->addEffectChain_l(chain);
+ }
}
sp<Client> client = registerPid(pid);
@@ -2623,6 +2652,49 @@
}
+status_t AudioFlinger::putOrphanEffectChain_l(const sp<AudioFlinger::EffectChain>& chain)
+{
+ audio_session_t session = (audio_session_t)chain->sessionId();
+ ssize_t index = mOrphanEffectChains.indexOfKey(session);
+ ALOGV("putOrphanEffectChain_l session %d index %d", session, index);
+ if (index >= 0) {
+ ALOGW("putOrphanEffectChain_l chain for session %d already present", session);
+ return ALREADY_EXISTS;
+ }
+ mOrphanEffectChains.add(session, chain);
+ return NO_ERROR;
+}
+
+sp<AudioFlinger::EffectChain> AudioFlinger::getOrphanEffectChain_l(audio_session_t session)
+{
+ sp<EffectChain> chain;
+ ssize_t index = mOrphanEffectChains.indexOfKey(session);
+ ALOGV("getOrphanEffectChain_l session %d index %d", session, index);
+ if (index >= 0) {
+ chain = mOrphanEffectChains.valueAt(index);
+ mOrphanEffectChains.removeItemsAt(index);
+ }
+ return chain;
+}
+
+bool AudioFlinger::updateOrphanEffectChains(const sp<AudioFlinger::EffectModule>& effect)
+{
+ Mutex::Autolock _l(mLock);
+ audio_session_t session = (audio_session_t)effect->sessionId();
+ ssize_t index = mOrphanEffectChains.indexOfKey(session);
+ ALOGV("updateOrphanEffectChains session %d index %d", session, index);
+ if (index >= 0) {
+ sp<EffectChain> chain = mOrphanEffectChains.valueAt(index);
+ if (chain->removeEffect_l(effect) == 0) {
+ ALOGV("updateOrphanEffectChains removing effect chain at index %d", index);
+ mOrphanEffectChains.removeItemsAt(index);
+ }
+ return true;
+ }
+ return false;
+}
+
+
struct Entry {
#define MAX_NAME 32 // %Y%m%d%H%M%S_%d.wav
char mName[MAX_NAME];
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 753314f..1003017 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -569,6 +569,23 @@
bool isNonOffloadableGlobalEffectEnabled_l();
void onNonOffloadableGlobalEffectEnable();
+ // Store an effect chain to mOrphanEffectChains keyed vector.
+ // Called when a thread exits and effects are still attached to it.
+ // If effects are later created on the same session, they will reuse the same
+ // effect chain and same instances in the effect library.
+ // return ALREADY_EXISTS if a chain with the same session already exists in
+ // mOrphanEffectChains. Note that this should never happen as there is only one
+ // chain for a given session and it is attached to only one thread at a time.
+ status_t putOrphanEffectChain_l(const sp<EffectChain>& chain);
+ // Get an effect chain for the specified session in mOrphanEffectChains and remove
+ // it if found. Returns 0 if not found (this is the most common case).
+ sp<EffectChain> getOrphanEffectChain_l(audio_session_t session);
+ // Called when the last effect handle on an effect instance is removed. If this
+ // effect belongs to an effect chain in mOrphanEffectChains, the chain is updated
+ // and removed from mOrphanEffectChains if it does not contain any effect.
+ // Return true if the effect was found in mOrphanEffectChains, false otherwise.
+ bool updateOrphanEffectChains(const sp<EffectModule>& effect);
+
class AudioHwDevice {
public:
enum Flags {
@@ -713,6 +730,9 @@
Vector < sp<SyncEvent> > mPendingSyncEvents; // sync events awaiting for a session
// to be created
+ // Effect chains without a valid thread
+ DefaultKeyedVector< audio_session_t , sp<EffectChain> > mOrphanEffectChains;
+
private:
sp<Client> registerPid(pid_t pid); // always returns non-0
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index 365f271..15f1f23 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -68,7 +68,8 @@
mStatus(NO_INIT), mState(IDLE),
// mMaxDisableWaitCnt is set by configure() and not used before then
// mDisableWaitCnt is set by process() and updateState() and not used before then
- mSuspended(false)
+ mSuspended(false),
+ mAudioFlinger(thread->mAudioFlinger)
{
ALOGV("Constructor %p", this);
int lStatus;
@@ -197,9 +198,19 @@
// destructor before we exit
sp<EffectModule> keep(this);
{
- sp<ThreadBase> thread = mThread.promote();
- if (thread != 0) {
- thread->disconnectEffect(keep, handle, unpinIfLast);
+ if (removeHandle(handle) == 0) {
+ if (!isPinned() || unpinIfLast) {
+ sp<ThreadBase> thread = mThread.promote();
+ if (thread != 0) {
+ Mutex::Autolock _l(thread->mLock);
+ thread->removeEffect_l(this);
+ }
+ sp<AudioFlinger> af = mAudioFlinger.promote();
+ if (af != 0) {
+ af->updateOrphanEffectChains(this);
+ }
+ AudioSystem::unregisterEffect(mId);
+ }
}
}
return mHandles.size();
@@ -1911,4 +1922,13 @@
return false;
}
+void AudioFlinger::EffectChain::setThread(const sp<ThreadBase>& thread)
+{
+ Mutex::Autolock _l(mLock);
+ mThread = thread;
+ for (size_t i = 0; i < mEffects.size(); i++) {
+ mEffects[i]->setThread(thread);
+ }
+}
+
}; // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 4170fd4..eaf90e7 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -153,6 +153,7 @@
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
bool mSuspended; // effect is suspended: temporarily disabled by framework
bool mOffloaded; // effect is currently offloaded to the audio DSP
+ wp<AudioFlinger> mAudioFlinger;
};
// The EffectHandle class implements the IEffect interface. It provides resources
@@ -347,6 +348,8 @@
void clearInputBuffer_l(sp<ThreadBase> thread);
+ void setThread(const sp<ThreadBase>& thread);
+
wp<ThreadBase> mThread; // parent mixer thread
Mutex mLock; // mutex protecting effect list
Vector< sp<EffectModule> > mEffects; // list of effect modules
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 97b1753..3d17c89 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1147,21 +1147,6 @@
}
}
-void AudioFlinger::ThreadBase::disconnectEffect(const sp<EffectModule>& effect,
- EffectHandle *handle,
- bool unpinIfLast) {
-
- Mutex::Autolock _l(mLock);
- ALOGV("disconnectEffect() %p effect %p", this, effect.get());
- // delete the effect module if removing last handle on it
- if (effect->removeHandle(handle) == 0) {
- if (!effect->isPinned() || unpinIfLast) {
- removeEffect_l(effect);
- AudioSystem::unregisterEffect(effect->id());
- }
- }
-}
-
void AudioFlinger::ThreadBase::getAudioPortConfig(struct audio_port_config *config)
{
config->type = AUDIO_PORT_TYPE_MIX;
@@ -2278,7 +2263,7 @@
}
}
}
-
+ chain->setThread(this);
chain->setInBuffer(buffer, ownsBuffer);
chain->setOutBuffer(reinterpret_cast<int16_t*>(mEffectBufferEnabled
? mEffectBuffer : mSinkBuffer));
@@ -6188,10 +6173,11 @@
{
// only one chain per input thread
if (mEffectChains.size() != 0) {
+ ALOGW("addEffectChain_l() already one chain %p on thread %p", chain.get(), this);
return INVALID_OPERATION;
}
ALOGV("addEffectChain_l() %p on thread %p", chain.get(), this);
-
+ chain->setThread(this);
chain->setInBuffer(NULL);
chain->setOutBuffer(NULL);
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 648502b..fd025b5 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -283,9 +283,6 @@
effect_descriptor_t *desc,
int *enabled,
status_t *status /*non-NULL*/);
- void disconnectEffect(const sp< EffectModule>& effect,
- EffectHandle *handle,
- bool unpinIfLast);
// return values for hasAudioSession (bit field)
enum effect_state {
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 864daa5..98bf96e 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -166,6 +166,7 @@
sp<NBAIO_Source> mTeeSource;
bool mTerminated;
track_type mType; // must be one of TYPE_DEFAULT, TYPE_OUTPUT, TYPE_PATCH ...
+ audio_io_handle_t mThreadIoHandle; // I/O handle of the thread the track is attached to
};
// PatchProxyBufferProvider interface is implemented by PatchTrack and PatchRecord.
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 6cbab04..c0a75b9 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -96,7 +96,8 @@
mServerProxy(NULL),
mId(android_atomic_inc(&nextTrackId)),
mTerminated(false),
- mType(type)
+ mType(type),
+ mThreadIoHandle(thread->id())
{
// if the caller is us, trust the specified uid
if (IPCThreadState::self()->getCallingPid() != getpid_cached || clientUid == -1) {
@@ -482,14 +483,15 @@
// this Track with its member mTrack.
sp<Track> keep(this);
{ // scope for mLock
+ bool wasActive = false;
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
Mutex::Autolock _l(thread->mLock);
PlaybackThread *playbackThread = (PlaybackThread *)thread.get();
- bool wasActive = playbackThread->destroyTrack_l(this);
- if (isExternalTrack() && !wasActive) {
- AudioSystem::releaseOutput(thread->id());
- }
+ wasActive = playbackThread->destroyTrack_l(this);
+ }
+ if (isExternalTrack() && !wasActive) {
+ AudioSystem::releaseOutput(mThreadIoHandle);
}
}
}
@@ -2050,7 +2052,7 @@
if (thread != 0) {
RecordThread *recordThread = (RecordThread *)thread.get();
if (recordThread->stop(this) && isExternalTrack()) {
- AudioSystem::stopInput(recordThread->id(), (audio_session_t)mSessionId);
+ AudioSystem::stopInput(mThreadIoHandle, (audio_session_t)mSessionId);
}
}
}
@@ -2060,14 +2062,14 @@
// see comments at AudioFlinger::PlaybackThread::Track::destroy()
sp<RecordTrack> keep(this);
{
+ if (isExternalTrack()) {
+ if (mState == ACTIVE || mState == RESUMING) {
+ AudioSystem::stopInput(mThreadIoHandle, (audio_session_t)mSessionId);
+ }
+ AudioSystem::releaseInput(mThreadIoHandle, (audio_session_t)mSessionId);
+ }
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
- if (isExternalTrack()) {
- if (mState == ACTIVE || mState == RESUMING) {
- AudioSystem::stopInput(thread->id(), (audio_session_t)mSessionId);
- }
- AudioSystem::releaseInput(thread->id(), (audio_session_t)mSessionId);
- }
Mutex::Autolock _l(thread->mLock);
RecordThread *recordThread = (RecordThread *) thread.get();
recordThread->destroyTrack_l(this);
diff --git a/services/audiopolicy/AudioPolicyManager.cpp b/services/audiopolicy/AudioPolicyManager.cpp
index 22c4e04..d5f6c1e 100644
--- a/services/audiopolicy/AudioPolicyManager.cpp
+++ b/services/audiopolicy/AudioPolicyManager.cpp
@@ -2294,14 +2294,14 @@
}
sp<DeviceDescriptor> srcDeviceDesc =
mAvailableInputDevices.getDeviceFromId(patch->sources[0].id);
+ if (srcDeviceDesc == 0) {
+ return BAD_VALUE;
+ }
//update source and sink with our own data as the data passed in the patch may
// be incomplete.
struct audio_patch newPatch = *patch;
srcDeviceDesc->toAudioPortConfig(&newPatch.sources[0], &patch->sources[0]);
- if (srcDeviceDesc == 0) {
- return BAD_VALUE;
- }
for (size_t i = 0; i < patch->num_sinks; i++) {
if (patch->sinks[i].type != AUDIO_PORT_TYPE_DEVICE) {
@@ -3670,8 +3670,11 @@
void AudioPolicyManager::checkOutputForAllStrategies()
{
- checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE);
+ if (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)
+ checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE);
checkOutputForStrategy(STRATEGY_PHONE);
+ if (mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] != AUDIO_POLICY_FORCE_SYSTEM_ENFORCED)
+ checkOutputForStrategy(STRATEGY_ENFORCED_AUDIBLE);
checkOutputForStrategy(STRATEGY_SONIFICATION);
checkOutputForStrategy(STRATEGY_SONIFICATION_RESPECTFUL);
checkOutputForStrategy(STRATEGY_MEDIA);
@@ -3752,23 +3755,28 @@
}
// check the following by order of priority to request a routing change if necessary:
- // 1: the strategy enforced audible is active on the output:
+ // 1: the strategy enforced audible is active and enforced on the output:
// use device for strategy enforced audible
// 2: we are in call or the strategy phone is active on the output:
// use device for strategy phone
- // 3: the strategy sonification is active on the output:
+ // 3: the strategy for enforced audible is active but not enforced on the output:
+ // use the device for strategy enforced audible
+ // 4: the strategy sonification is active on the output:
// use device for strategy sonification
- // 4: the strategy "respectful" sonification is active on the output:
+ // 5: the strategy "respectful" sonification is active on the output:
// use device for strategy "respectful" sonification
- // 5: the strategy media is active on the output:
+ // 6: the strategy media is active on the output:
// use device for strategy media
- // 6: the strategy DTMF is active on the output:
+ // 7: the strategy DTMF is active on the output:
// use device for strategy DTMF
- if (outputDesc->isStrategyActive(STRATEGY_ENFORCED_AUDIBLE)) {
+ if (outputDesc->isStrategyActive(STRATEGY_ENFORCED_AUDIBLE) &&
+ mForceUse[AUDIO_POLICY_FORCE_FOR_SYSTEM] == AUDIO_POLICY_FORCE_SYSTEM_ENFORCED) {
device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
} else if (isInCall() ||
outputDesc->isStrategyActive(STRATEGY_PHONE)) {
device = getDeviceForStrategy(STRATEGY_PHONE, fromCache);
+ } else if (outputDesc->isStrategyActive(STRATEGY_ENFORCED_AUDIBLE)) {
+ device = getDeviceForStrategy(STRATEGY_ENFORCED_AUDIBLE, fromCache);
} else if (outputDesc->isStrategyActive(STRATEGY_SONIFICATION)) {
device = getDeviceForStrategy(STRATEGY_SONIFICATION, fromCache);
} else if (outputDesc->isStrategyActive(STRATEGY_SONIFICATION_RESPECTFUL)) {
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 6f4a507..fe2f299 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -921,6 +921,13 @@
"stop preview: %s (%d)",
__FUNCTION__, mCameraId, strerror(-res), res);
}
+ {
+ // Ideally we should recover the override after recording stopped, but
+ // right now recording stream will live until here, so we are forced to
+ // recover here. TODO: find a better way to handle that (b/17495165)
+ SharedParameters::Lock l(mParameters);
+ l.mParameters.recoverOverriddenJpegSize();
+ }
// no break
case Parameters::WAITING_FOR_PREVIEW_WINDOW: {
SharedParameters::Lock l(mParameters);
@@ -1075,34 +1082,53 @@
// and we can't fail record start without stagefright asserting.
params.previewCallbackFlags = 0;
- res = updateProcessorStream<
- StreamingProcessor,
- &StreamingProcessor::updateRecordingStream>(mStreamingProcessor,
- params);
+ bool recordingStreamNeedsUpdate;
+ res = mStreamingProcessor->recordingStreamNeedsUpdate(params, &recordingStreamNeedsUpdate);
if (res != OK) {
- ALOGE("%s: Camera %d: Unable to update recording stream: %s (%d)",
- __FUNCTION__, mCameraId, strerror(-res), res);
+ ALOGE("%s: Camera %d: Can't query recording stream",
+ __FUNCTION__, mCameraId);
return res;
}
+ if (recordingStreamNeedsUpdate) {
+ // Need to stop stream here so updateProcessorStream won't trigger configureStream
+ // Right now camera device cannot handle configureStream failure gracefully
+ // when device is streaming
+ res = mStreamingProcessor->stopStream();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't stop streaming to update record stream",
+ __FUNCTION__, mCameraId);
+ return res;
+ }
+ res = mDevice->waitUntilDrained();
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Waiting to stop streaming failed: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ }
+ res = updateProcessorStream<
+ StreamingProcessor,
+ &StreamingProcessor::updateRecordingStream>(mStreamingProcessor,
+ params);
+
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Unable to update recording stream: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ return res;
+ }
+ }
+
Vector<int32_t> outputStreams;
outputStreams.push(getPreviewStreamId());
outputStreams.push(getRecordingStreamId());
res = mStreamingProcessor->startStream(StreamingProcessor::RECORD,
outputStreams);
- // try to reconfigure jpeg to video size if configureStreams failed
- if (res == BAD_VALUE) {
- ALOGV("%s: Camera %d: configure still size to video size before recording"
- , __FUNCTION__, mCameraId);
- params.overrideJpegSizeByVideoSize();
- res = updateProcessorStream(mJpegProcessor, params);
- if (res != OK) {
- ALOGE("%s: Camera %d: Can't configure still image size to video size: %s (%d)",
- __FUNCTION__, mCameraId, strerror(-res), res);
- return res;
- }
+ // startStream might trigger a configureStream call and device might fail
+ // configureStream due to jpeg size > video size. Try again with jpeg size overridden
+ // to video size.
+ if (res == BAD_VALUE) {
+ overrideVideoSnapshotSize(params);
res = mStreamingProcessor->startStream(StreamingProcessor::RECORD,
outputStreams);
}
@@ -1146,7 +1172,6 @@
mCameraService->playSound(CameraService::SOUND_RECORDING);
- l.mParameters.recoverOverriddenJpegSize();
res = startPreviewL(l.mParameters, true);
if (res != OK) {
ALOGE("%s: Camera %d: Unable to return to preview",
@@ -1343,6 +1368,12 @@
int lastJpegStreamId = mJpegProcessor->getStreamId();
res = updateProcessorStream(mJpegProcessor, l.mParameters);
+ // If video snapshot fail to configureStream, try override video snapshot size to
+ // video size
+ if (res == BAD_VALUE && l.mParameters.state == Parameters::VIDEO_SNAPSHOT) {
+ overrideVideoSnapshotSize(l.mParameters);
+ res = updateProcessorStream(mJpegProcessor, l.mParameters);
+ }
if (res != OK) {
ALOGE("%s: Camera %d: Can't set up still image stream: %s (%d)",
__FUNCTION__, mCameraId, strerror(-res), res);
@@ -1923,6 +1954,18 @@
return res;
}
+status_t Camera2Client::overrideVideoSnapshotSize(Parameters ¶ms) {
+ ALOGV("%s: Camera %d: configure still size to video size before recording"
+ , __FUNCTION__, mCameraId);
+ params.overrideJpegSizeByVideoSize();
+ status_t res = updateProcessorStream(mJpegProcessor, params);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Can't override video snapshot size to video size: %s (%d)",
+ __FUNCTION__, mCameraId, strerror(-res), res);
+ }
+ return res;
+}
+
const char* Camera2Client::kAutofocusLabel = "autofocus";
const char* Camera2Client::kTakepictureLabel = "take_picture";
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index f5c3a30..d68bb29 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -208,6 +208,9 @@
// Wait until the camera device has received the latest control settings
status_t syncWithDevice();
+
+ // Video snapshot jpeg size overriding helper function
+ status_t overrideVideoSnapshotSize(Parameters ¶ms);
};
}; // namespace android
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index 33bdaa3..1a4d9a6 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -122,6 +122,16 @@
mClientPid);
len = (len > SIZE - 1) ? SIZE - 1 : len;
write(fd, buffer, len);
+
+ len = snprintf(buffer, SIZE, "Latest set parameters:\n");
+ len = (len > SIZE - 1) ? SIZE - 1 : len;
+ write(fd, buffer, len);
+
+ mLatestSetParameters.dump(fd, args);
+
+ const char *enddump = "\n\n";
+ write(fd, enddump, strlen(enddump));
+
return mHardware->dump(fd, args);
}
@@ -550,6 +560,7 @@
status_t result = checkPidAndHardware();
if (result != NO_ERROR) return result;
+ mLatestSetParameters = CameraParameters(params);
CameraParameters p(params);
return mHardware->setParameters(p);
}
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 6779f5e..63a9d0f 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -142,6 +142,9 @@
// of the original one), we allocate mPreviewBuffer and reuse it if possible.
sp<MemoryHeapBase> mPreviewBuffer;
+ // Debugging information
+ CameraParameters mLatestSetParameters;
+
// We need to avoid the deadlock when the incoming command thread and
// the CameraHardwareInterface callback thread both want to grab mLock.
// An extra flag is used to tell the callback thread that it should stop
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index 8d00590..aa9d746 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -76,9 +76,29 @@
res = getFilteredSizes(MAX_VIDEO_SIZE, &availableVideoSizes);
if (res != OK) return res;
- // TODO: Pick more intelligently
- previewWidth = availablePreviewSizes[0].width;
- previewHeight = availablePreviewSizes[0].height;
+ // Select initial preview and video size that's under the initial bound and
+ // on the list of both preview and recording sizes
+ previewWidth = 0;
+ previewHeight = 0;
+ for (size_t i = 0 ; i < availablePreviewSizes.size(); i++) {
+ int newWidth = availablePreviewSizes[i].width;
+ int newHeight = availablePreviewSizes[i].height;
+ if (newWidth >= previewWidth && newHeight >= previewHeight &&
+ newWidth <= MAX_INITIAL_PREVIEW_WIDTH &&
+ newHeight <= MAX_INITIAL_PREVIEW_HEIGHT) {
+ for (size_t j = 0; j < availableVideoSizes.size(); j++) {
+ if (availableVideoSizes[j].width == newWidth &&
+ availableVideoSizes[j].height == newHeight) {
+ previewWidth = newWidth;
+ previewHeight = newHeight;
+ }
+ }
+ }
+ }
+ if (previewWidth == 0) {
+ ALOGE("%s: No initial preview size can be found!", __FUNCTION__);
+ return BAD_VALUE;
+ }
videoWidth = previewWidth;
videoHeight = previewHeight;
@@ -963,6 +983,13 @@
bool fixedLens = minFocusDistance.count == 0 ||
minFocusDistance.data.f[0] == 0;
+ camera_metadata_ro_entry_t focusDistanceCalibration =
+ staticInfo(ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION, 0, 0,
+ false);
+ bool canFocusInfinity = (focusDistanceCalibration.count &&
+ focusDistanceCalibration.data.u8[0] !=
+ ANDROID_LENS_INFO_FOCUS_DISTANCE_CALIBRATION_UNCALIBRATED);
+
camera_metadata_ro_entry_t availableFocalLengths =
staticInfo(ANDROID_LENS_INFO_AVAILABLE_FOCAL_LENGTHS);
if (!availableFocalLengths.count) return NO_INIT;
@@ -1013,6 +1040,13 @@
sceneModeOverrides.data.u8[i * kModesPerSceneMode + 2];
switch(afMode) {
case ANDROID_CONTROL_AF_MODE_OFF:
+ if (!fixedLens && !canFocusInfinity) {
+ ALOGE("%s: Camera %d: Scene mode override lists asks for"
+ " fixed focus on a device with focuser but not"
+ " calibrated for infinity focus", __FUNCTION__,
+ cameraId);
+ return NO_INIT;
+ }
modes.focusMode = fixedLens ?
FOCUS_MODE_FIXED : FOCUS_MODE_INFINITY;
break;
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index 5e6e6ab..815cc55 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -179,8 +179,13 @@
// Number of zoom steps to simulate
static const unsigned int NUM_ZOOM_STEPS = 100;
// Max preview size allowed
+ // This is set to a 1:1 value to allow for any aspect ratio that has
+ // a max long side of 1920 pixels
static const unsigned int MAX_PREVIEW_WIDTH = 1920;
- static const unsigned int MAX_PREVIEW_HEIGHT = 1080;
+ static const unsigned int MAX_PREVIEW_HEIGHT = 1920;
+ // Initial max preview/recording size bound
+ static const int MAX_INITIAL_PREVIEW_WIDTH = 1920;
+ static const int MAX_INITIAL_PREVIEW_HEIGHT = 1080;
// Aspect ratio tolerance
static const float ASPECT_RATIO_TOLERANCE = 0.001;
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index ab0af0d..9e7fff8 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -318,6 +318,44 @@
return OK;
}
+status_t StreamingProcessor::recordingStreamNeedsUpdate(
+ const Parameters ¶ms, bool *needsUpdate) {
+ status_t res;
+
+ if (needsUpdate == 0) {
+ ALOGE("%s: Camera %d: invalid argument", __FUNCTION__, mId);
+ return INVALID_OPERATION;
+ }
+
+ if (mRecordingStreamId == NO_STREAM) {
+ *needsUpdate = true;
+ return OK;
+ }
+
+ sp<CameraDeviceBase> device = mDevice.promote();
+ if (device == 0) {
+ ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId);
+ return INVALID_OPERATION;
+ }
+
+ uint32_t currentWidth, currentHeight;
+ res = device->getStreamInfo(mRecordingStreamId,
+ ¤tWidth, ¤tHeight, 0);
+ if (res != OK) {
+ ALOGE("%s: Camera %d: Error querying recording output stream info: "
+ "%s (%d)", __FUNCTION__, mId,
+ strerror(-res), res);
+ return res;
+ }
+
+ if (mRecordingConsumer == 0 || currentWidth != (uint32_t)params.videoWidth ||
+ currentHeight != (uint32_t)params.videoHeight) {
+ *needsUpdate = true;
+ }
+ *needsUpdate = false;
+ return res;
+}
+
status_t StreamingProcessor::updateRecordingStream(const Parameters ¶ms) {
ATRACE_CALL();
status_t res;
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
index 833bb8f..8466af4 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
@@ -54,6 +54,9 @@
status_t setRecordingBufferCount(size_t count);
status_t updateRecordingRequest(const Parameters ¶ms);
+ // If needsUpdate is set to true, a updateRecordingStream call with params will recreate
+ // recording stream
+ status_t recordingStreamNeedsUpdate(const Parameters ¶ms, bool *needsUpdate);
status_t updateRecordingStream(const Parameters ¶ms);
status_t deleteRecordingStream();
int getRecordingStreamId() const;
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
index fa65b74..de31e23 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
@@ -244,6 +244,46 @@
return mZslStreamId;
}
+status_t ZslProcessor3::updateRequestWithDefaultStillRequest(CameraMetadata &request) const {
+ sp<Camera2Client> client = mClient.promote();
+ if (client == 0) {
+ ALOGE("%s: Camera %d: Client does not exist", __FUNCTION__, mId);
+ return INVALID_OPERATION;
+ }
+ sp<Camera3Device> device =
+ static_cast<Camera3Device*>(client->getCameraDevice().get());
+ if (device == 0) {
+ ALOGE("%s: Camera %d: Device does not exist", __FUNCTION__, mId);
+ return INVALID_OPERATION;
+ }
+
+ CameraMetadata stillTemplate;
+ device->createDefaultRequest(CAMERA3_TEMPLATE_STILL_CAPTURE, &stillTemplate);
+
+ // Find some of the post-processing tags, and assign the value from template to the request.
+ // Only check the aberration mode and noise reduction mode for now, as they are very important
+ // for image quality.
+ uint32_t postProcessingTags[] = {
+ ANDROID_NOISE_REDUCTION_MODE,
+ ANDROID_COLOR_CORRECTION_ABERRATION_MODE,
+ ANDROID_COLOR_CORRECTION_MODE,
+ ANDROID_TONEMAP_MODE,
+ ANDROID_SHADING_MODE,
+ ANDROID_HOT_PIXEL_MODE,
+ ANDROID_EDGE_MODE
+ };
+
+ camera_metadata_entry_t entry;
+ for (size_t i = 0; i < sizeof(postProcessingTags) / sizeof(uint32_t); i++) {
+ entry = stillTemplate.find(postProcessingTags[i]);
+ if (entry.count > 0) {
+ request.update(postProcessingTags[i], entry.data.u8, 1);
+ }
+ }
+
+ return OK;
+}
+
status_t ZslProcessor3::pushToReprocess(int32_t requestId) {
ALOGV("%s: Send in reprocess request with id %d",
__FUNCTION__, requestId);
@@ -369,6 +409,13 @@
}
}
+ // Update post-processing settings
+ res = updateRequestWithDefaultStillRequest(request);
+ if (res != OK) {
+ ALOGW("%s: Unable to update post-processing tags, the reprocessed image quality "
+ "may be compromised", __FUNCTION__);
+ }
+
mLatestCapturedRequest = request;
res = client->getCameraDevice()->capture(request);
if (res != OK ) {
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
index 2975f7c..fc9f70c 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.h
@@ -135,6 +135,9 @@
nsecs_t getCandidateTimestampLocked(size_t* metadataIdx) const;
bool isFixedFocusMode(uint8_t afMode) const;
+
+ // Update the post-processing metadata with the default still capture request template
+ status_t updateRequestWithDefaultStillRequest(CameraMetadata &request) const;
};
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index fafe349..6a7f9e7 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -1044,6 +1044,11 @@
return INVALID_OPERATION;
}
+ if (!mRequestTemplateCache[templateId].isEmpty()) {
+ *request = mRequestTemplateCache[templateId];
+ return OK;
+ }
+
const camera_metadata_t *rawRequest;
ATRACE_BEGIN("camera3->construct_default_request_settings");
rawRequest = mHal3Device->ops->construct_default_request_settings(
@@ -1055,6 +1060,7 @@
return DEAD_OBJECT;
}
*request = rawRequest;
+ mRequestTemplateCache[templateId] = rawRequest;
return OK;
}
@@ -1086,6 +1092,10 @@
ALOGV("%s: Camera %d: Waiting until idle", __FUNCTION__, mId);
status_t res = waitUntilStateThenRelock(/*active*/ false, kShutdownTimeout);
+ if (res != OK) {
+ SET_ERR_L("Error waiting for HAL to drain: %s (%d)", strerror(-res),
+ res);
+ }
return res;
}
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index b99ed7e..ec6bba1 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -174,6 +174,8 @@
CameraMetadata mDeviceInfo;
+ CameraMetadata mRequestTemplateCache[CAMERA3_TEMPLATE_COUNT];
+
uint32_t mDeviceVersion;
enum Status {