Merge "Make sure the encoder instance is safely release()'d before its" into klp-dev
diff --git a/camera/tests/ProCameraTests.cpp b/camera/tests/ProCameraTests.cpp
index f203949..e9aa99d 100644
--- a/camera/tests/ProCameraTests.cpp
+++ b/camera/tests/ProCameraTests.cpp
@@ -271,7 +271,6 @@
CpuConsumer::LockedBuffer buf;
status_t ret;
- EXPECT_OK(ret);
if (OK == (ret = consumer->lockNextBuffer(&buf))) {
dout << "Frame received on streamId = " << streamId <<
@@ -482,7 +481,7 @@
* Creating a streaming request for these output streams from a template,
* and submit it
*/
- void createSubmitRequestForStreams(uint8_t* streamIds, size_t count, int requestCount=-1) {
+ void createSubmitRequestForStreams(int32_t* streamIds, size_t count, int requestCount=-1) {
ASSERT_NE((void*)NULL, streamIds);
ASSERT_LT(0u, count);
@@ -629,7 +628,7 @@
EXPECT_OK(mCamera->exclusiveTryLock());
- uint8_t streams[] = { depthStreamId };
+ int32_t streams[] = { depthStreamId };
ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(
streams,
/*count*/1));
@@ -706,7 +705,7 @@
// set the output streams to just this stream ID
// wow what a verbose API.
- uint8_t allStreams[] = { streamId, depthStreamId };
+ int32_t allStreams[] = { streamId, depthStreamId };
// IMPORTANT. bad things will happen if its not a uint8.
size_t streamCount = sizeof(allStreams) / sizeof(allStreams[0]);
camera_metadata_entry_t entry;
@@ -735,7 +734,7 @@
free_camera_metadata(request);
- for (int i = 0; i < streamCount; ++i) {
+ for (size_t i = 0; i < streamCount; ++i) {
EXPECT_OK(mCamera->deleteStream(allStreams[i]));
}
EXPECT_OK(mCamera->exclusiveUnlock());
@@ -777,7 +776,7 @@
// set the output streams to just this stream ID
- uint8_t allStreams[] = { streamId };
+ int32_t allStreams[] = { streamId };
camera_metadata_entry_t entry;
uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
int find = find_camera_metadata_entry(request, tag, &entry);
@@ -848,7 +847,7 @@
// set the output streams to just this stream ID
// wow what a verbose API.
- uint8_t allStreams[] = { streamId, depthStreamId };
+ int32_t allStreams[] = { streamId, depthStreamId };
size_t streamCount = 2;
camera_metadata_entry_t entry;
uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
@@ -923,7 +922,7 @@
// set the output streams to just this stream ID
- uint8_t allStreams[] = { streamId };
+ int32_t allStreams[] = { streamId };
size_t streamCount = 1;
camera_metadata_entry_t entry;
uint32_t tag = static_cast<uint32_t>(ANDROID_REQUEST_OUTPUT_STREAMS);
@@ -974,7 +973,7 @@
EXPECT_OK(mCamera->exclusiveTryLock());
- uint8_t streams[] = { streamId };
+ int32_t streams[] = { streamId };
ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1));
// Consume a couple of results
@@ -1002,7 +1001,7 @@
EXPECT_OK(mCamera->exclusiveTryLock());
- uint8_t streams[] = { streamId };
+ int32_t streams[] = { streamId };
ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
/*requests*/TEST_CPU_FRAME_COUNT));
@@ -1049,7 +1048,7 @@
EXPECT_OK(mCamera->exclusiveTryLock());
- uint8_t streams[] = { streamId, depthStreamId };
+ int32_t streams[] = { streamId, depthStreamId };
ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/2,
/*requests*/REQUEST_COUNT));
@@ -1128,7 +1127,7 @@
EXPECT_OK(mCamera->exclusiveTryLock());
- uint8_t streams[] = { streamId };
+ int32_t streams[] = { streamId };
ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
/*requests*/NUM_REQUESTS));
@@ -1172,7 +1171,6 @@
}
const int NUM_REQUESTS = 20 * TEST_CPU_FRAME_COUNT;
- const int CONSECUTIVE_FAILS_ASSUME_TIME_OUT = 5;
int streamId = -1;
sp<CpuConsumer> consumer;
@@ -1183,7 +1181,7 @@
EXPECT_OK(mCamera->exclusiveTryLock());
- uint8_t streams[] = { streamId };
+ int32_t streams[] = { streamId };
ASSERT_NO_FATAL_FAILURE(createSubmitRequestForStreams(streams, /*count*/1,
/*requests*/NUM_REQUESTS));
diff --git a/include/media/EffectsFactoryApi.h b/include/media/EffectsFactoryApi.h
index b1ed7b0..b1143b9 100644
--- a/include/media/EffectsFactoryApi.h
+++ b/include/media/EffectsFactoryApi.h
@@ -171,6 +171,30 @@
////////////////////////////////////////////////////////////////////////////////
int EffectIsNullUuid(const effect_uuid_t *pEffectUuid);
+////////////////////////////////////////////////////////////////////////////////
+//
+// Function: EffectGetSubEffects
+//
+// Description: Returns the descriptors of the sub effects of the effect
+// whose uuid is pointed to by first argument.
+//
+// Input:
+// pEffectUuid: pointer to the effect uuid.
+// size: size of the buffer pointed by pDescriptor.
+//
+// Input/Output:
+// pDescriptor: address where to return the sub effect descriptors.
+//
+// Output:
+// returned value: 0 successful operation.
+// -ENODEV factory failed to initialize
+// -EINVAL invalid pEffectUuid or pDescriptor
+// -ENOENT no effect with this uuid found
+// *pDescriptor: updated with the sub effect descriptors.
+//
+////////////////////////////////////////////////////////////////////////////////
+int EffectGetSubEffects(const effect_uuid_t *pEffectUuid, effect_descriptor_t *pDescriptors, size_t size);
+
#if __cplusplus
} // extern "C"
#endif
diff --git a/include/media/stagefright/MediaMuxer.h b/include/media/stagefright/MediaMuxer.h
index c1fdbad..ff6a66e 100644
--- a/include/media/stagefright/MediaMuxer.h
+++ b/include/media/stagefright/MediaMuxer.h
@@ -79,6 +79,16 @@
status_t setOrientationHint(int degrees);
/**
+ * Set the location.
+ * @param latitude The latitude in degree x 1000. Its value must be in the range
+ * [-900000, 900000].
+ * @param longitude The longitude in degree x 1000. Its value must be in the range
+ * [-1800000, 1800000].
+ * @return OK if no error.
+ */
+ status_t setLocation(int latitude, int longitude);
+
+ /**
* Stop muxing.
* This method is a blocking call. Depending on how
* much data is bufferred internally, the time needed for stopping
diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf
index 93f27cb..69a3c53 100644
--- a/media/libeffects/data/audio_effects.conf
+++ b/media/libeffects/data/audio_effects.conf
@@ -6,6 +6,23 @@
# }
# }
libraries {
+# This is a proxy library that will be an abstraction for
+# the HW and SW effects
+
+ #proxy {
+ #path /system/lib/soundfx/libeffectproxy.so
+ #}
+
+# This is the SW implementation library of the effect
+ #libSW {
+ #path /system/lib/soundfx/libswwrapper.so
+ #}
+
+# This is the HW implementation library for the effect
+ #libHW {
+ #path /system/lib/soundfx/libhwwrapper.so
+ #}
+
bundle {
path /system/lib/soundfx/libbundlewrapper.so
}
@@ -43,6 +60,28 @@
# }
effects {
+
+# additions for the proxy implementation
+# Proxy implementation
+ #effectname {
+ #library proxy
+ #uuid xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx
+
+ # SW implemetation of the effect. Added as a node under the proxy to
+ # indicate this as a sub effect.
+ #libsw {
+ #library libSW
+ #uuid yyyyyyyy-yyyy-yyyy-yyyy-yyyyyyyyyyyy
+ #} End of SW effect
+
+ # HW implementation of the effect. Added as a node under the proxy to
+ # indicate this as a sub effect.
+ #libhw {
+ #library libHW
+ #uuid zzzzzzzz-zzzz-zzzz-zzzz-zzzzzzzzzzzz
+ #}End of HW effect
+ #} End of effect proxy
+
bassboost {
library bundle
uuid 8631f300-72e2-11df-b57e-0002a5d5c51b
diff --git a/media/libeffects/factory/EffectsFactory.c b/media/libeffects/factory/EffectsFactory.c
index f158929..f8d6041 100644
--- a/media/libeffects/factory/EffectsFactory.c
+++ b/media/libeffects/factory/EffectsFactory.c
@@ -28,6 +28,9 @@
static list_elem_t *gEffectList; // list of effect_entry_t: all currently created effects
static list_elem_t *gLibraryList; // list of lib_entry_t: all currently loaded libraries
+// list of effect_descriptor and list of sub effects : all currently loaded
+// It does not contain effects without sub effects.
+static list_sub_elem_t *gSubEffectList;
static pthread_mutex_t gLibLock = PTHREAD_MUTEX_INITIALIZER; // controls access to gLibraryList
static uint32_t gNumEffects; // total number number of effects
static list_elem_t *gCurLib; // current library in enumeration process
@@ -50,6 +53,8 @@
static int loadLibrary(cnode *root, const char *name);
static int loadEffects(cnode *root);
static int loadEffect(cnode *node);
+// To get and add the effect pointed by the passed node to the gSubEffectList
+static int addSubEffect(cnode *root);
static lib_entry_t *getLibrary(const char *path);
static void resetEffectEnumeration();
static uint32_t updateNumEffects();
@@ -57,6 +62,10 @@
const effect_uuid_t *uuid,
lib_entry_t **lib,
effect_descriptor_t **desc);
+// To search a subeffect in the gSubEffectList
+int findSubEffect(const effect_uuid_t *uuid,
+ lib_entry_t **lib,
+ effect_descriptor_t **desc);
static void dumpEffectDescriptor(effect_descriptor_t *desc, char *str, size_t len);
static int stringToUuid(const char *str, effect_uuid_t *uuid);
static int uuidToString(const effect_uuid_t *uuid, char *str, size_t maxLen);
@@ -287,7 +296,12 @@
ret = findEffect(NULL, uuid, &l, &d);
if (ret < 0){
- goto exit;
+ // Sub effects are not associated with the library->effects,
+ // so, findEffect will fail. Search for the effect in gSubEffectList.
+ ret = findSubEffect(uuid, &l, &d);
+ if (ret < 0 ) {
+ goto exit;
+ }
}
// create effect in library
@@ -354,21 +368,27 @@
}
if (e1 == NULL) {
ret = -ENOENT;
+ pthread_mutex_unlock(&gLibLock);
goto exit;
}
// release effect in library
if (fx->lib == NULL) {
ALOGW("EffectRelease() fx %p library already unloaded", handle);
+ pthread_mutex_unlock(&gLibLock);
} else {
pthread_mutex_lock(&fx->lib->lock);
+ // Releasing the gLibLock here as the list access is over as the
+ // effect is removed from the list.
+ // If the gLibLock is not released, we will have a deadlock situation
+ // since we call the sub effect release inside the EffectRelease of Proxy
+ pthread_mutex_unlock(&gLibLock);
fx->lib->desc->release_effect(fx->subItfe);
pthread_mutex_unlock(&fx->lib->lock);
}
free(fx);
exit:
- pthread_mutex_unlock(&gLibLock);
return ret;
}
@@ -380,6 +400,49 @@
return 1;
}
+// Function to get the sub effect descriptors of the effect whose uuid
+// is pointed by the first argument. It searches the gSubEffectList for the
+// matching uuid and then copies the corresponding sub effect descriptors
+// to the inout param
+int EffectGetSubEffects(const effect_uuid_t *uuid,
+ effect_descriptor_t *pDescriptors, size_t size)
+{
+ ALOGV("EffectGetSubEffects() UUID: %08X-%04X-%04X-%04X-%02X%02X%02X%02X%02X"
+ "%02X\n",uuid->timeLow, uuid->timeMid, uuid->timeHiAndVersion,
+ uuid->clockSeq, uuid->node[0], uuid->node[1],uuid->node[2],
+ uuid->node[3],uuid->node[4],uuid->node[5]);
+
+ // Check if the size of the desc buffer is large enough for 2 subeffects
+ if ((uuid == NULL) || (pDescriptors == NULL) ||
+ (size < 2*sizeof(effect_descriptor_t))) {
+ ALOGW("NULL pointer or insufficient memory. Cannot query subeffects");
+ return -EINVAL;
+ }
+ int ret = init();
+ if (ret < 0)
+ return ret;
+ list_sub_elem_t *e = gSubEffectList;
+ sub_effect_entry_t *subeffect;
+ effect_descriptor_t *d;
+ int count = 0;
+ while (e != NULL) {
+ d = (effect_descriptor_t*)e->object;
+ if (memcmp(uuid, &d->uuid, sizeof(effect_uuid_t)) == 0) {
+ ALOGV("EffectGetSubEffects: effect found in the list");
+ list_elem_t *subefx = e->sub_elem;
+ while (subefx != NULL) {
+ subeffect = (sub_effect_entry_t*)subefx->object;
+ d = (effect_descriptor_t*)(subeffect->object);
+ pDescriptors[count++] = *d;
+ subefx = subefx->next;
+ }
+ ALOGV("EffectGetSubEffects end - copied the sub effect descriptors");
+ return count;
+ }
+ e = e->next;
+ }
+ return -ENOENT;
+}
/////////////////////////////////////////////////
// Local functions
/////////////////////////////////////////////////
@@ -503,6 +566,65 @@
return -EINVAL;
}
+// This will find the library and UUID tags of the sub effect pointed by the
+// node, gets the effect descriptor and lib_entry_t and adds the subeffect -
+// sub_entry_t to the gSubEffectList
+int addSubEffect(cnode *root)
+{
+ ALOGV("addSubEffect");
+ cnode *node;
+ effect_uuid_t uuid;
+ effect_descriptor_t *d;
+ lib_entry_t *l;
+ list_elem_t *e;
+ node = config_find(root, LIBRARY_TAG);
+ if (node == NULL) {
+ return -EINVAL;
+ }
+ l = getLibrary(node->value);
+ if (l == NULL) {
+ ALOGW("addSubEffect() could not get library %s", node->value);
+ return -EINVAL;
+ }
+ node = config_find(root, UUID_TAG);
+ if (node == NULL) {
+ return -EINVAL;
+ }
+ if (stringToUuid(node->value, &uuid) != 0) {
+ ALOGW("addSubEffect() invalid uuid %s", node->value);
+ return -EINVAL;
+ }
+ d = malloc(sizeof(effect_descriptor_t));
+ if (l->desc->get_descriptor(&uuid, d) != 0) {
+ char s[40];
+ uuidToString(&uuid, s, 40);
+ ALOGW("Error querying effect %s on lib %s", s, l->name);
+ free(d);
+ return -EINVAL;
+ }
+#if (LOG_NDEBUG==0)
+ char s[256];
+ dumpEffectDescriptor(d, s, 256);
+ ALOGV("addSubEffect() read descriptor %p:%s",d, s);
+#endif
+ if (EFFECT_API_VERSION_MAJOR(d->apiVersion) !=
+ EFFECT_API_VERSION_MAJOR(EFFECT_CONTROL_API_VERSION)) {
+ ALOGW("Bad API version %08x on lib %s", d->apiVersion, l->name);
+ free(d);
+ return -EINVAL;
+ }
+ sub_effect_entry_t *sub_effect = malloc(sizeof(sub_effect_entry_t));
+ sub_effect->object = d;
+ // lib_entry_t is stored since the sub effects are not linked to the library
+ sub_effect->lib = l;
+ e = malloc(sizeof(list_elem_t));
+ e->object = sub_effect;
+ e->next = gSubEffectList->sub_elem;
+ gSubEffectList->sub_elem = e;
+ ALOGV("addSubEffect end");
+ return 0;
+}
+
int loadEffects(cnode *root)
{
cnode *node;
@@ -571,9 +693,101 @@
e->next = l->effects;
l->effects = e;
+ // After the UUID node in the config_tree, if node->next is valid,
+ // that would be sub effect node.
+ // Find the sub effects and add them to the gSubEffectList
+ node = node->next;
+ int count = 2;
+ bool hwSubefx = false, swSubefx = false;
+ list_sub_elem_t *sube = NULL;
+ if (node != NULL) {
+ ALOGV("Adding the effect to gEffectSubList as there are sub effects");
+ sube = malloc(sizeof(list_sub_elem_t));
+ sube->object = d;
+ sube->sub_elem = NULL;
+ sube->next = gSubEffectList;
+ gSubEffectList = sube;
+ }
+ while (node != NULL && count) {
+ if (addSubEffect(node)) {
+ ALOGW("loadEffect() could not add subEffect %s", node->value);
+ // Change the gSubEffectList to point to older list;
+ gSubEffectList = sube->next;
+ free(sube->sub_elem);// Free an already added sub effect
+ sube->sub_elem = NULL;
+ free(sube);
+ return -ENOENT;
+ }
+ sub_effect_entry_t *subEntry = (sub_effect_entry_t*)gSubEffectList->sub_elem->object;
+ effect_descriptor_t *subEffectDesc = (effect_descriptor_t*)(subEntry->object);
+ // Since we return a dummy descriptor for the proxy during
+ // get_descriptor call,we replace it with the correspoding
+ // sw effect descriptor, but with Proxy UUID
+ // check for Sw desc
+ if (!((subEffectDesc->flags & EFFECT_FLAG_HW_ACC_MASK) ==
+ EFFECT_FLAG_HW_ACC_TUNNEL)) {
+ swSubefx = true;
+ *d = *subEffectDesc;
+ d->uuid = uuid;
+ ALOGV("loadEffect() Changed the Proxy desc");
+ } else
+ hwSubefx = true;
+ count--;
+ node = node->next;
+ }
+ // 1 HW and 1 SW sub effect found. Set the offload flag in the Proxy desc
+ if (hwSubefx && swSubefx) {
+ d->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED;
+ }
return 0;
}
+// Searches the sub effect matching to the specified uuid
+// in the gSubEffectList. It gets the lib_entry_t for
+// the matched sub_effect . Used in EffectCreate of sub effects
+int findSubEffect(const effect_uuid_t *uuid,
+ lib_entry_t **lib,
+ effect_descriptor_t **desc)
+{
+ list_sub_elem_t *e = gSubEffectList;
+ list_elem_t *subefx;
+ sub_effect_entry_t *effect;
+ lib_entry_t *l = NULL;
+ effect_descriptor_t *d = NULL;
+ int found = 0;
+ int ret = 0;
+
+ if (uuid == NULL)
+ return -EINVAL;
+
+ while (e != NULL && !found) {
+ subefx = (list_elem_t*)(e->sub_elem);
+ while (subefx != NULL) {
+ effect = (sub_effect_entry_t*)subefx->object;
+ l = (lib_entry_t *)effect->lib;
+ d = (effect_descriptor_t *)effect->object;
+ if (memcmp(&d->uuid, uuid, sizeof(effect_uuid_t)) == 0) {
+ ALOGV("uuid matched");
+ found = 1;
+ break;
+ }
+ subefx = subefx->next;
+ }
+ e = e->next;
+ }
+ if (!found) {
+ ALOGV("findSubEffect() effect not found");
+ ret = -ENOENT;
+ } else {
+ ALOGV("findSubEffect() found effect: %s in lib %s", d->name, l->name);
+ *lib = l;
+ if (desc != NULL) {
+ *desc = d;
+ }
+ }
+ return ret;
+}
+
lib_entry_t *getLibrary(const char *name)
{
list_elem_t *e;
diff --git a/media/libeffects/factory/EffectsFactory.h b/media/libeffects/factory/EffectsFactory.h
index c1d4319..147ff18 100644
--- a/media/libeffects/factory/EffectsFactory.h
+++ b/media/libeffects/factory/EffectsFactory.h
@@ -32,6 +32,15 @@
struct list_elem_s *next;
} list_elem_t;
+// Structure used for storing effects with their sub effects.
+// Used in creating gSubEffectList. Here,
+// object holds the effect desc and the list sub_elem holds the sub effects
+typedef struct list_sub_elem_s {
+ void *object;
+ list_elem_t *sub_elem;
+ struct list_sub_elem_s *next;
+} list_sub_elem_t;
+
typedef struct lib_entry_s {
audio_effect_library_t *desc;
char *name;
@@ -47,6 +56,16 @@
lib_entry_t *lib;
} effect_entry_t;
+// Structure used to store the lib entry
+// and the descriptor of the sub effects.
+// The library entry is to be stored in case of
+// sub effects as the sub effects are not linked
+// to the library list - gLibraryList.
+typedef struct sub_effect_entry_s {
+ lib_entry_t *lib;
+ void *object;
+} sub_effect_entry_t;
+
#if __cplusplus
} // extern "C"
#endif
diff --git a/media/libeffects/proxy/Android.mk b/media/libeffects/proxy/Android.mk
new file mode 100644
index 0000000..01b3be1
--- /dev/null
+++ b/media/libeffects/proxy/Android.mk
@@ -0,0 +1,34 @@
+# Copyright 2013 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+LOCAL_MODULE:= libeffectproxy
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+LOCAL_MODULE_TAGS := optional
+
+
+LOCAL_SRC_FILES := \
+ EffectProxy.cpp
+
+LOCAL_CFLAGS+= -fvisibility=hidden
+
+LOCAL_SHARED_LIBRARIES := liblog libcutils libutils libdl libeffects
+
+LOCAL_C_INCLUDES := \
+ system/media/audio_effects/include \
+ bionic/libc/include
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libeffects/proxy/EffectProxy.cpp b/media/libeffects/proxy/EffectProxy.cpp
new file mode 100644
index 0000000..77c6e89
--- /dev/null
+++ b/media/libeffects/proxy/EffectProxy.cpp
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "EffectProxy"
+//#define LOG_NDEBUG 0
+
+#include <cutils/log.h>
+#include <assert.h>
+#include <stdlib.h>
+#include <string.h>
+#include <new>
+#include <EffectProxy.h>
+#include <utils/threads.h>
+#include <media/EffectsFactoryApi.h>
+
+namespace android {
+// This is a dummy proxy descriptor just to return to Factory during the initial
+// GetDescriptor call. Later in the factory, it is replaced with the
+// SW sub effect descriptor
+const effect_descriptor_t gProxyDescriptor = {
+ EFFECT_UUID_INITIALIZER, // type
+ EFFECT_UUID_INITIALIZER, // uuid
+ EFFECT_CONTROL_API_VERSION, //version of effect control API
+ (EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_LAST |
+ EFFECT_FLAG_VOLUME_CTRL), // effect capability flags
+ 0, // CPU load
+ 1, // Data memory
+ "Proxy", //effect name
+ "AOSP", //implementor name
+};
+
+
+static const effect_descriptor_t *const gDescriptors[] =
+{
+ &gProxyDescriptor,
+};
+
+int EffectProxyCreate(const effect_uuid_t *uuid,
+ int32_t sessionId,
+ int32_t ioId,
+ effect_handle_t *pHandle) {
+
+ effect_descriptor_t* desc;
+ EffectContext* pContext;
+ if (pHandle == NULL || uuid == NULL) {
+ ALOGE("EffectProxyCreate() called with NULL pointer");
+ return -EINVAL;
+ }
+ ALOGV("EffectProxyCreate start..");
+ pContext = new EffectContext;
+ pContext->sessionId = sessionId;
+ pContext->ioId = ioId;
+ pContext->uuid = *uuid;
+ pContext->common_itfe = &gEffectInterface;
+ // The sub effects will be created in effect_command when the first command
+ // for the effect is received
+ pContext->eHandle[SUB_FX_HOST] = pContext->eHandle[SUB_FX_OFFLOAD] = NULL;
+
+ // Get the HW and SW sub effect descriptors from the effects factory
+ desc = new effect_descriptor_t[SUB_FX_COUNT];
+ pContext->desc = new effect_descriptor_t[SUB_FX_COUNT];
+ int retValue = EffectGetSubEffects(uuid, desc,
+ sizeof(effect_descriptor_t) * SUB_FX_COUNT);
+ // EffectGetSubEffects returns the number of sub-effects copied.
+ if (retValue != SUB_FX_COUNT) {
+ ALOGE("EffectCreate() could not get the sub effects");
+ delete desc;
+ delete pContext->desc;
+ return -EINVAL;
+ }
+ // Check which is the HW descriptor and copy the descriptors
+ // to the Context desc array
+ // Also check if there is only one HW and one SW descriptor.
+ // HW descriptor alone has the HW_TUNNEL flag.
+ if ((desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL) &&
+ !(desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) {
+ pContext->desc[SUB_FX_OFFLOAD] = desc[0];
+ pContext->desc[SUB_FX_HOST] = desc[1];
+ }
+ else if ((desc[1].flags & EFFECT_FLAG_HW_ACC_TUNNEL) &&
+ !(desc[0].flags & EFFECT_FLAG_HW_ACC_TUNNEL)) {
+ pContext->desc[SUB_FX_HOST] = desc[0];
+ pContext->desc[SUB_FX_OFFLOAD] = desc[1];
+ }
+ delete desc;
+#if (LOG_NDEBUG == 0)
+ effect_uuid_t uuid_print = pContext->desc[SUB_FX_HOST].uuid;
+ ALOGV("EffectCreate() UUID of HOST: %08X-%04X-%04X-%04X-%02X%02X%02X%02X"
+ "%02X%02X\n",uuid_print.timeLow, uuid_print.timeMid,
+ uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0],
+ uuid_print.node[1], uuid_print.node[2], uuid_print.node[3],
+ uuid_print.node[4], uuid_print.node[5]);
+ ALOGV("EffectCreate() UUID of OFFLOAD: %08X-%04X-%04X-%04X-%02X%02X%02X%02X"
+ "%02X%02X\n", uuid_print.timeLow, uuid_print.timeMid,
+ uuid_print.timeHiAndVersion, uuid_print.clockSeq, uuid_print.node[0],
+ uuid_print.node[1], uuid_print.node[2], uuid_print.node[3],
+ uuid_print.node[4], uuid_print.node[5]);
+#endif
+ *pHandle = (effect_handle_t)pContext;
+ ALOGV("EffectCreate end");
+ return 0;
+} //end EffectProxyCreate
+
+int EffectProxyRelease(effect_handle_t handle) {
+ EffectContext * pContext = (EffectContext *)handle;
+ if (pContext == NULL) {
+ ALOGV("ERROR : EffectRelease called with NULL pointer");
+ return -EINVAL;
+ }
+ ALOGV("EffectRelease");
+ delete pContext->desc;
+ if (pContext->eHandle[SUB_FX_HOST])
+ EffectRelease(pContext->eHandle[SUB_FX_HOST]);
+ if (pContext->eHandle[SUB_FX_OFFLOAD])
+ EffectRelease(pContext->eHandle[SUB_FX_OFFLOAD]);
+ delete pContext;
+ pContext = NULL;
+ return 0;
+} /*end EffectProxyRelease */
+
+int EffectProxyGetDescriptor(const effect_uuid_t *uuid,
+ effect_descriptor_t *pDescriptor) {
+ const effect_descriptor_t *desc = NULL;
+
+ if (pDescriptor == NULL || uuid == NULL) {
+ ALOGV("EffectGetDescriptor() called with NULL pointer");
+ return -EINVAL;
+ }
+ desc = &gProxyDescriptor;
+ *pDescriptor = *desc;
+ return 0;
+} /* end EffectProxyGetDescriptor */
+
+/* Effect Control Interface Implementation: Process */
+int Effect_process(effect_handle_t self,
+ audio_buffer_t *inBuffer,
+ audio_buffer_t *outBuffer) {
+
+ EffectContext *pContext = (EffectContext *) self;
+ int ret = 0;
+ if (pContext != NULL) {
+ int index = pContext->index;
+ // if the index refers to HW , do not do anything. Just return.
+ if (index == SUB_FX_HOST) {
+ ALOGV("Calling CoreProcess");
+ ret = (*pContext->eHandle[index])->process(pContext->eHandle[index],
+ inBuffer, outBuffer);
+ }
+ }
+ return ret;
+} /* end Effect_process */
+
+/* Effect Control Interface Implementation: Command */
+int Effect_command(effect_handle_t self,
+ uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData) {
+
+ EffectContext *pContext = (EffectContext *) self;
+ int status;
+ if (pContext == NULL) {
+ ALOGV("Effect_command() Proxy context is NULL");
+ return -EINVAL;
+ }
+ if (pContext->eHandle[SUB_FX_HOST] == NULL) {
+ ALOGV("Effect_command() Calling HOST EffectCreate");
+ status = EffectCreate(&pContext->desc[SUB_FX_HOST].uuid,
+ pContext->sessionId, pContext->ioId,
+ &(pContext->eHandle[SUB_FX_HOST]));
+ if (status != NO_ERROR || (pContext->eHandle[SUB_FX_HOST] == NULL)) {
+ ALOGV("Effect_command() Error creating SW sub effect");
+ return status;
+ }
+ }
+ if (pContext->eHandle[SUB_FX_OFFLOAD] == NULL) {
+ ALOGV("Effect_command() Calling OFFLOAD EffectCreate");
+ status = EffectCreate(&pContext->desc[SUB_FX_OFFLOAD].uuid,
+ pContext->sessionId, pContext->ioId,
+ &(pContext->eHandle[SUB_FX_OFFLOAD]));
+ if (status != NO_ERROR || (pContext->eHandle[SUB_FX_OFFLOAD] == NULL)) {
+ ALOGV("Effect_command() Error creating HW effect");
+ // Do not return error here as SW effect is created
+ // Return error if the CMD_OFFLOAD sends the index as OFFLOAD
+ }
+ pContext->index = SUB_FX_HOST;
+ }
+ // EFFECT_CMD_OFFLOAD used to (1) send whether the thread is offload or not
+ // (2) Send the ioHandle of the effectThread when the effect
+ // is moved from one type of thread to another.
+ // pCmdData points to a memory holding effect_offload_param_t structure
+ if (cmdCode == EFFECT_CMD_OFFLOAD) {
+ ALOGV("Effect_command() cmdCode = EFFECT_CMD_OFFLOAD");
+ if (cmdSize == 0 || pCmdData == NULL) {
+ ALOGV("effectsOffload: Effect_command: CMD_OFFLOAD has no data");
+ *(int*)pReplyData = FAILED_TRANSACTION;
+ return FAILED_TRANSACTION;
+ }
+ effect_offload_param_t* offloadParam = (effect_offload_param_t*)pCmdData;
+ // Assign the effect context index based on isOffload field of the structure
+ pContext->index = offloadParam->isOffload ? SUB_FX_OFFLOAD : SUB_FX_HOST;
+ // if the index is HW and the HW effect is unavailable, return error
+ // and reset the index to SW
+ if (pContext->eHandle[pContext->index] == NULL) {
+ ALOGV("Effect_command()CMD_OFFLOAD sub effect unavailable");
+ *(int*)pReplyData = FAILED_TRANSACTION;
+ return FAILED_TRANSACTION;
+ }
+ pContext->ioId = offloadParam->ioHandle;
+ ALOGV("Effect_command()CMD_OFFLOAD index:%d io %d", pContext->index, pContext->ioId);
+ // Update the DSP wrapper with the new ioHandle.
+ // Pass the OFFLOAD command to the wrapper.
+ // The DSP wrapper needs to handle this CMD
+ if (pContext->eHandle[SUB_FX_OFFLOAD])
+ status = (*pContext->eHandle[SUB_FX_OFFLOAD])->command(
+ pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize,
+ pCmdData, replySize, pReplyData);
+ return status;
+ }
+
+ int index = pContext->index;
+ if (index != SUB_FX_HOST && index != SUB_FX_OFFLOAD) {
+ ALOGV("Effect_command: effect index is neither offload nor host");
+ return -EINVAL;
+ }
+ ALOGV("Effect_command: pContext->eHandle[%d]: %p",
+ index, pContext->eHandle[index]);
+ if (pContext->eHandle[SUB_FX_HOST])
+ (*pContext->eHandle[SUB_FX_HOST])->command(
+ pContext->eHandle[SUB_FX_HOST], cmdCode, cmdSize,
+ pCmdData, replySize, pReplyData);
+ if (pContext->eHandle[SUB_FX_OFFLOAD]) {
+ // In case of SET CMD, when the offload stream is unavailable,
+ // we will store the effect param values in the DSP effect wrapper.
+ // When the offload effects get enabled, we send these values to the
+ // DSP during Effect_config.
+ // So,we send the params to DSP wrapper also
+ (*pContext->eHandle[SUB_FX_OFFLOAD])->command(
+ pContext->eHandle[SUB_FX_OFFLOAD], cmdCode, cmdSize,
+ pCmdData, replySize, pReplyData);
+ }
+ return 0;
+} /* end Effect_command */
+
+
+/* Effect Control Interface Implementation: get_descriptor */
+int Effect_getDescriptor(effect_handle_t self,
+ effect_descriptor_t *pDescriptor) {
+
+ EffectContext * pContext = (EffectContext *) self;
+ const effect_descriptor_t *desc;
+
+ ALOGV("Effect_getDescriptor");
+ if (pContext == NULL || pDescriptor == NULL) {
+ ALOGV("Effect_getDescriptor() invalid param");
+ return -EINVAL;
+ }
+ if (pContext->desc == NULL) {
+ ALOGV("Effect_getDescriptor() could not get descriptor");
+ return -EINVAL;
+ }
+ desc = &pContext->desc[SUB_FX_HOST];
+ *pDescriptor = *desc;
+ pDescriptor->uuid = pContext->uuid; // Replace the uuid with the Proxy UUID
+ // Also set/clear the EFFECT_FLAG_OFFLOAD_SUPPORTED flag based on the sub effects availability
+ if (pContext->eHandle[SUB_FX_OFFLOAD] != NULL)
+ pDescriptor->flags |= EFFECT_FLAG_OFFLOAD_SUPPORTED;
+ else
+ pDescriptor->flags &= ~EFFECT_FLAG_OFFLOAD_SUPPORTED;
+ return 0;
+} /* end Effect_getDescriptor */
+
+} // namespace android
+
+__attribute__ ((visibility ("default")))
+audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
+ tag : AUDIO_EFFECT_LIBRARY_TAG,
+ version : EFFECT_LIBRARY_API_VERSION,
+ name : "Effect Proxy",
+ implementor : "AOSP",
+ create_effect : android::EffectProxyCreate,
+ release_effect : android::EffectProxyRelease,
+ get_descriptor : android::EffectProxyGetDescriptor,
+};
diff --git a/media/libeffects/proxy/EffectProxy.h b/media/libeffects/proxy/EffectProxy.h
new file mode 100644
index 0000000..8992f93
--- /dev/null
+++ b/media/libeffects/proxy/EffectProxy.h
@@ -0,0 +1,75 @@
+/*
+ * Copyright (C) 2013 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <hardware/audio.h>
+#include <hardware/audio_effect.h>
+namespace android {
+enum {
+ SUB_FX_HOST, // Index of HOST in the descriptor and handle arrays
+ // of the Proxy context
+ SUB_FX_OFFLOAD, // Index of OFFLOAD in the descriptor and handle arrays
+ // of the Proxy context
+ SUB_FX_COUNT // The number of sub effects for a Proxy(1 HW, 1 SW)
+};
+#if __cplusplus
+extern "C" {
+#endif
+
+int EffectProxyCreate(const effect_uuid_t *uuid,
+ int32_t sessionId,
+ int32_t ioId,
+ effect_handle_t *pHandle);
+int EffectProxyRelease(effect_handle_t handle);
+int EffectProxyGetDescriptor(const effect_uuid_t *uuid,
+ effect_descriptor_t *pDescriptor);
+/* Effect Control Interface Implementation: Process */
+int Effect_process(effect_handle_t self,
+ audio_buffer_t *inBuffer,
+ audio_buffer_t *outBuffer);
+
+/* Effect Control Interface Implementation: Command */
+int Effect_command(effect_handle_t self,
+ uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData);
+int Effect_getDescriptor(effect_handle_t self,
+ effect_descriptor_t *pDescriptor);
+
+const struct effect_interface_s gEffectInterface = {
+ Effect_process,
+ Effect_command,
+ Effect_getDescriptor,
+ NULL,
+};
+
+struct EffectContext {
+ const struct effect_interface_s *common_itfe; // Holds the itfe of the Proxy
+ effect_descriptor_t* desc; // Points to the sub effect descriptors
+ effect_handle_t eHandle[SUB_FX_COUNT]; // The effect handles of the sub effects
+ int index; // The index that is currently active - HOST or OFFLOAD
+ int32_t sessionId; // The sessiond in which the effect is created.
+ // Stored in context to pass on to sub effect creation
+ int32_t ioId; // The ioId in which the effect is created.
+ // Stored in context to pass on to sub effect creation
+ effect_uuid_t uuid; // UUID of the Proxy
+};
+
+#if __cplusplus
+} // extern "C"
+#endif
+} //namespace android
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 66a0b4e..e0686be 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -31,6 +31,7 @@
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/ACodec.h>
#include <media/stagefright/BufferProducerWrapper.h>
+#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
@@ -104,10 +105,24 @@
bool needDedicatedLooper = false;
if (nameIsType && !strncasecmp(name, "video/", 6)) {
needDedicatedLooper = true;
- } else if (!nameIsType && !strncmp(name, "OMX.TI.DUCATI1.VIDEO.", 21)) {
- needDedicatedLooper = true;
- } else if (!nameIsType && !strncmp(name, "OMX.qcom.video.decoder.avc.secure", 33)) {
- needDedicatedLooper = true;
+ } else {
+ AString tmp = name;
+ if (tmp.endsWith(".secure")) {
+ tmp.erase(tmp.size() - 7, 7);
+ }
+ const MediaCodecList *mcl = MediaCodecList::getInstance();
+ ssize_t codecIdx = mcl->findCodecByName(tmp.c_str());
+ if (codecIdx >= 0) {
+ Vector<AString> types;
+ if (mcl->getSupportedTypes(codecIdx, &types) == OK) {
+ for (int i = 0; i < types.size(); i++) {
+ if (types[i].startsWith("video/")) {
+ needDedicatedLooper = true;
+ break;
+ }
+ }
+ }
+ }
}
if (needDedicatedLooper) {
diff --git a/media/libstagefright/MediaMuxer.cpp b/media/libstagefright/MediaMuxer.cpp
index 94ce5de..d87e910 100644
--- a/media/libstagefright/MediaMuxer.cpp
+++ b/media/libstagefright/MediaMuxer.cpp
@@ -103,6 +103,16 @@
return OK;
}
+status_t MediaMuxer::setLocation(int latitude, int longitude) {
+ Mutex::Autolock autoLock(mMuxerLock);
+ if (mState != INITIALIZED) {
+ ALOGE("setLocation() must be called before start().");
+ return INVALID_OPERATION;
+ }
+ ALOGV("Setting location: latitude = %d, longitude = %d", latitude, longitude);
+ return mWriter->setGeoData(latitude, longitude);
+}
+
status_t MediaMuxer::start() {
Mutex::Autolock autoLock(mMuxerLock);
if (mState == INITIALIZED) {
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index b8a6b37..626b5c2 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -98,6 +98,11 @@
size_t AudioFlinger::mTeeSinkTrackFrames = kTeeSinkTrackFramesDefault;
#endif
+//TODO: remove when effect offload is implemented
+// In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off
+// we define a minimum time during which a global effect is considered enabled.
+static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200);
+
// ----------------------------------------------------------------------------
static int load_audio_interface(const char *if_name, audio_hw_device_t **dev)
@@ -141,7 +146,8 @@
mMode(AUDIO_MODE_INVALID),
mBtNrecIsOff(false),
mIsLowRamDevice(true),
- mIsDeviceTypeKnown(false)
+ mIsDeviceTypeKnown(false),
+ mGlobalEffectEnableTime(0)
{
getpid_cached = getpid();
char value[PROPERTY_VALUE_MAX];
@@ -2314,6 +2320,38 @@
return NO_ERROR;
}
+bool AudioFlinger::isGlobalEffectEnabled_l()
+{
+ if (mGlobalEffectEnableTime != 0 &&
+ ((systemTime() - mGlobalEffectEnableTime) < kMinGlobalEffectEnabletimeNs)) {
+ return true;
+ }
+
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<EffectChain> ec =
+ mPlaybackThreads.valueAt(i)->getEffectChain_l(AUDIO_SESSION_OUTPUT_MIX);
+ if (ec != 0 && ec->isEnabled()) {
+ return true;
+ }
+ }
+ return false;
+}
+
+void AudioFlinger::onGlobalEffectEnable()
+{
+ Mutex::Autolock _l(mLock);
+
+ mGlobalEffectEnableTime = systemTime();
+
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+ if (t->mType == ThreadBase::OFFLOAD) {
+ t->invalidateTracks(AUDIO_STREAM_MUSIC);
+ }
+ }
+
+}
+
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 5df04f4..0992308 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -466,6 +466,10 @@
void removeClient_l(pid_t pid);
void removeNotificationClient(pid_t pid);
+ //TODO: remove when effect offload is implemented
+ bool isGlobalEffectEnabled_l();
+ void onGlobalEffectEnable();
+
class AudioHwDevice {
public:
enum Flags {
@@ -641,6 +645,8 @@
private:
bool mIsLowRamDevice;
bool mIsDeviceTypeKnown;
+ //TODO: remove when effect offload is implemented
+ nsecs_t mGlobalEffectEnableTime; // when a global effect was last enabled
};
#undef INCLUDING_FROM_AUDIOFLINGER_H
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index d5a21a7..86671a9 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -593,17 +593,6 @@
h->setEnabled(enabled);
}
}
-//EL_FIXME not sure why this is needed?
-// sp<ThreadBase> thread = mThread.promote();
-// if (thread == 0) {
-// return NO_ERROR;
-// }
-//
-// if ((thread->type() == ThreadBase::OFFLOAD) && (enabled)) {
-// PlaybackThread *p = (PlaybackThread *)thread.get();
-// ALOGV("setEnabled: Offload, invalidate tracks");
-// p->invalidateTracks(AUDIO_STREAM_MUSIC);
-// }
}
return NO_ERROR;
}
@@ -942,6 +931,17 @@
thread->checkSuspendOnEffectEnabled(mEffect, false, mEffect->sessionId());
}
mEnabled = false;
+ } else {
+ //TODO: remove when effect offload is implemented
+ if (thread != 0) {
+ if ((thread->type() == ThreadBase::OFFLOAD)) {
+ PlaybackThread *t = (PlaybackThread *)thread.get();
+ t->invalidateTracks(AUDIO_STREAM_MUSIC);
+ }
+ if (mEffect->sessionId() == AUDIO_SESSION_OUTPUT_MIX) {
+ thread->mAudioFlinger->onGlobalEffectEnable();
+ }
+ }
}
return status;
}
@@ -1728,4 +1728,16 @@
}
}
+bool AudioFlinger::EffectChain::isEnabled()
+{
+ Mutex::Autolock _l(mLock);
+ size_t size = mEffects.size();
+ for (size_t i = 0; i < size; i++) {
+ if (mEffects[i]->isEnabled()) {
+ return true;
+ }
+ }
+ return false;
+}
+
}; // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index 0b7fb83..bac50f2 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -303,6 +303,10 @@
void clearInputBuffer();
+ // At least one effect in the chain is enabled
+ bool isEnabled();
+
+
void dump(int fd, const Vector<String16>& args);
protected:
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index fda4211..e35f47e 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -939,8 +939,8 @@
mBytesRemaining(0),
mCurrentWriteLength(0),
mUseAsyncWrite(false),
- mWriteBlocked(false),
- mDraining(false),
+ mWriteAckSequence(0),
+ mDrainSequence(0),
mScreenState(AudioFlinger::mScreenState),
// index 0 is reserved for normal mixer's submix
mFastTrackAvailMask(((1 << FastMixerState::kMaxFastTracks) - 1) & ~1),
@@ -1491,29 +1491,31 @@
void AudioFlinger::PlaybackThread::writeCallback()
{
ALOG_ASSERT(mCallbackThread != 0);
- mCallbackThread->setWriteBlocked(false);
+ mCallbackThread->resetWriteBlocked();
}
void AudioFlinger::PlaybackThread::drainCallback()
{
ALOG_ASSERT(mCallbackThread != 0);
- mCallbackThread->setDraining(false);
+ mCallbackThread->resetDraining();
}
-void AudioFlinger::PlaybackThread::setWriteBlocked(bool value)
+void AudioFlinger::PlaybackThread::resetWriteBlocked(uint32_t sequence)
{
Mutex::Autolock _l(mLock);
- mWriteBlocked = value;
- if (!value) {
+ // reject out of sequence requests
+ if ((mWriteAckSequence & 1) && (sequence == mWriteAckSequence)) {
+ mWriteAckSequence &= ~1;
mWaitWorkCV.signal();
}
}
-void AudioFlinger::PlaybackThread::setDraining(bool value)
+void AudioFlinger::PlaybackThread::resetDraining(uint32_t sequence)
{
Mutex::Autolock _l(mLock);
- mDraining = value;
- if (!value) {
+ // reject out of sequence requests
+ if ((mDrainSequence & 1) && (sequence == mDrainSequence)) {
+ mDrainSequence &= ~1;
mWaitWorkCV.signal();
}
}
@@ -1833,9 +1835,11 @@
// Direct output and offload threads
size_t offset = (mCurrentWriteLength - mBytesRemaining) / sizeof(int16_t);
if (mUseAsyncWrite) {
- mWriteBlocked = true;
+ ALOGW_IF(mWriteAckSequence & 1, "threadLoop_write(): out of sequence write request");
+ mWriteAckSequence += 2;
+ mWriteAckSequence |= 1;
ALOG_ASSERT(mCallbackThread != 0);
- mCallbackThread->setWriteBlocked(true);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
}
// FIXME We should have an implementation of timestamps for direct output threads.
// They are used e.g for multichannel PCM playback over HDMI.
@@ -1844,9 +1848,9 @@
if (mUseAsyncWrite &&
((bytesWritten < 0) || (bytesWritten == (ssize_t)mBytesRemaining))) {
// do not wait for async callback in case of error of full write
- mWriteBlocked = false;
+ mWriteAckSequence &= ~1;
ALOG_ASSERT(mCallbackThread != 0);
- mCallbackThread->setWriteBlocked(false);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
}
}
@@ -1861,9 +1865,10 @@
if (mOutput->stream->drain) {
ALOGV("draining %s", (mMixerStatus == MIXER_DRAIN_TRACK) ? "early" : "full");
if (mUseAsyncWrite) {
- mDraining = true;
+ ALOGW_IF(mDrainSequence & 1, "threadLoop_drain(): out of sequence drain request");
+ mDrainSequence |= 1;
ALOG_ASSERT(mCallbackThread != 0);
- mCallbackThread->setDraining(true);
+ mCallbackThread->setDraining(mDrainSequence);
}
mOutput->stream->drain(mOutput->stream,
(mMixerStatus == MIXER_DRAIN_TRACK) ? AUDIO_DRAIN_EARLY_NOTIFY
@@ -2613,11 +2618,12 @@
ALOGV("Audio hardware entering standby, mixer %p, suspend count %d", this, mSuspended);
mOutput->stream->common.standby(&mOutput->stream->common);
if (mUseAsyncWrite != 0) {
- mWriteBlocked = false;
- mDraining = false;
+ // discard any pending drain or write ack by incrementing sequence
+ mWriteAckSequence = (mWriteAckSequence + 2) & ~1;
+ mDrainSequence = (mDrainSequence + 2) & ~1;
ALOG_ASSERT(mCallbackThread != 0);
- mCallbackThread->setWriteBlocked(false);
- mCallbackThread->setDraining(false);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ mCallbackThread->setDraining(mDrainSequence);
}
}
@@ -3704,8 +3710,8 @@
const sp<AudioFlinger::OffloadThread>& offloadThread)
: Thread(false /*canCallJava*/),
mOffloadThread(offloadThread),
- mWriteBlocked(false),
- mDraining(false)
+ mWriteAckSequence(0),
+ mDrainSequence(0)
{
}
@@ -3721,8 +3727,8 @@
bool AudioFlinger::AsyncCallbackThread::threadLoop()
{
while (!exitPending()) {
- bool writeBlocked;
- bool draining;
+ uint32_t writeAckSequence;
+ uint32_t drainSequence;
{
Mutex::Autolock _l(mLock);
@@ -3730,18 +3736,21 @@
if (exitPending()) {
break;
}
- writeBlocked = mWriteBlocked;
- draining = mDraining;
- ALOGV("AsyncCallbackThread mWriteBlocked %d mDraining %d", mWriteBlocked, mDraining);
+ ALOGV("AsyncCallbackThread mWriteAckSequence %d mDrainSequence %d",
+ mWriteAckSequence, mDrainSequence);
+ writeAckSequence = mWriteAckSequence;
+ mWriteAckSequence &= ~1;
+ drainSequence = mDrainSequence;
+ mDrainSequence &= ~1;
}
{
sp<AudioFlinger::OffloadThread> offloadThread = mOffloadThread.promote();
if (offloadThread != 0) {
- if (writeBlocked == false) {
- offloadThread->setWriteBlocked(false);
+ if (writeAckSequence & 1) {
+ offloadThread->resetWriteBlocked(writeAckSequence >> 1);
}
- if (draining == false) {
- offloadThread->setDraining(false);
+ if (drainSequence & 1) {
+ offloadThread->resetDraining(drainSequence >> 1);
}
}
}
@@ -3757,20 +3766,36 @@
mWaitWorkCV.broadcast();
}
-void AudioFlinger::AsyncCallbackThread::setWriteBlocked(bool value)
+void AudioFlinger::AsyncCallbackThread::setWriteBlocked(uint32_t sequence)
{
Mutex::Autolock _l(mLock);
- mWriteBlocked = value;
- if (!value) {
+ // bit 0 is cleared
+ mWriteAckSequence = sequence << 1;
+}
+
+void AudioFlinger::AsyncCallbackThread::resetWriteBlocked()
+{
+ Mutex::Autolock _l(mLock);
+ // ignore unexpected callbacks
+ if (mWriteAckSequence & 2) {
+ mWriteAckSequence |= 1;
mWaitWorkCV.signal();
}
}
-void AudioFlinger::AsyncCallbackThread::setDraining(bool value)
+void AudioFlinger::AsyncCallbackThread::setDraining(uint32_t sequence)
{
Mutex::Autolock _l(mLock);
- mDraining = value;
- if (!value) {
+ // bit 0 is cleared
+ mDrainSequence = sequence << 1;
+}
+
+void AudioFlinger::AsyncCallbackThread::resetDraining()
+{
+ Mutex::Autolock _l(mLock);
+ // ignore unexpected callbacks
+ if (mDrainSequence & 2) {
+ mDrainSequence |= 1;
mWaitWorkCV.signal();
}
}
@@ -3858,7 +3883,7 @@
}
tracksToRemove->add(track);
} else if (track->framesReady() && track->isReady() &&
- !track->isPaused() && !track->isTerminated()) {
+ !track->isPaused() && !track->isTerminated() && !track->isStopping_2()) {
ALOGVV("OffloadThread: track %d s=%08x [OK]", track->name(), cblk->mServer);
if (track->mFillingUpStatus == Track::FS_FILLED) {
track->mFillingUpStatus = Track::FS_ACTIVE;
@@ -3901,6 +3926,7 @@
standbyTime = systemTime() + standbyDelay;
if (last) {
mixerStatus = MIXER_DRAIN_TRACK;
+ mDrainSequence += 2;
if (mHwPaused) {
// It is possible to move from PAUSED to STOPPING_1 without
// a resume so we must ensure hardware is running
@@ -3911,7 +3937,7 @@
}
} else if (track->isStopping_2()) {
// Drain has completed, signal presentation complete
- if (!mDraining || !last) {
+ if (!(mDrainSequence & 1) || !last) {
track->mState = TrackBase::STOPPED;
size_t audioHALFrames =
(mOutput->stream->get_latency(mOutput->stream)*mSampleRate) / 1000;
@@ -3956,8 +3982,9 @@
// must be called with thread mutex locked
bool AudioFlinger::OffloadThread::waitingAsyncCallback_l()
{
- ALOGV("waitingAsyncCallback_l mWriteBlocked %d mDraining %d", mWriteBlocked, mDraining);
- if (mUseAsyncWrite && (mWriteBlocked || mDraining)) {
+ ALOGVV("waitingAsyncCallback_l mWriteAckSequence %d mDrainSequence %d",
+ mWriteAckSequence, mDrainSequence);
+ if (mUseAsyncWrite && ((mWriteAckSequence & 1) || (mDrainSequence & 1))) {
return true;
}
return false;
@@ -3993,11 +4020,12 @@
mPausedWriteLength = 0;
mPausedBytesRemaining = 0;
if (mUseAsyncWrite) {
- mWriteBlocked = false;
- mDraining = false;
+ // discard any pending drain or write ack by incrementing sequence
+ mWriteAckSequence = (mWriteAckSequence + 2) & ~1;
+ mDrainSequence = (mDrainSequence + 2) & ~1;
ALOG_ASSERT(mCallbackThread != 0);
- mCallbackThread->setWriteBlocked(false);
- mCallbackThread->setDraining(false);
+ mCallbackThread->setWriteBlocked(mWriteAckSequence);
+ mCallbackThread->setDraining(mDrainSequence);
}
}
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index 1333de2..3fe470c 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -377,9 +377,9 @@
void removeTracks_l(const Vector< sp<Track> >& tracksToRemove);
void writeCallback();
- void setWriteBlocked(bool value);
+ void resetWriteBlocked(uint32_t sequence);
void drainCallback();
- void setDraining(bool value);
+ void resetDraining(uint32_t sequence);
static int asyncCallback(stream_callback_event_t event, void *param, void *cookie);
@@ -577,8 +577,19 @@
size_t mBytesRemaining;
size_t mCurrentWriteLength;
bool mUseAsyncWrite;
- bool mWriteBlocked;
- bool mDraining;
+ // mWriteAckSequence contains current write sequence on bits 31-1. The write sequence is
+ // incremented each time a write(), a flush() or a standby() occurs.
+ // Bit 0 is set when a write blocks and indicates a callback is expected.
+ // Bit 0 is reset by the async callback thread calling resetWriteBlocked(). Out of sequence
+ // callbacks are ignored.
+ uint32_t mWriteAckSequence;
+ // mDrainSequence contains current drain sequence on bits 31-1. The drain sequence is
+ // incremented each time a drain is requested or a flush() or standby() occurs.
+ // Bit 0 is set when the drain() command is called at the HAL and indicates a callback is
+ // expected.
+ // Bit 0 is reset by the async callback thread calling resetDraining(). Out of sequence
+ // callbacks are ignored.
+ uint32_t mDrainSequence;
bool mSignalPending;
sp<AsyncCallbackThread> mCallbackThread;
@@ -755,13 +766,21 @@
virtual void onFirstRef();
void exit();
- void setWriteBlocked(bool value);
- void setDraining(bool value);
+ void setWriteBlocked(uint32_t sequence);
+ void resetWriteBlocked();
+ void setDraining(uint32_t sequence);
+ void resetDraining();
private:
wp<OffloadThread> mOffloadThread;
- bool mWriteBlocked;
- bool mDraining;
+ // mWriteAckSequence corresponds to the last write sequence passed by the offload thread via
+ // setWriteBlocked(). The sequence is shifted one bit to the left and the lsb is used
+ // to indicate that the callback has been received via resetWriteBlocked()
+ uint32_t mWriteAckSequence;
+ // mDrainSequence corresponds to the last drain sequence passed by the offload thread via
+ // setDraining(). The sequence is shifted one bit to the left and the lsb is used
+ // to indicate that the callback has been received via resetDraining()
+ uint32_t mDrainSequence;
Condition mWaitWorkCV;
Mutex mLock;
};
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 2042050..6002aa3 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -543,7 +543,17 @@
sp<ThreadBase> thread = mThread.promote();
if (thread != 0) {
- Mutex::Autolock _l(thread->mLock);
+ //TODO: remove when effect offload is implemented
+ if (isOffloaded()) {
+ Mutex::Autolock _laf(thread->mAudioFlinger->mLock);
+ Mutex::Autolock _lth(thread->mLock);
+ sp<EffectChain> ec = thread->getEffectChain_l(mSessionId);
+ if (thread->mAudioFlinger->isGlobalEffectEnabled_l() || (ec != 0 && ec->isEnabled())) {
+ invalidate();
+ return PERMISSION_DENIED;
+ }
+ }
+ Mutex::Autolock _lth(thread->mLock);
track_state state = mState;
// here the track could be either new, or restarted
// in both cases "unstop" the track
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 0a18501..bda2887 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -743,7 +743,7 @@
return res;
}
- Vector<uint8_t> outputStreams;
+ Vector<int32_t> outputStreams;
bool callbacksEnabled = (params.previewCallbackFlags &
CAMERA_FRAME_CALLBACK_FLAG_ENABLE_MASK) ||
params.previewCallbackSurface;
@@ -999,7 +999,7 @@
return res;
}
- Vector<uint8_t> outputStreams;
+ Vector<int32_t> outputStreams;
outputStreams.push(getPreviewStreamId());
outputStreams.push(getRecordingStreamId());
diff --git a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
index ad1590a..ca3198f 100644
--- a/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
+++ b/services/camera/libcameraservice/api1/client2/CaptureSequencer.cpp
@@ -437,7 +437,8 @@
status_t res;
ATRACE_CALL();
SharedParameters::Lock l(client->getParameters());
- Vector<uint8_t> outputStreams;
+ Vector<int32_t> outputStreams;
+ uint8_t captureIntent = static_cast<uint8_t>(ANDROID_CONTROL_CAPTURE_INTENT_STILL_CAPTURE);
/**
* Set up output streams in the request
@@ -456,6 +457,7 @@
if (l.mParameters.state == Parameters::VIDEO_SNAPSHOT) {
outputStreams.push(client->getRecordingStreamId());
+ captureIntent = static_cast<uint8_t>(ANDROID_CONTROL_CAPTURE_INTENT_VIDEO_SNAPSHOT);
}
res = mCaptureRequest.update(ANDROID_REQUEST_OUTPUT_STREAMS,
@@ -465,6 +467,10 @@
&mCaptureId, 1);
}
if (res == OK) {
+ res = mCaptureRequest.update(ANDROID_CONTROL_CAPTURE_INTENT,
+ &captureIntent, 1);
+ }
+ if (res == OK) {
res = mCaptureRequest.sort();
}
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
index dfe8580..6076dae 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.cpp
@@ -412,7 +412,7 @@
}
status_t StreamingProcessor::startStream(StreamType type,
- const Vector<uint8_t> &outputStreams) {
+ const Vector<int32_t> &outputStreams) {
ATRACE_CALL();
status_t res;
@@ -830,8 +830,8 @@
mRecordingHeapFree = mRecordingHeapCount;
}
-bool StreamingProcessor::isStreamActive(const Vector<uint8_t> &streams,
- uint8_t recordingStreamId) {
+bool StreamingProcessor::isStreamActive(const Vector<int32_t> &streams,
+ int32_t recordingStreamId) {
for (size_t i = 0; i < streams.size(); i++) {
if (streams[i] == recordingStreamId) {
return true;
diff --git a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
index d879b83..833bb8f 100644
--- a/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
+++ b/services/camera/libcameraservice/api1/client2/StreamingProcessor.h
@@ -64,7 +64,7 @@
RECORD
};
status_t startStream(StreamType type,
- const Vector<uint8_t> &outputStreams);
+ const Vector<int32_t> &outputStreams);
// Toggle between paused and unpaused. Stream must be started first.
status_t togglePauseStream(bool pause);
@@ -97,7 +97,7 @@
StreamType mActiveRequest;
bool mPaused;
- Vector<uint8_t> mActiveStreamIds;
+ Vector<int32_t> mActiveStreamIds;
// Preview-related members
int32_t mPreviewRequestId;
@@ -132,8 +132,8 @@
void releaseAllRecordingFramesLocked();
// Determine if the specified stream is currently in use
- static bool isStreamActive(const Vector<uint8_t> &streams,
- uint8_t recordingStreamId);
+ static bool isStreamActive(const Vector<int32_t> &streams,
+ int32_t recordingStreamId);
};
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
index 3b118f4..08ab357 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor.cpp
@@ -300,12 +300,12 @@
uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS;
res = request.update(ANDROID_REQUEST_TYPE,
&requestType, 1);
- uint8_t inputStreams[1] =
- { static_cast<uint8_t>(mZslReprocessStreamId) };
+ int32_t inputStreams[1] =
+ { mZslReprocessStreamId };
if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS,
inputStreams, 1);
- uint8_t outputStreams[1] =
- { static_cast<uint8_t>(client->getCaptureStreamId()) };
+ int32_t outputStreams[1] =
+ { client->getCaptureStreamId() };
if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS,
outputStreams, 1);
res = request.update(ANDROID_REQUEST_ID,
diff --git a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
index 7c4da50..3e05091 100644
--- a/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
+++ b/services/camera/libcameraservice/api1/client2/ZslProcessor3.cpp
@@ -247,13 +247,13 @@
uint8_t requestType = ANDROID_REQUEST_TYPE_REPROCESS;
res = request.update(ANDROID_REQUEST_TYPE,
&requestType, 1);
- uint8_t inputStreams[1] =
- { static_cast<uint8_t>(mZslStreamId) };
+ int32_t inputStreams[1] =
+ { mZslStreamId };
if (res == OK) request.update(ANDROID_REQUEST_INPUT_STREAMS,
inputStreams, 1);
// TODO: Shouldn't we also update the latest preview frame?
- uint8_t outputStreams[1] =
- { static_cast<uint8_t>(client->getCaptureStreamId()) };
+ int32_t outputStreams[1] =
+ { client->getCaptureStreamId() };
if (res == OK) request.update(ANDROID_REQUEST_OUTPUT_STREAMS,
outputStreams, 1);
res = request.update(ANDROID_REQUEST_ID,
diff --git a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
index f147c06..055ea12 100644
--- a/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
+++ b/services/camera/libcameraservice/api2/CameraDeviceClient.cpp
@@ -137,7 +137,7 @@
* Write in the output stream IDs which we calculate from
* the capture request's list of surface targets
*/
- Vector<uint8_t> outputStreamIds;
+ Vector<int32_t> outputStreamIds;
outputStreamIds.setCapacity(request->mSurfaceList.size());
for (size_t i = 0; i < request->mSurfaceList.size(); ++i) {
sp<Surface> surface = request->mSurfaceList[i];
diff --git a/services/camera/libcameraservice/device3/Camera3Device.cpp b/services/camera/libcameraservice/device3/Camera3Device.cpp
index 47321e0..b70a278 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.cpp
+++ b/services/camera/libcameraservice/device3/Camera3Device.cpp
@@ -281,6 +281,14 @@
}
write(fd, lines.string(), lines.size());
+ {
+ lines = String8(" Last request sent:\n");
+ write(fd, lines.string(), lines.size());
+
+ CameraMetadata lastRequest = getLatestRequest();
+ lastRequest.dump(fd, /*verbosity*/2, /*indentation*/6);
+ }
+
if (mHal3Device != NULL) {
lines = String8(" HAL device dump:\n");
write(fd, lines.string(), lines.size());
@@ -978,7 +986,7 @@
newRequest->mSettings.find(ANDROID_REQUEST_INPUT_STREAMS);
if (inputStreams.count > 0) {
if (mInputStream == NULL ||
- mInputStream->getId() != inputStreams.data.u8[0]) {
+ mInputStream->getId() != inputStreams.data.i32[0]) {
CLOGE("Request references unknown input stream %d",
inputStreams.data.u8[0]);
return NULL;
@@ -1007,7 +1015,7 @@
}
for (size_t i = 0; i < streams.count; i++) {
- int idx = mOutputStreams.indexOfKey(streams.data.u8[i]);
+ int idx = mOutputStreams.indexOfKey(streams.data.i32[i]);
if (idx == NAME_NOT_FOUND) {
CLOGE("Request references unknown stream %d",
streams.data.u8[i]);
@@ -1397,6 +1405,43 @@
}
}
+CameraMetadata Camera3Device::getLatestRequest() {
+ ALOGV("%s", __FUNCTION__);
+
+ bool locked = false;
+
+ /**
+ * Why trylock instead of autolock?
+ *
+ * We want to be able to call this function from
+ * dumpsys, which often happens during deadlocks.
+ */
+ for (size_t i = 0; i < kDumpLockAttempts; ++i) {
+ if (mLock.tryLock() == NO_ERROR) {
+ locked = true;
+ break;
+ } else {
+ usleep(kDumpSleepDuration);
+ }
+ }
+
+ if (!locked) {
+ ALOGW("%s: Possible deadlock detected", __FUNCTION__);
+ }
+
+ CameraMetadata retVal;
+
+ if (mRequestThread != NULL) {
+ retVal = mRequestThread->getLatestRequest();
+ }
+
+ if (locked) {
+ mLock.unlock();
+ }
+
+ return retVal;
+}
+
/**
* RequestThread inner class methods
*/
@@ -1677,6 +1722,14 @@
return false;
}
+ // Update the latest request sent to HAL
+ if (request.settings != NULL) { // Don't update them if they were unchanged
+ Mutex::Autolock al(mLatestRequestMutex);
+
+ camera_metadata_t* cloned = clone_camera_metadata(request.settings);
+ mLatestRequest.acquire(cloned);
+ }
+
if (request.settings != NULL) {
nextRequest->mSettings.unlock(request.settings);
}
@@ -1729,6 +1782,14 @@
return true;
}
+CameraMetadata Camera3Device::RequestThread::getLatestRequest() const {
+ Mutex::Autolock al(mLatestRequestMutex);
+
+ ALOGV("RequestThread::%s", __FUNCTION__);
+
+ return mLatestRequest;
+}
+
void Camera3Device::RequestThread::cleanUpFailedRequest(
camera3_capture_request_t &request,
sp<CaptureRequest> &nextRequest,
diff --git a/services/camera/libcameraservice/device3/Camera3Device.h b/services/camera/libcameraservice/device3/Camera3Device.h
index 6565048..0b3ad6e 100644
--- a/services/camera/libcameraservice/device3/Camera3Device.h
+++ b/services/camera/libcameraservice/device3/Camera3Device.h
@@ -127,6 +127,8 @@
virtual status_t flush();
private:
+ static const size_t kDumpLockAttempts = 10;
+ static const size_t kDumpSleepDuration = 100000; // 0.10 sec
static const size_t kInFlightWarnLimit = 20;
static const nsecs_t kShutdownTimeout = 5000000000; // 5 sec
struct RequestTrigger;
@@ -175,6 +177,13 @@
typedef List<sp<CaptureRequest> > RequestList;
/**
+ * Get the last request submitted to the hal by the request thread.
+ *
+ * Takes mLock.
+ */
+ virtual CameraMetadata getLatestRequest();
+
+ /**
* Lock-held version of waitUntilDrained. Will transition to IDLE on
* success.
*/
@@ -285,6 +294,12 @@
*/
status_t waitUntilRequestProcessed(int32_t requestId, nsecs_t timeout);
+ /**
+ * Get the latest request that was sent to the HAL
+ * with process_capture_request.
+ */
+ CameraMetadata getLatestRequest() const;
+
protected:
virtual bool threadLoop();
@@ -343,10 +358,11 @@
uint32_t mFrameNumber;
- Mutex mLatestRequestMutex;
+ mutable Mutex mLatestRequestMutex;
Condition mLatestRequestSignal;
// android.request.id for latest process_capture_request
int32_t mLatestRequestId;
+ CameraMetadata mLatestRequest;
typedef KeyedVector<uint32_t/*tag*/, RequestTrigger> TriggerMap;
Mutex mTriggerMutex;