Dynamically configure the number of video buffers used by camera source, if supported

o related-to-bug: 6920805

Change-Id: I413bb50954cc84e32ed40bcb713842dc7b58e2b6
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
index 5a35358..6d6b8a9 100644
--- a/include/media/stagefright/CameraSource.h
+++ b/include/media/stagefright/CameraSource.h
@@ -137,6 +137,7 @@
 
     int32_t  mCameraFlags;
     Size     mVideoSize;
+    int32_t  mNumInputBuffers;
     int32_t  mVideoFrameRate;
     int32_t  mColorFormat;
     status_t mInitCheck;
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index a604c8f..efd7af7 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -155,6 +155,7 @@
     const sp<Surface>& surface,
     bool storeMetaDataInVideoBuffers)
     : mCameraFlags(0),
+      mNumInputBuffers(0),
       mVideoFrameRate(-1),
       mCamera(0),
       mSurface(surface),
@@ -571,6 +572,18 @@
     // camera and recording is started by the applications. The applications
     // will connect to the camera in ICameraRecordingProxy::startRecording.
     int64_t token = IPCThreadState::self()->clearCallingIdentity();
+    if (mNumInputBuffers > 0) {
+        status_t err = mCamera->sendCommand(
+            CAMERA_CMD_SET_VIDEO_BUFFER_COUNT, mNumInputBuffers, 0);
+
+        // This could happen for CameraHAL1 clients; thus the failure is
+        // not a fatal error
+        if (err != OK) {
+            ALOGW("Failed to set video buffer count to %d due to %d",
+                mNumInputBuffers, err);
+        }
+    }
+
     if (mCameraFlags & FLAGS_HOT_CAMERA) {
         mCamera->unlock();
         mCamera.clear();
@@ -599,9 +612,18 @@
     }
 
     mStartTimeUs = 0;
-    int64_t startTimeUs;
-    if (meta && meta->findInt64(kKeyTime, &startTimeUs)) {
-        mStartTimeUs = startTimeUs;
+    mNumInputBuffers = 0;
+    if (meta) {
+        int64_t startTimeUs;
+        if (meta->findInt64(kKeyTime, &startTimeUs)) {
+            mStartTimeUs = startTimeUs;
+        }
+
+        int32_t nBuffers;
+        if (meta->findInt32(kKeyNumBuffers, &nBuffers)) {
+            CHECK_GT(nBuffers, 0);
+            mNumInputBuffers = nBuffers;
+        }
     }
 
     startCameraRecording();
diff --git a/services/camera/libcameraservice/Camera2Client.cpp b/services/camera/libcameraservice/Camera2Client.cpp
index 3f1a677..6e26b5f 100644
--- a/services/camera/libcameraservice/Camera2Client.cpp
+++ b/services/camera/libcameraservice/Camera2Client.cpp
@@ -58,7 +58,8 @@
         mCaptureStreamId(NO_STREAM),
         mCaptureRequest(NULL),
         mRecordingStreamId(NO_STREAM),
-        mRecordingRequest(NULL)
+        mRecordingRequest(NULL),
+        mRecordingHeapCount(kDefaultRecordingHeapCount)
 {
     ATRACE_CALL();
 
@@ -1544,6 +1545,30 @@
         } else {
             return NO_INIT;
         }
+    } else if (cmd == CAMERA_CMD_SET_VIDEO_BUFFER_COUNT) {
+        if (recordingEnabled()) {
+            ALOGE("%s: Camera %d: Error setting video buffer count after "
+                    "recording was started", __FUNCTION__, mCameraId);
+            return INVALID_OPERATION;
+        }
+
+        // 32 is the current upper limit on the video buffer count for BufferQueue
+        if (arg1 <= 0 || arg1 > 32) {
+            ALOGE("%s: Camera %d: Error setting %d as video buffer count value",
+                    __FUNCTION__, mCameraId, arg1);
+            return BAD_VALUE;
+        }
+
+        // Need to reallocate memory for heap
+        if (mRecordingHeapCount != arg1) {
+            if  (mRecordingHeap != 0) {
+                mRecordingHeap.clear();
+                mRecordingHeap = NULL;
+            }
+            mRecordingHeapCount = arg1;
+        }
+
+        return OK;
     }
 
     ALOGE("%s: Camera %d: Unimplemented command %d (%d, %d)", __FUNCTION__,
@@ -1649,17 +1674,17 @@
             const size_t bufferSize = 4 + sizeof(buffer_handle_t);
             ALOGV("%s: Camera %d: Creating recording heap with %d buffers of "
                     "size %d bytes", __FUNCTION__, mCameraId,
-                    kRecordingHeapCount, bufferSize);
+                    mRecordingHeapCount, bufferSize);
             if (mRecordingHeap != 0) {
                 ALOGV("%s: Camera %d: Previous heap has size %d "
                         "(new will be %d) bytes", __FUNCTION__, mCameraId,
                         mRecordingHeap->mHeap->getSize(),
-                        bufferSize * kRecordingHeapCount);
+                        bufferSize * mRecordingHeapCount);
             }
             // Need to allocate memory for heap
             mRecordingHeap.clear();
 
