Lưu ý: Trang này đề cập đến gói Camera2. Trừ phi ứng dụng của bạn yêu cầu các tính năng cụ thể ở cấp độ thấp từ Camera2, bạn nên sử dụng CameraX. Cả CameraX và Camera2 đều hỗ trợ Android 5.0 (API cấp 21) trở lên.
Một thiết bị chạy Android có thể có nhiều máy ảnh. Mỗi camera là một
CameraDevice
!
và CameraDevice
có thể xuất nhiều luồng cùng lúc.
Một lý do để làm điều này là để một luồng, các khung hình máy ảnh tuần tự xuất hiện
từ CameraDevice
được tối ưu hoá cho một tác vụ cụ thể, chẳng hạn như hiển thị
kính ngắm, trong khi những người khác có thể dùng để chụp ảnh hoặc tạo video
ghi lại.Các luồng hoạt động như các quy trình song song xử lý khung hình thô
ra khỏi máy ảnh lần lượt từng khung hình:
![](https://developer.android.com/static/images/training/camera/camera2/capture-sessions-requests-1.png?authuser=19&hl=vi)
Quá trình xử lý song song cho thấy có thể có giới hạn hiệu suất phụ thuộc vào công suất xử lý có sẵn từ CPU, GPU hoặc bộ xử lý khác. Nếu một quy trình không thể theo kịp các khung hình đến, nó sẽ bắt đầu bỏ các khung hình đó.
Mỗi quy trình có định dạng đầu ra riêng. Dữ liệu thô được thu thập là
đã được tự động chuyển đổi thành
định dạng đầu ra bằng logic ngầm ẩn
liên kết với mỗi quy trình. CameraDevice
được sử dụng trong toàn bộ thời gian của trang này
mã mẫu là không cụ thể, vì vậy trước tiên bạn phải liệt kê
tất cả các máy ảnh có sẵn trước khi tiếp tục.
Bạn có thể sử dụng CameraDevice
để tạo
CameraCaptureSession
,
dành riêng cho CameraDevice
đó. CameraDevice
phải nhận được
cấu hình khung cho từng khung thô bằng cách sử dụng CameraCaptureSession
. Chiến lược phát hành đĩa đơn
cấu hình chỉ định các thuộc tính của máy ảnh như tự động lấy nét, khẩu độ, hiệu ứng,
và độ phơi sáng. Do hạn chế về phần cứng, nên chỉ có một cấu hình duy nhất
hoạt động trong cảm biến của máy ảnh tại một thời điểm bất kỳ, được gọi là
đang hoạt động.
Tuy nhiên, các Trường hợp sử dụng phát trực tuyến giúp nâng cao và mở rộng các cách sử dụng CameraDevice
trước đây
để phát trực tuyến các phiên chụp, cho phép bạn tối ưu hóa luồng máy ảnh cho
trường hợp sử dụng cụ thể. Ví dụ: tính năng này có thể cải thiện thời lượng pin khi tối ưu hoá
cuộc gọi video.
CameraCaptureSession
mô tả tất cả các quy trình có thể liên kết với
CameraDevice
Khi một phiên được tạo, bạn không thể thêm hoặc xoá quy trình.
CameraCaptureSession
duy trì một hàng đợi
CaptureRequest
!
trở thành cấu hình hoạt động.
CaptureRequest
thêm cấu hình vào hàng đợi và chọn một cấu hình, hơn
một hoặc tất cả các quy trình có sẵn để nhận khung từ
CameraDevice
Bạn có thể gửi nhiều yêu cầu chụp trong suốt thời gian chụp
phiên hoạt động. Mỗi yêu cầu có thể thay đổi cấu hình đang hoạt động và tập hợp đầu ra
các đường ống nhận hình ảnh thô.
Sử dụng các trường hợp sử dụng phát trực tuyến để đạt được hiệu suất tốt hơn
Các trường hợp sử dụng phát trực tuyến là một cách để cải thiện hiệu suất chụp của Camera2 phiên hoạt động. Chúng cung cấp thêm thông tin cho thiết bị phần cứng để điều chỉnh thông số, mang lại trải nghiệm camera tốt hơn cho tác vụ cụ thể của bạn.
Chiến dịch này
cho phép thiết bị máy ảnh tối ưu hoá quy trình phần cứng và phần mềm của máy ảnh
dựa trên tình huống của người dùng
đối với từng luồng. Để biết thêm thông tin về việc sử dụng luồng
Các trường hợp, xem setStreamUseCase
.
Các trường hợp sử dụng phát trực tuyến cho phép bạn chỉ định cách sử dụng một luồng camera cụ thể trong
chi tiết hơn, ngoài việc thiết lập mẫu trong
CameraDevice.createCaptureRequest()
. Việc này giúp phần cứng của máy ảnh tối ưu hoá
các thông số, chẳng hạn như cách điều chỉnh, chế độ cảm biến hoặc cài đặt cảm biến của máy ảnh, dựa trên
chất lượng hoặc độ trễ phù hợp với các trường hợp sử dụng cụ thể.
Các trường hợp sử dụng phát trực tuyến bao gồm:
DEFAULT
: Bao gồm mọi hành vi hiện có của ứng dụng. Điều này tương đương với đặt bất kỳ Trường hợp sử dụng phát trực tuyến nào.PREVIEW
: Nên dùng cho kính ngắm hoặc phân tích hình ảnh trong ứng dụng.STILL_CAPTURE
: Được tối ưu hoá để chụp ảnh có độ phân giải cao chất lượng cao, và không duy trì tốc độ khung hình giống như bản xem trước.VIDEO_RECORD
: Được tối ưu hoá để quay video chất lượng cao, bao gồm cả video chất lượng cao ổn định hình ảnh, nếu được thiết bị hỗ trợ và được ứng dụng bật. Lựa chọn này có thể tạo ra các khung hình đầu ra có độ trễ đáng kể so với thời gian thực, để cho phép ổn định chất lượng cao nhất hoặc xử lý khác.VIDEO_CALL
: Nên dùng cho các camera sử dụng trong thời gian dài ở nơi tiêu hao pin mối lo ngại.PREVIEW_VIDEO_STILL
: Nên dùng cho các ứng dụng mạng xã hội hoặc một lần phát trực tuyến trường hợp. Đây là một luồng đa năng.VENDOR_START
: Dùng cho các trường hợp sử dụng do OEM (Nhà sản xuất thiết bị gốc) xác định.
Tạo CameraCaptureSession
Để tạo một phiên máy ảnh, hãy cung cấp cho phiên đó một hoặc nhiều vùng đệm đầu ra ứng dụng của bạn có thể ghi các khung đầu ra vào đó. Mỗi vùng đệm đại diện cho một quy trình. Bạn phải hãy làm việc này trước khi bắt đầu sử dụng camera để khung có thể định cấu hình kênh nội bộ của thiết bị và phân bổ vùng đệm bộ nhớ để gửi khung cho các mục tiêu đầu ra cần thiết.
Đoạn mã sau đây cho biết cách chuẩn bị một phiên camera với hai
vùng đệm đầu ra, một vùng đệm thuộc về
SurfaceView
và một URL khác dẫn đến một
ImageReader
. Thêm PREVIEW
trường hợp sử dụng phát trực tuyến vào previewSurface
và
STILL_CAPTURE
Sử dụng luồng
Trường hợp imReaderSurface
cho phép phần cứng thiết bị tối ưu hoá những luồng này ngay cả khi
thực tế hơn.
Kotlin
// Retrieve the target surfaces, which might be coming from a number of places: // 1. SurfaceView, if you want to display the image directly to the user // 2. ImageReader, if you want to read each frame or perform frame-by-frame // analysis // 3. OpenGL Texture or TextureView, although discouraged for maintainability reasons // 4. RenderScript.Allocation, if you want to do parallel processing val surfaceView = findViewByIdS<urfaceView(>...) val imageReader = ImageReader.newInstance(...) // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() val previewSurface = surfaceView.holder.surface val imReaderSurface = imageReader.surface val targets = listOf(previewSurface, imReaderSurface) // Create a capture session using the predefined targets; this also involves // defining the session state callback to be notified of when the session is // ready // Setup Stream Use Case while setting up your Output Configuration. @RequiresApi(Build.VERSION_CODES.TIRAMISU) fun configureSession(device: CameraDevice, targets: ListS<urface)>{ val configs = mutableListOfO<utputConfiguration(>) val streamUseCase = CameraMetadata .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL targets.forEach { val config = OutputConfiguration(it) config.streamUseCase = streamUseCase.toLong() configs.add(config) } ... device.createCaptureSession(session) }
Java
// Retrieve the target surfaces, which might be coming from a number of places: // 1. SurfaceView, if you want to display the image directly to the user // 2. ImageReader, if you want to read each frame or perform frame-by-frame analysis // 3. RenderScript.Allocation, if you want to do parallel processing // 4. OpenGL Texture or TextureView, although discouraged for maintainability reasons Surface surfaceView = findViewByIdS<urfaceView(>...); ImageReader imageReader = ImageReader.newInstance(...); // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() Surface previewSurface = surfaceView.getHolder().getSurface(); Surface imageSurface = imageReader.getSurface(); ListS<urface >targets = Arrays.asList(previewSurface, imageSurface); // Create a capture session using the predefined targets; this also involves defining the // session state callback to be notified of when the session is ready private void configureSession(CameraDevice device, ListS<urface >targets){ ArrayListO<utputConfiguration >configs= new ArrayList() String streamUseCase= CameraMetadata .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL for(Surface s : targets){ OutputConfiguration config = new OutputConfiguration(s) config.setStreamUseCase(String.toLong(streamUseCase)) configs.add(config) } device.createCaptureSession(session) }
Đến đây, bạn chưa xác định được cấu hình đang hoạt động của camera. Khi phiên được định cấu hình, bạn có thể tạo và gửi ảnh chụp yêu cầu thực hiện việc đó.
Phép biến đổi được áp dụng cho dữ liệu đầu vào khi chúng được ghi vào bộ đệm là
được xác định theo loại của mỗi mục tiêu, điều này phải là
Surface
. Khung Android biết cách
chuyển đổi hình ảnh thô trong cấu hình đang hoạt động sang định dạng phù hợp cho
từng mục tiêu. Việc chuyển đổi được kiểm soát bởi định dạng và kích thước pixel của
Surface
cụ thể.
Khung này sẽ cố gắng hết sức, nhưng một số Surface
có thể không hoạt động, gây ra các sự cố như phiên
không được tạo, báo lỗi thời gian chạy khi bạn gửi yêu cầu, hoặc
suy giảm hiệu suất. Khung này đưa ra đảm bảo cho các
các tổ hợp tham số thiết bị, giao diện và yêu cầu. Tài liệu về
createCaptureSession()
sẽ cung cấp thêm thông tin.
CaptureRequests duy nhất
Cấu hình dùng cho mỗi khung được mã hoá trong CaptureRequest
,
gửi đến máy ảnh. Để tạo một yêu cầu chụp ảnh, bạn có thể sử dụng một trong
được xác định trước
template (mẫu),
hoặc bạn có thể sử dụng TEMPLATE_MANUAL
để có toàn quyền kiểm soát. Khi bạn chọn một
mẫu, bạn cần cung cấp một hoặc nhiều vùng đệm đầu ra để sử dụng cùng
yêu cầu. Bạn chỉ có thể sử dụng vùng đệm đã được xác định khi chụp
mà bạn dự định sử dụng.
Yêu cầu chụp ảnh sử dụng
trình tạo mô hình
đồng thời mang lại cho nhà phát triển cơ hội đặt nhiều kiểu
các lựa chọn bao gồm
tự động phơi sáng,
tự động lấy nét,
và
khẩu độ ống kính.
Trước khi đặt một trường, hãy đảm bảo rằng có sẵn tuỳ chọn cụ thể cho
thiết bị bằng cách gọi
CameraCharacteristics.getAvailableCaptureRequestKeys()
và chọn camera phù hợp để hỗ trợ giá trị mong muốn
đặc tính, chẳng hạn như độ phơi sáng tự động có sẵn
chế độ.
Để tạo yêu cầu chụp cho SurfaceView
bằng mẫu
được thiết kế để xem trước mà không sửa đổi, hãy sử dụng
CameraDevice.TEMPLATE_PREVIEW
:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) captureRequest.addTarget(previewSurface)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest.Builder captureRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); captureRequest.addTarget(previewSurface);
Khi xác định được yêu cầu chụp, bạn hiện có thể gửi video đó vào phiên camera:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest = ... // from CameraDevice.createCaptureRequest() // The first null argument corresponds to the capture callback, which you // provide if you want to retrieve frame metadata or keep track of failed capture // requests that can indicate dropped frames; the second null argument // corresponds to the Handler used by the asynchronous callback, which falls // back to the current thread's looper if null session.capture(captureRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest captureRequest = ...; // from CameraDevice.createCaptureRequest() // The first null argument corresponds to the capture callback, which you // provide if you want to retrieve frame metadata or keep track of failed // capture // requests that can indicate dropped frames; the second null argument // corresponds to the Handler used by the asynchronous callback, which falls // back to the current thread's looper if null session.capture(captureRequest.build(), null, null);
Khi một khung hình đầu ra được đưa vào vùng đệm cụ thể, lệnh
lệnh gọi lại
được kích hoạt. Trong nhiều trường hợp, các lệnh gọi lại bổ sung, chẳng hạn như
ImageReader.OnImageAvailableListener
!
được kích hoạt khi khung có chứa được xử lý. Bây giờ là lúc
điểm này mà bạn có thể truy xuất dữ liệu hình ảnh từ vùng đệm được chỉ định.
Lặp lại CaptureRequests
Các yêu cầu về camera đơn rất dễ thực hiện, nhưng để hiển thị một video phát trực tiếp bản xem trước hoặc video đều không hữu ích lắm. Trong trường hợp đó, bạn cần nhận được luồng khung hình liên tục chứ không chỉ một luồng đơn. Đoạn mã sau đây cho biết cách thêm yêu cầu lặp lại vào phiên họp:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest = ... // from CameraDevice.createCaptureRequest() // This keeps sending the capture request as frequently as possible until // the // session is torn down or session.stopRepeating() is called // session.setRepeatingRequest(captureRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest captureRequest = ...; // from CameraDevice.createCaptureRequest() // This keeps sending the capture request as frequently as possible until the // session is torn down or session.stopRepeating() is called // session.setRepeatingRequest(captureRequest.build(), null, null);
Yêu cầu chụp lặp lại giúp thiết bị máy ảnh liên tục chụp
bằng cách sử dụng chế độ cài đặt trong CaptureRequest
được cung cấp. API Camera2
cho phép người dùng quay video từ máy ảnh bằng cách gửi
lặp lại CaptureRequests
như trong nội dung này
Mẫu Camera2
kho lưu trữ trên GitHub. Ứng dụng này cũng có thể kết xuất video chuyển động chậm bằng cách chụp
video tốc độ cao (chuyển động chậm) sử dụng loạt ảnh lặp lại CaptureRequests
như giới thiệu trong ứng dụng mẫu video chuyển động chậm Camera2
trên GitHub.
Xen kẽ CaptureRequests
Để gửi yêu cầu chụp thứ hai trong khi yêu cầu chụp lặp lại đang hoạt động, hiển thị kính ngắm và cho phép người dùng chụp ảnh, bạn không cần dừng yêu cầu lặp lại đang diễn ra. Thay vào đó, bạn tạo một ảnh chụp không lặp lại trong khi yêu cầu lặp lại tiếp tục chạy.
Bạn cần định cấu hình mọi vùng đệm đầu ra được sử dụng trong phiên hoạt động của máy ảnh khi phiên được tạo lần đầu tiên. Các yêu cầu lặp lại có mức độ ưu tiên thấp hơn yêu cầu một khung hình hoặc yêu cầu hàng loạt để cho phép mẫu sau hoạt động:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback // Create the repeating request and dispatch it val repeatingRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_PREVIEW) repeatingRequest.addTarget(previewSurface) session.setRepeatingRequest(repeatingRequest.build(), null, null) // Some time later... // Create the single request and dispatch it // NOTE: This can disrupt the ongoing repeating request momentarily val singleRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE) singleRequest.addTarget(imReaderSurface) session.capture(singleRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback // Create the repeating request and dispatch it CaptureRequest.Builder repeatingRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); repeatingRequest.addTarget(previewSurface); session.setRepeatingRequest(repeatingRequest.build(), null, null); // Some time later... // Create the single request and dispatch it // NOTE: This can disrupt the ongoing repeating request momentarily CaptureRequest.Builder singleRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); singleRequest.addTarget(imReaderSurface); session.capture(singleRequest.build(), null, null);
Tuy nhiên, phương pháp này có nhược điểm: bạn không biết chính xác thời điểm thì yêu cầu duy nhất xảy ra. Trong hình sau, nếu A là đoạn lặp lại và B là yêu cầu chụp một khung hình, đây là cách phiên xử lý hàng đợi yêu cầu:
![](https://developer.android.com/static/images/training/camera/camera2/capture-sessions-requests-2.png?authuser=19&hl=vi)
Chúng tôi không đảm bảo về độ trễ giữa lần yêu cầu lặp lại gần đây nhất từ A trước khi yêu cầu B kích hoạt và vào lần tiếp theo A được sử dụng nên bạn có thể sẽ gặp một số khung hình bị bỏ qua. Có một số điều bạn có thể làm để giảm thiểu vấn đề này:
Thêm các mục tiêu đầu ra từ yêu cầu A vào yêu cầu B. Bằng cách đó, khi Khung của B đã sẵn sàng, khung này được sao chép vào mục tiêu đầu ra của A. Ví dụ: đây là tính năng cần thiết khi tạo ảnh chụp nhanh video để duy trì tốc độ khung hình ổn định. Trong mã trước, bạn thêm
singleRequest.addTarget(previewSurface)
trước khi tạo yêu cầu.Sử dụng kết hợp các mẫu được thiết kế để hoạt động trong trường hợp cụ thể này, chẳng hạn như zero-Shutter-lag.