Architektura nagrywania filmów w aplikacji CameraX

System przechwytywania zwykle nagrywa strumienie wideo i audio, kompresuje je, multipleksuje i zapisuje wynikowy strumień na dysku.

schemat koncepcyjny systemu do przechwytywania obrazu i dźwięku
Rysunek 1. Schemat koncepcyjny systemu do przechwytywania obrazu i dźwięku.

W CameraX rozwiązaniem do nagrywania filmów jest przypadek użycia VideoCapture:

schemat koncepcyjny pokazujący, jak CameraX obsługuje przypadek użycia związany z nagrywaniem filmów
Rysunek 2. Diagram koncepcyjny pokazujący, jak CameraX obsługuje przypadek użycia VideoCapture.

Jak pokazano na rysunku 2, przechwytywanie wideo w CameraX obejmuje kilka komponentów architektury wysokiego poziomu:

  • SurfaceProvider jako źródło filmu.
  • AudioSource jako źródło dźwięku.
  • 2 kodery do kodowania i kompresowania obrazu i dźwięku;
  • multiplekser multimediów do multipleksowania 2 strumieni.
  • Zapisujący plik, który zapisuje wynik.

Interfejs VideoCapture API upraszcza złożony mechanizm przechwytywania i zapewnia aplikacjom znacznie prostszy i bardziej przejrzysty interfejs API.

Omówienie interfejsu VideoCapture API

VideoCapture to przypadek użycia CameraX, który dobrze sprawdza się samodzielnie lub w połączeniu z innymi przypadkami użycia. Konkretne obsługiwane kombinacje zależą od możliwości sprzętowych aparatu, ale Preview i VideoCapture to prawidłowa kombinacja przypadków użycia na wszystkich urządzeniach.

Interfejs VideoCapture API składa się z tych obiektów, które komunikują się z aplikacjami:

  • VideoCapture to klasa przypadków użycia najwyższego poziomu. VideoCapture wiąże się z LifecycleOwner za pomocą CameraSelector i innych UseCases CameraX. Więcej informacji o tych pojęciach i zastosowaniach znajdziesz w artykule Architektura CameraX.
  • Recorder to implementacja VideoOutput, która jest ściśle powiązana z VideoCapture. Recorder służy do nagrywania obrazu i dźwięku. Aplikacja tworzy nagrania z Recorder.
  • PendingRecording konfiguruje nagrywanie, udostępniając opcje takie jak włączenie dźwięku i ustawienie detektora zdarzeń. Aby utworzyć PendingRecording, musisz użyć Recorder. PendingRecording nie nagrywa niczego.
  • Recording wykonuje nagranie. Aby utworzyć Recording, musisz użyć PendingRecording.

Rysunek 3 przedstawia relacje między tymi obiektami:

diagram pokazujący interakcje, które występują w przypadku użycia przechwytywania wideo
Rysunek 3. Diagram przedstawiający interakcje, które występują w przypadku użycia VideoCapture.

Legenda:

  1. Utwórz Recorder za pomocą QualitySelector.
  2. Skonfiguruj Recorder za pomocą jednego z OutputOptions.
  3. W razie potrzeby włącz dźwięk za pomocą withAudioEnabled().
  4. Zadzwoń do start(), korzystając z VideoRecordEvent, aby rozpocząć nagrywanie.
  5. Aby sterować nagrywaniem, użyj pause()/resume()/stop() na Recording.
  6. Odpowiedz na VideoRecordEvents w detektorze zdarzeń.

Szczegółową listę interfejsów API znajdziesz w pliku current.txt w kodzie źródłowym.

Korzystanie z interfejsu VideoCapture API

Aby zintegrować przypadek użycia VideoCapture CameraX z aplikacją, wykonaj te czynności:

  1. Powiąż VideoCapture.
  2. Przygotuj i skonfiguruj nagrywanie.
  3. Rozpocznij i kontroluj nagrywanie środowiska wykonawczego.

W sekcjach poniżej opisujemy, co możesz zrobić na każdym etapie, aby uzyskać pełną sesję nagrywania.

Powiąż przechwytywanie wideo

