Podstawy aplikacji

Ulepszanie działania aparatu na różnych urządzeniach dzięki CameraX i funkcjom AI na urządzeniu

Głośniki

Informacje

Informacje o wystąpieniu

Użytkownicy oczekują, że aplikacje innych firm będą robić zdjęcia i nagrywać filmy z taką samą wysoką jakością jak natywny aparat urządzenia, niezależnie od warunków oświetleniowych i poziomu sprzętu. Zapewnienie tej spójności w rozszerzającym się ekosystemie Androida wiąże się z kluczowymi kwestiami architektonicznymi. Podczas tej sesji dowiesz się, jak tworzyć solidne, profesjonalne potoki przechwytywania, które maksymalizują możliwości urządzenia, a jednocześnie są niezawodne na wszystkich formatach.

Przedstawimy najnowsze osiągnięcia w CameraX i pokażemy, jak zintegrować najnowocześniejsze funkcje, takie jak zdjęcia ruchome i poprawki AI na urządzeniu, za pomocą usług Google Play, a także jak korzystać z funkcji przestrzennych za pomocą pakietu Jetpack XR SDK. Przyjrzymy się też naszej umiejętności CameraX, która pozwala wybranemu agentowi kodowania automatycznie wdrażać koncepcje z tej prezentacji i nie tylko.

Podsumowanie

  • Jetpack CameraX to zalecana biblioteka aparatu w aplikacjach na Androida, ponieważ Camera1 została wycofana. W przyszłym roku w Androidzie 18 zaczniemy egzekwować zasady Google Play i wyłączać Camera1.
  • CameraXViewfinder dostosowuje podgląd z aparatu do różnych formatów, np. urządzeń składanych. W CameraX 1.7 dodano sterowanie gestami i obsługę błysku na ekranie.
  • Okulary audio i okulary z wyświetlaczem uruchamiają aplikacje na telefonie hosta, jednocześnie udostępniając aparaty na obu urządzeniach. Przekazanie ProjectedDeviceContext umożliwia CameraX robienie zdjęć bezpośrednio z okularów.
  • Interfejs Feature Group API pomaga aplikacjom wybierać obsługiwane kombinacje funkcji aparatu. Od wersji 1.6 CameraX obsługuje nagrywanie w 4K ze stabilizacją obrazu.
  • Uczenie maszynowe na urządzeniu analizuje podglądy z aparatu i zarejestrowane multimedia za pomocą ML Kit i MediaPipe. Deweloperzy mogą wykonywać zadania od wykrywania gestów w czasie rzeczywistym po generowanie opisów obrazów wygenerowanych przez AI.

Zasoby

Gemini Notebook

Tematy

  • camerax
  • android-camera
  • on-device-ml
  • gemini-nano
  • mediapipe
  • ml-kit
  • android-xr

Transkrypcja

Pobierz transkrypcję

Wprowadzenie i omówienie CameraX

  • Cześć!

  • Witamy na naszej sesji.

  • Nazywam się Aman, a ze mną jest Scott.

  • Dziś z przyjemnością opowiemy o tym, jak możesz ulepszyć działanie aparatu na różnych urządzeniach dzięki CameraX i funkcjom AI na urządzeniu.

  • Zaczynajmy.

  • Zastanów się, jak korzystasz z telefonu.

  • Często używamy aparatu w telefonie, aby uwieczniać chwile i dzielić się nimi z bliskimi.

  • Aparat stał się kluczowym elementem smartfona.

  • W rzeczywistości dobry aparat jest często kluczowym czynnikiem przy zakupie smartfona.

  • Dzisiaj omówimy 2 główne tematy związane z tworzeniem wysokiej jakości funkcji aparatu.

  • Najpierw omówimy niektóre funkcje CameraX, a potem poznamy techniki uczenia maszynowego na urządzeniu.

  • Zacznijmy od CameraX.

  • Jetpack CameraX obsługuje aparat na Androidzie.

  • Oprócz CameraX mamy też Camera1 i Camera2, które są częścią platformy Androida.

  • Której biblioteki należy użyć?

  • Camera1 to oryginalny interfejs API aparatu na Androidzie.

  • Został on wycofany, więc nowe aplikacje powinny używać Camera2 lub CameraX.

  • Camera2 zastąpiła Camera1.

  • Jeśli więc potrzebujesz kontroli nad aparatem na niskim poziomie w przypadku złożonych zastosowań, Camera2 jest dobrym rozwiązaniem.

  • Wymaga to jednak samodzielnego zarządzania konfiguracjami poszczególnych urządzeń.

  • I wreszcie Jetpack CameraX, który bazuje na Camera2.

  • Zapewnia spójny interfejs API i automatycznie obsługuje konfiguracje dostosowane do konkretnych urządzeń.

  • Jest to zalecany wybór dla większości deweloperów.

  • Mamy bardzo ważną informację.

  • Planujemy wyłączyć Camera1. Zaczniemy od egzekwowania zasad Google Play w przyszłym roku w Androidzie 18.

  • Jeśli nadal używasz starszego interfejsu Camera1 API, musisz przejść na CameraX lub Camera2.

  • Dlaczego warto używać CameraX?

  • Po pierwsze, obsługuje Androida 7 i nowsze wersje, dzięki czemu jest zgodny z prawie 99% aktywnych urządzeń z Androidem.

  • Po drugie, jest łatwy w użyciu i udostępnia proste interfejsy API do typowych przypadków użycia, takich jak podgląd, robienie zdjęć i nagrywanie, przy mniejszej ilości kodu.

  • Po trzecie, zapewnia spójne działanie na różnych urządzeniach, ograniczając problemy związane z konkretnymi urządzeniami, takie jak rozciąganie podglądu, które jest dość powszechne na urządzeniach składanych i tabletach.

  • Upraszcza też wdrażanie zaawansowanych funkcji OEM, takich jak tryb nocny i portretowy.

  • Teraz chciałbym zaprosić Scotta, który opowie o niektórych funkcjach CameraX.

  • Scott, oddaję Ci głos.

  • Dziękuję, Aman.

  • Cześć!

  • Mam na imię Scott.

  • Dzisiaj omówimy kilka funkcji CameraX, które pozwolą Ci stworzyć profesjonalną aplikację do obsługi aparatu.

