API quản lý vùng đệm HAL3 cho máy ảnh

Android 10 giới thiệu các API quản lý vùng đệm camera HAL3 không bắt buộc, cho phép bạn triển khai logic quản lý vùng đệm để đạt được các mức độ đánh đổi khác nhau về bộ nhớ và độ trễ chụp trong quá trình triển khai HAL máy ảnh.

HAL máy ảnh yêu cầu N yêu cầu (trong đó N bằng với chiều sâu quy trình) được đưa vào hàng đợi trong quy trình của nó, nhưng thường không yêu cầu tất cả N bộ vùng đệm đầu ra cùng một lúc.

Ví dụ: HAL có thể có 8 yêu cầu được đưa vào hàng đợi trong quy trình, nhưng chỉ yêu cầu vùng đệm đầu ra cho 2 yêu cầu ở các giai đoạn cuối của quy trình. Trên các thiết bị chạy Android 9 trở xuống, khung máy ảnh sẽ phân bổ vùng đệm khi yêu cầu được đưa vào hàng đợi trong HAL, vì vậy, có thể có 6 nhóm vùng đệm trong HAL không được sử dụng. Trong Android 10, các API quản lý vùng đệm HAL3 của máy ảnh cho phép tách rời các vùng đệm đầu ra để giải phóng 6 bộ vùng đệm. Điều này có thể giúp tiết kiệm hàng trăm megabyte bộ nhớ trên các thiết bị cao cấp và cũng có thể có lợi cho các thiết bị có bộ nhớ thấp.

Hình 1 cho thấy sơ đồ giao diện HAL của máy ảnh dành cho các thiết bị chạy Android 9 trở xuống. Hình 2 cho thấy giao diện HAL của máy ảnh trong Android 10 với các API quản lý bộ đệm HAL3 của máy ảnh được triển khai.

Quản lý vùng đệm trong Android 9 trở xuống

Hình 1. Giao diện HAL của máy ảnh trong Android 9 trở xuống

Quản lý vùng đệm trong Android 10

Hình 2. Giao diện HAL máy ảnh trong Android 10 sử dụng các API quản lý bộ đệm

Triển khai các API quản lý vùng đệm

Để triển khai các API quản lý vùng đệm, HAL máy ảnh phải:

HAL máy ảnh sử dụng các phương thức requestStreamBuffersreturnStreamBuffers trong ICameraDeviceCallback.hal để yêu cầu và trả về vùng đệm. HAL cũng phải triển khai phương thức signalStreamFlush trong ICameraDeviceSession.hal để báo hiệu HAL của máy ảnh trả về vùng đệm.

requestStreamBuffers

Sử dụng phương thức requestStreamBuffers để yêu cầu vùng đệm từ khung máy ảnh. Khi sử dụng API quản lý bộ đệm HAL3 của máy ảnh, các yêu cầu chụp từ khung máy ảnh sẽ không chứa bộ đệm đầu ra, tức là trường bufferId trong StreamBuffer0. Do đó, HAL máy ảnh phải sử dụng requestStreamBuffers để yêu cầu vùng đệm từ khung máy ảnh.

Phương thức requestStreamBuffers cho phép phương thức gọi yêu cầu nhiều vùng đệm từ nhiều luồng đầu ra trong một lệnh gọi, cho phép giảm số lệnh gọi HIDL IPC. Tuy nhiên, các lệnh gọi sẽ mất nhiều thời gian hơn khi nhiều vùng đệm được yêu cầu cùng một lúc và điều này có thể ảnh hưởng tiêu cực đến tổng độ trễ từ yêu cầu đến kết quả. Ngoài ra, vì các lệnh gọi đến requestStreamBuffers được chuyển đổi tuần tự trong dịch vụ máy ảnh, nên HAL máy ảnh nên sử dụng một luồng ưu tiên cao chuyên dụng để yêu cầu vùng đệm.

Nếu yêu cầu vùng đệm không thành công, HAL máy ảnh phải có khả năng xử lý đúng cách các lỗi không nghiêm trọng. Danh sách sau đây mô tả các lý do thường gặp khiến yêu cầu vùng đệm không thành công và cách HAL máy ảnh xử lý các yêu cầu đó.

  • Ứng dụng ngắt kết nối khỏi luồng đầu ra: Đây là lỗi không nghiêm trọng. HAL máy ảnh sẽ gửi ERROR_REQUEST cho mọi yêu cầu chụp nhắm đến luồng đã ngắt kết nối và sẵn sàng xử lý các yêu cầu tiếp theo một cách bình thường.
  • Hết thời gian chờ: Điều này có thể xảy ra khi một ứng dụng đang bận xử lý chuyên sâu trong khi giữ một số vùng đệm. HAL máy ảnh sẽ gửi ERROR_REQUEST cho các yêu cầu chụp không thể thực hiện được do lỗi hết thời gian chờ và sẵn sàng xử lý các yêu cầu tiếp theo một cách bình thường.
  • Khung máy ảnh đang chuẩn bị cấu hình luồng mới: HAL máy ảnh sẽ đợi cho đến khi lệnh gọi configureStreams tiếp theo hoàn tất trước khi gọi lại requestStreamBuffers.
  • HAL máy ảnh đã đạt đến giới hạn bộ đệm (trường maxBuffers): HAL máy ảnh sẽ đợi cho đến khi trả về ít nhất một bộ đệm của luồng trước khi gọi lại requestStreamBuffers.