Aby powiązać przypadek użycia VideoCapture, wykonaj te czynności:

  1. Utwórz obiekt Recorder.
  2. Utwórz obiekt VideoCapture.
  3. Powiąż z: Lifecycle.

Interfejs CameraX VideoCapture API jest zgodny ze wzorcem projektowym kreatora. Aplikacje używają Recorder.Builder do tworzenia Recorder. Możesz też skonfigurować rozdzielczość wideo dla Recorder za pomocą obiektu QualitySelector.

CameraX Recorder obsługuje te wstępnie zdefiniowane Qualities rozdzielczości wideo:

  • Quality.UHD w przypadku rozmiaru filmu 4K Ultra HD (2160p)
  • Quality.QHD dla rozmiaru wideo Quad HD (1440p), wprowadzonego w CameraX 1.7.0-alpha02
  • Quality.FHD w przypadku rozmiaru filmu Full HD (1080p)
  • Quality.HD w przypadku rozmiaru wideo HD (720p)
  • Quality.SD w przypadku rozmiaru SD (480p),

Pamiętaj, że CameraX może też wybrać inne rozdzielczości, jeśli aplikacja na to zezwala.

Dokładny rozmiar filmu w przypadku każdej opcji zależy od możliwości kamery i kodera. Więcej informacji znajdziesz w dokumentacji CamcorderProfile.

Aplikacje mogą konfigurować rozdzielczość, tworząc obiekt QualitySelector. Możesz utworzyć QualitySelector za pomocą jednej z tych metod:

  • Podaj kilka preferowanych rozdzielczości za pomocą fromOrderedList i uwzględnij strategię zastępczą na wypadek, gdyby żadna z preferowanych rozdzielczości nie była obsługiwana.

    CameraX może wybrać najlepsze dopasowanie zastępcze na podstawie możliwości wybranej kamery. Więcej informacji znajdziesz w FallbackStrategy specification (QualitySelector). Na przykład ten kod żąda najwyższej obsługiwanej rozdzielczości nagrywania, a jeśli żadna z żądanych rozdzielczości nie jest obsługiwana, upoważnia CameraX do wybrania rozdzielczości najbliższej Quality.SD:

    val qualitySelector = QualitySelector.fromOrderedList(
             listOf(Quality.UHD, Quality.QHD, Quality.FHD, Quality.HD, Quality.SD),
             FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))
    
  • Najpierw sprawdź możliwości kamery i wybierz jedną z obsługiwanych rozdzielczości za pomocą QualitySelector.from:

    val cameraInfo = cameraProvider.availableCameraInfos.filter {
        it.cameraCharacteristics.get(CameraCharacteristics.LENS_FACING) ==
            CameraMetadata.LENS_FACING_BACK
    }
    
    // Filter to SDR resolutions supported by both the camera sensor and Recorder
    // encoder so the UI picker only displays valid options.
    val supportedQualities =
        Recorder.getVideoCapabilities(cameraInfo[0]).getSupportedQualities(DynamicRange.SDR)
    val filteredQualities = arrayListOf (Quality.UHD, Quality.QHD, Quality.FHD, Quality.HD, Quality.SD)
                           .filter { supportedQualities.contains(it) }
    
    viewBinding.simpleQualityListView.apply {
        adapter = ArrayAdapter(context,
                               android.R.layout.simple_list_item_1,
                               filteredQualities.map { it.qualityToString() })
    
        setOnItemClickListener { _, _, position, _ ->
            // QualitySelector is configured at Recorder build time, so selecting
            // a new resolution requires building a new Recorder instance.
            val qualitySelector = QualitySelector.from(filteredQualities[position])
    
            val recorder = Recorder.Builder()
                .setQualitySelector(qualitySelector).build()
    
             // ...
        }
    }
    
    fun Quality.qualityToString() : String {
        return when (this) {
            Quality.UHD -> "UHD"
            Quality.QHD -> "QHD"
            Quality.FHD -> "FHD"
            Quality.HD -> "HD"
            Quality.SD -> "SD"
            else -> throw IllegalArgumentException()
        }
    }
    
    

    Każdy obiekt Quality zwracany przez VideoCapabilities.getSupportedQualities (z Recorder.getVideoCapabilities dla DynamicRange.SDR) działa w przypadku użycia VideoCapture samodzielnie lub w połączeniu z przypadkiem użycia Preview. Jeśli powiążesz też ImageCapture lub ImageAnalysis, CameraX może nie powiązać, jeśli aparat nie obsługuje tej kombinacji.