Funkcje wizjera adaptacyjnego

  • Pierwsza z nich to adaptacyjny wizjer.

  • Został zaprojektowany tak, aby całkowicie wyeliminować problemy z podglądem na różnych formatach i rozmiarach ekranu.

  • Użytkownicy mają teraz do dyspozycji różne urządzenia, w tym urządzenia składane, telefony, laptopy i urządzenia z XR.

  • Dlatego tak ważne jest, aby dostosować funkcje aparatu do nowych formatów.

  • Podgląd z kamery jest bardzo trudny do prawidłowego wyświetlenia, zwłaszcza na składanym urządzeniu z szerokim ekranem.

  • Na tych urządzeniach po rozłożeniu wewnętrzny aparat jest zamontowany poziomo, a tylny – pionowo.

  • Jeśli więc założysz, że wszystkie czujniki aparatu są zamontowane pionowo, doprowadzi to do niedopasowania przestrzennego i zniekształcenia lub bocznego przesunięcia.

  • CameraXViewfinder, która została zaprojektowana tak, aby całkowicie uprościć złożoność, dzięki czemu podgląd z kamery działa bezproblemowo na wszystkich formatach.

  • Zobaczmy, jak używać funkcji kompozycyjnej CameraXViewfinder.

    var surfaceRequest by remember {
        mutableStateOf<SurfaceRequest?>(null)
    }
    
    LaunchedEffect(...) {
        preview.setSurfaceProvider { surfaceRequest = it }
        cameraProvider.bindToLifecycle(..., preview)
    }
    
    surfaceRequest?.let { request ->
        CameraXViewfinder(
            surfaceRequest = request,
            contentScale = ContentScale.Fit
        )
    }
  • Najpierw deklarujesz zmienną treści surfaceRequest, która będzie przechowywać żądanie platformy.

  • Przepraszam.

  • Po drugie, w pliku LaunchedEffect ustawiasz dostawcę platformy, aby przechwytywał obiekt żądania platformy.

  • Następnie powiąż z cyklem życia i uruchom aparat.

  • Na koniec, gdy stan zostanie zaktualizowany o prawidłowe żądanie powierzchni i wywoła rekompozycję, ustawiasz żądanie bezpośrednio na CameraXViewfinder.

  • Tutaj możesz ustawić contentScale, aby dopasować lub przyciąć obraz, w zależności od preferencji.

  • I to wszystko.

  • W CameraX 1.7 dodaliśmy do CameraXViewfinder kilka nowych funkcji.

  • Obejmuje to nowe gesty, takie jak kliknięcie, aby ustawić ostrość, lub ściągnięcie palców, aby powiększyć.

  • Obsługuje też błysk na ekranie, który pozwala rozjaśnić selfie za pomocą ekranu.

  • Wdrożenie tej funkcji jest łatwe.

    CameraXViewfinder(
        surfaceRequest = viewModel.surfaceRequest,
        contentScale = ContentScale.Fit,
        isTapToFocusEnabled = true,
        isPinchToZoomEnabled = true,
        onScreenFlashReady =
            {
               imageCapture.screenFlash = it
               imageCapture.flashMode = FLASH_MODE_SCREEN
            }
    )
  • Wystarczy, że włączysz opcje „Kliknij, aby ustawić ostrość” i „Ściągnij palce, aby powiększyć”.

  • W przypadku błysku na ekranie pobierasz instancję błysku na ekranie z wywołania zwrotnego onScreenFlashReady, a następnie przekazujesz ją do ImageCapture i ustawiasz tryb lampy błyskowej na FLASH_MODE_SCREEN.

  • To wszystko.

Robienie zdjęć aparatem na Androidzie XR

  • Widzieliśmy, jak CameraX umożliwia podgląd z aparatu, który działa bezproblemowo na wszystkich formatach.

  • Teraz idziemy o krok dalej i dodajemy możliwość robienia zdjęć aparatem na Androidzie XR.

  • Istnieją 3 kategorie urządzeń z XR. Pierwsza to gogle XR.

  • Są to całkowicie zamknięte i zapewniające efekt immersji gogle, które wykorzystują aparat do rejestrowania świata fizycznego i wyświetlania go wewnątrz gogli.

  • Drugi to przewodowe okulary XR.

  • Dzisiaj podczas prezentacji w tej kategorii ogłosiliśmy wprowadzenie XREAL Aura.

  • Jak widać na prezentacjach, są to przezroczyste okulary połączone z jednostką obliczeniową, które umożliwiają wyświetlanie treści wirtualnych nałożonych na świat rzeczywisty.

  • Trzecia kategoria to okulary z funkcjami audio i wyświetlaczem.

  • To są lekkie okulary.

  • Mogą łączyć dźwięk, aparat i wyświetlacz, aby zapewnić obsługiwane bez użycia rąk i szybkie korzystanie.

  • Urządzenia z pierwszych 2 kategorii, czyli gogle XR i przewodowe okulary XR, obsługują też interfejsy Camera2 i CameraX, podobnie jak zwykły telefon.

  • Dlatego dzisiaj skupimy się tylko na dźwięku i okularach z wyświetlaczem.

  • W przypadku tych okularów aplikacje działają na telefonie z Androidem, a nie na okularach, ale mają dostęp zarówno do aparatu telefonu, jak i aparatu okularów.

  • Kluczowe pytanie brzmi: skąd wiadomo, do której kamery się mówi?

  • To proste.

  • W przypadku standardowego kontekstu CameraX uzyskuje dostęp do aparatu telefonu.

  • Dzięki ProjectedDeviceContext CameraX uzyskuje dostęp do kamery w okularach.

  • Różne konteksty będą więc prowadzić do różnych kamer docelowych.

  • Pamiętaj, że uprawnienia do aparatu i okularów są oddzielne.

  • Uprawnienia dotyczące telefonu i okularów są oddzielne, więc musisz poprosić o uprawnienia dotyczące okularów osobno.

  • Inną opcją jest użycie aktywności rzutowanej, która również działa na urządzeniach z Androidem, ale interfejs jest rzutowany na okulary.

  • W tym przypadku reguła jest odwrotna.

  • Jeśli używasz standardowego kontekstu, uzyskasz dostęp do kamery w okularach.

  • Jeśli chcesz uzyskać dostęp do aparatu telefonu, musisz użyć HostContext.

  • Spójrzmy na kod i zobaczmy, jak możemy robić zdjęcia za pomocą aparatu w okularach.

    // In a coroutine
    val imageCapture = ImageCapture.Builder().build()
    
    ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext)
        .collectLatest { isConnected ->
            if (isConnected) {
                val projectedContext = ProjectedContext.createProjectedDeviceContext(activity)
                val cameraConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
                    .setRepeatingStreamForced(false)
                    .build()
                val cameraProvider = LifecycleCameraProvider.createInstance(projectedContext, cameraConfig)
                cameraProvider.bindToLifecycle(activity, DEFAULT_BACK_CAMERA, imageCapture)
            }
        }
    // Shutter button clicked
    imageCapture.takePicture(...)
  • Najpierw wywołujesz isProjectedDeviceConnected, aby sprawdzić, czy okulary są połączone.

  • Następnie dzwonisz do createProjectedDeviceContext, aby uzyskać projectedContext.

  • Po drugie, do inicjowania CameraX używasz LifecycleCameraProvider zamiast ProcessCameraProvider.

  • ProcessCameraProvider jest singletonem, którego kontekstu nie można zmienić po inicjalizacji.

  • Dzięki temu LifecycleCameraProvider pozwala na zmianę kontekstu w dowolnym momencie.

  • Następnie musisz wyłączyć dodatkowy strumień powtarzający się w CameraX.

  • Jest to konieczne, ponieważ obecnie okulary obsługują tylko 1 strumień.

  • Interfejs API jest obecnie wewnętrzny, ale wkrótce udostępnimy go publicznie.

  • To wszystko.

  • Od tego momentu używasz standardowego interfejsu CameraX. Wystarczy powiązać ImageCapture i możesz robić zdjęcia za pomocą okularów.

