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 HAL3 (không bắt buộc) cho máy ảnh, cho phép bạn triển khai logic quản lý vùng đệm để đạt được bộ nhớ khác nhau và nắm bắt sự đánh đổi độ trễ khi triển khai lớp trừu tượng phần cứng (HAL) cho máy ảnh.

HAL của máy ảnh yêu cầu N yêu cầu (trong đó N bằng độ sâu của quy trình) được đưa vào hàng đợi trong quy trình, nhưng thường không yêu cầu tất cả N bộ đệ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, API quản lý vùng đệm HAL3 cho máy ảnh cho phép tách 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 9 cấp trở xuống

Hình 1. Giao diện HAL (Lớp trừu tượng phần cứng) cho máy ảnh trong Android 9 trở xuống

Quản lý bộ đệm trong Android 10

Hình 2. Giao diện HAL (Lớp trừu tượng phần cứng) cho máy ảnh trong Android 10 sử dụng API quản lý vùng đệ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ý vùng đệm HAL3 cho máy ảnh, các yêu cầu chụp từ khung máy ảnh không chứa vùng đệ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 vào requestStreamBuffers được chuyển đổi tuần tự trong dịch vụ máy ảnh, nên lớp trừu tượng phần cứng (HAL) của máy ảnh nên sử dụng một luồng chuyên dụng có mức độ ưu tiên cao để 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ờ chứa nhiều vùng đệm hơn mức yêu cầu, thì việc triển khai HAL (Lớp trừu tượng phần cứng) cho máy ảnh không cần gọi phương thức returnStreamBuffers.

tín hiệuStreamFlush

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 (Lớp trừu tượng phần cứng) của 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 để loại bỏ 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 cho máy ảnh nên sử dụng đối số streamConfigCounter để xác định xem lệnh gọi signalStreamFlush có đến sau 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 các API quản lý vùng đệm để triển khai logic quản lý vùng đệm, hãy cân nhắc các thay đổi có thể xảy ra sau đây về hành vi đối với máy ảnh và máy ảnh và dùng lớp trừu tượng phần cứng (HAL):

  • 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 tại một thời điểm (giá trị này được HAL của 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 số 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 sẽ tăng tương ứng với số lượng vùng đệm được yêu cầu trong mỗi lệnh gọi.
    • Ứng dụng đang lưu vùng đệm và đang bận xử lý. Điều này có thể khiến các yêu cầu liên quan đến bộ đệm chậm lại hoặc hết thời gian chờ do thiếu bộ đệm hoặc CPU đang 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ó.
  • Tiết kiệm bộ nhớ tối đa: HAL của máy ảnh chỉ yêu cầu các vùng đệm đầu ra ngay trước khi cần lấp đầy một vùng đệm đầu ra. 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 cách 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++.