Merge "audio policy: fix output stream selection" into lmp-dev
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index 3486d21..1742fbe 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -582,9 +582,13 @@
}
binder->linkToDeath(gAudioPolicyServiceClient);
gAudioPolicyService = interface_cast<IAudioPolicyService>(binder);
- gAudioPolicyService->registerClient(gAudioPolicyServiceClient);
gLock.unlock();
+ // Registering the client takes the AudioPolicyService lock.
+ // Don't hold the AudioSystem lock at the same time.
+ gAudioPolicyService->registerClient(gAudioPolicyServiceClient);
} else {
+ // There exists a benign race condition where gAudioPolicyService
+ // is set, but gAudioPolicyServiceClient is not yet registered.
gLock.unlock();
}
return gAudioPolicyService;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index 4a5d18a..c8bf8f0 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -994,6 +994,8 @@
ALOGV("both audio and video are flushed now.");
+ mPendingAudioAccessUnit.clear();
+
if (mTimeDiscontinuityPending) {
mRenderer->signalTimeDiscontinuity();
mTimeDiscontinuityPending = false;
@@ -1250,14 +1252,48 @@
sp<ABuffer> accessUnit;
+ // 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 needMoreData = false;
+ int numSmallBuffers = 0;
+ bool gotTime = false;
+
bool dropAccessUnit;
do {
- status_t err = mSource->dequeueAccessUnit(audio, &accessUnit);
+ status_t err;
+ // Did we save an accessUnit earlier because of a discontinuity?
+ if (audio && (mPendingAudioAccessUnit != NULL)) {
+ accessUnit = mPendingAudioAccessUnit;
+ mPendingAudioAccessUnit.clear();
+ err = mPendingAudioErr;
+ ALOGV("feedDecoderInputData() use mPendingAudioAccessUnit");
+ } else {
+ err = mSource->dequeueAccessUnit(audio, &accessUnit);
+ }
if (err == -EWOULDBLOCK) {
- return err;
+ ALOGD("feedDecoderInputData() got EWOULDBLOCK");
+ if (biggerBuffer == NULL) {
+ return err;
+ } else {
+ break; // Reply with data that we already have.
+ }
} else if (err != OK) {
if (err == INFO_DISCONTINUITY) {
+ if (biggerBuffer != NULL) {
+ // We already have some data so save this for later.
+ mPendingAudioErr = err;
+ mPendingAudioAccessUnit = accessUnit;
+ accessUnit.clear();
+ ALOGD("feedDecoderInputData() save discontinuity for later");
+ break;
+ }
int32_t type;
CHECK(accessUnit->meta()->findInt32("discontinuity", &type));
@@ -1362,7 +1398,52 @@
dropAccessUnit = true;
++mNumFramesDropped;
}
- } while (dropAccessUnit);
+
+ size_t smallSize = accessUnit->size();
+ needMoreData = false;
+ if (doBufferAggregation && (biggerBuffer == NULL)
+ // Don't bother if only room for a few small buffers.
+ && (smallSize < (kAudioBigBufferSizeBytes / 3))) {
+ // Create a larger buffer for combining smaller buffers from the extractor.
+ biggerBuffer = new ABuffer(kAudioBigBufferSizeBytes);
+ biggerBuffer->setRange(0, 0); // start empty
+ }
+
+ if (biggerBuffer != NULL) {
+ int64_t timeUs;
+ bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs);
+ // Will the smaller buffer fit?
+ size_t bigSize = biggerBuffer->size();
+ size_t roomLeft = biggerBuffer->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)) {
+ mPendingAudioErr = err;
+ mPendingAudioAccessUnit = accessUnit;
+ accessUnit.clear();
+ } else {
+ // Append small buffer to the bigger buffer.
+ memcpy(biggerBuffer->base() + bigSize, accessUnit->data(), smallSize);
+ bigSize += smallSize;
+ biggerBuffer->setRange(0, bigSize);
+
+ // Keep looping until we run out of room in the biggerBuffer.
+ 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++;
+ }
+ }
+ } while (dropAccessUnit || needMoreData);
// ALOGV("returned a valid buffer of %s data", audio ? "audio" : "video");
@@ -1378,7 +1459,13 @@
mCCDecoder->decode(accessUnit);
}
- reply->setBuffer("buffer", accessUnit);
+ if (biggerBuffer != NULL) {
+ ALOGV("feedDecoderInputData() reply with aggregated buffer, %d", numSmallBuffers);
+ reply->setBuffer("buffer", biggerBuffer);
+ } else {
+ reply->setBuffer("buffer", accessUnit);
+ }
+
reply->post();
return OK;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index 0c7f531..89ae11c 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -158,6 +158,9 @@
// notion of time has changed.
bool mTimeDiscontinuityPending;
+ sp<ABuffer> mPendingAudioAccessUnit;
+ status_t mPendingAudioErr;
+
FlushStatus mFlushingAudio;
FlushStatus mFlushingVideo;
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
index 09324ae..35cd514 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDriver.cpp
@@ -627,11 +627,11 @@
case MEDIA_PLAYBACK_COMPLETE:
{
if (mLooping && mState != STATE_RESET_IN_PROGRESS) {
- mLock.unlock();
mPlayer->seekToAsync(0);
- mLock.lock();
break;
}
+ mPlayer->pause();
+ mState = STATE_PAUSED;
// fall through
}
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index da50c56..1fdb244 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -338,7 +338,7 @@
status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
binder,
String16("TimedEventQueue"),
- String16("media"));
+ String16("media")); // not oneway
IPCThreadState::self()->restoreCallingIdentity(token);
if (status == NO_ERROR) {
mWakeLockToken = binder;
@@ -363,7 +363,7 @@
CHECK(mWakeLockToken != 0);
if (mPowerManager != 0) {
int64_t token = IPCThreadState::self()->clearCallingIdentity();
- mPowerManager->releaseWakeLock(mWakeLockToken, 0);
+ mPowerManager->releaseWakeLock(mWakeLockToken, 0); // not oneway
IPCThreadState::self()->restoreCallingIdentity(token);
}
mWakeLockToken.clear();
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 942bff6..97b1753 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -662,12 +662,14 @@
binder,
getWakeLockTag(),
String16("media"),
- uid);
+ uid,
+ true /* FIXME force oneway contrary to .aidl */);
} else {
status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
binder,
getWakeLockTag(),
- String16("media"));
+ String16("media"),
+ true /* FIXME force oneway contrary to .aidl */);
}
if (status == NO_ERROR) {
mWakeLockToken = binder;
@@ -687,7 +689,8 @@
if (mWakeLockToken != 0) {
ALOGV("releaseWakeLock_l() %s", mName);
if (mPowerManager != 0) {
- mPowerManager->releaseWakeLock(mWakeLockToken, 0);
+ mPowerManager->releaseWakeLock(mWakeLockToken, 0,
+ true /* FIXME force oneway contrary to .aidl */);
}
mWakeLockToken.clear();
}
@@ -723,7 +726,8 @@
if (mPowerManager != 0) {
sp<IBinder> binder = new BBinder();
status_t status;
- status = mPowerManager->updateWakeLockUids(mWakeLockToken, uids.size(), uids.array());
+ status = mPowerManager->updateWakeLockUids(mWakeLockToken, uids.size(), uids.array(),
+ true /* FIXME force oneway contrary to .aidl */);
ALOGV("acquireWakeLock_l() %s status %d", mName, status);
}
}
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index bc40971..d59ee51 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -1088,6 +1088,22 @@
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;
+ }
+ res = mStreamingProcessor->startStream(StreamingProcessor::RECORD,
+ outputStreams);
+ }
+
if (res != OK) {
ALOGE("%s: Camera %d: Unable to start recording stream: %s (%d)",
__FUNCTION__, mCameraId, strerror(-res), res);
@@ -1127,6 +1143,7 @@
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",
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.cpp b/services/camera/libcameraservice/api1/client2/Parameters.cpp
index e7f9a78..8d00590 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.cpp
+++ b/services/camera/libcameraservice/api1/client2/Parameters.cpp
@@ -249,6 +249,9 @@
// TODO: Pick maximum
pictureWidth = availableJpegSizes[0].width;
pictureHeight = availableJpegSizes[0].height;
+ pictureWidthLastSet = pictureWidth;
+ pictureHeightLastSet = pictureHeight;
+ pictureSizeOverriden = false;
params.setPictureSize(pictureWidth,
pictureHeight);
@@ -1381,8 +1384,8 @@
// PICTURE_SIZE
newParams.getPictureSize(&validatedParams.pictureWidth,
&validatedParams.pictureHeight);
- if (validatedParams.pictureWidth == pictureWidth ||
- validatedParams.pictureHeight == pictureHeight) {
+ if (validatedParams.pictureWidth != pictureWidth ||
+ validatedParams.pictureHeight != pictureHeight) {
Vector<Size> availablePictureSizes = getAvailableJpegSizes();
for (i = 0; i < availablePictureSizes.size(); i++) {
if ((availablePictureSizes[i].width ==
@@ -1798,6 +1801,7 @@
/** Update internal parameters */
*this = validatedParams;
+ updateOverriddenJpegSize();
/** Update external parameters calculated from the internal ones */
@@ -2115,6 +2119,52 @@
return OK;
}
+status_t Parameters::overrideJpegSizeByVideoSize() {
+ if (pictureSizeOverriden) {
+ ALOGV("Picture size has been overridden. Skip overriding");
+ return OK;
+ }
+
+ pictureSizeOverriden = true;
+ pictureWidthLastSet = pictureWidth;
+ pictureHeightLastSet = pictureHeight;
+ pictureWidth = videoWidth;
+ pictureHeight = videoHeight;
+ // This change of picture size is invisible to app layer.
+ // Do not update app visible params
+ return OK;
+}
+
+status_t Parameters::updateOverriddenJpegSize() {
+ if (!pictureSizeOverriden) {
+ ALOGV("Picture size has not been overridden. Skip checking");
+ return OK;
+ }
+
+ pictureWidthLastSet = pictureWidth;
+ pictureHeightLastSet = pictureHeight;
+
+ if (pictureWidth <= videoWidth && pictureHeight <= videoHeight) {
+ // Picture size is now smaller than video size. No need to override anymore
+ return recoverOverriddenJpegSize();
+ }
+
+ pictureWidth = videoWidth;
+ pictureHeight = videoHeight;
+
+ return OK;
+}
+
+status_t Parameters::recoverOverriddenJpegSize() {
+ if (!pictureSizeOverriden) {
+ ALOGV("Picture size has not been overridden. Skip recovering");
+ return OK;
+ }
+ pictureSizeOverriden = false;
+ pictureWidth = pictureWidthLastSet;
+ pictureHeight = pictureHeightLastSet;
+ return OK;
+}
const char* Parameters::getStateName(State state) {
#define CASE_ENUM_TO_CHAR(x) case x: return(#x); break;
diff --git a/services/camera/libcameraservice/api1/client2/Parameters.h b/services/camera/libcameraservice/api1/client2/Parameters.h
index d9d33c4..5e6e6ab 100644
--- a/services/camera/libcameraservice/api1/client2/Parameters.h
+++ b/services/camera/libcameraservice/api1/client2/Parameters.h
@@ -52,6 +52,9 @@
int previewTransform; // set by CAMERA_CMD_SET_DISPLAY_ORIENTATION
int pictureWidth, pictureHeight;
+ // Store the picture size before they are overriden by video snapshot
+ int pictureWidthLastSet, pictureHeightLastSet;
+ bool pictureSizeOverriden;
int32_t jpegThumbSize[2];
uint8_t jpegQuality, jpegThumbQuality;
@@ -253,6 +256,12 @@
// Add/update JPEG entries in metadata
status_t updateRequestJpeg(CameraMetadata *request) const;
+ /* Helper functions to override jpeg size for video snapshot */
+ // Override jpeg size by video size. Called during startRecording.
+ status_t overrideJpegSizeByVideoSize();
+ // Recover overridden jpeg size. Called during stopRecording.
+ status_t recoverOverriddenJpegSize();
+
// Calculate the crop region rectangle based on current stream sizes
struct CropRegion {
float left;
@@ -348,6 +357,12 @@
// Get max size (from the size array) that matches the given aspect ratio.
Size getMaxSizeForRatio(float ratio, const int32_t* sizeArray, size_t count);
+ // Helper function for overriding jpeg size for video snapshot
+ // Check if overridden jpeg size needs to be updated after Parameters::set.
+ // The behavior of this function is tailored to the implementation of Parameters::set.
+ // Do not use this function for other purpose.
+ status_t updateOverriddenJpegSize();
+
struct StreamConfiguration {
int32_t format;
int32_t width;
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 6f78db5..fafe349 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -601,10 +601,18 @@
if (mStatus == STATUS_UNCONFIGURED || mNeedConfig) {
res = configureStreamsLocked();
+ // Stream configuration failed due to unsupported configuration.
+ // Device back to unconfigured state. Client might try other configuraitons
+ if (res == BAD_VALUE && mStatus == STATUS_UNCONFIGURED) {
+ CLOGE("No streams configured");
+ return NULL;
+ }
+ // Stream configuration failed for other reason. Fatal.
if (res != OK) {
SET_ERR_L("Can't set up streams: %s (%d)", strerror(-res), res);
return NULL;
}
+ // Stream configuration successfully configure to empty stream configuration.
if (mStatus == STATUS_UNCONFIGURED) {
CLOGE("No streams configured");
return NULL;
diff --git a/services/camera/libcameraservice/device3/Camera3Stream.cpp b/services/camera/libcameraservice/device3/Camera3Stream.cpp
index 29ce38c..3c0e908 100644
--- a/services/camera/libcameraservice/device3/Camera3Stream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Stream.cpp
@@ -233,8 +233,7 @@
camera3_stream::usage = oldUsage;
camera3_stream::max_buffers = oldMaxBuffers;
- mState = STATE_CONSTRUCTED;
-
+ mState = (mState == STATE_IN_RECONFIG) ? STATE_CONFIGURED : STATE_CONSTRUCTED;
return OK;
}