Funkcje i interfejsy API do przechwytywania

  • OK.

  • Teraz zobaczmy, jak CameraX pomaga znaleźć kombinację funkcji, które ze sobą współpracują i pozwalają najlepiej wykorzystać możliwości urządzenia.

  • Nowoczesne aplikacje do obsługi aparatu charakteryzują się zaawansowanymi funkcjami, a użytkownicy oczekują, że aparat będzie obsługiwać takie funkcje jak HDR, 60 kl./s i stabilizacja podglądu, i to wszystkie jednocześnie.

  • Wiemy jednak, że połączenie tych funkcji nie zawsze jest proste.

  • Sprawdzenie obsługi poszczególnych funkcji nie gwarantuje, że będą one działać razem.

  • Ta niepewność zmusiła deweloperów do zachowania ostrożności i ograniczenia możliwości użytkowników na urządzeniach o odpowiednich parametrach.

  • Aby rozwiązać ten problem, wprowadziliśmy interfejs Feature Group API, który jest oparty na platformie zapytań o kombinacje funkcji Camera2.

  • Można go używać na 2 sposoby: po pierwsze, użyj requiredFeatureGroup, aby sprawdzić i włączyć kombinację funkcji, gdy jest obsługiwana.

  • Po drugie, możesz określić priorytet funkcji za pomocą preferredFeatureGroup, a następnie CameraX wybierze dla Ciebie najlepszą obsługiwaną kombinację.

  • Oto przykład.

  • Po prawej, po lewej, przepraszam, bez włączania niczego, zwykły telefon, zwykłe nagrywanie.

  • Po lewej stronie włączamy jednocześnie HDR, 60 kl./s i stabilizację podglądu.

  • Możesz zauważyć, że jakość filmu jest znacznie lepsza, ponieważ jest on jaśniejszy, płynniejszy i nie ma drgań.

  • Przyjrzyjmy się kodowi.

    cameraProvider.bindToLifecycle(
        lifecycleOwner,
        cameraSelector,
        SessionConfig(
            useCases = listOf(preview, videoCapture),
            // HDR_HLG10 > FPS_60 > Preview Stabilization.
            preferredFeatureGroup =
               listOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION),
        )
    )
  • W tym przypadku używamy preferredFeatureGroup, aby podać listę priorytetów funkcji, czyli HDR, 60 kl./s i stabilizację podglądu.

  • CameraX sprawdzi kombinację i wybierze najlepsze opcje obsługiwane przez urządzenie.

  • W CameraX 1.6 i nowszych wersjach dodajemy nagrywanie w 4K i stabilizację obrazu bezpośrednio do interfejsu Feature Group API.

    // CameraX 1.6+ supports:
    // 4K, video stabilization & Effect
    val sessionConfig = SessionConfig(
       useCases = listOf(preview, videoCapture),
       requiredFeatureGroup =
         setOf(UHD_RECORDING, VIDEO_STABILIZATION),
       effects = listOf(overlayEffect)
    )
  • Połączenie rozdzielczości 4K i stabilizacji wideo jest obsługiwane przez wiele urządzeń premium.

  • Teraz dzięki interfejsowi Feature Group API możesz bez obaw włączyć je razem.

  • Dodatkowo możesz teraz stosować efekt w czasie rzeczywistym za pomocą interfejsu Feature Group API, co wcześniej nie było możliwe.

  • Porozmawiajmy teraz o tym, jak robić zdjęcia wysokiej jakości za pomocą CameraX.

  • CameraX obsługuje teraz zapisywanie zdjęć w formacie DNG RAW.

  • Zapewnia dostęp do nieprzetworzonego, nieskompresowanego obrazu RAW bezpośrednio z matrycy aparatu, co umożliwia profesjonalną edycję.

  • Ulepszyliśmy też rozszerzenie Tryb nocny.

  • To rozszerzenie umożliwia robienie jasnych i wyraźnych zdjęć przy słabym oświetleniu bez użycia lampy błyskowej.

  • Wprowadziliśmy też nowy interfejs API wskaźnika trybu nocnego.

  • Będzie on sprawdzać, czy w obecnym otoczeniu jest ciemno i czy można włączyć tryb nocny. Możesz go używać do wyświetlania wskaźnika trybu nocnego.

  • Ultra HDR jest teraz obsługiwany w rozszerzeniach CameraX.

  • Zapewnia to szerszy zakres dynamiki i żywe kolory w trybie nocnym. Tryb nocny z Ultra HDR jest obecnie obsługiwany na wielu najnowszych urządzeniach premium.

  • Tutaj możesz zobaczyć różnicę między trybem normalnym, nocnym i nocnym z Ultra HDR.

  • Zobaczmy, jak to zrobić.

    val cameraInfo = cameraProvider.getCameraInfo(cameraSelector,
            ExtensionSessionConfig(ExtensionMode.NIGHT, extensionsManager))
    
    val imageCapabilities = ImageCapture.getImageCaptureCapabilities(cameraInfo)
    
    val imageCapture = ImageCapture.Builder().apply {
           if (imageCapabilities.supportedOutputFormats
                    .contains(OUTPUT_FORMAT_JPEG_ULTRA_HDR)) {
               setOutputFormat(OUTPUT_FORMAT_JPEG_ULTRA_HDR)
           }
         }.build()
    
    val extensionSessionConfig = ExtensionSessionConfig(
        ExtensionMode.NIGHT, extensionsManager, preview, imageCapture
    )
    
    cameraProvider.bindToLifecycle(...,  extensionSessionConfig)
  • Najpierw utwórz ExtensionSessionConfig dla trybu nocnego i użyj go, aby uzyskać instancję CameraInfo.

  • Następnie używasz CameraInfo, aby uzyskać możliwość robienia zdjęć, a potem sprawdzasz, czy obsługiwany jest tryb Ultra HDR.

  • Następnie ustaw format wyjściowy na Ultra HDR.

  • Na koniec ponownie tworzysz ExtensionSessionConfig z przypadkami użycia i powiązujesz go z cyklem życia.

  • Teraz możesz robić zdjęcia w trybie nocnym w formacie ultra HDR.

  • Wprowadzamy też obsługę zdjęć ruchomych w CameraX.

  • Zdjęcia ruchome rejestrują dźwięk i ruch wokół zdjęcia, dzięki czemu uwieczniają chwilę, a nie tylko nieruchomy obraz.

  • Ujednolicamy tę funkcję na różnych telefonach i urządzeniach z Androidem, aby ułatwić obsługę zdjęć ruchomych.

  • Zdjęcia ruchome będą dostępne w CameraX w I kwartale 2027 r.

  • Gdy funkcja będzie dostępna, po prostu ustaw tryb zdjęć ruchomych w aplikacji ImageCapture.Builder i zacznij robić zdjęcia ruchome.

    val imageCapture = ImageCapture.Builder()
            .setMotionPhotoMode(
                 ImageCapture.MOTION_PHOTO_MODE_ON,
                 customConfig
             )
            .build()
    
    cameraProvider.bindToLifecycle(.. imageCapture)
    imageCapture.takePicture(...)
  • Teraz przyjrzyjmy się, jak CameraX umożliwia nagrywanie filmów z dużą szybkością.

  • Wyraźne nagrywanie dynamicznych scen to podstawowa funkcja nowoczesnych aplikacji do obsługi aparatu.

  • Jest to szczególnie przydatne w przypadku dynamicznych scen, takich jak wydarzenia sportowe.

  • Jest to możliwe dzięki szybkiemu nagrywaniu z prędkością 120 lub 240 kl./s.

  • Możesz go używać na 2 sposoby.

  • Najpierw kodowanie z tą samą liczbą klatek, a następnie utworzenie filmu z prawdziwie wysoką liczbą klatek.

  • Inną opcją jest zakodowanie go przy 30 klatkach na sekundę, aby utworzyć gotowy do odtworzenia film w zwolnionym tempie.

  • Na przykład 1 sekunda nagrana przy 120 kl./s będzie odtwarzana przez 4 sekundy przy 30 kl./s.

  • Dzięki temu ten fragment filmu jest odtwarzany w zwolnionym tempie.

  • Aby wdrożyć szybkie nagrywanie, utwórz HighSpeedVideoSessionConfig za pomocą videoCapture.

    val recorder = Recorder.Builder().build()
    val videoCapture = VideoCapture.withOutput(recorder)
    
    val frameRateRange = cameraInfo.getSupportedFrameRateRanges(
           HighSpeedVideoSessionConfig(videoCapture, preview)).first()
    
    val sessionConfig = HighSpeedVideoSessionConfig(
        videoCapture,
        preview,
        frameRateRange = frameRateRange,
        isSlowMotionEnabled = true
    )
    
    cameraProvider.bindToLifecycle(
         lifecycleOwner, cameraSelector, sessionConfig)
    
    // Start recording slow motion videos.
    val recording = recorder.prepareRecording(context, outputOption)
          .start(executor, {})
  • Następnie ustawiasz wartość isSlowMotionEnabled na „true”, aby zakodować film z szybkością 30 kl./s i utworzyć film w zwolnionym tempie, lub na „false”, aby utworzyć film z dużą liczbą klatek na sekundę.

  • Gdy włączony jest tryb zwolnionego tempa, Zdjęcia Google automatycznie go rozpoznają i dodają plakietkę zwolnionego tempa do miniatury.

  • Zobaczmy, jak to działa w Zdjęciach Google.

  • W tym przypadku zwolnione tempo nadaje kotu dramatyczny efekt.

  • Mój kot nigdy nie chce, żebym go dotykał.

  • Przykro mi.

  • Na koniec przyjrzyjmy się ulepszeniom w interfejsie Concurrent Camera API.

  • Interfejs API Concurrent umożliwia strumieniowe przesyłanie obrazu z przedniego i tylnego aparatu jednocześnie.

  • W tym roku wprowadziliśmy pewne ulepszenia w interfejsie Concurrent Camera API.

  • Po powiązaniu z cyklem życia i otwarciu kamery możesz dynamicznie aktualizować kompozycję kamery.

  • Obejmuje to zmianę pozycji, rozmiaru i kolejności renderowania.

  • Możesz też dodać style, takie jak zaokrąglony róg, szerokość obramowania i kolor obramowania.

  • Aby to zrobić, użyj CompositionSettings do skonfigurowania stylu, np. zaokrąglenia rogów, szerokości obramowania i koloru, oraz setZOrder do dostosowania kolejności renderowania.

    val concurrentCamera =
            cameraProvider.bindToLifecycle(list(config1, config2))
    // CameraX 1.7 features: rounded corner, border width & color
    val pipSettings = CompositionSettings.Builder()
        .setOffset(0.2f, 0.2f)
        .setScale(0.35f, 0.35f)
        .setRoundedCornerRatio(0.15f)
        .setBorderWidthRatio(0.02f)
        .setBorderColor(Color.WHITE)
        .setZOrder(1)
        .build()
    // CameraX 1.7 features: dynamical composition update
    concurrentCamera.setCompositionSettings(
    listOf(mainSettings, pipSettings))
  • Następnie wystarczy wywołać setCompositionSettings na aktywnej kamerze równoczesnej, aby dynamicznie aktualizować kompozycję.

  • To wszystko.

  • Możesz teraz dynamicznie aktualizować kompozycję.

  • OK.

  • Omówiliśmy już wszystkie funkcje.

  • Mamy tu ważną informację, dlatego zdecydowanie zalecamy przejście na CameraX 1.6 lub nowszą wersję.

  • Dzięki temu unikniesz awarii na nadchodzących urządzeniach, które obsługują nowy format Eclipsa Video, a rozszerzenie CameraX będzie nadal działać w przyszłej aktualizacji Pixela.

  • To już koniec pierwszej sesji.

  • Omówiliśmy funkcje CameraX, które pomagają tworzyć profesjonalne funkcje aparatu. Teraz zapraszam Amana, który poprowadzi kolejną sesję na temat AI na urządzeniu.

