Camera: HAL1 recording batching support

Test: Angler HAL1 + batching normal recording mode
Bug: 35997432
Change-Id: If088715ccdb40163f44150c16deed62432692c07
diff --git a/camera/Camera.cpp b/camera/Camera.cpp
index bf9904c..c6c35ef 100644
--- a/camera/Camera.cpp
+++ b/camera/Camera.cpp
@@ -240,6 +240,14 @@
     c->releaseRecordingFrameHandle(handle);
 }
 
+void Camera::releaseRecordingFrameHandleBatch(
+        const std::vector<native_handle_t*> handles) {
+    ALOGV("releaseRecordingFrameHandleBatch");
+    sp <::android::hardware::ICamera> c = mCamera;
+    if (c == 0) return;
+    c->releaseRecordingFrameHandleBatch(handles);
+}
+
 // get preview state
 bool Camera::previewEnabled()
 {
@@ -418,6 +426,37 @@
     }
 }
 
+void Camera::recordingFrameHandleCallbackTimestampBatch(
+        const std::vector<nsecs_t>& timestamps,
+        const std::vector<native_handle_t*>& handles)
+{
+    // If recording proxy listener is registered, forward the frame and return.
+    // The other listener (mListener) is ignored because the receiver needs to
+    // call releaseRecordingFrameHandle.
+    sp<ICameraRecordingProxyListener> proxylistener;
+    {
+        Mutex::Autolock _l(mLock);
+        proxylistener = mRecordingProxyListener;
+    }
+    if (proxylistener != NULL) {
+        proxylistener->recordingFrameHandleCallbackTimestampBatch(timestamps, handles);
+        return;
+    }
+
+    sp<CameraListener> listener;
+    {
+        Mutex::Autolock _l(mLock);
+        listener = mListener;
+    }
+
+    if (listener != NULL) {
+        listener->postRecordingFrameHandleTimestampBatch(timestamps, handles);
+    } else {
+        ALOGW("No listener was set. Drop a batch of recording frames.");
+        releaseRecordingFrameHandleBatch(handles);
+    }
+}
+
 sp<ICameraRecordingProxy> Camera::getRecordingProxy() {
     ALOGV("getProxy");
     return new RecordingProxy(this);
@@ -448,6 +487,12 @@
     mCamera->releaseRecordingFrameHandle(handle);
 }
 
+void Camera::RecordingProxy::releaseRecordingFrameHandleBatch(
+        const std::vector<native_handle_t*>& handles) {
+    ALOGV("RecordingProxy::releaseRecordingFrameHandleBatch");
+    mCamera->releaseRecordingFrameHandleBatch(handles);
+}
+
 Camera::RecordingProxy::RecordingProxy(const sp<Camera>& camera)
 {
     mCamera = camera;
diff --git a/camera/ICamera.cpp b/camera/ICamera.cpp
index 2bf956d..f0945c7 100644
--- a/camera/ICamera.cpp
+++ b/camera/ICamera.cpp
@@ -55,6 +55,7 @@
     SET_VIDEO_BUFFER_MODE,
     SET_VIDEO_BUFFER_TARGET,
     RELEASE_RECORDING_FRAME_HANDLE,
+    RELEASE_RECORDING_FRAME_HANDLE_BATCH,
 };
 
 class BpCamera: public BpInterface<ICamera>
@@ -172,6 +173,24 @@
         native_handle_delete(handle);
     }
 
+    void releaseRecordingFrameHandleBatch(const std::vector<native_handle_t*>& handles) {
+        ALOGV("releaseRecordingFrameHandleBatch");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICamera::getInterfaceDescriptor());
+        uint32_t n = handles.size();
+        data.writeUint32(n);
+        for (auto& handle : handles) {
+            data.writeNativeHandle(handle);
+        }
+        remote()->transact(RELEASE_RECORDING_FRAME_HANDLE_BATCH, data, &reply);
+
+        // Close the native handle because camera received a dup copy.
+        for (auto& handle : handles) {
+            native_handle_close(handle);
+            native_handle_delete(handle);
+        }
+    }
+
     status_t setVideoBufferMode(int32_t videoBufferMode)
     {
         ALOGV("setVideoBufferMode: %d", videoBufferMode);
@@ -378,6 +397,19 @@
             releaseRecordingFrameHandle(data.readNativeHandle());
             return NO_ERROR;
         } break;
+        case RELEASE_RECORDING_FRAME_HANDLE_BATCH: {
+            ALOGV("RELEASE_RECORDING_FRAME_HANDLE_BATCH");
+            CHECK_INTERFACE(ICamera, data, reply);
+            // releaseRecordingFrameHandle will be responsble to close the native handle.
+            uint32_t n = data.readUint32();
+            std::vector<native_handle_t*> handles;
+            handles.reserve(n);
+            for (uint32_t i = 0; i < n; i++) {
+                handles.push_back(data.readNativeHandle());
+            }
+            releaseRecordingFrameHandleBatch(handles);
+            return NO_ERROR;
+        } break;
         case SET_VIDEO_BUFFER_MODE: {
             ALOGV("SET_VIDEO_BUFFER_MODE");
             CHECK_INTERFACE(ICamera, data, reply);
diff --git a/camera/ICameraClient.cpp b/camera/ICameraClient.cpp
index 1b6fac4..7e6297c 100644
--- a/camera/ICameraClient.cpp
+++ b/camera/ICameraClient.cpp
@@ -32,6 +32,7 @@
     DATA_CALLBACK,
     DATA_CALLBACK_TIMESTAMP,
     RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP,
+    RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH,
 };
 
 class BpCameraClient: public BpInterface<ICameraClient>
@@ -91,6 +92,29 @@
         remote()->transact(RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP, data, &reply,
                 IBinder::FLAG_ONEWAY);
     }
+
+    void recordingFrameHandleCallbackTimestampBatch(
+            const std::vector<nsecs_t>& timestamps,
+            const std::vector<native_handle_t*>& handles) {
+        ALOGV("recordingFrameHandleCallbackTimestampBatch");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraClient::getInterfaceDescriptor());
+        uint32_t n = timestamps.size();
+        if (n != handles.size()) {
+            ALOGE("%s: size of timestamps(%zu) and handles(%zu) mismatch!",
+                    __FUNCTION__, timestamps.size(), handles.size());
+            return;
+        }
+        data.writeUint32(n);
+        for (auto ts : timestamps) {
+            data.writeInt64(ts);
+        }
+        for (auto& handle : handles) {
+            data.writeNativeHandle(handle);
+        }
+        remote()->transact(RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH, data, &reply,
+                IBinder::FLAG_ONEWAY);
+    }
 };
 
 IMPLEMENT_META_INTERFACE(CameraClient, "android.hardware.ICameraClient");