-            mRecordingHeap = new Camera2Heap(bufferSize, kRecordingHeapCount,
+            mRecordingHeap = new Camera2Heap(bufferSize, mRecordingHeapCount,
                     "Camera2Client::RecordingHeap");
             if (mRecordingHeap->mHeap->getSize() == 0) {
                 ALOGE("%s: Camera %d: Unable to allocate memory for recording",
@@ -1668,7 +1693,7 @@
                 return;
             }
             mRecordingHeapHead = 0;
-            mRecordingHeapFree = kRecordingHeapCount;
+            mRecordingHeapFree = mRecordingHeapCount;
         }
 
         if ( mRecordingHeapFree == 0) {
@@ -1678,7 +1703,7 @@
             return;
         }
         heapIdx = mRecordingHeapHead;
-        mRecordingHeapHead = (mRecordingHeapHead + 1) % kRecordingHeapCount;
+        mRecordingHeapHead = (mRecordingHeapHead + 1) % mRecordingHeapCount;
         mRecordingHeapFree--;
 
         ALOGV("%s: Camera %d: Timestamp %lld",
@@ -2688,7 +2713,7 @@
 
     if (mRecordingConsumer == 0) {
         // Create CPU buffer queue endpoint
-        mRecordingConsumer = new MediaConsumer(kRecordingHeapCount);
+        mRecordingConsumer = new MediaConsumer(mRecordingHeapCount);
         mRecordingConsumer->setFrameAvailableListener(new RecordingWaiter(this));
         mRecordingConsumer->setName(String8("Camera2Client::RecordingConsumer"));
         mRecordingWindow = new SurfaceTextureClient(
diff --git a/services/camera/libcameraservice/Camera2Client.h b/services/camera/libcameraservice/Camera2Client.h
index 8ae16a4..9bea8f1 100644
--- a/services/camera/libcameraservice/Camera2Client.h
+++ b/services/camera/libcameraservice/Camera2Client.h
@@ -288,7 +288,8 @@
     // TODO: This needs to be queried from somewhere, or the BufferQueue needs
     // to be passed all the way to stagefright. Right now, set to a large number
     // to avoid starvation of the video encoders.
-    static const size_t kRecordingHeapCount = 8;
+    static const size_t kDefaultRecordingHeapCount = 8;
+    size_t mRecordingHeapCount;
     size_t mRecordingHeapHead, mRecordingHeapFree;
     // Handle new recording image buffers
     void onRecordingFrameAvailable();
diff --git a/services/camera/libcameraservice/CameraClient.cpp b/services/camera/libcameraservice/CameraClient.cpp
index 54829ef..562384d 100644
--- a/services/camera/libcameraservice/CameraClient.cpp
+++ b/services/camera/libcameraservice/CameraClient.cpp
@@ -619,6 +619,9 @@
         return OK;
     } else if (cmd == CAMERA_CMD_PLAY_RECORDING_SOUND) {
         mCameraService->playSound(CameraService::SOUND_RECORDING);
+    } else if (cmd == CAMERA_CMD_SET_VIDEO_BUFFER_COUNT) {
+        // Silently ignore this command
+        return INVALID_OPERATION;
     } else if (cmd == CAMERA_CMD_PING) {
         // If mHardware is 0, checkPidAndHardware will return error.
         return OK;