Uczenie maszynowe na urządzeniu

  • Dziękuję, Scott, za omówienie funkcji CameraX.

  • Teraz przyjrzyjmy się, jak możesz wykorzystać uczenie maszynowe na urządzeniu do tworzenia inteligentnych funkcji aparatu.

  • Uczenie maszynowe na urządzeniu umożliwia szeroki zakres zastosowań aparatu w 2 głównych obszarach.

  • Pierwsza to wstępne przechwytywanie.

  • Możesz tu analizować podgląd z aparatu w czasie rzeczywistym, aby np. wykrywać obiekty, lub ulepszać sam podgląd.

  • Drugi to etap po zrobieniu zdjęcia.

  • Po zrobieniu zdjęcia możesz poprawić jego jakość lub wykonać inne czynności, np. wygenerować opis.

  • Teraz przyjrzyjmy się niektórym rozwiązaniom uczenia maszynowego na urządzeniu, które oferujemy w aplikacjach do obsługi aparatu.

  • Pierwszą biblioteką ML na urządzeniu, którą omówimy, jest ML Kit.

  • Zestaw ML Kit został stworzony przede wszystkim do rozwiązywania typowych problemów związanych z rozpoznawaniem obrazów i językiem naturalnym, takich jak rozpoznawanie tekstu, wykrywanie twarzy i tłumaczenie.

  • W zeszłym roku wprowadziliśmy ML Kit.

  • Oto kilka przykładów interfejsów ML Kit Vision API, takich jak wykrywanie siatki twarzy, segmentacja obiektów i wykrywanie obiektów w czasie rzeczywistym.

  • Tak.

  • W zeszłym roku wprowadziliśmy interfejsy API generatywnej AI.

  • Te interfejsy API wykorzystują możliwości Gemini Nano, aby umożliwić m.in. podsumowywanie tekstu i opisywanie obrazów.

  • Oto przykład opisu obrazu za pomocą interfejsu Prompt API.

  • Przekazujemy do modelu bitmapę obrazu z promptem tekstowym, a wynikowy opis brzmi: „Mały zielony robot Android o wyglądzie kaktusa siedzi na czarnej powierzchni”.

  • Wszystko to odbywa się na urządzeniu.

  • Całkiem nieźle, prawda?

  • Adobe Photoshop używa interfejsu Prompt API, aby poprawić wrażenia użytkowników w aplikacji.

  • Podczas edytowania komponentów użytkownicy często muszą zarządzać wieloma warstwami w aplikacji Photoshop.

  • Dzięki interfejsowi Prompt API aplikacja może teraz automatycznie zmieniać nazwy nieuporządkowanych warstw.

  • Aplikacja używa też interfejsu Prompt API do generowania podpisów i hashtagów do obrazów, co jest bardzo przydatne podczas udostępniania w mediach społecznościowych.

  • Przyjrzyjmy się kodowi opisu obrazu za pomocą interfejsu Prompt API.

    // 1. Initialize the on-device GenerativeModel client
    val generativeModel = Generation.getClient()
    
    // 2. Check whether Gemini Nano is available on the device
    val status = generativeModel.checkStatus()
    when (status) {
        FeatureStatus.UNAVAILABLE -> {}
        FeatureStatus.DOWNLOADABLE -> {}
        FeatureStatus.DOWNLOADING -> {}
        FeatureStatus.AVAILABLE -> {}
    }
  • Najpierw potrzebujesz obiektu klienta generativeModel, a następnie sprawdź, czy Gemini Nano jest dostępny lub czy można go pobrać na urządzenie.

  • Następnie wygeneruj prośbę o treść, łącząc obraz i prompt tekstowy, a na koniec prześlij tę prośbę do wywołania generateContent, aby przeprowadzić wnioskowanie w Gemini Nano i uzyskać opis obrazu.

    // 3. Build a multimodal request with the image and text prompt
    val request = generateContentRequest(
        ImagePart(bitmap),
        TextPart("Describe this image in detail.")
    ) {}
    
    // 4. Run inference on-device and get the description
    val response = generativeModel.generateContent(request)
    val description = response.candidates.first().text
  • Tak, kolejną biblioteką ML na urządzeniu jest MediaPipe.

  • MediaPipe oferuje szeroką gamę rozwiązań uczenia maszynowego dla Twoich aplikacji.

  • Możesz szybko odwiedzić witrynę MediaPipe, aby wypróbować różne przykłady, takie jak wykrywanie twarzy, wykrywanie obiektów, segmentacja obrazu i inne.

  • Wszystkie te przypadki użycia brzmią bardzo podobnie do ML Kit, więc której biblioteki należy użyć?

  • MediaPipe i ML Kit rozwiązują podobne problemy, ale istnieją między nimi pewne kluczowe różnice.

  • Pierwsza z nich to obsługa platform.

  • MediaPipe działa na Androidzie, iOS, w internecie i Pythonie, natomiast ML Kit koncentruje się tylko na Androidzie i iOS.

  • MediaPipe oferuje dostosowywanie modeli, a ML Kit – gotowe do użycia interfejsy API.

  • I wreszcie: MediaPipe jest rozwiązaniem typu open source, a ML Kit to pakiet SDK Google.

  • Oto przykład rozpoznawania gestów za pomocą interfejsów MediaPipe Vision API.

  • Ten interfejs API wykrywa gesty dłoni w czasie rzeczywistym na podstawie obrazu z kamery.

  • Można na przykład rozpocząć lub zatrzymać nagrywanie za pomocą prostych gestów dłoni, stojąc w pewnej odległości od telefonu.

  • Przyjrzyjmy się kodowi.

  • Najpierw musisz utworzyć obiekt GestureRecognizerOptions, podając ścieżkę modelu, tryb transmitowania na żywo i detektor wyników.

    // 1. Build GestureRecognizerOptions
    val options = GestureRecognizerOptions.builder().setBaseOptions(
        BaseOptions.builder().setModelAssetPath(MP_RECOGNIZER_TASK).build())
        .setRunningMode(RunningMode.LIVE_STREAM)
        .setResultListener { result, image -> /* Handle detected gesture */ }
        .build()
    
    // 2. Create the GestureRecognizer
    val gestureRecognizer = GestureRecognizer.createFromOptions(context, options)
    
    // 3. Convert CameraX ImageProxy to MediaPipe MPImage
    val bitmap = rotateBitmap(imageProxy.toBitmap(),
    imageProxy.imageInfo.rotationDegrees)
    val mpImage = BitmapImageBuilder(bitmap).build()
    
    // 4. Run gesture recognition on the image
    gestureRecognizer.recognizeAsync(mpImage, frameTime)
  • Następnie utwórz obiekt rozpoznawania gestów na podstawie opcji.

  • Następnie pobierz ImageProxy CameraX i przekonwertuj go na bitmapę, a potem użyj bitmapy do wygenerowania obiektu MediaPipe mpImage.

  • Na koniec musisz podać obiekt MediaPipe mpImage w wywołaniu recognizeAsync, aby wykrywać gesty w czasie rzeczywistym.