@@ -154,6 +178,41 @@
             recordingFrameHandleCallbackTimestamp(timestamp, handle);
             return NO_ERROR;
         } break;
+        case RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH: {
+            ALOGV("RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH");
+            CHECK_INTERFACE(ICameraClient, data, reply);
+            uint32_t n = 0;
+            status_t res = data.readUint32(&n);
+            if (res != OK) {
+                ALOGE("%s: Failed to read batch size: %s (%d)", __FUNCTION__, strerror(-res), res);
+                return BAD_VALUE;
+            }
+            std::vector<nsecs_t> timestamps;
+            std::vector<native_handle_t*> handles;
+            timestamps.reserve(n);
+            handles.reserve(n);
+            for (uint32_t i = 0; i < n; i++) {
+                res = data.readInt64(&timestamps[i]);
+                if (res != OK) {
+                    ALOGE("%s: Failed to read timestamp[%d]: %s (%d)",
+                            __FUNCTION__, i, strerror(-res), res);
+                    return BAD_VALUE;
+                }
+            }
+            for (uint32_t i = 0; i < n; i++) {
+                native_handle_t* handle = data.readNativeHandle();
+                if (handle == nullptr) {
+                    ALOGE("%s: Received a null native handle at handles[%d]",
+                            __FUNCTION__, i);
+                    return BAD_VALUE;
+                }
+                handles.push_back(handle);
+            }
+
+            // The native handle will be freed in BpCamera::releaseRecordingFrameHandleBatch.
+            recordingFrameHandleCallbackTimestampBatch(timestamps, handles);
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/camera/ICameraRecordingProxy.cpp b/camera/ICameraRecordingProxy.cpp
index c9f8b5c..bd6af75 100644
--- a/camera/ICameraRecordingProxy.cpp
+++ b/camera/ICameraRecordingProxy.cpp
@@ -32,6 +32,7 @@
     STOP_RECORDING,
     RELEASE_RECORDING_FRAME,
     RELEASE_RECORDING_FRAME_HANDLE,
+    RELEASE_RECORDING_FRAME_HANDLE_BATCH,
 };
 
 
@@ -82,6 +83,24 @@
         native_handle_close(handle);
         native_handle_delete(handle);
     }
+
+    void releaseRecordingFrameHandleBatch(const std::vector<native_handle_t*>& handles) {
+        ALOGV("releaseRecordingFrameHandleBatch");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraRecordingProxy::getInterfaceDescriptor());
+        uint32_t n = handles.size();
+        data.writeUint32(n);
+        for (auto& handle : handles) {
+            data.writeNativeHandle(handle);
+        }
+        remote()->transact(RELEASE_RECORDING_FRAME_HANDLE_BATCH, data, &reply);
+
+        // Close the native handle because camera received a dup copy.
+        for (auto& handle : handles) {
+            native_handle_close(handle);
+            native_handle_delete(handle);
+        }
+    }
 };
 
 IMPLEMENT_META_INTERFACE(CameraRecordingProxy, "android.hardware.ICameraRecordingProxy");
@@ -121,6 +140,31 @@
             releaseRecordingFrameHandle(data.readNativeHandle());
             return NO_ERROR;
         } break;
+        case RELEASE_RECORDING_FRAME_HANDLE_BATCH: {
+            ALOGV("RELEASE_RECORDING_FRAME_HANDLE_BATCH");
+            CHECK_INTERFACE(ICameraRecordingProxy, data, reply);
+            uint32_t n = 0;
+            status_t res = data.readUint32(&n);
+            if (res != OK) {
+                ALOGE("%s: Failed to read batch size: %s (%d)", __FUNCTION__, strerror(-res), res);
+                return BAD_VALUE;
+            }
+            std::vector<native_handle_t*> handles;
+            handles.reserve(n);
+            for (uint32_t i = 0; i < n; i++) {
+                native_handle_t* handle = data.readNativeHandle();
+                if (handle == nullptr) {
+                    ALOGE("%s: Received a null native handle at handles[%d]",
+                            __FUNCTION__, i);
+                    return BAD_VALUE;
+                }
+                handles.push_back(handle);
+            }
+
+            // releaseRecordingFrameHandleBatch will be responsble to close the native handle.
+            releaseRecordingFrameHandleBatch(handles);
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/camera/ICameraRecordingProxyListener.cpp b/camera/ICameraRecordingProxyListener.cpp
index 8529d3e..c954241 100644
--- a/camera/ICameraRecordingProxyListener.cpp
+++ b/camera/ICameraRecordingProxyListener.cpp
@@ -28,6 +28,7 @@
 enum {
     DATA_CALLBACK_TIMESTAMP = IBinder::FIRST_CALL_TRANSACTION,
     RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP,
+    RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH
 };
 
 class BpCameraRecordingProxyListener: public BpInterface<ICameraRecordingProxyListener>
@@ -62,6 +63,36 @@
         native_handle_close(handle);
         native_handle_delete(handle);
     }
+
+    void recordingFrameHandleCallbackTimestampBatch(
+            const std::vector<nsecs_t>& timestamps,
+            const std::vector<native_handle_t*>& handles) {
+        ALOGV("recordingFrameHandleCallbackTimestampBatch");
+        Parcel data, reply;
+        data.writeInterfaceToken(ICameraRecordingProxyListener::getInterfaceDescriptor());
+
+        uint32_t n = timestamps.size();
+        if (n != handles.size()) {
+            ALOGE("%s: size of timestamps(%zu) and handles(%zu) mismatch!",
+                    __FUNCTION__, timestamps.size(), handles.size());
+            return;
+        }
+        data.writeUint32(n);
+        for (auto ts : timestamps) {
+            data.writeInt64(ts);
+        }
+        for (auto& handle : handles) {
+            data.writeNativeHandle(handle);
+        }
+        remote()->transact(RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH, data, &reply,
+                IBinder::FLAG_ONEWAY);
+
+        // The native handle is dupped in ICameraClient so we need to free it here.
+        for (auto& handle : handles) {
+            native_handle_close(handle);
+            native_handle_delete(handle);
+        }
+    }
 };
 
 IMPLEMENT_META_INTERFACE(CameraRecordingProxyListener, "android.hardware.ICameraRecordingProxyListener");