Gdy masz już QualitySelector, aplikacja może utworzyć obiekt VideoCapture i wykonać powiązanie. Pamiętaj, że to powiązanie jest takie samo jak w przypadku innych zastosowań:

val recorder = Recorder.Builder()
    .setExecutor(cameraExecutor).setQualitySelector(qualitySelector)
    .build()
val videoCapture = VideoCapture.withOutput(recorder)

try {
    // Bind use cases to camera.
    cameraProvider.bindToLifecycle(
        this, CameraSelector.DEFAULT_BACK_CAMERA, preview, videoCapture
    )
} catch (exc: Exception) {
    Log.e(TAG, "Use case binding failed", exc)
}

Zwróć uwagę, że bindToLifecycle() zwraca obiekt Camera. Więcej informacji o sterowaniu obrazem z kamery, np. powiększeniem i ekspozycją, znajdziesz w tym przewodniku.

Recorder wybiera najbardziej odpowiedni format dla systemu. Najpopularniejszym kodekiem wideo jest H.264 AVC z formatem kontenera MPEG-4.

Konfigurowanie i tworzenie nagrania

Z poziomu Recorder aplikacja może tworzyć obiekty nagrywania, aby przechwytywać obraz i dźwięk. Aplikacje tworzą nagrania w ten sposób:

  1. Skonfiguruj OutputOptions za pomocą prepareRecording().
  2. (Opcjonalnie) Włącz nagrywanie dźwięku.
  3. Użyj start(), aby zarejestrować detektor VideoRecordEvent i rozpocząć nagrywanie filmu.

Gdy wywołasz funkcję start(), Recorder zwraca obiekt Recording. Aplikacja może używać tego obiektu Recording do zakończenia przechwytywania lub wykonywania innych działań, takich jak wstrzymywanie lub wznawianie.

Recorder obsługuje tylko 1 obiekt Recording naraz. Możesz rozpocząć nowe nagrywanie, gdy wywołasz metodę Recording.stop() lub Recording.close() na poprzednim obiekcie Recording.

Przyjrzyjmy się tym krokom bardziej szczegółowo. Najpierw aplikacja konfiguruje OutputOptions dla Dyktafonu z Recorder.prepareRecording(). Recorder obsługuje te typy OutputOptions:

  • FileDescriptorOutputOptions, aby przechwycić je do FileDescriptor.
  • FileOutputOptions, aby zapisać je w File.
  • MediaStoreOutputOptions, aby przechwycić je do MediaStore.

Wszystkie typy OutputOptions umożliwiają ustawienie maksymalnego rozmiaru pliku za pomocą setFileSizeLimit(). Inne opcje są specyficzne dla poszczególnych typów danych wyjściowych, np. ParcelFileDescriptor dla FileDescriptorOutputOptions.

prepareRecording() zwraca obiekt PendingRecording, który jest obiektem pośrednim używanym do utworzenia odpowiedniego obiektu Recording. PendingRecording to klasa przejściowa, która w większości przypadków powinna być niewidoczna i rzadko jest przechowywana w pamięci podręcznej aplikacji.

Aplikacje mogą dodatkowo skonfigurować nagrywanie, na przykład:

  • Włącz dźwięk za pomocą withAudioEnabled().
  • Zarejestruj detektor, aby otrzymywać zdarzenia nagrywania filmów za pomocą start(Executor, Consumer<VideoRecordEvent>).
  • Zezwalaj na nagrywanie ciągłe, gdy obiekt VideoCapture, do którego jest dołączone, jest ponownie powiązany z inną kamerą, za pomocą PendingRecording.asPersistentRecording().