Ulepszanie obrazów i filmów

  • Następnie mamy ulepszanie obrazów i filmów na urządzeniu dostępne za pomocą interfejsu API Usług Google Play.

  • Ten model zapewnia funkcje takie jak mapowanie tonów, usuwanie rozmycia i superrozdzielczość na większości urządzeń premium.

  • Oto przykład obrazu, którego mapowanie tonów i skalowanie zostało wykonane za pomocą tego modelu.

  • Na ulepszonym obrazie wyraźnie widać bogatsze kolory nieba i morza oraz lepszą ostrość.

  • Przyjrzyjmy się kodowi ulepszania obrazu.

    // Step 1: Verify device support & install module
    val enhancementClient = Enhancement.getClient(context)
    
    suspend fun prepare(): Boolean {
        if (!enhancementClient.isDeviceSupported().await()) return false
        if (!enhancementClient.isModuleInstalled().await()) {
            check(enhancementClient.installModule().await()) {
                "Enhancement module install failed"
            }
        }
        return true
    }
  • Najpierw musisz sprawdzić, czy urządzenie obsługuje tę funkcję, a jeśli to konieczne, zainstalować na nim moduł ulepszeń.

  • Następnie utwórz obiekt EnhancementOptions, określając bitmapę i ulepszenia, które chcesz zastosować.

    // Step 2: Select Enhancement options & create session
    val options = EnhancementOptions(
        width = bitmap.width,
        height = bitmap.height,
        enhancementMode = EnhancementMode.BITMAP,
        isTonemappingEnabled = true,
        isDeblurAndDenoisePhotoEnabled = true,
        isDeblurAndDenoiseVideoEnabled = false,
        isUpscalePhotoEnabled = false,
        isUpscaleVideoEnabled = false
    )
    
    val enhancementSession = enhancementClient
        .createSessionAsync(options, enhancementExecutor)
  • Następnie utwórz sesję klienta za pomocą interfejsu API usług Google Play, zdefiniuj wywołanie zwrotne do obsługi wynikowej bitmapy i wywołaj funkcję procesu z bitmapą i opcjami ulepszania obrazu.

    // Step 3: Process Enhancement
    val enhancedBitmap = suspendCancellableCoroutine { continuation ->
        val enhancementCallback = object : EnhancementCallback {
            override fun onBitmapProcessed(result: Bitmap) {
                if (continuation.isActive) continuation.resume(result)
            }
            override fun onError(statusCode: Int) {
                if (continuation.isActive) {
                    continuation.resumeWithException(
                        Exception("Bitmap processing failed: $statusCode")
                    )
                }
            }
            override fun onSurfaceProcessed(timestamp: Long) {}
        }
    
        enhancementSession.process(bitmap, options, enhancementCallback)
    }
  • Niedawno zintegrowaliśmy interfejsy API ulepszenia filmu z Jetpack Media3.

  • Możesz obejrzeć naszą prezentację, aby dowiedzieć się więcej o interfejsach Image Enhancement API i Video Enhancement API.

  • Ostatnią funkcją uczenia maszynowego na urządzeniu, którą omówimy, jest doświetlanie przy słabym oświetleniu.

  • Robienie zdjęć przy słabym oświetleniu to częsty problem użytkowników smartfonów.

  • Wyobraź sobie użytkowników robiących zdjęcia lub prowadzących rozmowy wideo w słabo oświetlonym otoczeniu.

  • Funkcja ulepszania obrazu przy słabym oświetleniu rozwiązuje ten problem, rozjaśniając podgląd z kamery w czasie rzeczywistym.

  • Dostępne są 2 warianty doświetlania przy słabym oświetleniu: sprzętowy i programowy.

  • Wersja sprzętowa wykorzystuje sprzęt OEM do ulepszania słabo oświetlonych klatek bez użycia uczenia maszynowego.

  • Wersja oprogramowania wykorzystuje natomiast model ML na urządzeniu do ulepszania klatek.

  • Przyjrzyjmy się szybko, jak możemy zintegrować CameraX z oprogramowaniem doświetlającym przy słabym oświetleniu, które jest też znane jako doświetlanie przy słabym oświetleniu od Google.

  • Google Low Light Boost wymaga 2 kluczowych powierzchni: wejściowej dla nieprzetworzonych klatek z aparatu i wyjściowej dla ulepszonych klatek.

  • Aby zintegrować tę funkcję z CameraX, wystarczy umieścić potok ulepszonego oświetlenia Google w CameraEffect.

  • CameraX automatycznie i płynnie obsługuje cykl życia i routing powierzchni.

  • W IV kwartale 2026 r. wprowadzimy też w CameraX efekt doświetlania przy słabym oświetleniu.

    // Integrates SW Low Light Boost
    val lowLightBoostEffect =
        LowLightBoostEffect(
            CameraEffect.PREVIEW or
            CameraEffect.VIDEO_CAPTURE or
            CameraEffect.IMAGE_CAPTURE
        )
    
    val sessionConfig = SessionConfig(
        useCases = listOf(preview, imageCapture),
        effects = lowLightBoostEffect
    )
    
    cameraProvider.bindToLifecycle(.. sessionConfig)
  • Dzięki temu możesz utworzyć obiekt Low Light Boost i dodać go do konfiguracji sesji, a następnie powiązać konfigurację sesji z cyklem życia aparatu. Będzie to działać na obsługiwanych urządzeniach.