@@ -101,6 +132,41 @@
             recordingFrameHandleCallbackTimestamp(timestamp, handle);
             return NO_ERROR;
         } break;
+        case RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH: {
+            ALOGV("RECORDING_FRAME_HANDLE_CALLBACK_TIMESTAMP_BATCH");
+            CHECK_INTERFACE(ICameraRecordingProxyListener, data, reply);
+            uint32_t n = 0;
+            status_t res = data.readUint32(&n);
+            if (res != OK) {
+                ALOGE("%s: Failed to read batch size: %s (%d)", __FUNCTION__, strerror(-res), res);
+                return BAD_VALUE;
+            }
+            std::vector<nsecs_t> timestamps;
+            std::vector<native_handle_t*> handles;
+            timestamps.reserve(n);
+            handles.reserve(n);
+            for (uint32_t i = 0; i < n; i++) {
+                res = data.readInt64(&timestamps[i]);
+                if (res != OK) {
+                    ALOGE("%s: Failed to read timestamp[%d]: %s (%d)",
+                            __FUNCTION__, i, strerror(-res), res);
+                    return BAD_VALUE;
+                }
+            }
+            for (uint32_t i = 0; i < n; i++) {
+                native_handle_t* handle = data.readNativeHandle();
+                if (handle == nullptr) {
+                    ALOGE("%s: Received a null native handle at handles[%d]",
+                            __FUNCTION__, i);
+                    return BAD_VALUE;
+                }
+                handles.push_back(handle);
+            }
+            // The native handle will be freed in
+            // BpCameraRecordingProxy::releaseRecordingFrameHandleBatch.
+            recordingFrameHandleCallbackTimestampBatch(timestamps, handles);
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/camera/include/camera/Camera.h b/camera/include/camera/Camera.h
index 57dc228..430aa1c 100644
--- a/camera/include/camera/Camera.h
+++ b/camera/include/camera/Camera.h
@@ -44,6 +44,9 @@
                           camera_frame_metadata_t *metadata) = 0;
     virtual void postDataTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr) = 0;
     virtual void postRecordingFrameHandleTimestamp(nsecs_t timestamp, native_handle_t* handle) = 0;
+    virtual void postRecordingFrameHandleTimestampBatch(
+            const std::vector<nsecs_t>& timestamps,
+            const std::vector<native_handle_t*>& handles) = 0;
 };
 
 class Camera;
@@ -118,6 +121,10 @@
             // release a recording frame handle
             void        releaseRecordingFrameHandle(native_handle_t *handle);
 
+            // release a batch of recording frame handles
+            void        releaseRecordingFrameHandleBatch(
+                    const std::vector<native_handle_t*> handles);
+
             // autoFocus - status returned from callback
             status_t    autoFocus();
 
@@ -166,6 +173,10 @@
                                      camera_frame_metadata_t *metadata);
     virtual void        dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr);
     virtual void        recordingFrameHandleCallbackTimestamp(nsecs_t timestamp, native_handle_t* handle);
+    virtual void        recordingFrameHandleCallbackTimestampBatch(
+                                const std::vector<nsecs_t>& timestamps,
+                                const std::vector<native_handle_t*>& handles);
+
 
     class RecordingProxy : public BnCameraRecordingProxy
     {
@@ -177,6 +188,8 @@
         virtual void stopRecording();
         virtual void releaseRecordingFrame(const sp<IMemory>& mem);
         virtual void releaseRecordingFrameHandle(native_handle_t* handle);
+        virtual void releaseRecordingFrameHandleBatch(
+                const std::vector<native_handle_t*>& handles);
 
     private:
         sp<Camera>         mCamera;
diff --git a/camera/include/camera/ICameraRecordingProxy.h b/camera/include/camera/ICameraRecordingProxy.h
index cb6824a..02af2f3 100644
--- a/camera/include/camera/ICameraRecordingProxy.h
+++ b/camera/include/camera/ICameraRecordingProxy.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_HARDWARE_ICAMERA_RECORDING_PROXY_H
 #define ANDROID_HARDWARE_ICAMERA_RECORDING_PROXY_H
 
+#include <vector>
 #include <binder/IInterface.h>
 #include <cutils/native_handle.h>
 #include <utils/RefBase.h>
@@ -85,6 +86,8 @@
     virtual void            stopRecording() = 0;
     virtual void            releaseRecordingFrame(const sp<IMemory>& mem) = 0;
     virtual void            releaseRecordingFrameHandle(native_handle_t *handle) = 0;
+    virtual void            releaseRecordingFrameHandleBatch(
+                                    const std::vector<native_handle_t*>& handles) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/camera/include/camera/ICameraRecordingProxyListener.h b/camera/include/camera/ICameraRecordingProxyListener.h
index 1fee5b9..da03c56 100644
--- a/camera/include/camera/ICameraRecordingProxyListener.h
+++ b/camera/include/camera/ICameraRecordingProxyListener.h
@@ -17,6 +17,7 @@
 #ifndef ANDROID_HARDWARE_ICAMERA_RECORDING_PROXY_LISTENER_H
 #define ANDROID_HARDWARE_ICAMERA_RECORDING_PROXY_LISTENER_H
 
+#include <vector>
 #include <binder/IInterface.h>
 #include <cutils/native_handle.h>
 #include <stdint.h>
@@ -38,6 +39,10 @@
 
     virtual void recordingFrameHandleCallbackTimestamp(nsecs_t timestamp,
                                                        native_handle_t* handle) = 0;
+
+    virtual void recordingFrameHandleCallbackTimestampBatch(
+            const std::vector<nsecs_t>& timestamps,
+            const std::vector<native_handle_t*>& handles) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/camera/include/camera/android/hardware/ICamera.h b/camera/include/camera/android/hardware/ICamera.h
index 315669e..80823d6 100644
--- a/camera/include/camera/android/hardware/ICamera.h
+++ b/camera/include/camera/android/hardware/ICamera.h
@@ -101,6 +101,11 @@
     // ICameraClient::recordingFrameHandleCallbackTimestamp.
     virtual void            releaseRecordingFrameHandle(native_handle_t *handle) = 0;
 
+    // Release a batch of recording frame handles that was received via
+    // ICameraClient::recordingFrameHandleCallbackTimestampBatch
+    virtual void            releaseRecordingFrameHandleBatch(
+            const std::vector<native_handle_t*>& handles) = 0;
+
     // auto focus
     virtual status_t        autoFocus() = 0;
 
diff --git a/camera/include/camera/android/hardware/ICameraClient.h b/camera/include/camera/android/hardware/ICameraClient.h
index f6ee311..8e46d17 100644
--- a/camera/include/camera/android/hardware/ICameraClient.h
+++ b/camera/include/camera/android/hardware/ICameraClient.h
@@ -41,6 +41,13 @@
     // ICamera::releaseRecordingFrameHandle to release the frame handle.
     virtual void            recordingFrameHandleCallbackTimestamp(nsecs_t timestamp,
                                          native_handle_t* handle) = 0;
+
+    // Invoked to send a batch of recording frame handles with timestamp. Call
+    // ICamera::releaseRecordingFrameHandleBatch to release the frame handles.
+    // Size of timestamps and handles must match
+    virtual void            recordingFrameHandleCallbackTimestampBatch(
+                                        const std::vector<nsecs_t>& timestamps,
+                                        const std::vector<native_handle_t*>& handles) = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/camera/tests/CameraZSLTests.cpp b/camera/tests/CameraZSLTests.cpp
index 6c91fdc..ecca354 100644
--- a/camera/tests/CameraZSLTests.cpp
+++ b/camera/tests/CameraZSLTests.cpp
@@ -51,6 +51,9 @@
             const sp<IMemory>&) override {};
     void recordingFrameHandleCallbackTimestamp(nsecs_t,
             native_handle_t*) override {};
