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ń.
Jest to konieczne, 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 źródła: 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 poszczególnych strumieni. 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 ich przesłania. 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
używając ustawień w 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. Jest kilka 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.