Podsumowanie i migracja

  • Na tym kończymy obie sekcje.

  • Podsumujmy krótko, co dziś omówiliśmy.

  • Dzisiaj omówiliśmy, jak możesz ulepszyć działanie aparatu na różnych urządzeniach dzięki CameraX i uczeniu maszynowemu na urządzeniu.

  • Po pierwsze, dzięki CameraXViewfinder możesz włączyć podgląd z kamery, który dostosowuje się do różnych formatów.

  • Następnie dowiedzieliśmy się, że CameraX umożliwia też nagrywanie na urządzeniach z XR.

  • Następnie interfejs Feature Group API pomaga znaleźć optymalną kombinację funkcji sprzętowych.

  • Omówiliśmy też ulepszenia funkcji robienia zdjęć i dowiedzieliśmy się o nagrywaniu filmów z dużą szybkością, które umożliwia tworzenie filmów w zwolnionym tempie.

  • Na koniec dowiedzieliśmy się, jak dynamicznie ulepszać podgląd z jednoczesnych kamer.

  • Następnie przyjrzeliśmy się niektórym rozwiązaniom uczenia maszynowego na urządzeniu, takim jak ML Kit i MediaPipe, które umożliwiają tworzenie inteligentnych funkcji wizualnych.

  • Na koniec zobaczyliśmy ulepszanie obrazów i ulepszenie filmów na urządzeniu oraz funkcję rozjaśniania słabego oświetlenia, która rozjaśnia podgląd z aparatu w czasie rzeczywistym.

  • Jak wspomnieliśmy wcześniej, planujemy wyłączyć Camera1, dlatego zachęcamy do przejścia na CameraX.

  • Aby ułatwić tę migrację, niedawno wprowadziliśmy umiejętność CameraX.

  • Możesz też skorzystać z przewodnika po migracji i repozytorium z przykładami dotyczącymi aparatu.

  • Kliknij ten link, aby uzyskać dostęp do dodatkowych materiałów na temat funkcji, które dziś omówiliśmy.

  • Zawiera linki do dokumentacji, bloga i zasobów dotyczących migracji – wszystko w jednym miejscu.

  • Zrobię sobie przerwę i odpocznę.

  • OK.

  • Dziękuję wszystkim.

  • Na tym kończymy naszą prezentację.