+    void recordingFrameHandleCallbackTimestampBatch(
+            const std::vector<nsecs_t>&,
+            const std::vector<native_handle_t*>&) override {};
 
     status_t waitForPreviewStart();
     status_t waitForEvent(Mutex &mutex, Condition &condition, bool &flag);
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index 0fe44eb..a569f5d 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -58,6 +58,10 @@
 
     virtual void postRecordingFrameHandleTimestamp(nsecs_t timestamp, native_handle_t* handle);
 
+    virtual void postRecordingFrameHandleTimestampBatch(
+                const std::vector<nsecs_t>& timestamps,
+                const std::vector<native_handle_t*>& handles);
+
 protected:
     virtual ~CameraSourceListener();
 
@@ -110,6 +114,20 @@
     }
 }
 
+void CameraSourceListener::postRecordingFrameHandleTimestampBatch(
+        const std::vector<nsecs_t>& timestamps,
+        const std::vector<native_handle_t*>& handles) {
+    sp<CameraSource> source = mSource.promote();
+    if (source.get() != nullptr) {
+        int n = timestamps.size();
+        std::vector<nsecs_t> modifiedTimestamps(n);
+        for (int i = 0; i < n; i++) {
+            modifiedTimestamps[i] = timestamps[i] / 1000;
+        }
+        source->recordingFrameHandleCallbackTimestampBatch(modifiedTimestamps, handles);
+    }
+}
+
 static int32_t getColorFormat(const char* colorFormat) {
     if (!colorFormat) {
         ALOGE("Invalid color format");
@@ -952,10 +970,35 @@
         }
 
         if (handle != nullptr) {
-            // Frame contains a VideoNativeHandleMetadata. Send the handle back to camera.
-            releaseRecordingFrameHandle(handle);
-            mMemoryBases.push_back(frame);
-            mMemoryBaseAvailableCond.signal();
+            uint32_t batchSize = 0;
+            {
+                Mutex::Autolock autoLock(mBatchLock);
+                if (mInflightBatchSizes.size() > 0) {
+                    batchSize = mInflightBatchSizes[0];
+                }
+            }
+            if (batchSize == 0) { // return buffers one by one
+                // Frame contains a VideoNativeHandleMetadata. Send the handle back to camera.
+                releaseRecordingFrameHandle(handle);
+                mMemoryBases.push_back(frame);
+                mMemoryBaseAvailableCond.signal();
+            } else { // Group buffers in batch then return
+                Mutex::Autolock autoLock(mBatchLock);
+                mInflightReturnedHandles.push_back(handle);
+                mInflightReturnedMemorys.push_back(frame);
+                if (mInflightReturnedHandles.size() == batchSize) {
+                    releaseRecordingFrameHandleBatch(mInflightReturnedHandles);
+
+                    mInflightBatchSizes.pop_front();
+                    mInflightReturnedHandles.clear();
+                    for (const auto& mem : mInflightReturnedMemorys) {
+                        mMemoryBases.push_back(mem);
+                        mMemoryBaseAvailableCond.signal();
+                    }
+                    mInflightReturnedMemorys.clear();
+                }
+            }
+
         } else if (mCameraRecordingProxy != nullptr) {
             // mCamera is created by application. Return the frame back to camera via camera
             // recording proxy.
@@ -1126,6 +1169,21 @@
     }
 }
 
+void CameraSource::releaseRecordingFrameHandleBatch(const std::vector<native_handle_t*>& handles) {
+    if (mCameraRecordingProxy != nullptr) {
+        mCameraRecordingProxy->releaseRecordingFrameHandleBatch(handles);
+    } else if (mCamera != nullptr) {
+        int64_t token = IPCThreadState::self()->clearCallingIdentity();
+        mCamera->releaseRecordingFrameHandleBatch(handles);
+        IPCThreadState::self()->restoreCallingIdentity(token);
+    } else {
+        for (auto& handle : handles) {
+            native_handle_close(handle);
+            native_handle_delete(handle);
+        }
+    }
+}
+
 void CameraSource::recordingFrameHandleCallbackTimestamp(int64_t timestampUs,
                 native_handle_t* handle) {
     ALOGV("%s: timestamp %lld us", __FUNCTION__, (long long)timestampUs);
@@ -1163,6 +1221,62 @@
     mFrameAvailableCondition.signal();
 }
 
