OSDN Git Service

Camera: ProCamera - implement onResultReceived callback for metadata callbacks
authorIgor Murashkin <iam@google.com>
Thu, 21 Feb 2013 20:02:29 +0000 (12:02 -0800)
committerIgor Murashkin <iam@google.com>
Fri, 22 Feb 2013 18:50:15 +0000 (10:50 -0800)
Change-Id: I46775402b007244bc383d6343a620eebbd492aad

camera/IProCameraCallbacks.cpp
camera/ProCamera.cpp
camera/tests/ProCameraTests.cpp
include/camera/IProCameraCallbacks.h
include/camera/ProCamera.h
services/camera/libcameraservice/Android.mk
services/camera/libcameraservice/ProCamera2Client.cpp
services/camera/libcameraservice/ProCamera2Client.h
services/camera/libcameraservice/camera2/ProFrameProcessor.cpp [new file with mode: 0644]
services/camera/libcameraservice/camera2/ProFrameProcessor.h [new file with mode: 0644]

index 756fba2..6cd36bf 100644 (file)
@@ -28,6 +28,8 @@
 
 #include <camera/IProCameraCallbacks.h>
 
+#include <system/camera_metadata.h>
+
 namespace android {
 
 enum {
@@ -35,8 +37,12 @@ enum {
     DATA_CALLBACK,
     DATA_CALLBACK_TIMESTAMP,
     LOCK_STATUS_CHANGED,
+    RESULT_RECEIVED,
 };
 
+void readMetadata(const Parcel& data, camera_metadata_t** out);
+void writeMetadata(Parcel& data, camera_metadata_t* metadata);
+
 class BpProCameraCallbacks: public BpInterface<IProCameraCallbacks>
 {
 public:
@@ -96,6 +102,15 @@ public:
         remote()->transact(LOCK_STATUS_CHANGED, data, &reply,
                            IBinder::FLAG_ONEWAY);
     }
+
+    void onResultReceived(int32_t frameId, camera_metadata* result) {
+        ALOGV("onResultReceived");
+        Parcel data, reply;
+        data.writeInterfaceToken(IProCameraCallbacks::getInterfaceDescriptor());
+        data.writeInt32(frameId);
+        writeMetadata(data, result);
+        remote()->transact(RESULT_RECEIVED, data, &reply, IBinder::FLAG_ONEWAY);
+    }
 };
 
 IMPLEMENT_META_INTERFACE(ProCameraCallbacks,
@@ -152,6 +167,16 @@ status_t BnProCameraCallbacks::onTransact(
             onLockStatusChanged(newLockStatus);
             return NO_ERROR;
         } break;
+        case RESULT_RECEIVED: {
+            ALOGV("RESULT_RECEIVED");
+            CHECK_INTERFACE(IProCameraCallbacks, data, reply);
+            int32_t frameId = data.readInt32();
+            camera_metadata_t *result = NULL;
+            readMetadata(data, &result);
+            onResultReceived(frameId, result);
+            return NO_ERROR;
+            break;
+        }
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
index 5ee0e4d..142c03b 100644 (file)
@@ -33,6 +33,8 @@
 #include <gui/IGraphicBufferProducer.h>
 #include <gui/Surface.h>
 
+#include <system/camera_metadata.h>
+
 namespace android {
 
 // client singleton for camera service binder interface
@@ -198,6 +200,22 @@ void ProCamera::onLockStatusChanged(
     }
 }
 
+void ProCamera::onResultReceived(int32_t frameId, camera_metadata* result) {
+    ALOGV("%s: frameId = %d, result = %p", __FUNCTION__, frameId, result);
+
+    sp<ProCameraListener> listener;
+    {
+        Mutex::Autolock _l(mLock);
+        listener = mListener;
+    }
+    if (listener != NULL) {
+        listener->onResultReceived(frameId, result);
+    } else {
+        free_camera_metadata(result);
+    }
+
+}
+
 status_t ProCamera::exclusiveTryLock()
 {
     sp <IProCameraUser> c = mCamera;
index dafa995..021fbae 100644 (file)
@@ -66,8 +66,13 @@ enum ProEvent {
     RELEASED,
     STOLEN,
     BUFFER_RECEIVED,
+    RESULT_RECEIVED,
 };
 
+inline int ProEvent_Mask(ProEvent e) {
+    return (1 << static_cast<int>(e));
+}
+
 typedef Vector<ProEvent> EventList;
 
 class ProCameraTestThread : public Thread
@@ -93,6 +98,12 @@ public:
 class ProCameraTestListener : public ProCameraListener {
 
 public:
+    static const int EVENT_MASK_ALL = 0xFFFFFFFF;
+
+    ProCameraTestListener() {
+        mEventMask = EVENT_MASK_ALL;
+    }
+
     status_t WaitForEvent() {
         Mutex::Autolock cal(mConditionMutex);
 
@@ -136,15 +147,26 @@ public:
         return ev;
     }
 
+    void SetEventMask(int eventMask) {
+        Mutex::Autolock al(mListenerMutex);
+        mEventMask = eventMask;
+    }
+
 private:
     void QueueEvent(ProEvent ev) {
+        bool eventAdded = false;
         {
             Mutex::Autolock al(mListenerMutex);
-            mProEventList.push(ev);
-        }
 
+            if (ProEvent_Mask(ev) & mEventMask) {
+                mProEventList.push(ev);
+                eventAdded = true;
+            }
+        }
 
-        mListenerCondition.broadcast();
+        if (eventAdded) {
+            mListenerCondition.broadcast();
+        }
     }
 
 protected:
@@ -184,8 +206,11 @@ protected:
         QueueEvent(BUFFER_RECEIVED);
 
     }
-    virtual void onRequestReceived(
-                                   camera_metadata* request) {
+    virtual void onResultReceived(int32_t frameId,
+                                  camera_metadata* request) {
+        dout << "Result received frameId = " << frameId
+             << ", requestPtr = " << (void*)request << std::endl;
+        QueueEvent(RESULT_RECEIVED);
         free_camera_metadata(request);
     }
 
@@ -201,6 +226,7 @@ protected:
     Mutex             mListenerMutex;
     Mutex             mConditionMutex;
     Condition         mListenerCondition;
+    int               mEventMask;
 };
 
 class ProCameraTest : public ::testing::Test {
@@ -309,6 +335,10 @@ TEST_F(ProCameraTest, LockingImmediate) {
         return;
     }
 
+    mListener->SetEventMask(ProEvent_Mask(ACQUIRED) |
+                            ProEvent_Mask(STOLEN)   |
+                            ProEvent_Mask(RELEASED));
+
     EXPECT_FALSE(mCamera->hasExclusiveLock());
     EXPECT_EQ(OK, mCamera->exclusiveTryLock());
     // at this point we definitely have the lock
@@ -332,13 +362,17 @@ TEST_F(ProCameraTest, LockingAsynchronous) {
         return;
     }
 
+
+    mListener->SetEventMask(ProEvent_Mask(ACQUIRED) |
+                            ProEvent_Mask(STOLEN)   |
+                            ProEvent_Mask(RELEASED));
+
     // TODO: Add another procamera that has a lock here.
     // then we can be test that the lock wont immediately be acquired
 
     EXPECT_FALSE(mCamera->hasExclusiveLock());
-    EXPECT_EQ(OK, mCamera->exclusiveLock());
-    // at this point we may or may not have the lock
-    // we cant be sure until we get an ACQUIRED event
+    EXPECT_EQ(OK, mCamera->exclusiveTryLock());
+    // at this point we definitely have the lock
 
     EXPECT_EQ(OK, mListener->WaitForEvent());
     EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
@@ -353,7 +387,7 @@ TEST_F(ProCameraTest, LockingAsynchronous) {
 }
 
 // Stream directly to the screen.
-TEST_F(ProCameraTest, StreamingImageSingle) {
+TEST_F(ProCameraTest, DISABLED_StreamingImageSingle) {
     if (HasFatalFailure()) {
         return;
     }
@@ -433,7 +467,7 @@ TEST_F(ProCameraTest, StreamingImageSingle) {
 }
 
 // Stream directly to the screen.
-TEST_F(ProCameraTest, StreamingImageDual) {
+TEST_F(ProCameraTest, DISABLED_StreamingImageDual) {
     if (HasFatalFailure()) {
         return;
     }
@@ -523,6 +557,9 @@ TEST_F(ProCameraTest, CpuConsumerSingle) {
     if (HasFatalFailure()) {
         return;
     }
+
+    mListener->SetEventMask(ProEvent_Mask(BUFFER_RECEIVED));
+
     int streamId = -1;
     EXPECT_OK(mCamera->createStreamCpu(/*width*/320, /*height*/240,
         TEST_FORMAT_DEPTH, TEST_CPU_HEAP_COUNT, &streamId));
@@ -585,6 +622,9 @@ TEST_F(ProCameraTest, CpuConsumerDual) {
     if (HasFatalFailure()) {
         return;
     }
+
+    mListener->SetEventMask(ProEvent_Mask(BUFFER_RECEIVED));
+
     int streamId = -1;
     EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
                             TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &streamId));
@@ -596,8 +636,6 @@ TEST_F(ProCameraTest, CpuConsumerDual) {
     EXPECT_NE(-1, depthStreamId);
 
     EXPECT_OK(mCamera->exclusiveTryLock());
-    EXPECT_EQ(OK, mListener->WaitForEvent());
-    EXPECT_EQ(ACQUIRED, mListener->ReadEvent());
     /*
     */
     /* iterate in a loop submitting requests every frame.
@@ -657,6 +695,72 @@ TEST_F(ProCameraTest, CpuConsumerDual) {
     EXPECT_OK(mCamera->exclusiveUnlock());
 }
 
+TEST_F(ProCameraTest, ResultReceiver) {
+    if (HasFatalFailure()) {
+        return;
+    }
+
+    mListener->SetEventMask(ProEvent_Mask(RESULT_RECEIVED));
+    //FIXME: if this is run right after the previous test we get BUFFER_RECEIVED
+    // need to filter out events at read time
+
+    int streamId = -1;
+    EXPECT_OK(mCamera->createStreamCpu(/*width*/1280, /*height*/960,
+                             TEST_FORMAT_MAIN, TEST_CPU_HEAP_COUNT, &streamId));
+    EXPECT_NE(-1, streamId);
+
+    EXPECT_OK(mCamera->exclusiveTryLock());
+    /*
+    */
+    /* iterate in a loop submitting requests every frame.
+     *  what kind of requests doesnt really matter, just whatever.
+     */
+
+    camera_metadata_t *request = NULL;
+    EXPECT_OK(mCamera->createDefaultRequest(CAMERA2_TEMPLATE_PREVIEW,
+                                            /*out*/&request));
+    EXPECT_NE((void*)NULL, request);
+
+    /*FIXME*/
+    if(request == NULL) request = allocate_camera_metadata(10, 100);
+
+    // set the output streams to just this stream ID
+
+    uint8_t allStreams[] = { streamId };
+    size_t streamCount = 1;
+    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);
+    if (find == -ENOENT) {
+        if (add_camera_metadata_entry(request, tag, &allStreams,
+                                      /*data_count*/streamCount) != OK) {
+            camera_metadata_t *tmp = allocate_camera_metadata(1000, 10000);
+            ASSERT_OK(append_camera_metadata(tmp, request));
+            free_camera_metadata(request);
+            request = tmp;
+
+            ASSERT_OK(add_camera_metadata_entry(request, tag, &allStreams,
+                                                /*data_count*/streamCount));
+        }
+    } else {
+        ASSERT_OK(update_camera_metadata_entry(request, entry.index,
+                               &allStreams, /*data_count*/streamCount, &entry));
+    }
+
+    EXPECT_OK(mCamera->submitRequest(request, /*streaming*/true));
+
+    // Consume a couple of results
+    for (int i = 0; i < TEST_CPU_FRAME_COUNT; ++i) {
+        EXPECT_EQ(OK, mListener->WaitForEvent());
+        EXPECT_EQ(RESULT_RECEIVED, mListener->ReadEvent());
+    }
+
+    // Done: clean up
+    free_camera_metadata(request);
+    EXPECT_OK(mCamera->deleteStream(streamId));
+    EXPECT_OK(mCamera->exclusiveUnlock());
+}
+
 }
 }
 }
index e5be099..fc24026 100644 (file)
@@ -24,6 +24,8 @@
 #include <utils/Timers.h>
 #include <system/camera.h>
 
+struct camera_metadata;
+
 namespace android {
 
 class IProCameraCallbacks: public IInterface
@@ -47,6 +49,11 @@ public:
     };
 
     virtual void            onLockStatusChanged(LockStatus newLockStatus) = 0;
+
+    /** Missing by design: implementation is client-side in ProCamera.cpp **/
+    // virtual void onBufferReceived(int streamId,
+    //                               const CpuConsumer::LockedBufer& buf);
+    virtual void onResultReceived(int32_t frameId, camera_metadata* result) = 0;
 };
 
 // ----------------------------------------------------------------------------