Kod

Fragmenty kodu

Fragment kodu w języku Kotlin w 04:18 w filmie „Funkcje adaptacyjnego wizjera”
var surfaceRequest by remember {
    mutableStateOf<SurfaceRequest?>(null)
}

LaunchedEffect(...) {
    preview.setSurfaceProvider { surfaceRequest = it }
    cameraProvider.bindToLifecycle(..., preview)
}

surfaceRequest?.let { request ->
    CameraXViewfinder(
        surfaceRequest = request,
        contentScale = ContentScale.Fit
    )
}
Fragment kodu w języku Kotlin w 05:14 w filmie „Funkcje adaptacyjnego wizjera”
CameraXViewfinder(
    surfaceRequest = viewModel.surfaceRequest,
    contentScale = ContentScale.Fit,
    isTapToFocusEnabled = true,
    isPinchToZoomEnabled = true,
    onScreenFlashReady =
        {
           imageCapture.screenFlash = it
           imageCapture.flashMode = FLASH_MODE_SCREEN
        }
)
Fragment kodu w języku Kotlin w 07:30 w filmie „Przechwytywanie obrazu z kamery na Androidzie XR”
// In a coroutine
val imageCapture = ImageCapture.Builder().build()

ProjectedContext.isProjectedDeviceConnected(activity, coroutineContext)
    .collectLatest { isConnected ->
        if (isConnected) {
            val projectedContext = ProjectedContext.createProjectedDeviceContext(activity)
            val cameraConfig = CameraXConfig.Builder.fromConfig(Camera2Config.defaultConfig())
                .setRepeatingStreamForced(false)
                .build()
            val cameraProvider = LifecycleCameraProvider.createInstance(projectedContext, cameraConfig)
            cameraProvider.bindToLifecycle(activity, DEFAULT_BACK_CAMERA, imageCapture)
        }
    }
// Shutter button clicked
imageCapture.takePicture(...)
Fragment kodu w języku Kotlin w 09:42 w filmie „Funkcje i interfejsy API do przechwytywania”
cameraProvider.bindToLifecycle(
    lifecycleOwner,
    cameraSelector,
    SessionConfig(
        useCases = listOf(preview, videoCapture),
        // HDR_HLG10 > FPS_60 > Preview Stabilization.
        preferredFeatureGroup =
           listOf(HDR_HLG10, FPS_60, PREVIEW_STABILIZATION),
    )
)
Fragment kodu w języku Kotlin w 09:58 w filmie „Funkcje i interfejsy API do przechwytywania”
// CameraX 1.6+ supports:
// 4K, video stabilization & Effect
val sessionConfig = SessionConfig(
   useCases = listOf(preview, videoCapture),
   requiredFeatureGroup =
     setOf(UHD_RECORDING, VIDEO_STABILIZATION),
   effects = listOf(overlayEffect)
)
Fragment kodu w języku Kotlin w 11:20 w filmie „Funkcje i interfejsy API do przechwytywania”
val cameraInfo = cameraProvider.getCameraInfo(cameraSelector,
        ExtensionSessionConfig(ExtensionMode.NIGHT, extensionsManager))

val imageCapabilities = ImageCapture.getImageCaptureCapabilities(cameraInfo)

val imageCapture = ImageCapture.Builder().apply {
       if (imageCapabilities.supportedOutputFormats
                .contains(OUTPUT_FORMAT_JPEG_ULTRA_HDR)) {
           setOutputFormat(OUTPUT_FORMAT_JPEG_ULTRA_HDR)
       }
     }.build()