+void CameraSource::recordingFrameHandleCallbackTimestampBatch(
+        const std::vector<int64_t>& timestampsUs,
+        const std::vector<native_handle_t*>& handles) {
+    size_t n = timestampsUs.size();
+    if (n != handles.size()) {
+        ALOGE("%s: timestampsUs(%zu) and handles(%zu) size mismatch!",
+                __FUNCTION__, timestampsUs.size(), handles.size());
+    }
+
+    Mutex::Autolock autoLock(mLock);
+    int batchSize = 0;
+    for (size_t i = 0; i < n; i++) {
+        int64_t timestampUs = timestampsUs[i];
+        native_handle_t* handle = handles[i];
+
+        ALOGV("%s: timestamp %lld us", __FUNCTION__, (long long)timestampUs);
+        if (handle == nullptr) continue;
+
+        if (shouldSkipFrameLocked(timestampUs)) {
+            releaseRecordingFrameHandle(handle);
+            continue;
+        }
+
+        while (mMemoryBases.empty()) {
+            if (mMemoryBaseAvailableCond.waitRelative(mLock, kMemoryBaseAvailableTimeoutNs) ==
+                    TIMED_OUT) {
+                ALOGW("Waiting on an available memory base timed out. Dropping a recording frame.");
+                releaseRecordingFrameHandle(handle);
+                continue;
+            }
+        }
+        ++batchSize;
+        ++mNumFramesReceived;
+        sp<IMemory> data = *mMemoryBases.begin();
+        mMemoryBases.erase(mMemoryBases.begin());
+
+        // Wrap native handle in sp<IMemory> so it can be pushed to mFramesReceived.
+        VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(data->pointer());
+        metadata->eType = kMetadataBufferTypeNativeHandleSource;
+        metadata->pHandle = handle;
+
+        mFramesReceived.push_back(data);
+        int64_t timeUs = mStartTimeUs + (timestampUs - mFirstFrameTimeUs);
+        mFrameTimes.push_back(timeUs);
+        ALOGV("initial delay: %" PRId64 ", current time stamp: %" PRId64, mStartTimeUs, timeUs);
+
+    }
+    if (batchSize > 0) {
+        Mutex::Autolock autoLock(mBatchLock);
+        mInflightBatchSizes.push_back(batchSize);
+    }
+    for (int i = 0; i < batchSize; i++) {
+        mFrameAvailableCondition.signal();
+    }
+}
+
 CameraSource::BufferQueueListener::BufferQueueListener(const sp<BufferItemConsumer>& consumer,
         const sp<CameraSource>& cameraSource) {
     mConsumer = consumer;
@@ -1279,6 +1393,17 @@
     mSource->recordingFrameHandleCallbackTimestamp(timestamp / 1000, handle);
 }
 
+void CameraSource::ProxyListener::recordingFrameHandleCallbackTimestampBatch(
+        const std::vector<int64_t>& timestampsUs,
+        const std::vector<native_handle_t*>& handles) {
+    int n = timestampsUs.size();
+    std::vector<nsecs_t> modifiedTimestamps(n);
+    for (int i = 0; i < n; i++) {
+        modifiedTimestamps[i] = timestampsUs[i] / 1000;
+    }
+    mSource->recordingFrameHandleCallbackTimestampBatch(modifiedTimestamps, handles);
+}
+
 void CameraSource::DeathNotifier::binderDied(const wp<IBinder>& who __unused) {
     ALOGI("Camera recording proxy died");
 }
diff --git a/media/libstagefright/CameraSourceTimeLapse.cpp b/media/libstagefright/CameraSourceTimeLapse.cpp
index 390c556..970526a 100644
--- a/media/libstagefright/CameraSourceTimeLapse.cpp
+++ b/media/libstagefright/CameraSourceTimeLapse.cpp
@@ -315,6 +315,17 @@
     CameraSource::recordingFrameHandleCallbackTimestamp(timestampUs, handle);
 }
 
+void CameraSourceTimeLapse::recordingFrameHandleCallbackTimestampBatch(
+        const std::vector<int64_t>& timestampsUs,
+        const std::vector<native_handle_t*>& handles) {
+    ALOGV("recordingFrameHandleCallbackTimestampBatch");
+    int n = timestampsUs.size();
+    for (int i = 0; i < n; i++) {
+        // Don't do batching for CameraSourceTimeLapse for now
+        recordingFrameHandleCallbackTimestamp(timestampsUs[i], handles[i]);
+    }
+}
+
 void CameraSourceTimeLapse::processBufferQueueFrame(BufferItem& buffer) {
     ALOGV("processBufferQueueFrame");
     int64_t timestampUs = buffer.mTimestamp / 1000;
diff --git a/media/libstagefright/include/CameraSource.h b/media/libstagefright/include/CameraSource.h
index c604f2d..aa56d27 100644
--- a/media/libstagefright/include/CameraSource.h
+++ b/media/libstagefright/include/CameraSource.h
@@ -18,6 +18,7 @@
 
 #define CAMERA_SOURCE_H_
 
+#include <deque>
 #include <media/stagefright/MediaBuffer.h>
 #include <media/stagefright/MediaSource.h>
 #include <camera/android/hardware/ICamera.h>
@@ -141,6 +142,9 @@
                 const sp<IMemory> &data);
         virtual void recordingFrameHandleCallbackTimestamp(int64_t timestampUs,
                 native_handle_t* handle);
+        virtual void recordingFrameHandleCallbackTimestampBatch(
+                const std::vector<int64_t>& timestampsUs,
+                const std::vector<native_handle_t*>& handles);
 
     private:
         sp<CameraSource> mSource;
@@ -213,6 +217,8 @@
     virtual status_t startCameraRecording();
     virtual void releaseRecordingFrame(const sp<IMemory>& frame);
     virtual void releaseRecordingFrameHandle(native_handle_t* handle);
+    // stagefright recorder not using this for now
+    virtual void releaseRecordingFrameHandleBatch(const std::vector<native_handle_t*>& handles);
 
     // Returns true if need to skip the current frame.
     // Called from dataCallbackTimestamp.
@@ -227,6 +233,10 @@
     virtual void recordingFrameHandleCallbackTimestamp(int64_t timestampUs,
             native_handle_t* handle);
 
