Uwaga: ta strona dotyczy pakietu Camera2. Jeśli Twoja aplikacja nie wymaga określonych funkcji niskiego poziomu z Camera2, zalecamy używanie CameraX. Zarówno CameraX, jak i Camera2 obsługują Androida 5.0 (poziom interfejsu API 21) i nowsze wersje.
Na urządzeniach z Androidem kamery i podglądy z nich nie zawsze są w tej samej orientacji.
Aparat jest umieszczony w stałym miejscu na urządzeniu, niezależnie od tego, czy jest to telefon, tablet czy komputer. Gdy zmienia się orientacja urządzenia, zmienia się też orientacja kamery.
W rezultacie aplikacje aparatu zwykle zakładają stałą zależność między orientacją urządzenia a formatem obrazu podglądu z aparatu. Gdy telefon jest w orientacji pionowej, podgląd z aparatu jest wyższy niż szerszy. Gdy telefon (i aparat) jest obrócony do orientacji poziomej, podgląd z aparatu powinien być szerszy niż wyższy.
Te założenia są jednak podważane przez nowe formaty, takie jak urządzenia składane, oraz tryby wyświetlania, takie jak tryb wielu okien i tryb wielu wyświetlaczy. Urządzenia składane zmieniają rozmiar wyświetlacza i format obrazu bez zmiany orientacji. Tryb wielu okien ogranicza aplikacje aparatu do części ekranu, skalując podgląd z aparatu niezależnie od orientacji urządzenia. Tryb wielu wyświetlaczy umożliwia korzystanie z dodatkowych wyświetlaczy, które mogą nie być w tej samej orientacji co wyświetlacz główny.
Orientacja aparatu
Definicja zgodności Androida określa, że czujnik obrazu aparatu „MUSI być zorientowany tak, aby dłuższy wymiar aparatu był zgodny z dłuższym wymiarem ekranu. Oznacza to, że gdy urządzenie jest trzymane w orientacji poziomej, aparaty MUSZĄ robić zdjęcia w orientacji poziomej. Dotyczy to wszystkich urządzeń, niezależnie od ich naturalnej orientacji, czyli zarówno urządzeń z orientacją poziomą, jak i pionową”.
Układ aparatu i ekranu maksymalizuje obszar wyświetlania wizjera aparatu w aplikacji aparatu. Ponadto czujniki obrazu zwykle generują dane w poziomym formacie obrazu, najczęściej 4:3.
Naturalna orientacja czujnika aparatu to pozioma. Na rysunku 1 czujnik przedniego aparatu (aparatu skierowanego w tę samą stronę co wyświetlacz) jest obrócony o 270 stopni względem telefonu, aby spełniać wymagania definicji zgodności Androida.
Aby udostępnić aplikacjom informacje o obrocie czujnika, interfejs API camera2 zawiera stałą SENSOR_ORIENTATION. W przypadku większości telefonów i tabletów urządzenie zgłasza orientację czujnika 270 stopni w przypadku przednich aparatów i 90 stopni (punkt widzenia z tyłu urządzenia) w przypadku tylnych aparatów, co wyrównuje długą krawędź czujnika z długą krawędzią urządzenia. Kamery laptopów zwykle zgłaszają orientację czujnika wynoszącą 0 lub 180 stopni.
Czujniki obrazu aparatu generują dane (bufor obrazu) w naturalnej orientacji czujnika (poziomej), dlatego bufor obrazu musi zostać obrócony o liczbę stopni określoną przez SENSOR_ORIENTATION, aby podgląd z aparatu był wyświetlany w naturalnej orientacji urządzenia. W przypadku przednich aparatów obrót jest wykonywany w lewo, a w przypadku tylnych – w prawo.
Na przykład w przypadku przedniego aparatu na rysunku 1 bufor obrazu wygenerowany przez czujnik aparatu wygląda tak:
Obraz musi zostać obrócony o 270 stopni w lewo, aby orientacja podglądu była zgodna z orientacją urządzenia:
Tylny aparat wygeneruje bufor obrazu o takiej samej orientacji jak poprzedni bufor, ale wartość SENSOR_ORIENTATION wynosi 90 stopni. W efekcie bufor jest obracany o 90 stopni w prawo.
Obracanie urządzenia
Obrót urządzenia to liczba stopni, o jaką urządzenie jest obrócone względem swojej naturalnej orientacji. Na przykład telefon w orientacji poziomej ma obrót urządzenia o 90 lub 270 stopni, w zależności od kierunku obrotu.
Bufor obrazu czujnika aparatu musi być obrócony o taką samą liczbę stopni jak urządzenie (oprócz stopni orientacji czujnika), aby podgląd z aparatu był wyświetlany w pionie.
Obliczanie orientacji
Prawidłowa orientacja podglądu z aparatu uwzględnia orientację czujnika i obrót urządzenia.
Ogólny obrót bufora obrazu czujnika można obliczyć za pomocą tego wzoru:
rotation = (sensorOrientationDegrees - deviceOrientationDegrees * sign + 360) % 360
gdzie sign to 1 w przypadku przednich aparatów i -1 w przypadku tylnych aparatów.
W przypadku aparatów przednich bufor obrazu jest obracany w lewo (od naturalnej orientacji czujnika). W przypadku tylnych aparatów bufor obrazu czujnika jest obracany zgodnie z ruchem wskazówek zegara.
Wyrażenie deviceOrientationDegrees * sign + 360 konwertuje obrót urządzenia z ruchu przeciwnego do ruchu wskazówek zegara na ruch zgodny z ruchem wskazówek zegara w przypadku aparatów tylnych (np. konwertuje 270 stopni przeciwnie do ruchu wskazówek zegara na 90 stopni zgodnie z ruchem wskazówek zegara). Operacja modulo skaluje wynik do wartości mniejszej niż 360 stopni (np. skaluje obrót o 540 stopni do 180 stopni).
Różne interfejsy API raportują obrót urządzenia w różny sposób:
Display#getRotation()określa obrót urządzenia w kierunku przeciwnym do ruchu wskazówek zegara (z punktu widzenia użytkownika). Ta wartość jest wstawiana do wcześniejszego wzoru w niezmienionej postaci.OrientationEventListener#onOrientationChanged()zwraca obrót urządzenia w prawo (z punktu widzenia użytkownika). Zaneguj wartość, aby użyć jej we wcześniejszej formule.
Przednie aparaty
Oto bufor obrazu wygenerowany przez czujnik aparatu na rysunku 4:
Bufor musi zostać obrócony o 270 stopni w lewo, aby dostosować go do orientacji czujnika (patrz Orientacja aparatu powyżej):
Następnie bufor jest obracany o kolejne 90 stopni w lewo, aby uwzględnić obrót urządzenia, co daje prawidłową orientację podglądu z kamery na rysunku 4:
Oto aparat obrócony w prawo do orientacji poziomej:
Oto bufor obrazu:
Bufor musi zostać obrócony o 270 stopni w lewo, aby dostosować go do orientacji czujnika:
Następnie bufor jest obracany o kolejne 270 stopni w kierunku przeciwnym do ruchu wskazówek zegara, aby uwzględnić obrót urządzenia:
Tylne aparaty
Tylne aparaty mają zwykle orientację czujnika wynoszącą 90 stopni (patrząc od tyłu urządzenia). Podczas orientowania podglądu z aparatu bufor obrazu z czujnika jest obracany zgodnie z ruchem wskazówek zegara o wartość obrotu czujnika (a nie przeciwnie do ruchu wskazówek zegara, jak w przypadku aparatów przednich), a następnie bufor obrazu jest obracany przeciwnie do ruchu wskazówek zegara o wartość obrotu urządzenia.
Oto bufor obrazu z czujnika aparatu na rysunku 12:
Bufor musi zostać obrócony o 90 stopni w prawo, aby dostosować go do orientacji czujnika:
Następnie bufor jest obracany o 270 stopni w lewo, aby uwzględnić obrót urządzenia:
Format obrazu
Format obrazu zmienia się, gdy zmienia się orientacja urządzenia, ale także gdy urządzenia składane są składane i rozkładane, gdy zmienia się rozmiar okien w środowiskach z wieloma oknami i gdy aplikacje otwierają się na dodatkowych wyświetlaczach.
Bufor obrazu czujnika aparatu musi być zorientowany i skalowany tak, aby odpowiadał orientacji i formatowi obrazu elementu interfejsu wizjera, gdy interfejs dynamicznie zmienia orientację – niezależnie od tego, czy urządzenie zmienia orientację.
Jeśli w przypadku nowych formatów lub środowisk z wieloma oknami lub wyświetlaczami aplikacja zakłada, że podgląd z aparatu ma taką samą orientację jak urządzenie (pionową lub poziomą), podgląd może być nieprawidłowo zorientowany lub przeskalowany.
Na rysunku 16 aplikacja błędnie założyła, że urządzenie zostało obrócone o 90 stopni w lewo, i obróciła podgląd o tę samą wartość.
Na rysunku 17 aplikacja nie dostosowała formatu obrazu bufora, aby umożliwić prawidłowe skalowanie do nowych wymiarów elementu interfejsu podglądu z aparatu.
Aplikacje do obsługi aparatu o stałej orientacji zwykle mają problemy na urządzeniach składanych i innych urządzeniach z dużym ekranem, takich jak laptopy:
Na rysunku 18 interfejs aplikacji aparatu jest obrócony, ponieważ orientacja aplikacji jest ograniczona do pionowej. Obraz w wizjerze jest prawidłowo zorientowany względem matrycy aparatu.
Tryb portretowy wstawki
Aplikacje do obsługi aparatu, które nie obsługują trybu wielu okien (resizeableActivity="false") i ograniczają orientację (screenOrientation="portrait" lub screenOrientation="landscape"), można umieścić w trybie portretowym na urządzeniach z dużym ekranem, aby prawidłowo ustawić podgląd z aparatu.
Wstawiony tryb portretowy wyświetla aplikacje tylko w orientacji pionowej, nawet jeśli format obrazu jest poziomy. Aplikacje działające tylko w orientacji poziomej są wyświetlane w trybie letterbox w orientacji poziomej, mimo że format obrazu wyświetlacza jest pionowy. Obraz z kamery jest obracany, aby dopasować go do interfejsu aplikacji, przycinany do formatu obrazu podglądu z kamery, a następnie skalowany, aby wypełnić podgląd.
Tryb portretowy z wstawką jest aktywowany, gdy format obrazu czujnika obrazu aparatu i format obrazu głównej aktywności aplikacji nie są zgodne.
Na rysunku 19 aplikacja aparatu, która działa tylko w orientacji pionowej, została obrócona, aby interfejs był wyświetlany na ekranie laptopa w orientacji pionowej. Aplikacja jest wyświetlana w formacie letterbox ze względu na różnicę w formacie obrazu między aplikacją w orientacji pionowej a wyświetlaczem w orientacji poziomej. Obraz podglądu z aparatu został obrócony, aby skompensować obrót interfejsu aplikacji (z powodu trybu portretowego), a obraz został przycięty i przeskalowany, aby pasował do orientacji pionowej, co zmniejszyło pole widzenia.
Obracanie, przycinanie, skalowanie
Tryb portretowy w ramce jest wywoływany w przypadku aplikacji aparatu działającej tylko w trybie portretowym na wyświetlaczu o poziomym formacie obrazu:
Aplikacja jest wyświetlana w orientacji pionowej z czarnymi pasami:
Obraz z kamery jest obracany o 90 stopni, aby dostosować go do zmiany orientacji aplikacji:
Obraz jest przycinany do formatu obrazu podglądu z aparatu, a następnie skalowany, aby wypełnić podgląd (pole widzenia jest zmniejszone):
Na urządzeniach składanych czujnik aparatu może być ustawiony w orientacji pionowej, podczas gdy format obrazu wyświetlacza jest poziomy:
Podgląd z aparatu jest obracany, aby dostosować go do orientacji czujnika, więc obraz w wizjerze jest prawidłowo zorientowany, ale aplikacja działająca tylko w trybie pionowym jest obrócona na bok.
W trybie portretowym z wstawką wystarczy dodać pasy do aplikacji w orientacji pionowej, aby prawidłowo ustawić aplikację i podgląd z aparatu:
Interfejs API
Od Androida 12 (poziom API 31) aplikacje mogą też bezpośrednio kontrolować tryb portretowy z wcięciem za pomocą właściwości SCALER_ROTATE_AND_CROP klasy CaptureRequest.
Wartością domyślną jest SCALER_ROTATE_AND_CROP_AUTO, co umożliwia systemowi wywoływanie trybu portretowego w ramce.
SCALER_ROTATE_AND_CROP_90 to zachowanie trybu portretowego z obrazem wstawionym, opisane wcześniej.
Nie wszystkie urządzenia obsługują wszystkie wartości SCALER_ROTATE_AND_CROP. Listę obsługiwanych wartości znajdziesz w CameraCharacteristics#SCALER_AVAILABLE_ROTATE_AND_CROP_MODES.
CameraX
Biblioteka Jetpack CameraX ułatwia tworzenie wizjera aparatu, który dostosowuje się do orientacji czujnika i obrotu urządzenia.
Element układu PreviewView tworzy podgląd z kamery, automatycznie dostosowując orientację czujnika, obrót urządzenia i skalowanie.
PreviewView zachowuje format obrazu z kamery, stosując typ skalowania FILL_CENTER, który wyśrodkowuje obraz, ale może go przyciąć, aby dopasować go do wymiarów PreviewView. Aby dodać czarne pasy do obrazu z kamery, ustaw typ skali na FIT_CENTER.
Aby poznać podstawy tworzenia podglądu z kamery za pomocą PreviewView, przeczytaj artykuł Implementowanie podglądu.
Pełną przykładową implementację znajdziesz w repozytorium CameraXBasic na GitHubie.
CameraViewfinder
Podobnie jak w przypadku podglądu biblioteka CameraViewfinder udostępnia zestaw narzędzi, które upraszczają tworzenie podglądu z aparatu. Nie zależy od CameraX Core, dzięki czemu można go bezproblemowo zintegrować z istniejącą bazą kodu Camera2.
Zamiast używać bezpośrednio Surface, możesz użyć widżetu CameraViewfinder, aby wyświetlić przekaz z kamery w Camera2.
CameraViewfinder wewnętrznie używa TextureView lub SurfaceView do wyświetlania przekazu z kamery i stosuje do nich wymagane przekształcenia, aby prawidłowo wyświetlać wizjer. Obejmuje to korektę formatu obrazu,
skali i obrotu.
Aby poprosić o powierzchnię z obiektu CameraViewfinder, musisz utworzyć ViewfinderSurfaceRequest.
To żądanie zawiera wymagania dotyczące rozdzielczości powierzchni i informacje o urządzeniu z aparatem z CameraCharacteristics.
Wywołanie requestSurfaceAsync() wysyła żądanie do dostawcy platformy, którym jest TextureView lub SurfaceView, i otrzymuje ListenableFuture Surface.
Wywołanie metody markSurfaceSafeToRelease() powiadamia dostawcę powierzchni, że powierzchnia nie jest potrzebna i powiązane zasoby można zwolnić.
fun startCamera() {
val previewResolution = Size(width, height)
val viewfinderSurfaceRequest =
ViewfinderSurfaceRequest(previewResolution, characteristics)
val surfaceListenableFuture =
cameraViewfinder.requestSurfaceAsync(viewfinderSurfaceRequest)
Futures.addCallback(surfaceListenableFuture, object : FutureCallback<Surface> {
override fun onSuccess(surface: Surface) {
/* create a CaptureSession using this surface as usual */
}
override fun onFailure(t: Throwable) { /* something went wrong */ }
}, ContextCompat.getMainExecutor(context))
}
SurfaceView
SurfaceView to proste podejście do tworzenia podglądu z kamery, jeśli nie wymaga on przetwarzania i nie jest animowany.
SurfaceView automatycznie obraca bufor obrazu czujnika aparatu, aby dopasować go do orientacji wyświetlacza, uwzględniając zarówno orientację czujnika, jak i obrót urządzenia.
Jednak bufor obrazu jest skalowany tak, aby pasował do wymiarów SurfaceView bez uwzględniania formatu obrazu.
Musisz się upewnić, że format obrazu bufora obrazu jest zgodny z formatem obrazu SurfaceView. Możesz to zrobić, skalując zawartość SurfaceView w metodzie onMeasure() komponentu:
(Kod źródłowy computeRelativeRotation() znajduje się poniżej w sekcji Względny obrót).
override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) { val width = MeasureSpec.getSize(widthMeasureSpec) val height = MeasureSpec.getSize(heightMeasureSpec) val relativeRotation = computeRelativeRotation(characteristics, surfaceRotationDegrees) if (previewWidth > 0f && previewHeight > 0f) { /* Scale factor required to scale the preview to its original size on the x-axis. */ val scaleX = if (relativeRotation % 180 == 0) { width.toFloat() / previewWidth } else { width.toFloat() / previewHeight } /* Scale factor required to scale the preview to its original size on the y-axis. */ val scaleY = if (relativeRotation % 180 == 0) { height.toFloat() / previewHeight } else { height.toFloat() / previewWidth } /* Scale factor required to fit the preview to the SurfaceView size. */ val finalScale = min(scaleX, scaleY) setScaleX(1 / scaleX * finalScale) setScaleY(1 / scaleY * finalScale) } setMeasuredDimension(width, height) }
Więcej informacji o implementowaniu SurfaceView jako podglądu z kamery znajdziesz w artykule o orientacjach kamery.
TextureView
TextureView jest mniej wydajny niż SurfaceView i wymaga więcej pracy, ale TextureView zapewnia maksymalną kontrolę nad podglądem z kamery.
TextureView obraca bufor obrazu czujnika na podstawie orientacji czujnika, ale nie obsługuje obracania urządzenia ani skalowania podglądu.
Skalowanie i obrót można zakodować w przekształceniu Matrix.
Aby dowiedzieć się, jak prawidłowo skalować i obracać TextureView, przeczytaj artykuł o obsłudze powierzchni o zmiennym rozmiarze w aplikacji aparatu.
Obrót względny
Względny obrót czujnika aparatu to stopień obrotu wymagany do wyrównania obrazu z czujnika aparatu z orientacją urządzenia.
Względny obrót jest używany przez komponenty takie jak SurfaceView i TextureView do określania współczynników skalowania x i y obrazu podglądu. Służy też do określania obrotu bufora obrazu czujnika.
Klasy CameraCharacteristics i Surface umożliwiają obliczenie względnego obrotu czujnika aparatu:
/** * Computes rotation required to transform the camera sensor output orientation to the * device's current orientation in degrees. * * @param characteristics The CameraCharacteristics to query for the sensor orientation. * @param surfaceRotationDegrees The current device orientation as a Surface constant. * @return Relative rotation of the camera sensor output. */ public fun computeRelativeRotation( characteristics: CameraCharacteristics, surfaceRotationDegrees: Int ): Int { val sensorOrientationDegrees = characteristics.get(CameraCharacteristics.SENSOR_ORIENTATION) ?: 0 // Reverse device orientation for back-facing cameras. val sign = if (characteristics.get(CameraCharacteristics.LENS_FACING) == CameraCharacteristics.LENS_FACING_FRONT ) 1 else -1 // Calculate desired orientation relative to camera orientation to make // the image upright relative to the device orientation. return (sensorOrientationDegrees - surfaceRotationDegrees * sign + 360) % 360 }
Dane dotyczące okna
Rozmiar ekranu nie powinien być używany do określania wymiarów wizjera aparatu. Aplikacja aparatu może działać na części ekranu, w trybie wielu okien na urządzeniach mobilnych lub w trybie swobodnym w ChromeOS.
WindowManager#getCurrentWindowMetrics() (dodana na poziomie API 30) zwraca rozmiar okna aplikacji, a nie rozmiar ekranu. Metody biblioteki Jetpack WindowManager WindowMetricsCalculator#computeCurrentWindowMetrics() i WindowInfoTracker#currentWindowMetrics() zapewniają podobną obsługę ze zgodnością wsteczną do poziomu interfejsu API 14.
Obrót o 180 stopni
Obrócenie urządzenia o 180 stopni (np. z orientacji naturalnej do orientacji naturalnej do góry nogami) nie wywołuje wywołania zwrotnego onConfigurationChanged(). W efekcie podgląd z aparatu może być odwrócony do góry nogami.
Aby wykryć obrót o 180 stopni, zaimplementuj DisplayListener i sprawdź obrót urządzenia, wywołując Display#getRotation() w wywołaniu zwrotnym onDisplayChanged().
Zasoby na wyłączność
Przed Androidem 10 tylko najwyższa widoczna aktywność w środowisku z wieloma oknami była w stanie RESUMED. Było to mylące dla użytkowników, ponieważ system nie wskazywał, która aktywność została wznowiona.
W Androidzie 10 (poziom API 29) wprowadzono jednoczesne wznowienie, w którym wszystkie widoczne aktywności są w stanie RESUMED. Widoczne aktywności mogą nadal przejść w stan PAUSED, jeśli na przykład nad aktywnością znajduje się aktywność przezroczysta lub aktywność nie może być aktywna, np. w trybie obrazu w obrazie (patrz Obsługa obrazu w obrazie).
Aplikacja korzystająca z kamery, mikrofonu lub dowolnego zasobu wyłącznego lub pojedynczego na poziomie API 29 lub wyższym musi obsługiwać jednoczesne wznowienie. Jeśli na przykład 3 wznowione aktywności chcą używać aparatu, tylko jedna z nich może uzyskać dostęp do tego zasobu na wyłączność. Każda aktywność musi implementować wywołanie zwrotne onDisconnected(), aby otrzymywać informacje o wywłaszczającym dostępie do aparatu przez aktywność o wyższym priorytecie.
Więcej informacji znajdziesz w artykule na temat jednoczesnego wznowienia.
Dodatkowe materiały
- Przykład użycia Camera2 znajdziesz w aplikacji Camera2Basic na GitHubie.
- Więcej informacji o przypadku użycia podglądu CameraX znajdziesz w artykule Implement a preview (Implementowanie podglądu) w CameraX.
- Przykładową implementację podglądu z aparatu CameraX znajdziesz w repozytorium CameraXBasic na GitHubie.
- Informacje o podglądzie z kamery w ChromeOS znajdziesz w artykule na temat orientacji kamery.
- Więcej informacji o tworzeniu aplikacji na urządzenia składane znajdziesz w artykule Informacje o urządzeniach składanych.