index 4dda533..7cd9138 100644 (file)
@@ -49,17 +49,19 @@ public:
     // OnBufferReceived and OnRequestReceived can come in with any order,
     // use android.sensor.timestamp and LockedBuffer.timestamp to correlate them
 
-    // TODO: implement in IProCameraCallbacks, ProCamera2Client
-
     // A new frame buffer has been received for this stream.
     // -- This callback only fires for createStreamCpu streams
+    // -- Use buf.timestamp to correlate with metadata's
+    //    android.sensor.timestamp
     // -- The buffer must not be accessed after this function call completes
     virtual void onBufferReceived(int streamId,
                                   const CpuConsumer::LockedBuffer& buf) = 0;
-    // A new metadata buffer has been received.
-    // -- Ownership of request passes on to the callee,
-    //    free with free_camera_metadata.
-    virtual void onRequestReceived(camera_metadata* request) = 0;
+    /**
+      * A new metadata buffer has been received.
+      * -- Ownership of request passes on to the callee, free with
+      *    free_camera_metadata.
+      */
+    virtual void onResultReceived(int32_t frameId, camera_metadata* result) = 0;
 };
 
 class ProCamera : public BnProCameraCallbacks, public IBinder::DeathRecipient
@@ -189,6 +191,9 @@ protected:
     virtual void        onLockStatusChanged(
                                 IProCameraCallbacks::LockStatus newLockStatus);
 