+    virtual void recordingFrameHandleCallbackTimestampBatch(
+            const std::vector<int64_t>& timestampsUs,
+            const std::vector<native_handle_t*>& handles);
+
     // Process a buffer item received in BufferQueueListener.
     virtual void processBufferQueueFrame(BufferItem& buffer);
 
@@ -271,6 +281,13 @@
     KeyedVector<ANativeWindowBuffer*, BufferItem> mReceivedBufferItemMap;
     sp<BufferQueueListener> mBufferQueueListener;
 
+    Mutex mBatchLock; // protecting access to mInflightXXXXX members below
+    // Start of members protected by mBatchLock
+    std::deque<uint32_t> mInflightBatchSizes;
+    std::vector<native_handle_t*> mInflightReturnedHandles;
+    std::vector<const sp<IMemory>> mInflightReturnedMemorys;
+    // End of members protected by mBatchLock
+
     void releaseQueuedFrames();
     void releaseOneRecordingFrame(const sp<IMemory>& frame);
     void createVideoBufferMemoryHeap(size_t size, uint32_t bufferCount);
diff --git a/media/libstagefright/include/CameraSourceTimeLapse.h b/media/libstagefright/include/CameraSourceTimeLapse.h
index 871c1d9..b066f9a 100644
--- a/media/libstagefright/include/CameraSourceTimeLapse.h
+++ b/media/libstagefright/include/CameraSourceTimeLapse.h
@@ -147,12 +147,23 @@
 
     // In the video camera case calls skipFrameAndModifyTimeStamp() to modify
     // timestamp and set mSkipCurrentFrame.
-    // Then it calls the base CameraSource::recordingFrameHandleCallbackTimestamp()
+    // Then it calls the base CameraSource::recordingFrameHandleCallbackTimestamp() or
+    // CameraSource::recordingFrameHandleCallbackTimestampBatch()
     // This will be called in VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA mode when
     // the metadata is VideoNativeHandleMetadata.
     virtual void recordingFrameHandleCallbackTimestamp(int64_t timestampUs,
             native_handle_t* handle);
 
+    // In the video camera case calls skipFrameAndModifyTimeStamp() to modify
+    // timestamp and set mSkipCurrentFrame.
+    // Then it calls the base CameraSource::recordingFrameHandleCallbackTimestamp() or
+    // CameraSource::recordingFrameHandleCallbackTimestampBatch()
+    // This will be called in VIDEO_BUFFER_MODE_DATA_CALLBACK_METADATA mode when
+    // the metadata is VideoNativeHandleMetadata.
+    virtual void recordingFrameHandleCallbackTimestampBatch(
+            const std::vector<int64_t>& timestampsUs,
+            const std::vector<native_handle_t*>& handles);
+
     // Process a buffer item received in CameraSource::BufferQueueListener.
     // This will be called in VIDEO_BUFFER_MODE_BUFFER_QUEUE mode.
     virtual void processBufferQueueFrame(BufferItem& buffer);
diff --git a/services/camera/libcameraservice/CameraFlashlight.cpp b/services/camera/libcameraservice/CameraFlashlight.cpp
index 0ff9314..4537ae6 100644
--- a/services/camera/libcameraservice/CameraFlashlight.cpp
+++ b/services/camera/libcameraservice/CameraFlashlight.cpp
@@ -944,7 +944,7 @@
     }
 
     // need to set __get_memory in set_callbacks().
-    device->setCallbacks(NULL, NULL, NULL, NULL);
+    device->setCallbacks(NULL, NULL, NULL, NULL, NULL);
 
     mParameters = device->getParameters();
 
diff --git a/services/camera/libcameraservice/api1/Camera2Client.cpp b/services/camera/libcameraservice/api1/Camera2Client.cpp
index 3aec562..335e999 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.cpp
+++ b/services/camera/libcameraservice/api1/Camera2Client.cpp
@@ -1261,6 +1261,13 @@
     ALOGW("%s: Not supported in buffer queue mode.", __FUNCTION__);
 }
 
+void Camera2Client::releaseRecordingFrameHandleBatch(
+        const std::vector<native_handle_t*>& handles) {
+    (void)handles;
+    ATRACE_CALL();
+    ALOGW("%s: Not supported in buffer queue mode.", __FUNCTION__);
+}
+
 status_t Camera2Client::autoFocus() {
     ATRACE_CALL();
     Mutex::Autolock icl(mBinderSerializationLock);
diff --git a/services/camera/libcameraservice/api1/Camera2Client.h b/services/camera/libcameraservice/api1/Camera2Client.h
index 87c91a0..9738aca 100644
--- a/services/camera/libcameraservice/api1/Camera2Client.h
+++ b/services/camera/libcameraservice/api1/Camera2Client.h
@@ -72,6 +72,8 @@
     virtual bool            recordingEnabled();
     virtual void            releaseRecordingFrame(const sp<IMemory>& mem);
     virtual void            releaseRecordingFrameHandle(native_handle_t *handle);
+    virtual void            releaseRecordingFrameHandleBatch(
+                                    const std::vector<native_handle_t*>& handles);
     virtual status_t        autoFocus();
     virtual status_t        cancelAutoFocus();
     virtual status_t        takePicture(int msgType);
diff --git a/services/camera/libcameraservice/api1/CameraClient.cpp b/services/camera/libcameraservice/api1/CameraClient.cpp
index ffb657e..df8726e 100644
--- a/services/camera/libcameraservice/api1/CameraClient.cpp
+++ b/services/camera/libcameraservice/api1/CameraClient.cpp
@@ -98,6 +98,7 @@
     mHardware->setCallbacks(notifyCallback,
             dataCallback,
             dataCallbackTimestamp,
+            handleCallbackTimestampBatch,
             (void *)(uintptr_t)mCameraId);
 
     // Enable zoom, error, focus, and metadata messages by default
@@ -533,6 +534,50 @@
     mHardware->releaseRecordingFrame(dataPtr);
 }
 