Aby rozpocząć nagrywanie, zadzwoń pod numer PendingRecording.start(). CameraX przekształca PendingRecording w Recording, dodaje żądanie nagrywania do kolejki i zwraca nowo utworzony obiekt Recording do aplikacji. Gdy rozpocznie się nagrywanie na odpowiednim urządzeniu z aparatem, CameraX wysyła zdarzenie VideoRecordEvent.EVENT_TYPE_START.

Poniższy przykład pokazuje, jak nagrać obraz i dźwięk do pliku MediaStore:

// Create MediaStoreOutputOptions for our recorder.
val name = "CameraX-recording-" +
    SimpleDateFormat(FILENAME_FORMAT, Locale.US)
        .format(System.currentTimeMillis()) + ".mp4"
val contentValues = ContentValues().apply {
    put(MediaStore.Video.Media.DISPLAY_NAME, name)
}
val mediaStoreOutput = MediaStoreOutputOptions.Builder(
    this.contentResolver,
    MediaStore.Video.Media.EXTERNAL_CONTENT_URI
)
    .setContentValues(contentValues)
    .build()

// 2. Configure Recorder and Start recording to the mediaStoreOutput.
val recording = videoCapture.output
    .prepareRecording(context, mediaStoreOutput)
    .withAudioEnabled()
    .start(ContextCompat.getMainExecutor(this), captureListener)

Podczas gdy podgląd z aparatu jest domyślnie odzwierciedlany na przednim aparacie, filmy nagrane przez VideoCapture nie są domyślnie odzwierciedlane. W CameraX 1.3 można teraz tworzyć lustrzane odbicia nagrań wideo, dzięki czemu podgląd z przedniego aparatu i nagrany film są takie same.

Dostępne są 3 opcje MirrorMode: MIRROR_MODE_OFF, MIRROR_MODE_ON i MIRROR_MODE_ON_FRONT_ONLY. Aby dopasować obraz do podglądu z aparatu, Google zaleca użycie trybu MIROR_MODE_ON_FRONT_ONLY, co oznacza, że odbicie lustrzane nie jest włączone w przypadku tylnego aparatu, ale jest włączone w przypadku przedniego. Więcej informacji o MirrorMode znajdziesz w artykule MirrorMode constants.

Ten fragment kodu pokazuje, jak wywołać VideoCapture.Builder.setMirrorMode() za pomocą MIRROR_MODE_ON_FRONT_ONLY. Więcej informacji znajdziesz w setMirrorMode().

val recorder = Recorder.Builder().build()

val videoCapture = VideoCapture.Builder(recorder)
    .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
    .build()

useCases.add(videoCapture)

Zakres dynamiczny (film HDR)

CameraX obsługuje 10-bitowe nagrywanie filmów w trybie High Dynamic Range (HDR), np. HLG10, HDR10, HDR10+ lub Dolby Vision, przy użyciu DynamicRange.

Aby nagrywać filmy w trybie HDR, wykonaj te czynności:

  1. Sprawdź obsługiwane zakresy dynamiczne aparatu za pomocą VideoCapabilities.getSupportedDynamicRanges().
  2. Opcjonalnie: jeśli skonfigurujesz niestandardowy QualitySelector, sprawdź, które jakości są obsługiwane w przypadku tego zakresu dynamicznego, używając getSupportedQualities(DynamicRange).
  3. Skonfiguruj VideoCapture.Builder za pomocą setDynamicRange().

Poniższy przykład pokazuje, jak włączyć nagrywanie w 10-bitowym trybie HDR HLG, gdy aparat obsługuje tę funkcję:

Kotlin

val videoCapabilities = Recorder.getVideoCapabilities(cameraInfo)
val supportedHdrRanges = videoCapabilities.supportedDynamicRanges
    .filter { it != DynamicRange.SDR }

val recorder = Recorder.Builder().build()
val videoCapture = VideoCapture.Builder(recorder)
    .apply {
        if (supportedHdrRanges.contains(DynamicRange.HLG_10_BIT)) {
            setDynamicRange(DynamicRange.HLG_10_BIT)
        }
    }
    .build()

Java

VideoCapabilities videoCapabilities = Recorder.getVideoCapabilities(cameraInfo);
Set<DynamicRange> supportedRanges = videoCapabilities.getSupportedDynamicRanges();