+    virtual void        onResultReceived(int32_t frameId,
+                                         camera_metadata* result);
+
     class DeathNotifier: public IBinder::DeathRecipient
     {
     public:
index c7a8e4a..f76c861 100644 (file)
@@ -20,7 +20,8 @@ LOCAL_SRC_FILES:=               \
     camera2/ZslProcessor.cpp \
     camera2/BurstCapture.cpp \
     camera2/JpegCompressor.cpp \
-    camera2/CaptureSequencer.cpp
+    camera2/CaptureSequencer.cpp \
+    camera2/ProFrameProcessor.cpp \
 
 LOCAL_SHARED_LIBRARIES:= \
     libui \
index f850034..7611796 100644 (file)
@@ -26,6 +26,7 @@
 #include <gui/Surface.h>
 #include "camera2/Parameters.h"
 #include "ProCamera2Client.h"
+#include "camera2/ProFrameProcessor.h"
 
 namespace android {
 using namespace camera2;
@@ -82,6 +83,16 @@ status_t ProCamera2Client::initialize(camera_module_t *module)
 
     res = mDevice->setNotifyCallback(this);
 
+    String8 threadName;
+    mFrameProcessor = new ProFrameProcessor(this);
+    threadName = String8::format("PC2-%d-FrameProc",
+            mCameraId);
+    mFrameProcessor->run(threadName.string());
+
+    mFrameProcessor->registerListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
+                                      FRAME_PROCESSOR_LISTENER_MAX_ID,
+                                      /*listener*/this);
+
     return OK;
 }
 