+void CameraClient::releaseRecordingFrameHandleBatch(const std::vector<native_handle_t*>& handles) {
+    size_t n = handles.size();
+    std::vector<sp<IMemory>> frames;
+    frames.reserve(n);
+    bool error = false;
+    for (auto& handle : handles) {
+        sp<IMemory> dataPtr;
+        {
+            Mutex::Autolock l(mAvailableCallbackBuffersLock);
+            if (!mAvailableCallbackBuffers.empty()) {
+                dataPtr = mAvailableCallbackBuffers.back();
+                mAvailableCallbackBuffers.pop_back();
+            }
+        }
+
+        if (dataPtr == nullptr) {
+            ALOGE("%s: %d: No callback buffer available. Dropping frames.", __FUNCTION__,
+                    __LINE__);
+            error = true;
+            break;
+        } else if (dataPtr->size() != sizeof(VideoNativeHandleMetadata)) {
+            ALOGE("%s: %d: Callback buffer must be VideoNativeHandleMetadata", __FUNCTION__,
+                    __LINE__);
+            error = true;
+            break;
+        }
+
+        VideoNativeHandleMetadata *metadata = (VideoNativeHandleMetadata*)(dataPtr->pointer());
+        metadata->eType = kMetadataBufferTypeNativeHandleSource;
+        metadata->pHandle = handle;
+        frames.push_back(dataPtr);
+    }
+
+    if (error) {
+        for (auto& handle : handles) {
+            native_handle_close(handle);
+            native_handle_delete(handle);
+        }
+    } else {
+        mHardware->releaseRecordingFrameBatch(frames);
+    }
+    return;
+}
+
 status_t CameraClient::setVideoBufferMode(int32_t videoBufferMode) {
     LOG1("setVideoBufferMode: %d", videoBufferMode);
     bool enableMetadataInBuffers = false;
@@ -855,6 +900,49 @@
     client->handleGenericDataTimestamp(timestamp, msgType, dataPtr);
 }
 