Recorder recorder = new Recorder.Builder().build();
VideoCapture.Builder<Recorder> builder = new VideoCapture.Builder<>(recorder);
if (supportedRanges.contains(DynamicRange.HLG_10_BIT)) {
    builder.setDynamicRange(DynamicRange.HLG_10_BIT);
}
VideoCapture<Recorder> videoCapture = builder.build();

Stabilizacja wideo

CameraX obsługuje stabilizację wideo, aby zmniejszyć drgania i zakłócenia podczas nagrywania filmów.

Aby włączyć stabilizację obrazu, wykonaj te czynności:

  1. Sprawdź, czy aparat obsługuje stabilizację, używając VideoCapabilities.isStabilizationSupported().
  2. Włącz go w VideoCapture.Builder, używając setVideoStabilizationEnabled(true).

Poniższy przykład włącza stabilizację, gdy aparat ją obsługuje:

Kotlin

val videoCapabilities = Recorder.getVideoCapabilities(cameraInfo)
val videoCapture = VideoCapture.Builder(recorder)
    .setVideoStabilizationEnabled(videoCapabilities.isStabilizationSupported)
    .build()

Java

VideoCapabilities videoCapabilities = Recorder.getVideoCapabilities(cameraInfo);
VideoCapture<Recorder> videoCapture = new VideoCapture.Builder<>(recorder)
    .setVideoStabilizationEnabled(videoCapabilities.isStabilizationSupported())
    .build();

Sterowanie aktywnym nagrywaniem

Możesz wstrzymać, wznowić i zatrzymać trwający proces Recording, korzystając z tych metod:

  • pause, aby wstrzymać bieżące aktywne nagrywanie.
  • resume(), aby wznowić wstrzymane aktywne nagrywanie.
  • stop(), aby zakończyć nagrywanie i usunąć wszystkie powiązane obiekty nagrywania.
  • mute(), aby wyciszyć lub wyłączyć wyciszenie bieżącego nagrania.

Pamiętaj, że możesz wywołać metodę stop(), aby zakończyć Recording niezależnie od tego, czy nagrywanie jest wstrzymane, czy aktywne.

Jeśli masz zarejestrowany EventListener w PendingRecording.start(), Recording komunikuje się za pomocą VideoRecordEvent.

  • VideoRecordEvent.EVENT_TYPE_STATUS służy do rejestrowania statystyk, takich jak bieżący rozmiar pliku i zarejestrowany przedział czasu.
  • VideoRecordEvent.EVENT_TYPE_FINALIZE jest używany do zapisywania wyniku nagrywania i zawiera informacje takie jak identyfikator URI pliku końcowego oraz powiązane błędy.

Gdy aplikacja otrzyma obiekt EVENT_TYPE_FINALIZE, który wskazuje na pomyślne zakończenie sesji nagrywania, możesz uzyskać dostęp do nagranego filmu z lokalizacji określonej w obiekcie OutputOptions.

Amplituda i stan dźwięku w czasie rzeczywistym

Podczas nagrywania zdarzenia VideoRecordEvent.Status dostarczają statystyki dźwięku w czasie rzeczywistym za pomocą AudioStats:

  • AudioStats.getAudioAmplitude() zwraca bieżącą szczytową amplitudę dźwięku z zakresu od 0.0 do 1.0. Możesz użyć tej wartości do sterowania miernikami interfejsu lub wizualizatorami fali dźwiękowej.
  • AudioStats.getAudioState() wskazuje bieżący stan strumienia audio, np. AUDIO_STATE_ACTIVE, AUDIO_STATE_MUTED lub AUDIO_STATE_SOURCE_SILENCED.

W tym przykładzie odczytywana jest amplituda dźwięku ze zdarzenia stanu:

Kotlin

when (event) {
    is VideoRecordEvent.Status -> {
        val audioStats = event.recordingStats.audioStats
        if (audioStats.hasAudio()) {
            val amplitude = audioStats.audioAmplitude
            updateAudioWaveform(amplitude)
        }
    }
    // ...
}

Java