@@ -307,6 +318,7 @@ status_t ProCamera2Client::dump(int fd, const Vector<String16>& args) {
     result.append("  State: ");
 
     // TODO: print dynamic/request section from most recent requests
+    mFrameProcessor->dump(fd, args);
 
 #define CASE_APPEND_ENUM(x) case x: result.append(#x "\n"); break;
 
@@ -338,7 +350,12 @@ void ProCamera2Client::disconnect() {
     if (mDevice == 0) return;
 
     ALOGV("Camera %d: Shutting down", mCameraId);
+    mFrameProcessor->removeListener(FRAME_PROCESSOR_LISTENER_MIN_ID,
+                                    FRAME_PROCESSOR_LISTENER_MAX_ID,
+                                    /*listener*/this);
+    mFrameProcessor->requestExit();
     ALOGV("Camera %d: Waiting for threads", mCameraId);
+    mFrameProcessor->join();
     ALOGV("Camera %d: Disconnecting device", mCameraId);
 
     mDevice->disconnect();
@@ -446,4 +463,22 @@ void ProCamera2Client::SharedCameraCallbacks::clear() {
     mRemoteCallback.clear();
 }
 
+void ProCamera2Client::onFrameAvailable(int32_t frameId,
+                                        const CameraMetadata& frame) {
+    ATRACE_CALL();
+    ALOGV("%s", __FUNCTION__);
+
+    Mutex::Autolock icl(mIProCameraUserLock);
+    SharedCameraCallbacks::Lock l(mSharedCameraCallbacks);
+
+    if (mRemoteCallback != NULL) {
+        CameraMetadata tmp(frame);
+        camera_metadata_t* meta = tmp.release();
+        ALOGV("%s: meta = %p ", __FUNCTION__, meta);
+        mRemoteCallback->onResultReceived(frameId, meta);
+        tmp.acquire(meta);
+    }
+
+}
+
 } // namespace android
index b72fd63..dfea1e1 100644 (file)
@@ -19,6 +19,7 @@
 
 #include "Camera2Device.h"
 #include "CameraService.h"
+#include "camera2/ProFrameProcessor.h"
 
 namespace android {
 
@@ -29,7 +30,8 @@ class IMemory;
  */
 class ProCamera2Client :
         public CameraService::ProClient,
-        public Camera2Device::NotificationListener
+        public Camera2Device::NotificationListener,
+        public camera2::ProFrameProcessor::FilteredListener
 {
 public:
     /**
@@ -120,6 +122,10 @@ public:
         mutable Mutex mRemoteCallbackLock;
     } mSharedCameraCallbacks;
 
+protected:
+    /** FilteredListener implementation **/
+    virtual void onFrameAvailable(int32_t frameId, const CameraMetadata& frame);
+
 private:
     /** IProCameraUser interface-related private members */
 
@@ -137,6 +143,10 @@ private:
     sp<IBinder> mPreviewSurface;
 
     /** Preview callback related members */
+    sp<camera2::ProFrameProcessor> mFrameProcessor;
+    static const int32_t FRAME_PROCESSOR_LISTENER_MIN_ID = 0;
+    static const int32_t FRAME_PROCESSOR_LISTENER_MAX_ID = 0x7fffffffL;
+
     /** Camera2Device instance wrapping HAL2 entry */
 
     sp<Camera2Device> mDevice;
diff --git a/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp b/services/camera/libcameraservice/camera2/ProFrameProcessor.cpp
new file mode 100644 (file)
index 0000000..8d4933c
--- /dev/null
@@ -0,0 +1,168 @@
+/*
+ * 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 "Camera2-ProFrameProcessor"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include "ProFrameProcessor.h"
+#include "../Camera2Device.h"
+#include "../ProCamera2Client.h"
+
+namespace android {
+namespace camera2 {
+
+ProFrameProcessor::ProFrameProcessor(wp<ProCamera2Client> client):
+        Thread(false), mClient(client) {
+}
+
+ProFrameProcessor::~ProFrameProcessor() {
+    ALOGV("%s: Exit", __FUNCTION__);
+}
+
+status_t ProFrameProcessor::registerListener(int32_t minId,
+        int32_t maxId, wp<FilteredListener> listener) {
+    Mutex::Autolock l(mInputMutex);
+    ALOGV("%s: Registering listener for frame id range %d - %d",
+            __FUNCTION__, minId, maxId);
+    RangeListener rListener = { minId, maxId, listener };
+    mRangeListeners.push_back(rListener);
+    return OK;
+}
+
+status_t ProFrameProcessor::removeListener(int32_t minId,
+        int32_t maxId, wp<FilteredListener> listener) {
+    Mutex::Autolock l(mInputMutex);
+    List<RangeListener>::iterator item = mRangeListeners.begin();
+    while (item != mRangeListeners.end()) {
+        if (item->minId == minId &&
+                item->maxId == maxId &&
+                item->listener == listener) {
+            item = mRangeListeners.erase(item);
+        } else {
+            item++;
+        }
+    }
+    return OK;
+}
+
+void ProFrameProcessor::dump(int fd, const Vector<String16>& args) {
+    String8 result("    Latest received frame:\n");
+    write(fd, result.string(), result.size());
+    mLastFrame.dump(fd, 2, 6);
+}
+
+bool ProFrameProcessor::threadLoop() {
+    status_t res;
+
+    sp<Camera2Device> device;
+    {
+        sp<ProCamera2Client> client = mClient.promote();
+        if (client == 0) return false;
+        device = client->getCameraDevice();
+        if (device == 0) return false;
+    }
+
+    res = device->waitForNextFrame(kWaitDuration);
+    if (res == OK) {
+        sp<ProCamera2Client> client = mClient.promote();
+        if (client == 0) return false;
+        processNewFrames(client);
+    } else if (res != TIMED_OUT) {
+        ALOGE("ProCamera2Client::ProFrameProcessor: Error waiting for new "
+                "frames: %s (%d)", strerror(-res), res);
+    }
+
+    return true;
+}
+
+void ProFrameProcessor::processNewFrames(sp<ProCamera2Client> &client) {
+    status_t res;
+    ATRACE_CALL();
+    CameraMetadata frame;
+    while ( (res = client->getCameraDevice()->getNextFrame(&frame)) == OK) {
+        camera_metadata_entry_t entry;
+
+        entry = frame.find(ANDROID_REQUEST_FRAME_COUNT);
+        if (entry.count == 0) {
+            ALOGE("%s: Camera %d: Error reading frame number",
+                    __FUNCTION__, client->getCameraId());
+            break;
+        }
+        ATRACE_INT("cam2_frame", entry.data.i32[0]);
+
+        res = processListeners(frame, client);
+        if (res != OK) break;
+
+        if (!frame.isEmpty()) {
+            mLastFrame.acquire(frame);
+        }
+    }
+    if (res != NOT_ENOUGH_DATA) {
+        ALOGE("%s: Camera %d: Error getting next frame: %s (%d)",
+                __FUNCTION__, client->getCameraId(), strerror(-res), res);
+        return;
+    }
+
+    return;
+}
+
+status_t ProFrameProcessor::processListeners(const CameraMetadata &frame,
+        sp<ProCamera2Client> &client) {
+    status_t res;
+    ATRACE_CALL();
+    camera_metadata_ro_entry_t entry;
+
+    entry = frame.find(ANDROID_REQUEST_ID);
+    if (entry.count == 0) {
+        ALOGE("%s: Camera %d: Error reading frame id",
+                __FUNCTION__, client->getCameraId());
+        return BAD_VALUE;
+    }
+    int32_t frameId = entry.data.i32[0];
+
+    List<sp<FilteredListener> > listeners;
+    {
+        Mutex::Autolock l(mInputMutex);
+
+        List<RangeListener>::iterator item = mRangeListeners.begin();
+        while (item != mRangeListeners.end()) {
+            if (frameId >= item->minId &&
+                    frameId < item->maxId) {
+                sp<FilteredListener> listener = item->listener.promote();
+                if (listener == 0) {
+                    item = mRangeListeners.erase(item);
+                    continue;
+                } else {
+                    listeners.push_back(listener);
+                }
+            }
+            item++;
+        }
+    }
+    ALOGV("Got %d range listeners out of %d", listeners.size(), mRangeListeners.size());
+    List<sp<FilteredListener> >::iterator item = listeners.begin();
+    for (; item != listeners.end(); item++) {
+        (*item)->onFrameAvailable(frameId, frame);
+    }
+    return OK;
+}
+
+}; // namespace camera2
+}; // namespace android
diff --git a/services/camera/libcameraservice/camera2/ProFrameProcessor.h b/services/camera/libcameraservice/camera2/ProFrameProcessor.h
new file mode 100644 (file)
index 0000000..e4094a6
--- /dev/null
@@ -0,0 +1,81 @@
+/*
+ * 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.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_CAMERA2_PROFRAMEPROCESSOR_H
+#define ANDROID_SERVERS_CAMERA_CAMERA2_PROFRAMEPROCESSOR_H
+
+#include <utils/Thread.h>
+#include <utils/String16.h>
+#include <utils/Vector.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <camera/CameraMetadata.h>
+
+struct camera_frame_metadata;
+
+namespace android {
+
+class ProCamera2Client;
+
+namespace camera2 {
+
+/* Output frame metadata processing thread.  This thread waits for new
+ * frames from the device, and analyzes them as necessary.
+ */
+class ProFrameProcessor: public Thread {
+  public:
+    ProFrameProcessor(wp<ProCamera2Client> client);
+    ~ProFrameProcessor();
+
+    struct FilteredListener: virtual public RefBase {
+        virtual void onFrameAvailable(int32_t frameId,
+                const CameraMetadata &frame) = 0;
+    };
+
+    // Register a listener for a range of IDs [minId, maxId). Multiple listeners
+    // can be listening to the same range
+    status_t registerListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener);
+    status_t removeListener(int32_t minId, int32_t maxId, wp<FilteredListener> listener);
+
+    void dump(int fd, const Vector<String16>& args);
+  private:
+    static const nsecs_t kWaitDuration = 10000000; // 10 ms
+    wp<ProCamera2Client> mClient;
+
+    virtual bool threadLoop();
+
+    Mutex mInputMutex;
+
+    struct RangeListener {
+        int32_t minId;
+        int32_t maxId;
+        wp<FilteredListener> listener;
+    };
+    List<RangeListener> mRangeListeners;
+
+    void processNewFrames(sp<ProCamera2Client> &client);
+
+    status_t processListeners(const CameraMetadata &frame,
+            sp<ProCamera2Client> &client);
+
+    CameraMetadata mLastFrame;
+};
+
+
+}; //namespace camera2
+}; //namespace android
+
+#endif