val extensionSessionConfig = ExtensionSessionConfig(
    ExtensionMode.NIGHT, extensionsManager, preview, imageCapture
)

cameraProvider.bindToLifecycle(...,  extensionSessionConfig)
Fragment kodu w języku Kotlin w 12:11 w filmie „Funkcje i interfejsy API do przechwytywania”
val imageCapture = ImageCapture.Builder()
        .setMotionPhotoMode(
             ImageCapture.MOTION_PHOTO_MODE_ON,
             customConfig
         )
        .build()

cameraProvider.bindToLifecycle(.. imageCapture)
imageCapture.takePicture(...)
Fragment kodu w języku Kotlin w 13:06 w filmie „Funkcje i interfejsy API do przechwytywania”
val recorder = Recorder.Builder().build()
val videoCapture = VideoCapture.withOutput(recorder)

val frameRateRange = cameraInfo.getSupportedFrameRateRanges(
       HighSpeedVideoSessionConfig(videoCapture, preview)).first()

val sessionConfig = HighSpeedVideoSessionConfig(
    videoCapture,
    preview,
    frameRateRange = frameRateRange,
    isSlowMotionEnabled = true
)

cameraProvider.bindToLifecycle(
     lifecycleOwner, cameraSelector, sessionConfig)

// Start recording slow motion videos.
val recording = recorder.prepareRecording(context, outputOption)
      .start(executor, {})
Fragment kodu w języku Kotlin w 14:18 w filmie „Funkcje i interfejsy API do przechwytywania”
val concurrentCamera =
        cameraProvider.bindToLifecycle(list(config1, config2))
// CameraX 1.7 features: rounded corner, border width & color
val pipSettings = CompositionSettings.Builder()
    .setOffset(0.2f, 0.2f)
    .setScale(0.35f, 0.35f)
    .setRoundedCornerRatio(0.15f)
    .setBorderWidthRatio(0.02f)
    .setBorderColor(Color.WHITE)
    .setZOrder(1)
    .build()
// CameraX 1.7 features: dynamical composition update
concurrentCamera.setCompositionSettings(
listOf(mainSettings, pipSettings))
Fragment kodu w języku Kotlin w 17:51 w filmie „Uczenie maszynowe na urządzeniu”
// 1. Initialize the on-device GenerativeModel client
val generativeModel = Generation.getClient()

// 2. Check whether Gemini Nano is available on the device
val status = generativeModel.checkStatus()
when (status) {
    FeatureStatus.UNAVAILABLE -> {}
    FeatureStatus.DOWNLOADABLE -> {}
    FeatureStatus.DOWNLOADING -> {}
    FeatureStatus.AVAILABLE -> {}
}
Fragment kodu w języku Kotlin w 18:06 w filmie „Uczenie maszynowe na urządzeniu”
// 3. Build a multimodal request with the image and text prompt
val request = generateContentRequest(
    ImagePart(bitmap),
    TextPart("Describe this image in detail.")
) {}

// 4. Run inference on-device and get the description
val response = generativeModel.generateContent(request)
val description = response.candidates.first().text
Fragment kodu w języku Kotlin w 19:38 w filmie „Uczenie maszynowe na urządzeniu”
// 1. Build GestureRecognizerOptions
val options = GestureRecognizerOptions.builder().setBaseOptions(
    BaseOptions.builder().setModelAssetPath(MP_RECOGNIZER_TASK).build())
    .setRunningMode(RunningMode.LIVE_STREAM)
    .setResultListener { result, image -> /* Handle detected gesture */ }
    .build()

// 2. Create the GestureRecognizer
val gestureRecognizer = GestureRecognizer.createFromOptions(context, options)

// 3. Convert CameraX ImageProxy to MediaPipe MPImage
val bitmap = rotateBitmap(imageProxy.toBitmap(),
imageProxy.imageInfo.rotationDegrees)
val mpImage = BitmapImageBuilder(bitmap).build()

// 4. Run gesture recognition on the image
gestureRecognizer.recognizeAsync(mpImage, frameTime)
Fragment kodu w Kotlinie w 20:44 w filmie „Ulepszanie obrazu i filmu”
// Step 1: Verify device support & install module
val enhancementClient = Enhancement.getClient(context)

suspend fun prepare(): Boolean {
    if (!enhancementClient.isDeviceSupported().await()) return false
    if (!enhancementClient.isModuleInstalled().await()) {
        check(enhancementClient.installModule().await()) {
            "Enhancement module install failed"
        }
    }
    return true
}
Fragment kodu w Kotlinie w 20:57 w filmie „Ulepszanie obrazów i filmów”
// Step 2: Select Enhancement options & create session
val options = EnhancementOptions(
    width = bitmap.width,
    height = bitmap.height,
    enhancementMode = EnhancementMode.BITMAP,
    isTonemappingEnabled = true,
    isDeblurAndDenoisePhotoEnabled = true,
    isDeblurAndDenoiseVideoEnabled = false,
    isUpscalePhotoEnabled = false,
    isUpscaleVideoEnabled = false
)

val enhancementSession = enhancementClient
    .createSessionAsync(options, enhancementExecutor)
Fragment kodu w języku Kotlin w 21:05 w filmie „Ulepszanie obrazów i filmów”
// Step 3: Process Enhancement
val enhancedBitmap = suspendCancellableCoroutine { continuation ->
    val enhancementCallback = object : EnhancementCallback {
        override fun onBitmapProcessed(result: Bitmap) {
            if (continuation.isActive) continuation.resume(result)
        }
        override fun onError(statusCode: Int) {
            if (continuation.isActive) {
                continuation.resumeWithException(
                    Exception("Bitmap processing failed: $statusCode")
                )
            }
        }
        override fun onSurfaceProcessed(timestamp: Long) {}
    }

    enhancementSession.process(bitmap, options, enhancementCallback)
}
Fragment kodu w Kotlinie w 22:51 w filmie „Ulepszanie obrazów i filmów”
// Integrates SW Low Light Boost
val lowLightBoostEffect =
    LowLightBoostEffect(
        CameraEffect.PREVIEW or
        CameraEffect.VIDEO_CAPTURE or
        CameraEffect.IMAGE_CAPTURE
    )

val sessionConfig = SessionConfig(
    useCases = listOf(preview, imageCapture),
    effects = lowLightBoostEffect
)

cameraProvider.bindToLifecycle(.. sessionConfig)