Merge "Parse the last none-empty line of .m3u8 file" into jb-mr2-dev
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
index 5bdbfbb..115b07c 100644
--- a/cmds/stagefright/stagefright.cpp
+++ b/cmds/stagefright/stagefright.cpp
@@ -523,7 +523,7 @@
}
sp<MetaData> params = new MetaData;
- params->setInt32(kKeyNotRealTime, true);
+ params->setInt32(kKeyRealTimeRecording, false);
CHECK_EQ(writer->start(params.get()), (status_t)OK);
while (!writer->reachedEOS()) {
diff --git a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
index 00f6de3..06fc29d 100644
--- a/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
+++ b/drm/mediadrm/plugins/mock/MockDrmCryptoPlugin.cpp
@@ -291,16 +291,30 @@
{
Mutex::Autolock lock(mLock);
ALOGD("MockDrmPlugin::getSecureStops()");
- const uint8_t ss1[] = {0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89};
- const uint8_t ss2[] = {0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99};
- Vector<uint8_t> vec;
- vec.appendArray(ss1, sizeof(ss1));
- secureStops.push_back(vec);
+ // Properties used in mock test, set by cts test app returned from mock plugin
+ // byte[] mock-secure-stop1 -> first secure stop in list
+ // byte[] mock-secure-stop2 -> second secure stop in list
- vec.clear();
- vec.appendArray(ss2, sizeof(ss2));
- secureStops.push_back(vec);
+ Vector<uint8_t> ss1, ss2;
+ ssize_t index = mByteArrayProperties.indexOfKey(String8("mock-secure-stop1"));
+ if (index < 0) {
+ ALOGD("Missing 'mock-secure-stop1' parameter for mock");
+ return BAD_VALUE;
+ } else {
+ ss1 = mByteArrayProperties.valueAt(index);
+ }
+
+ index = mByteArrayProperties.indexOfKey(String8("mock-secure-stop2"));
+ if (index < 0) {
+ ALOGD("Missing 'mock-secure-stop2' parameter for mock");
+ return BAD_VALUE;
+ } else {
+ ss2 = mByteArrayProperties.valueAt(index);
+ }
+
+ secureStops.push_back(ss1);
+ secureStops.push_back(ss2);
return OK;
}
@@ -309,6 +323,11 @@
Mutex::Autolock lock(mLock);
ALOGD("MockDrmPlugin::releaseSecureStops(%s)",
vectorToString(ssRelease).string());
+
+ // Properties used in mock test, set by mock plugin and verifed cts test app
+ // byte[] secure-stop-release -> mock-ssrelease
+ mByteArrayProperties.add(String8("mock-ssrelease"), ssRelease);
+
return OK;
}
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
index 88df6b0..3ef6b9a 100644
--- a/include/media/stagefright/MPEG4Writer.h
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -74,6 +74,7 @@
int mFd;
status_t mInitCheck;
+ bool mIsRealTimeRecording;
bool mUse4ByteNalLength;
bool mUse32BitOffset;
bool mIsFileSizeLimitExplicitlyRequested;
@@ -168,6 +169,13 @@
// Only makes sense for H.264/AVC
bool useNalLengthFour();
+ // Return whether the writer is used for real time recording.
+ // In real time recording mode, new samples will be allowed to buffered into
+ // chunks in higher priority thread, even though the file writer has not
+ // drained the chunks yet.
+ // By default, real time recording is on.
+ bool isRealTimeRecording() const;
+
void lock();
void unlock();
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index 9ab3edc..de3fc36 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -112,7 +112,7 @@
// kKeyTrackTimeStatus is used to track progress in elapsed time
kKeyTrackTimeStatus = 'tktm', // int64_t
- kKeyNotRealTime = 'ntrt', // bool (int32_t)
+ kKeyRealTimeRecording = 'rtrc', // bool (int32_t)
kKeyNumBuffers = 'nbbf', // int32_t
// Ogg files can be tagged to be automatically looping...
diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorUtils.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditorUtils.cpp
index 5309bd4..5a7237d 100755
--- a/libvideoeditor/vss/stagefrightshells/src/VideoEditorUtils.cpp
+++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorUtils.cpp
@@ -189,8 +189,8 @@
if (meta->findInt64(kKeyTrackTimeStatus, &int64Data)) {
LOG1("displayMetaData kKeyTrackTimeStatus %lld", int64Data);
}
- if (meta->findInt32(kKeyNotRealTime, &int32Data)) {
- LOG1("displayMetaData kKeyNotRealTime %d", int32Data);
+ if (meta->findInt32(kKeyRealTimeRecording, &int32Data)) {
+ LOG1("displayMetaData kKeyRealTimeRecording %d", int32Data);
}
}
diff --git a/media/libmedia/IDrm.cpp b/media/libmedia/IDrm.cpp
index 1578846..902aeb2 100644
--- a/media/libmedia/IDrm.cpp
+++ b/media/libmedia/IDrm.cpp
@@ -590,6 +590,7 @@
size_t size = iter->size();
reply->writeInt32(size);
reply->write(iter->array(), iter->size());
+ iter++;
}
reply->writeInt32(result);
return OK;
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index a5ff0ca..50ebf9c 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -66,7 +66,9 @@
}
NuPlayer::RTSPSource::~RTSPSource() {
- mLooper->stop();
+ if (mLooper != NULL) {
+ mLooper->stop();
+ }
}
void NuPlayer::RTSPSource::prepareAsync() {
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 316f669..a0f17b5 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -212,7 +212,6 @@
int64_t mTrackDurationUs;
int64_t mMaxChunkDurationUs;
- bool mIsRealTimeRecording;
int64_t mEstimatedTrackSizeBytes;
int64_t mMdatSizeBytes;
int32_t mTimeScale;
@@ -335,6 +334,7 @@
MPEG4Writer::MPEG4Writer(const char *filename)
: mFd(-1),
mInitCheck(NO_INIT),
+ mIsRealTimeRecording(true),
mUse4ByteNalLength(true),
mUse32BitOffset(true),
mIsFileSizeLimitExplicitlyRequested(false),
@@ -359,6 +359,7 @@
MPEG4Writer::MPEG4Writer(int fd)
: mFd(dup(fd)),
mInitCheck(mFd < 0? NO_INIT: OK),
+ mIsRealTimeRecording(true),
mUse4ByteNalLength(true),
mUse32BitOffset(true),
mIsFileSizeLimitExplicitlyRequested(false),
@@ -596,6 +597,11 @@
mUse4ByteNalLength = false;
}
+ int32_t isRealTimeRecording;
+ if (param && param->findInt32(kKeyRealTimeRecording, &isRealTimeRecording)) {
+ mIsRealTimeRecording = isRealTimeRecording;
+ }
+
mStartTimestampUs = -1;
if (mStarted) {
@@ -1640,12 +1646,18 @@
mChunkReadyCondition.wait(mLock);
}
- // Actual write without holding the lock in order to
- // reduce the blocking time for media track threads.
+ // In real time recording mode, write without holding the lock in order
+ // to reduce the blocking time for media track threads.
+ // Otherwise, hold the lock until the existing chunks get written to the
+ // file.
if (chunkFound) {
- mLock.unlock();
+ if (mIsRealTimeRecording) {
+ mLock.unlock();
+ }
writeChunkToFile(&chunk);
- mLock.lock();
+ if (mIsRealTimeRecording) {
+ mLock.lock();
+ }
}
}
@@ -1695,18 +1707,10 @@
mRotation = rotationDegrees;
}
- mIsRealTimeRecording = true;
- {
- int32_t isNotRealTime;
- if (params && params->findInt32(kKeyNotRealTime, &isNotRealTime)) {
- mIsRealTimeRecording = (isNotRealTime == 0);
- }
- }
-
initTrackingProgressStatus(params);
sp<MetaData> meta = new MetaData;
- if (mIsRealTimeRecording && mOwner->numTracks() > 1) {
+ if (mOwner->isRealTimeRecording() && mOwner->numTracks() > 1) {
/*
* This extra delay of accepting incoming audio/video signals
* helps to align a/v start time at the beginning of a recording
@@ -2084,7 +2088,10 @@
} else {
prctl(PR_SET_NAME, (unsigned long)"VideoTrackEncoding", 0, 0, 0);
}
- androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
+
+ if (mOwner->isRealTimeRecording()) {
+ androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
+ }
sp<MetaData> meta_data;
@@ -2245,7 +2252,7 @@
}
- if (mIsRealTimeRecording) {
+ if (mOwner->isRealTimeRecording()) {
if (mIsAudio) {
updateDriftTime(meta_data);
}
@@ -2531,6 +2538,10 @@
return mDriftTimeUs;
}
+bool MPEG4Writer::isRealTimeRecording() const {
+ return mIsRealTimeRecording;
+}
+
bool MPEG4Writer::useNalLengthFour() {
return mUse4ByteNalLength;
}
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index b948fe2..388c65b 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -107,6 +107,7 @@
Mutex::Autolock autoLock(mMuxerLock);
if (mState == INITIALIZED) {
mState = STARTED;
+ mFileMeta->setInt32(kKeyRealTimeRecording, false);
return mWriter->start(mFileMeta.get());
} else {
ALOGE("start() is called in invalid state %d", mState);
diff --git a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
index 8ba2afb..cf81c16 100644
--- a/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
+++ b/media/libstagefright/codecs/aacdec/SoftAAC2.cpp
@@ -29,6 +29,7 @@
#define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */
#define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */
#define MAX_CHANNEL_COUNT 6 /* maximum number of audio channels that can be decoded */
// names of properties that can be used to override the default DRC settings
#define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level"
@@ -146,6 +147,8 @@
unsigned boost = atoi(value);
ALOGV("AAC decoder using AAC_DRC_BOOST_FACTOR of %d", boost);
aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, boost);
+ } else {
+ aacDecoder_SetParam(mAACDecoder, AAC_DRC_BOOST_FACTOR, DRC_DEFAULT_MOBILE_DRC_BOOST);
}
return status;
@@ -592,6 +595,12 @@
void SoftAAC2::onReset() {
drainDecoder();
+ // reset the "configured" state
+ mInputBufferCount = 0;
+ mNumSamplesOutput = 0;
+ // To make the codec behave the same before and after a reset, we need to invalidate the
+ // streaminfo struct. This does that:
+ mStreamInfo->sampleRate = 0;
}
void SoftAAC2::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
diff --git a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
index 922ac61..4115324 100644
--- a/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
+++ b/media/libstagefright/codecs/vorbis/dec/SoftVorbis.cpp
@@ -411,8 +411,19 @@
}
void SoftVorbis::onReset() {
+ mInputBufferCount = 0;
mNumFramesOutput = 0;
- vorbis_dsp_restart(mState);
+ if (mState != NULL) {
+ vorbis_dsp_clear(mState);
+ delete mState;
+ mState = NULL;
+ }
+
+ if (mVi != NULL) {
+ vorbis_info_clear(mVi);
+ delete mVi;
+ mVi = NULL;
+ }
}
void SoftVorbis::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
diff --git a/services/audioflinger/ISchedulingPolicyService.cpp b/services/audioflinger/ISchedulingPolicyService.cpp
index 909b77e..218aa6b 100644
--- a/services/audioflinger/ISchedulingPolicyService.cpp
+++ b/services/audioflinger/ISchedulingPolicyService.cpp
@@ -44,7 +44,7 @@
data.writeInt32(pid);
data.writeInt32(tid);
data.writeInt32(prio);
- remote()->transact(REQUEST_PRIORITY_TRANSACTION, data, &reply);
+ remote()->transact(REQUEST_PRIORITY_TRANSACTION, data, &reply, IBinder::FLAG_ONEWAY);
// fail on exception
if (reply.readExceptionCode() != 0) return -1;
return reply.readInt32();
diff --git a/services/camera/libcameraservice/Camera3Device.cpp b/services/camera/libcameraservice/Camera3Device.cpp
index 08aef83..d67b535 100644
--- a/services/camera/libcameraservice/Camera3Device.cpp
+++ b/services/camera/libcameraservice/Camera3Device.cpp
@@ -51,6 +51,8 @@
mId(id),
mHal3Device(NULL),
mStatus(STATUS_UNINITIALIZED),
+ mNextResultFrameNumber(0),
+ mNextShutterFrameNumber(0),
mListener(NULL)
{
ATRACE_CALL();
@@ -246,8 +248,22 @@
mOutputStreams[i]->dump(fd,args);
}
+ lines = String8(" In-flight requests:\n");
+ if (mInFlightMap.size() == 0) {
+ lines.append(" None\n");
+ } else {
+ for (size_t i = 0; i < mInFlightMap.size(); i++) {
+ InFlightRequest r = mInFlightMap.valueAt(i);
+ lines.appendFormat(" Frame %d | Timestamp: %lld, metadata"
+ " arrived: %s, buffers left: %d\n", mInFlightMap.keyAt(i),
+ r.captureTimestamp, r.haveResultMetadata ? "true" : "false",
+ r.numBuffersLeft);
+ }
+ }
+ write(fd, lines.string(), lines.size());
+
if (mHal3Device != NULL) {
- lines = String8(" HAL device dump:\n");
+ lines = String8(" HAL device dump:\n");
write(fd, lines.string(), lines.size());
mHal3Device->ops->dump(mHal3Device, fd);
}
@@ -927,15 +943,36 @@
}
void Camera3Device::setErrorStateLockedV(const char *fmt, va_list args) {
- // Only accept the first failure cause
+ // Print out all error messages to log
+ String8 errorCause = String8::formatV(fmt, args);
+ ALOGE("Camera %d: %s", mId, errorCause.string());
+
+ // But only do error state transition steps for the first error
if (mStatus == STATUS_ERROR) return;
- mErrorCause = String8::formatV(fmt, args);
- ALOGE("Camera %d: %s", mId, mErrorCause.string());
+ mErrorCause = errorCause;
+
+ mRequestThread->setPaused(true);
mStatus = STATUS_ERROR;
}
/**
+ * In-flight request management
+ */
+
+status_t Camera3Device::registerInFlight(int32_t frameNumber,
+ int32_t numBuffers) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mInFlightLock);
+
+ ssize_t res;
+ res = mInFlightMap.add(frameNumber, InFlightRequest(numBuffers));
+ if (res < 0) return res;
+
+ return OK;
+}
+
+/**
* Camera HAL device callback methods
*/
@@ -944,47 +981,107 @@
status_t res;
- if (result->result == NULL) {
- SET_ERR("No metadata provided by HAL for frame %d",
- result->frame_number);
+ uint32_t frameNumber = result->frame_number;
+ if (result->result == NULL && result->num_output_buffers == 0) {
+ SET_ERR("No result data provided by HAL for frame %d",
+ frameNumber);
return;
}
+ // Get capture timestamp from list of in-flight requests, where it was added
+ // by the shutter notification for this frame. Then update the in-flight
+ // status and remove the in-flight entry if all result data has been
+ // received.
nsecs_t timestamp = 0;
+ {
+ Mutex::Autolock l(mInFlightLock);
+ ssize_t idx = mInFlightMap.indexOfKey(frameNumber);
+ if (idx == NAME_NOT_FOUND) {
+ SET_ERR("Unknown frame number for capture result: %d",
+ frameNumber);
+ return;
+ }
+ InFlightRequest &request = mInFlightMap.editValueAt(idx);
+ timestamp = request.captureTimestamp;
+ if (timestamp == 0) {
+ SET_ERR("Called before shutter notify for frame %d",
+ frameNumber);
+ return;
+ }
+
+ if (result->result != NULL) {
+ if (request.haveResultMetadata) {
+ SET_ERR("Called multiple times with metadata for frame %d",
+ frameNumber);
+ return;
+ }
+ request.haveResultMetadata = true;
+ }
+
+ request.numBuffersLeft -= result->num_output_buffers;
+
+ if (request.numBuffersLeft < 0) {
+ SET_ERR("Too many buffers returned for frame %d",
+ frameNumber);
+ return;
+ }
+
+ if (request.haveResultMetadata && request.numBuffersLeft == 0) {
+ mInFlightMap.removeItemsAt(idx, 1);
+ }
+
+ // Sanity check - if we have too many in-flight frames, something has
+ // likely gone wrong
+ if (mInFlightMap.size() > kInFlightWarnLimit) {
+ CLOGE("In-flight list too large: %d", mInFlightMap.size());
+ }
+
+ }
+
AlgState cur3aState;
AlgState new3aState;
int32_t aeTriggerId = 0;
int32_t afTriggerId = 0;
- NotificationListener *listener;
+ NotificationListener *listener = NULL;
- {
+ // Process the result metadata, if provided
+ if (result->result != NULL) {
Mutex::Autolock l(mOutputLock);
- // Push result metadata into queue
- mResultQueue.push_back(CameraMetadata());
- // Lets avoid copies! Too bad there's not a #back method
- CameraMetadata &captureResult = *(--mResultQueue.end());
+ if (frameNumber != mNextResultFrameNumber) {
+ SET_ERR("Out-of-order capture result metadata submitted! "
+ "(got frame number %d, expecting %d)",
+ frameNumber, mNextResultFrameNumber);
+ return;
+ }
+ mNextResultFrameNumber++;
+
+ CameraMetadata &captureResult =
+ *mResultQueue.insert(mResultQueue.end(), CameraMetadata());
captureResult = result->result;
if (captureResult.update(ANDROID_REQUEST_FRAME_COUNT,
- (int32_t*)&result->frame_number, 1) != OK) {
+ (int32_t*)&frameNumber, 1) != OK) {
SET_ERR("Failed to set frame# in metadata (%d)",
- result->frame_number);
+ frameNumber);
} else {
ALOGVV("%s: Camera %d: Set frame# in metadata (%d)",
- __FUNCTION__, mId, result->frame_number);
+ __FUNCTION__, mId, frameNumber);
}
- // Get timestamp from result metadata
+ // Check that there's a timestamp in the result metadata
camera_metadata_entry entry =
captureResult.find(ANDROID_SENSOR_TIMESTAMP);
if (entry.count == 0) {
SET_ERR("No timestamp provided by HAL for frame %d!",
- result->frame_number);
- } else {
- timestamp = entry.data.i64[0];
+ frameNumber);
+ }
+ if (timestamp != entry.data.i64[0]) {
+ SET_ERR("Timestamp mismatch between shutter notify and result"
+ " metadata for frame %d (%lld vs %lld respectively)",
+ frameNumber, timestamp, entry.data.i64[0]);
}
// Get 3A states from result metadata
@@ -992,7 +1089,7 @@
entry = captureResult.find(ANDROID_CONTROL_AE_STATE);
if (entry.count == 0) {
CLOGE("No AE state provided by HAL for frame %d!",
- result->frame_number);
+ frameNumber);
} else {
new3aState.aeState =
static_cast<camera_metadata_enum_android_control_ae_state>(
@@ -1002,7 +1099,7 @@
entry = captureResult.find(ANDROID_CONTROL_AF_STATE);
if (entry.count == 0) {
CLOGE("No AF state provided by HAL for frame %d!",
- result->frame_number);
+ frameNumber);
} else {
new3aState.afState =
static_cast<camera_metadata_enum_android_control_af_state>(
@@ -1012,7 +1109,7 @@
entry = captureResult.find(ANDROID_CONTROL_AWB_STATE);
if (entry.count == 0) {
CLOGE("No AWB state provided by HAL for frame %d!",
- result->frame_number);
+ frameNumber);
} else {
new3aState.awbState =
static_cast<camera_metadata_enum_android_control_awb_state>(
@@ -1022,7 +1119,7 @@
entry = captureResult.find(ANDROID_CONTROL_AF_TRIGGER_ID);
if (entry.count == 0) {
CLOGE("No AF trigger ID provided by HAL for frame %d!",
- result->frame_number);
+ frameNumber);
} else {
afTriggerId = entry.data.i32[0];
}
@@ -1030,7 +1127,7 @@
entry = captureResult.find(ANDROID_CONTROL_AE_PRECAPTURE_ID);
if (entry.count == 0) {
CLOGE("No AE precapture trigger ID provided by HAL"
- " for frame %d!", result->frame_number);
+ " for frame %d!", frameNumber);
} else {
aeTriggerId = entry.data.i32[0];
}
@@ -1041,7 +1138,8 @@
m3AState = new3aState;
} // scope for mOutputLock
- // Return completed buffers to their streams
+ // Return completed buffers to their streams with the timestamp
+
for (size_t i = 0; i < result->num_output_buffers; i++) {
Camera3Stream *stream =
Camera3Stream::cast(result->output_buffers[i].stream);
@@ -1050,20 +1148,21 @@
// last reference to it.
if (res != OK) {
SET_ERR("Can't return buffer %d for frame %d to its stream: "
- " %s (%d)", i, result->frame_number, strerror(-res), res);
+ " %s (%d)", i, frameNumber, strerror(-res), res);
}
}
- // Dispatch any 3A change events to listeners
- if (listener != NULL) {
+ // Finally, dispatch any 3A change events to listeners if we got metadata
+
+ if (result->result != NULL && listener != NULL) {
if (new3aState.aeState != cur3aState.aeState) {
ALOGVV("%s: AE state changed from 0x%x to 0x%x",
- __FUNCTION__, cur3aState.aeState, new3aState.aeState);
+ __FUNCTION__, cur3aState.aeState, new3aState.aeState);
listener->notifyAutoExposure(new3aState.aeState, aeTriggerId);
}
if (new3aState.afState != cur3aState.afState) {
ALOGVV("%s: AF state changed from 0x%x to 0x%x",
- __FUNCTION__, cur3aState.afState, new3aState.afState);
+ __FUNCTION__, cur3aState.afState, new3aState.afState);
listener->notifyAutoFocus(new3aState.afState, afTriggerId);
}
if (new3aState.awbState != cur3aState.awbState) {
@@ -1077,12 +1176,11 @@
NotificationListener *listener;
{
Mutex::Autolock l(mOutputLock);
- if (mListener == NULL) return;
listener = mListener;
}
if (msg == NULL) {
- SET_ERR_L("HAL sent NULL notify message!");
+ SET_ERR("HAL sent NULL notify message!");
return;
}
@@ -1095,17 +1193,50 @@
msg->message.error.error_stream);
streamId = stream->getId();
}
- listener->notifyError(msg->message.error.error_code,
- msg->message.error.frame_number, streamId);
+ if (listener != NULL) {
+ listener->notifyError(msg->message.error.error_code,
+ msg->message.error.frame_number, streamId);
+ }
break;
}
case CAMERA3_MSG_SHUTTER: {
- listener->notifyShutter(msg->message.shutter.frame_number,
- msg->message.shutter.timestamp);
+ ssize_t idx;
+ uint32_t frameNumber = msg->message.shutter.frame_number;
+ nsecs_t timestamp = msg->message.shutter.timestamp;
+ // Verify ordering of shutter notifications
+ {
+ Mutex::Autolock l(mOutputLock);
+ if (frameNumber != mNextShutterFrameNumber) {
+ SET_ERR("Shutter notification out-of-order. Expected "
+ "notification for frame %d, got frame %d",
+ mNextShutterFrameNumber, frameNumber);
+ break;
+ }
+ mNextShutterFrameNumber++;
+ }
+
+ // Set timestamp for the request in the in-flight tracking
+ {
+ Mutex::Autolock l(mInFlightLock);
+ idx = mInFlightMap.indexOfKey(frameNumber);
+ if (idx >= 0) {
+ mInFlightMap.editValueAt(idx).captureTimestamp = timestamp;
+ }
+ }
+ if (idx < 0) {
+ SET_ERR("Shutter notification for non-existent frame number %d",
+ frameNumber);
+ break;
+ }
+
+ // Call listener, if any
+ if (listener != NULL) {
+ listener->notifyShutter(frameNumber, timestamp);
+ }
break;
}
default:
- SET_ERR_L("Unknown notify message from HAL: %d",
+ SET_ERR("Unknown notify message from HAL: %d",
msg->type);
}
}
@@ -1119,6 +1250,7 @@
Thread(false),
mParent(parent),
mHal3Device(hal3Device),
+ mId(getId(parent)),
mReconfigured(false),
mDoPause(false),
mPaused(true),
@@ -1158,6 +1290,12 @@
return OK;
}
+int Camera3Device::RequestThread::getId(const wp<Camera3Device> &device) {
+ sp<Camera3Device> d = device.promote();
+ if (d != NULL) return d->mId;
+ return 0;
+}
+
status_t Camera3Device::RequestThread::queueTriggerLocked(
RequestTrigger trigger) {
@@ -1170,9 +1308,8 @@
case TYPE_INT32:
break;
default:
- ALOGE("%s: Type not supported: 0x%x",
- __FUNCTION__,
- trigger.getTagType());
+ ALOGE("%s: Type not supported: 0x%x", __FUNCTION__,
+ trigger.getTagType());
return INVALID_OPERATION;
}
@@ -1340,6 +1477,22 @@
request.frame_number = mFrameNumber++;
+ // Log request in the in-flight queue
+ sp<Camera3Device> parent = mParent.promote();
+ if (parent == NULL) {
+ CLOGE("RequestThread: Parent is gone");
+ cleanUpFailedRequest(request, nextRequest, outputBuffers);
+ return false;
+ }
+
+ res = parent->registerInFlight(request.frame_number,
+ request.num_output_buffers);
+ if (res != OK) {
+ SET_ERR("RequestThread: Unable to register new in-flight request:"
+ " %s (%d)", strerror(-res), res);
+ cleanUpFailedRequest(request, nextRequest, outputBuffers);
+ return false;
+ }
// Submit request and block until ready for next one
diff --git a/services/camera/libcameraservice/Camera3Device.h b/services/camera/libcameraservice/Camera3Device.h
index 2e4a303..6cad08e 100644
--- a/services/camera/libcameraservice/Camera3Device.h
+++ b/services/camera/libcameraservice/Camera3Device.h
@@ -108,7 +108,8 @@
buffer_handle_t *buffer, wp<BufferReleasedListener> listener);
private:
- static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec
+ static const size_t kInFlightWarnLimit = 20;
+ static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec
struct RequestTrigger;
Mutex mLock;
@@ -262,6 +263,8 @@
virtual bool threadLoop();
private:
+ static int getId(const wp<Camera3Device> &device);
+
status_t queueTriggerLocked(RequestTrigger trigger);
// Mix-in queued triggers into this request
int32_t insertTriggers(const sp<CaptureRequest> &request);
@@ -291,6 +294,8 @@
wp<Camera3Device> mParent;
camera3_device_t *mHal3Device;
+ const int mId;
+
Mutex mRequestLock;
Condition mRequestSignal;
RequestList mRequestQueue;
@@ -308,7 +313,7 @@
sp<CaptureRequest> mPrevRequest;
int32_t mPrevTriggers;
- int32_t mFrameNumber;
+ uint32_t mFrameNumber;
Mutex mLatestRequestMutex;
Condition mLatestRequestSignal;
@@ -324,6 +329,39 @@
sp<RequestThread> mRequestThread;
/**
+ * In-flight queue for tracking completion of capture requests.
+ */
+
+ struct InFlightRequest {
+ // Set by notify() SHUTTER call.
+ nsecs_t captureTimestamp;
+ // Set by process_capture_result call with valid metadata
+ bool haveResultMetadata;
+ // Decremented by calls to process_capture_result with valid output
+ // buffers
+ int numBuffersLeft;
+
+ InFlightRequest() :
+ captureTimestamp(0),
+ haveResultMetadata(false),
+ numBuffersLeft(0) {
+ }
+
+ explicit InFlightRequest(int numBuffers) :
+ captureTimestamp(0),
+ haveResultMetadata(false),
+ numBuffersLeft(numBuffers) {
+ }
+ };
+ // Map from frame number to the in-flight request state
+ typedef KeyedVector<uint32_t, InFlightRequest> InFlightMap;
+
+ Mutex mInFlightLock; // Protects mInFlightMap
+ InFlightMap mInFlightMap;
+
+ status_t registerInFlight(int32_t frameNumber, int32_t numBuffers);
+
+ /**
* Output result queue and current HAL device 3A state
*/
@@ -332,6 +370,8 @@
/**** Scope for mOutputLock ****/
+ uint32_t mNextResultFrameNumber;
+ uint32_t mNextShutterFrameNumber;
List<CameraMetadata> mResultQueue;
Condition mResultSignal;
NotificationListener *mListener;