Uwaga: ta strona dotyczy pakietu Aparat2. Jeśli Twoja aplikacja nie wymaga konkretnych, niskopoziomowych funkcji z Aparatu 2, zalecamy używanie AparatuX. Aparaty CameraX i Aparat 2 obsługują Androida 5.0 (poziom interfejsu API 21) i nowsze wersje.
Jedno urządzenie z Androidem może mieć kilka aparatów. Każda kamera jest
CameraDevice
,
i CameraDevice
mogą jednocześnie wysyłać więcej niż 1 strumień.
Jednym z powodów jest to, aby przesyłać jeden strumień, kolejne klatki z kamery
z CameraDevice
, jest zoptymalizowana pod kątem określonego zadania, np. wyświetlania
za pomocą wizjera, a inne mogą być wykorzystywane do robienia zdjęć lub kręcenia filmów.
rejestrowania. Strumienie działają jak równoległe potoki przetwarzające nieprzetworzone klatki.
ujętych z kamery: jedna klatka po klatce:
Przetwarzanie równoległe sugeruje, że mogą występować ograniczenia wydajności w zależności od dostępną moc obliczeniową CPU, GPU lub inny procesor. Jeśli potok nie jest w stanie nadążyć za ramkami przychodzącymi, więc zaczyna je zrzucać.
Każdy potok ma własny format wyjściowy. Nieprzetworzone dane są
automatycznie przekształcone w odpowiednie
format wyjściowy za pomocą logiki niejawnej
powiązane z każdym potokiem. Parametr CameraDevice
używany na tej stronie
Nie jest to konkretny przykład, więc najpierw wyliczamy
wszystkich dostępnych kamer, zanim przejdziesz dalej.
CameraDevice
umożliwia utworzenie
CameraCaptureSession
,
która dotyczy tylko tego elementu: CameraDevice
. CameraDevice
musi otrzymać
konfiguracji każdej nieprzetworzonej klatki z wykorzystaniem CameraCaptureSession
.
określa atrybuty aparatu, takie jak autofokus, przysłona, efekty,
i ekspozycji. Ze względu na ograniczenia sprzętowe dopuszczalna jest tylko jedna konfiguracja
aktywny czujnik w aparacie w dowolnym momencie. Jest to tzw.
active.
Przypadki użycia strumienia jednak stanowią rozszerzenie i rozszerzają dotychczasowe sposoby korzystania z usługi CameraDevice
do transmitowania sesji nagrywania, które pozwalają optymalizować
dla konkretnego przypadku użycia. Może na przykład poprawić żywotność baterii podczas optymalizacji
rozmowy wideo.
CameraCaptureSession
opisuje wszystkie możliwe potoki powiązane z
CameraDevice
Po utworzeniu sesji nie możesz dodawać ani usuwać potoków.
CameraCaptureSession
utrzymuje kolejkę:
CaptureRequest
,
, które stają się konfiguracją aktywną.
CaptureRequest
dodaje konfigurację do kolejki i wybiera jedną, więcej niż
jeden lub wszystkie dostępne potoki, które mogą odbierać ramkę z
CameraDevice
W całym cyklu życia zdjęcia możesz wysyłać wiele żądań zapisu
. Każde żądanie może zmienić aktywną konfigurację i zestaw danych wyjściowych
potoków, które odbierają nieprzetworzony obraz.
Przypadki użycia strumienia w celu zwiększenia skuteczności
Przypadki użycia dotyczące strumieniowania to sposób na poprawę wydajności przechwytywania obrazu za pomocą Aparatu 2 sesji. Dostarczają urządzeniu więcej informacji potrzebnych do dostrajania parametrów, co daje więcej możliwości korzystania z kamery do konkretnego zadania.
Ten
pozwala aparatowi optymalizować działanie sprzętu i oprogramowania kamery
na podstawie scenariusza dotyczącego użytkownika. Więcej informacji o korzystaniu ze strumienia
Przypadki: zobacz setStreamUseCase
.
Przypadki użycia strumienia pozwalają określić sposób użycia danego strumienia z kamery
bardziej szczegółowo poza ustawieniem szablonu
CameraDevice.createCaptureRequest()
Umożliwia optymalizację sprzętu aparatu
takich jak dostrajanie, tryb czujnika czy ustawienia czujnika aparatu,
kompromisów związanych z jakością i opóźnieniami, odpowiednio do konkretnych przypadków użycia.
Przykłady użycia strumienia:
DEFAULT
: obejmuje wszystkie istniejące zachowania aplikacji. Jest to odpowiednik nie dowolnego przypadku użycia strumienia.PREVIEW
: zalecany do użycia w wizjerze lub analizie obrazu w aplikacji.STILL_CAPTURE
: zoptymalizowana pod kątem wysokiej jakości nagrywania w wysokiej rozdzielczości, nie powinien utrzymać liczbę klatek podobną do podglądu.VIDEO_RECORD
: zoptymalizowany pod kątem nagrywania filmów wysokiej jakości, w tym wysokiej jakości stabilizacji obrazu, jeśli jest obsługiwana przez urządzenie i włączona przez aplikację. Ta opcja może spowodować wygenerowanie klatek wyjściowych ze znacznym opóźnieniem w stosunku do czasu rzeczywistego, aby uzyskać najwyższą jakość stabilizacji lub innego rodzaju przetwarzania.VIDEO_CALL
: opcja zalecana w przypadku długotrwałego korzystania z kamery w miejscach, gdzie zużycie energii jest ograniczone obawy.PREVIEW_VIDEO_STILL
: zalecany w przypadku aplikacji do mediów społecznościowych lub pojedynczych transmisji przypadków. To wielofunkcyjny strumień.VENDOR_START
: używany w przypadkach użycia określonych przez OEM.
Tworzenie sesji CameraCapture
Aby utworzyć sesję kamery, udostępnij jej co najmniej 1 bufor wyjściowy aplikacja może zapisywać ramki wyjściowe. Każdy bufor reprezentuje potok. Musisz zrób to, zanim zaczniesz korzystać z kamery, aby platforma mogła wewnętrznych potoków urządzenia i przydziela bufory pamięci do wysyłania ramek do potrzebnych celów wyjściowych.
Ten fragment kodu pokazuje, jak przygotować sesję kamery z 2 osobami
bufory wyjściowe, jeden należący do
SurfaceView
, a drugi do
ImageReader
Dodaję przypadek użycia strumienia PREVIEW
do previewSurface
i
STILL_CAPTURE
Korzystanie ze strumienia
Obudowa do imReaderSurface
pozwala sprzętowi urządzenia optymalizować te strumienie
dalej.
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 = findViewById<SurfaceView>(...) 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: List<Surface>){ val configs = mutableListOf<OutputConfiguration>() 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 = findViewById<SurfaceView>(...); ImageReader imageReader = ImageReader.newInstance(...); // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() Surface previewSurface = surfaceView.getHolder().getSurface(); Surface imageSurface = imageReader.getSurface(); List<Surface> 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, List<Surface> targets){ ArrayList<OutputConfiguration> 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) }
Na tym etapie nie zdefiniowano aktywnej konfiguracji kamery. Po skonfigurowaniu sesji możesz utworzyć i wysłać przechwytywanie o takie potraktowanie.
Przekształcanie danych wejściowych w momencie zapisywania ich w buforze to
zależy od typu każdej lokalizacji docelowej. Musi to być
Surface
Platformy Androida wiedzą, jak
konwertuje nieprzetworzony obraz w aktywnej konfiguracji na format odpowiedni dla
każdego miejsca docelowego. Konwersja zależy od formatu pikseli i rozmiaru
konkretny element Surface
.
Staramy się wykorzystać cały system, ale niektóre Surface
kombinacje konfiguracji mogą nie działać i powodować problemy takie jak sesja
nie zostanie utworzony, powoduje zgłoszenie błędu podczas wysyłania żądania;
pogorszenie wydajności. Ten schemat zapewnia gwarancje w odniesieniu do określonych
kombinacji parametrów urządzenia, powierzchni i parametrów żądań. Dokumentacja
createCaptureSession()
zawiera więcej informacji.
Pojedyncze żądania przechwytywania
Konfiguracja używana w przypadku każdej ramki jest zakodowana w CaptureRequest
, który jest ciągiem
do kamery. Aby utworzyć żądanie zapisu, możesz użyć jednej z
wstępnie zdefiniowane
szablony,
Możesz też użyć TEMPLATE_MANUAL
, aby mieć pełną kontrolę. Po wybraniu
, musisz podać co najmniej 1 bufor wyjściowy, którego chcesz używać
do danej prośby. Możesz używać tylko buforów, które zostały już zdefiniowane podczas przechwytywania
której chcesz użyć.
Żądania przechwytywania używają
wzorzec kompilacji
i umożliwiać programistom określenie wielu różnych
, w tym
automatyczna ekspozycja,
autofokus,
oraz
przysłona obiektywu.
Przed skonfigurowaniem pola upewnij się, że w przypadku
na tym urządzeniu, łącząc
CameraCharacteristics.getAvailableCaptureRequestKeys()
i że żądana wartość jest obsługiwana przez sprawdzenie odpowiedniego aparatu
właściwości, takich jak dostępna automatyczna ekspozycja
.
Aby utworzyć żądanie zapisu dotyczące obiektu SurfaceView
za pomocą szablonu
przeznaczone do podglądu bez żadnych modyfikacji, należy używać
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);
Po zdefiniowaniu żądania przechwytywania możesz wysłać do sesji kamery:
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);
Po umieszczeniu ramki wyjściowej w określonym buforze przechwytywanie zostanie przechwycone
oddzwanianie
W wielu przypadkach dodatkowe wywołania zwrotne, takie jak
ImageReader.OnImageAvailableListener
,
jest wyzwalany po przetworzeniu klatki, którą zawiera. Jest na
w którym możesz pobrać dane obrazu z określonego bufora.
Powtórz żądania przechwytywania
Wysyłanie prośby o pojedynczą kamerę jest proste, ale w przypadku wyświetlenia obrazu na żywo ani podglądu, nie są raczej przydatne. W takim przypadku musisz otrzymać ciągłym strumieniem klatek, a nie tylko jedną. Następujący fragment kodu: pokazuje, jak dodać powtarzająca się prośba do sesji:
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);
Powtarzające się żądanie nagrania powoduje, że aparat stale rejestruje
zgodnie z ustawieniami w tym artykule: CaptureRequest
. Interfejs API Camera2
Użytkownicy mogą też rejestrować
filmy z kamery, wysyłając
powtarzanie elementu CaptureRequests
jak w tym
Przykład z Aparatu2
w repozytorium GitHub. Może też renderować filmy w zwolnionym tempie, rejestrując
film w szybkim (zwolnionym tempie) z powtarzającą się serią CaptureRequests
co widać w przykładowej aplikacji Camera2 w zwolnionym tempie.
w GitHubie.
Przeplot żądań przechwytywania
Aby wysłać drugie żądanie zapisu, gdy powtarzająca się prośba o nagranie jest aktywna, np. aby wyświetlić wizjer i umożliwić użytkownikom zrobienie zdjęcia, nie trzeba zatrzymać powtarzające się żądania. Zamiast tego nagrywasz niepowtarzający się film. .
Każdy używany bufor wyjściowy musi być skonfigurowany w ramach sesji kamery przy tworzeniu sesji. Powtarzające się żądania mają niższy priorytet niż żądania jednoklatkowe lub seryjne, które umożliwiają działanie następującego przykładu:
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);
Takie podejście ma jednak wadę: nie wiadomo, kiedy tylko jedno żądanie. Jeśli na wykresie A to powtarzający się element a B to żądanie przechwytywania pojedynczej klatki. W ten sposób sesja przetwarza kolejkę żądań:
Nie ma gwarancji czasu oczekiwania między ostatnim cyklicznym żądaniem z A przed aktywacją żądania B i przy następnym użyciu użycia A dlatego możesz zauważyć kilka pominiętych klatek. Są pewne rzeczy, co możesz zrobić, aby zniwelować ten problem:
Dodaj docelowe dane wyjściowe z żądania A, by wysłać żądanie B. Dzięki temu, gdy Ramka B jest gotowa i skopiowana do docelowych elementów wyjściowych A. Jest to niezbędne np. przy tworzeniu zrzutów filmu, aby zapewnić stałą liczbę klatek. W poprzednim kodzie dodajesz
singleRequest.addTarget(previewSurface)
.Użyj kombinacji szablonów przeznaczonych do tego konkretnego scenariusza, takich jak zero-opóźnienie.