returnStreamBuffers

Sử dụng phương thức returnStreamBuffers để trả về các vùng đệm bổ sung cho khung máy ảnh. HAL máy ảnh thường trả về vùng đệm cho khung máy ảnh thông qua phương thức processCaptureResult, nhưng chỉ có thể tính đến các yêu cầu chụp đã được gửi đến HAL máy ảnh. Với phương thức requestStreamBuffers, việc triển khai HAL máy ảnh có thể giữ lại nhiều vùng đệm hơn so với yêu cầu của khung máy ảnh. Đây là thời điểm bạn nên sử dụng phương thức returnStreamBuffers. Nếu quá trình triển khai HAL không bao giờ giữ nhiều vùng đệm hơn yêu cầu, thì quá trình triển khai HAL của máy ảnh không cần gọi phương thức returnStreamBuffers.

signalStreamFlush

Phương thức signalStreamFlush được khung máy ảnh gọi để thông báo cho HAL máy ảnh trả về tất cả các vùng đệm hiện có. Phương thức này thường được gọi khi khung máy ảnh sắp gọi configureStreams và phải xả quy trình chụp ảnh của máy ảnh. Tương tự như phương thức returnStreamBuffers, nếu việc triển khai HAL máy ảnh không chứa nhiều vùng đệm hơn yêu cầu, thì phương thức này có thể được triển khai trống.

Sau khi khung máy ảnh gọi signalStreamFlush, khung này sẽ ngừng gửi các yêu cầu chụp mới đến HAL máy ảnh cho đến khi tất cả bộ đệm được trả về khung máy ảnh. Khi tất cả vùng đệm được trả về, các lệnh gọi phương thức requestStreamBuffers sẽ không thành công và khung máy ảnh có thể tiếp tục hoạt động ở trạng thái sạch. Sau đó, khung máy ảnh sẽ gọi phương thức configureStreams hoặc processCaptureRequest. Nếu khung máy ảnh gọi phương thức configureStreams, thì HAL máy ảnh có thể bắt đầu yêu cầu vùng đệm lại sau khi lệnh gọi configureStreams trả về thành công. Nếu khung máy ảnh gọi phương thức processCaptureRequest, thì HAL máy ảnh có thể bắt đầu yêu cầu vùng đệm trong lệnh gọi processCaptureRequest.

Ngữ nghĩa của phương thức signalStreamFlush và phương thức flush là khác nhau. Khi phương thức flush được gọi, HAL có thể huỷ các yêu cầu chụp đang chờ xử lý bằng ERROR_REQUEST để rút cạn quy trình càng sớm càng tốt. Khi phương thức signalStreamFlush được gọi, HAL phải hoàn tất tất cả các yêu cầu chụp đang chờ xử lý và trả về tất cả bộ đệm cho khung máy ảnh.

Một điểm khác biệt khác giữa phương thức signalStreamFlush và các phương thức khác là signalStreamFlush là phương thức HIDL một chiều, nghĩa là khung máy ảnh có thể gọi vào các API chặn khác trước khi HAL nhận được lệnh gọi signalStreamFlush. Điều này có nghĩa là phương thức signalStreamFlush và các phương thức khác (cụ thể là phương thức configureStreams) có thể đến HAL máy ảnh theo thứ tự khác với thứ tự được gọi trong khung máy ảnh. Để giải quyết vấn đề không đồng bộ này, trường streamConfigCounter đã được thêm vào StreamConfiguration và thêm làm đối số vào phương thức signalStreamFlush. Việc triển khai HAL máy ảnh phải sử dụng đối số streamConfigCounter để xác định xem lệnh gọi signalStreamFlush có đến muộn hơn lệnh gọi configureStreams tương ứng hay không. Hãy xem Hình 3 để biết ví dụ.

Xử lý các cuộc gọi đến muộn

Hình 3. Cách HAL máy ảnh phát hiện và xử lý các lệnh gọi signalStreamFlush đến muộn