if (event instanceof VideoRecordEvent.Status) {
    VideoRecordEvent.Status status = (VideoRecordEvent.Status) event;
    AudioStats audioStats = status.getRecordingStats().getAudioStats();
    if (audioStats.hasAudio()) {
        double amplitude = audioStats.getAudioAmplitude();
        updateAudioWaveform(amplitude);
    }
}

Filmy w trybie przyspieszonym i zwolnionym

CameraX obsługuje nagrywanie filmów z dużą szybkością, np. 120, 240 lub 480 kl./s, co umożliwia 2 różne zastosowania:

  • Film z dużą liczbą klatek na sekundę (zwolnione tempo obsługiwane przez odtwarzacz): CameraX rejestruje i zapisuje klatki z dużą liczbą klatek na sekundę (np. 120 kl./s). Odtwarzacz wideo kontroluje szybkość odtwarzania, umożliwiając użytkownikom przełączanie się między normalną szybkością a zwolnionym tempem lub analizowanie klatka po klatce.
  • Gotowy do odtworzenia film w zwolnionym tempie: CameraX przetwarza strumień o wysokiej szybkości i generuje standardowy plik wideo o częstotliwości 30 kl./s z dostosowanymi sygnaturami czasowymi klatek. Efekt zwolnionego tempa jest „wbudowany”, więc film jest automatycznie odtwarzany w zwolnionym tempie w każdym standardowym odtwarzaczu bez specjalnej obsługi. Na przykład nagranie z szybkością 120 kl./s odtwarzane z szybkością 30 kl./s daje film w zwolnionym tempie 1/4x.

Sprawdzanie możliwości szybkiego połączenia

Nagrywanie filmów z dużą szybkością wymaga obsługi sprzętowej i odpowiednich definicji CamcorderProfile od producentów urządzeń, takich jak CamcorderProfile.QUALITY_HIGH_SPEED_1080P lub QUALITY_HIGH_SPEED_720P.

Przed skonfigurowaniem nagrywania z dużą szybkością sprawdź możliwości nagrywania z dużą szybkością za pomocą Recorder.getHighSpeedVideoCapabilities(CameraInfo):

Kotlin

val highSpeedCapabilities = Recorder.getHighSpeedVideoCapabilities(cameraInfo)
if (highSpeedCapabilities == null) {
    // Camera does not support high-speed video capture
    return
}
val supportedQualities = highSpeedCapabilities.getSupportedQualities(DynamicRange.SDR)

Java

VideoCapabilities highSpeedCapabilities = Recorder.getHighSpeedVideoCapabilities(cameraInfo);
if (highSpeedCapabilities == null) {
    // Camera does not support high-speed video capture
    return;
}
List<Quality> supportedQualities = highSpeedCapabilities.getSupportedQualities(DynamicRange.SDR);

Konfigurowanie HighSpeedVideoSessionConfig

Aby korzystać z szybkiego przechwytywania, połącz przypadki użycia VideoCapture i Preview w HighSpeedVideoSessionConfig. Aby skonfigurować sesję, wykonaj te czynności:

  1. Utwórz przypadek użycia VideoCapture i przypadek użycia Preview.
  2. Zainicjuj HighSpeedVideoSessionConfig.Builder z przypadkiem użycia VideoCapture i dołącz Preview.
  3. Sprawdź obsługiwane zakresy liczby klatek za pomocą CameraInfo.getSupportedFrameRateRanges(SessionConfig) i ustaw wybrany zakres.
  4. Wybierz film z dużą liczbą klatek lub gotowy do odtworzenia film w zwolnionym tempie:
    • Aby utworzyć film z dużą liczbą klatek, pozostaw wartość setSlowMotionEnabled(false) (domyślna).
    • Aby utworzyć gotowy do odtworzenia film w zwolnionym tempie, wywołaj setSlowMotionEnabled(true).
  5. Powiąż konfigurację sesji za pomocą cameraProvider.bindToLifecycle().

Poniższy przykład konfiguruje i wiąże sesję w zwolnionym tempie gotową do odtworzenia:

Kotlin

val preview = Preview.Builder().build()
val recorder = Recorder.Builder()
    .setQualitySelector(QualitySelector.from(supportedQualities.first()))
    .build()