+void CameraClient::handleCallbackTimestampBatch(
+        int32_t msgType, const std::vector<HandleTimestampMessage>& msgs, void* user) {
+    LOG2("dataCallbackTimestampBatch");
+    sp<CameraClient> client = getClientFromCookie(user);
+    if (client.get() == nullptr) return;
+    if (!client->lockIfMessageWanted(msgType)) return;
+
+    sp<hardware::ICameraClient> c = client->mRemoteCallback;
+    client->mLock.unlock();
+    if (c != 0 && msgs.size() > 0) {
+        size_t n = msgs.size();
+        std::vector<nsecs_t> timestamps;
+        std::vector<native_handle_t*> handles;
+        timestamps.reserve(n);
+        handles.reserve(n);
+        for (auto& msg : msgs) {
+            native_handle_t* handle = nullptr;
+            if (msg.dataPtr->size() != sizeof(VideoNativeHandleMetadata)) {
+                ALOGE("%s: dataPtr does not contain VideoNativeHandleMetadata!", __FUNCTION__);
+                return;
+            }
+            VideoNativeHandleMetadata *metadata =
+                (VideoNativeHandleMetadata*)(msg.dataPtr->pointer());
+            if (metadata->eType == kMetadataBufferTypeNativeHandleSource) {
+                handle = metadata->pHandle;
+            }
+
+            if (handle == nullptr) {
+                ALOGE("%s: VideoNativeHandleMetadata type mismatch or null handle passed!",
+                        __FUNCTION__);
+                return;
+            }
+            {
+                Mutex::Autolock l(client->mAvailableCallbackBuffersLock);
+                client->mAvailableCallbackBuffers.push_back(msg.dataPtr);
+            }
+            timestamps.push_back(msg.timestamp);
+            handles.push_back(handle);
+        }
+        c->recordingFrameHandleCallbackTimestampBatch(timestamps, handles);
+    }
+}
+
 // snapshot taken callback
 void CameraClient::handleShutter(void) {
     if (mPlayShutterSound) {
diff --git a/services/camera/libcameraservice/api1/CameraClient.h b/services/camera/libcameraservice/api1/CameraClient.h
index 91f00e3..1073384 100644
--- a/services/camera/libcameraservice/api1/CameraClient.h
+++ b/services/camera/libcameraservice/api1/CameraClient.h
@@ -50,6 +50,8 @@
     virtual bool            recordingEnabled();
     virtual void            releaseRecordingFrame(const sp<IMemory>& mem);
     virtual void            releaseRecordingFrameHandle(native_handle_t *handle);
+    virtual void            releaseRecordingFrameHandleBatch(
+                                    const std::vector<native_handle_t*>& handles);
     virtual status_t        autoFocus();
     virtual status_t        cancelAutoFocus();
     virtual status_t        takePicture(int msgType);
@@ -109,6 +111,8 @@
     static void             dataCallback(int32_t msgType, const sp<IMemory>& dataPtr,
             camera_frame_metadata_t *metadata, void* user);
     static void             dataCallbackTimestamp(nsecs_t timestamp, int32_t msgType, const sp<IMemory>& dataPtr, void* user);
+    static void             handleCallbackTimestampBatch(
+                                    int32_t msgType, const std::vector<HandleTimestampMessage>&, void* user);
     // handlers for messages
     void                    handleShutter(void);
     void                    handlePreviewData(int32_t msgType, const sp<IMemory>& mem,
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
index b52c0d8..9c058bc 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.cpp
@@ -193,6 +193,36 @@
     return hardware::Void();
 }
 
+hardware::Return<void> CameraHardwareInterface::handleCallbackTimestampBatch(
+        DataCallbackMsg msgType,
+        const hardware::hidl_vec<hardware::camera::device::V1_0::HandleTimestampMessage>& messages) {
+    std::vector<android::HandleTimestampMessage> msgs;
+    msgs.reserve(messages.size());
+
+    for (const auto& hidl_msg : messages) {
+        if (mHidlMemPoolMap.count(hidl_msg.data) == 0) {
+            ALOGE("%s: memory pool ID %d not found", __FUNCTION__, hidl_msg.data);
+            return hardware::Void();
+        }
+        sp<CameraHeapMemory> mem(
+                static_cast<CameraHeapMemory *>(mHidlMemPoolMap.at(hidl_msg.data)->handle));
+
+        if (hidl_msg.bufferIndex >= mem->mNumBufs) {
+            ALOGE("%s: invalid buffer index %d, max allowed is %d", __FUNCTION__,
+                 hidl_msg.bufferIndex, mem->mNumBufs);
+            return hardware::Void();
+        }
+        VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*)
+                mem->mBuffers[hidl_msg.bufferIndex]->pointer();
+        md->pHandle = const_cast<native_handle_t*>(hidl_msg.frameData.getNativeHandle());
+
+        msgs.push_back({hidl_msg.timestamp, mem->mBuffers[hidl_msg.bufferIndex]});
+    }
+
+    mDataCbTimestampBatch((int32_t) msgType, msgs, mCbUser);
+    return hardware::Void();
+}
+
 std::pair<bool, uint64_t> CameraHardwareInterface::getBufferId(
         ANativeWindowBuffer* anb) {
     std::lock_guard<std::mutex> lock(mBufferIdMapLock);
@@ -468,11 +498,13 @@
 void CameraHardwareInterface::setCallbacks(notify_callback notify_cb,
         data_callback data_cb,
         data_callback_timestamp data_cb_timestamp,
+        data_callback_timestamp_batch data_cb_timestamp_batch,
         void* user)
 {
     mNotifyCb = notify_cb;
     mDataCb = data_cb;
     mDataCbTimestamp = data_cb_timestamp;
+    mDataCbTimestampBatch = data_cb_timestamp_batch;
     mCbUser = user;
 
     ALOGV("%s(%s)", __FUNCTION__, mName.string());
@@ -628,6 +660,44 @@
     }
 }
 
+void CameraHardwareInterface::releaseRecordingFrameBatch(const std::vector<sp<IMemory>>& frames)
+{
+    ALOGV("%s(%s)", __FUNCTION__, mName.string());
+    size_t n = frames.size();
+    std::vector<VideoFrameMessage> msgs;
+    msgs.reserve(n);
+    for (auto& mem : frames) {
+        if (CC_LIKELY(mHidlDevice != nullptr)) {
+            ssize_t offset;
+            size_t size;
+            sp<IMemoryHeap> heap = mem->getMemory(&offset, &size);
+            if (size == sizeof(VideoNativeHandleMetadata)) {
+                uint32_t heapId = heap->getHeapID();
+                uint32_t bufferIndex = offset / size;
+                VideoNativeHandleMetadata* md = (VideoNativeHandleMetadata*) mem->pointer();
+                // Caching the handle here because md->pHandle will be subject to HAL's edit
+                native_handle_t* nh = md->pHandle;
+                VideoFrameMessage msg;
+                msgs.push_back({nh, heapId, bufferIndex});
+            } else {
+                ALOGE("%s only supports VideoNativeHandleMetadata mode", __FUNCTION__);
+                return;
+            }
+        } else {
+            ALOGE("Non HIDL mode do not support %s", __FUNCTION__);
+            return;
+        }
+    }
+
+    mHidlDevice->releaseRecordingFrameHandleBatch(msgs);
+
+    for (auto& msg : msgs) {
+        native_handle_t* nh = const_cast<native_handle_t*>(msg.frameData.getNativeHandle());
+        native_handle_close(nh);
+        native_handle_delete(nh);
+    }
+}
+
 status_t CameraHardwareInterface::autoFocus()
 {
     ALOGV("%s(%s)", __FUNCTION__, mName.string());
diff --git a/services/camera/libcameraservice/device1/CameraHardwareInterface.h b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
index 88ab2e9..c2b0e9c 100644
--- a/services/camera/libcameraservice/device1/CameraHardwareInterface.h
+++ b/services/camera/libcameraservice/device1/CameraHardwareInterface.h
@@ -48,6 +48,15 @@
                             const sp<IMemory> &dataPtr,
                             void *user);
 
+struct HandleTimestampMessage {
+    nsecs_t timestamp;
+    const sp<IMemory> dataPtr;
+};
+
+typedef void (*data_callback_timestamp_batch)(
+        int32_t msgType,
+        const std::vector<HandleTimestampMessage>&, void* user);
+
 /**
  * CameraHardwareInterface.h defines the interface to the
  * camera hardware abstraction layer, used for setting and getting
@@ -112,6 +121,7 @@
     void setCallbacks(notify_callback notify_cb,
                       data_callback data_cb,
                       data_callback_timestamp data_cb_timestamp,
+                      data_callback_timestamp_batch data_cb_timestamp_batch,
                       void* user);
 
     /**
@@ -227,6 +237,20 @@
     void releaseRecordingFrame(const sp<IMemory>& mem);
 
     /**
+     * Release a batch of recording frames previously returned by
+     * CAMERA_MSG_VIDEO_FRAME. This method only supports frames that are
+     * stored as VideoNativeHandleMetadata.
+     *
+     * It is camera hal client's responsibility to release video recording
+     * frames sent out by the camera hal before the camera hal receives
+     * a call to disableMsgType(CAMERA_MSG_VIDEO_FRAME). After it receives
+     * the call to disableMsgType(CAMERA_MSG_VIDEO_FRAME), it is camera hal's
+     * responsibility of managing the life-cycle of the video recording
+     * frames.
+     */
+    void releaseRecordingFrameBatch(const std::vector<sp<IMemory>>& frames);
+
+    /**
      * Start auto focus, the notification callback routine is called
      * with CAMERA_MSG_FOCUS once when focusing is complete. autoFocus()
      * will be called again if another auto focus is needed.
@@ -416,6 +440,10 @@
             hardware::camera::device::V1_0::DataCallbackMsg msgType,
             const hardware::hidl_handle& frameData, uint32_t data,
             uint32_t bufferIndex, int64_t timestamp) override;
+    hardware::Return<void> handleCallbackTimestampBatch(
+            hardware::camera::device::V1_0::DataCallbackMsg msgType,
+            const hardware::hidl_vec<
+                    hardware::camera::device::V1_0::HandleTimestampMessage>&) override;
 
     /**
      * Implementation of android::hardware::camera::device::V1_0::ICameraDevicePreviewCallback
@@ -450,9 +478,10 @@
 
     struct camera_preview_window mHalPreviewWindow;
 
-    notify_callback         mNotifyCb;
-    data_callback           mDataCb;
-    data_callback_timestamp mDataCbTimestamp;
+    notify_callback               mNotifyCb;
+    data_callback                 mDataCb;
+    data_callback_timestamp       mDataCbTimestamp;
+    data_callback_timestamp_batch mDataCbTimestampBatch;
     void *mCbUser;
 
     // Cached values for preview stream parameters