Hành vi thay đổi khi triển khai các API quản lý vùng đệm

Khi sử dụng API quản lý vùng đệm để triển khai logic quản lý vùng đệm, hãy cân nhắc những thay đổi về hành vi có thể xảy ra đối với việc triển khai máy ảnh và HAL máy ảnh:

  • Các yêu cầu chụp đến HAL máy ảnh nhanh hơn và thường xuyên hơn: Nếu không có API quản lý vùng đệm, khung máy ảnh sẽ yêu cầu vùng đệm đầu ra cho mỗi yêu cầu chụp trước khi gửi yêu cầu chụp đến HAL máy ảnh. Khi sử dụng API quản lý vùng đệm, khung máy ảnh không cần phải đợi vùng đệm nữa và do đó có thể gửi yêu cầu chụp đến HAL máy ảnh sớm hơn.

    Ngoài ra, nếu không có API quản lý vùng đệm, khung máy ảnh sẽ ngừng gửi yêu cầu chụp nếu một trong các luồng đầu ra của yêu cầu chụp đã đạt đến số lượng vùng đệm tối đa mà HAL có thể chứa cùng một lúc (giá trị này do HAL máy ảnh chỉ định trong trường HalStream::maxBuffers trong giá trị trả về của lệnh gọi configureStreams). Với các API quản lý vùng đệm, hành vi điều tiết này không còn tồn tại và việc triển khai HAL máy ảnh không được chấp nhận các lệnh gọi processCaptureRequest khi HAL có quá nhiều yêu cầu chụp được đưa vào hàng đợi.

  • Độ trễ lệnh gọi requestStreamBuffers thay đổi đáng kể: Có nhiều lý do khiến lệnh gọi requestStreamBuffers có thể mất nhiều thời gian hơn mức trung bình. Ví dụ:

    • Đối với một vài vùng đệm đầu tiên của luồng mới tạo, các lệnh gọi có thể mất nhiều thời gian hơn vì thiết bị cần phân bổ bộ nhớ.
    • Độ trễ dự kiến tăng theo tỷ lệ với số lượng vùng đệm được yêu cầu trong mỗi lệnh gọi.
    • Ứng dụng đang giữ vùng đệm và đang bận xử lý. Điều này có thể khiến các yêu cầu vùng đệm bị chậm hoặc hết thời gian chờ do thiếu vùng đệm hoặc CPU bị bận.

Chiến lược quản lý bộ đệm

API quản lý vùng đệm cho phép triển khai nhiều loại chiến lược quản lý vùng đệm. Dưới đây là một số ví dụ:

  • Tương thích ngược: HAL yêu cầu vùng đệm cho yêu cầu chụp trong lệnh gọi processCaptureRequest. Chiến lược này không giúp tiết kiệm bộ nhớ, nhưng có thể đóng vai trò là phương thức triển khai đầu tiên của các API quản lý vùng đệm, chỉ yêu cầu rất ít thay đổi mã đối với HAL máy ảnh hiện có.
  • Tối đa hoá việc tiết kiệm bộ nhớ: HAL máy ảnh chỉ yêu cầu vùng đệm đầu ra ngay trước khi cần lấp đầy vùng đệm. Chiến lược này cho phép tiết kiệm bộ nhớ tối đa. Nhược điểm tiềm ẩn là quy trình máy ảnh bị giật nhiều hơn khi các yêu cầu vùng đệm mất nhiều thời gian hơn bình thường để hoàn tất.
  • Đã lưu vào bộ nhớ đệm: HAL máy ảnh lưu một số vùng đệm vào bộ nhớ đệm để giảm khả năng bị ảnh hưởng bởi một yêu cầu vùng đệm chậm.

HAL máy ảnh có thể áp dụng nhiều chiến lược cho các trường hợp sử dụng cụ thể, ví dụ: sử dụng chiến lược tiết kiệm bộ nhớ tối đa cho các trường hợp sử dụng tốn nhiều bộ nhớ và sử dụng chiến lược tương thích ngược cho các trường hợp sử dụng khác.

Phương thức triển khai mẫu trong HAL máy ảnh bên ngoài

HAL máy ảnh bên ngoài được giới thiệu trong Android 9 và có thể tìm thấy trong cây nguồn tại hardware/interfaces/camera/device/3.5/. Trong Android 10, API này đã được cập nhật để bao gồm ExternalCameraDeviceSession.cpp, một phương thức triển khai API quản lý vùng đệm. HAL máy ảnh ngoài này triển khai chiến lược tiết kiệm bộ nhớ tối đa được đề cập trong Chiến lược quản lý vùng đệm trong vài trăm dòng mã C++.