val videoCapture = VideoCapture.withOutput(recorder)

// Build HighSpeedVideoSessionConfig
val sessionConfigBuilder = HighSpeedVideoSessionConfig.Builder(videoCapture)
    .setPreview(preview)
    // Enable automatic slow-motion playback at 30 fps
    .setSlowMotionEnabled(true)

// Query and set a supported high-speed frame rate range (such as 120 or 240 fps).
// The set is non-empty because getHighSpeedVideoCapabilities() returned non-null earlier.
val supportedFpsRanges = cameraInfo.getSupportedFrameRateRanges(sessionConfigBuilder.build())
sessionConfigBuilder.setFrameRateRange(supportedFpsRanges.first())

val sessionConfig = sessionConfigBuilder.build()

// Bind session config to lifecycle
cameraProvider.bindToLifecycle(
    lifecycleOwner,
    CameraSelector.DEFAULT_BACK_CAMERA,
    sessionConfig
)

Java

Preview preview = new Preview.Builder().build();
Recorder recorder = new Recorder.Builder()
    .setQualitySelector(QualitySelector.from(supportedQualities.get(0)))
    .build();
VideoCapture<Recorder> videoCapture = VideoCapture.withOutput(recorder);

HighSpeedVideoSessionConfig.Builder sessionConfigBuilder =
    new HighSpeedVideoSessionConfig.Builder(videoCapture)
        .setPreview(preview)
        .setSlowMotionEnabled(true);

// The set is non-empty because getHighSpeedVideoCapabilities() returned non-null earlier.
Set<Range<Integer>> supportedFpsRanges =
    cameraInfo.getSupportedFrameRateRanges(sessionConfigBuilder.build());
sessionConfigBuilder.setFrameRateRange(supportedFpsRanges.iterator().next());

HighSpeedVideoSessionConfig sessionConfig = sessionConfigBuilder.build();

cameraProvider.bindToLifecycle(
    lifecycleOwner,
    CameraSelector.DEFAULT_BACK_CAMERA,
    sessionConfig
);

Możesz też użyć języka DSL w Kotlin, który został wprowadzony w CameraX 1.7.0-alpha02, aby szybko skonfigurować sesje o wysokiej szybkości:

val sessionConfig = highSpeedVideoSessionConfig(videoCapture) {
    this.preview = preview
    isSlowMotionEnabled = true
    frameRateRange = supportedFpsRanges.first()
}
cameraProvider.bindToLifecycle(
    lifecycleOwner,
    CameraSelector.DEFAULT_BACK_CAMERA,
    sessionConfig
)

Nagrywanie filmów w trybie przyspieszonym lub w zwolnionym tempie

Po powiązaniu HighSpeedVideoSessionConfig rozpocznij nagrywanie za pomocą VideoCapture w taki sam sposób jak w przypadku standardowych nagrań wideo:

Kotlin

val recording = videoCapture.output
    .prepareRecording(context, mediaStoreOutput)
    .withAudioEnabled()
    .start(ContextCompat.getMainExecutor(context)) { event ->
        when (event) {
            is VideoRecordEvent.Start -> Log.d(TAG, "Recording started")
            is VideoRecordEvent.Finalize -> {
                if (!event.hasError()) {
                    Log.d(TAG, "Video saved: ${event.outputResults.outputUri}")
                }
            }
        }
    }

Java

Recording recording = videoCapture.getOutput()
    .prepareRecording(context, mediaStoreOutput)
    .withAudioEnabled()
    .start(ContextCompat.getMainExecutor(context), event -> {
        if (event instanceof VideoRecordEvent.Start) {
            Log.d(TAG, "Recording started");
        } else if (event instanceof VideoRecordEvent.Finalize) {
            VideoRecordEvent.Finalize finalize = (VideoRecordEvent.Finalize) event;
            if (!finalize.hasError()) {
                Log.d(TAG, "Video saved: " + finalize.getOutputResults().getOutputUri());
            }
        }
    });

Dodatkowe materiały

Więcej informacji o CameraX znajdziesz w tych materiałach: