Arsitektur perekaman video CameraX

Sistem perekaman umumnya merekam streaming video dan audio, mengompresinya, menggabungkan dua streaming secara multipleks, lalu menulis streaming yang dihasilkan ke disk.

diagram konseptual untuk sistem perekaman video dan audio
Gambar 1. Diagram konseptual untuk sistem perekaman video dan audio.

Di CameraX, solusi untuk perekaman video adalah kasus penggunaan VideoCapture :

diagram konseptual yang menunjukkan cara CameraX menangani
         kasus penggunaan perekaman video
Gambar 2. Diagram konseptual yang menunjukkan cara CameraX menangani kasus penggunaan VideoCapture.

Seperti yang ditunjukkan dalam gambar 2, perekaman video CameraX menyertakan beberapa komponen arsitektur tingkat tinggi:

  • SurfaceProvider untuk sumber video.
  • AudioSource untuk sumber audio.
  • Dua encoder untuk mengenkode dan mengompresi video/audio.
  • Penggabung multipleks media untuk menggabungkan dua streaming secara multipleks.
  • Penyimpan file untuk menuliskan hasilnya.

VideoCapture API meringkas mesin perekaman yang kompleks serta memberikan API yang jauh lebih sederhana dan mudah ke aplikasi.

Ringkasan VideoCapture API

VideoCapture adalah kasus penggunaan CameraX yang berfungsi dengan sendirinya atau saat digabungkan dengan kasus penggunaan lainnya. Kombinasi tertentu yang didukung bergantung pada kemampuan hardware kamera, tetapi Preview dan VideoCapture adalah kombinasi kasus penggunaan yang valid di semua perangkat.

VideoCapture API terdiri dari objek berikut yang berkomunikasi dengan aplikasi:

  • VideoCapture adalah class kasus penggunaan level teratas. VideoCapture terikat pada LifecycleOwner dengan CameraSelector dan UseCases CameraX lainnya. Untuk mengetahui informasi konsep dan penggunaan ini lebih lanjut, lihat Arsitektur CameraX.
  • Recorder adalah implementasi VideoOutput yang dikaitkan erat dengan VideoCapture. Recorder digunakan untuk melakukan perekaman video dan audio. Aplikasi membuat rekaman dari Recorder.
  • PendingRecording mengonfigurasi perekaman, yang memberikan opsi seperti mengaktifkan audio dan menyetel pemroses peristiwa. Anda harus menggunakan Recorder untuk membuat PendingRecording. PendingRecording tidak merekam apa pun.
  • Recording akan melakukan perekaman yang sebenarnya. Anda harus menggunakan PendingRecording untuk membuat Recording.

Gambar 3 menunjukkan hubungan antara objek ini:

diagram yang menunjukkan interaksi yang terjadi dalam kasus penggunaan
         perekaman video
Gambar 3. Diagram yang menunjukkan interaksi yang terjadi dalam kasus penggunaan VideoCapture.

Keterangan:

  1. Membuat Recorder dengan QualitySelector.
  2. Mengonfigurasi Recorder dengan salah satu OutputOptions.
  3. Mengaktifkan audio dengan withAudioEnabled() jika diperlukan.
  4. Memanggil start() dengan pemroses VideoRecordEvent untuk mulai merekam.
  5. Menggunakan pause()/resume()/stop() pada Recording untuk mengontrol perekaman.
  6. Merespons VideoRecordEvents dalam pemroses peristiwa Anda.

Daftar API mendetail ada di current.txt dalam kode sumber.

Menggunakan VideoCapture API

Untuk mengintegrasikan kasus penggunaan VideoCapture CameraX ke dalam aplikasi Anda, lakukan langkah-langkah berikut:

  1. Ikat VideoCapture.
  2. Siapkan dan konfigurasikan perekaman.
  3. Mulai dan kontrol perekaman runtime.

Bagian berikut menguraikan hal-hal yang dapat Anda lakukan di setiap langkah untuk mendapatkan sesi perekaman menyeluruh.

Mengikat VideoCapture

Untuk mengikat kasus penggunaan VideoCapure, lakukan langkah-langkah berikut:

  1. Buat objek Recorder.
  2. Buat objek VideoCapture.
  3. Ikatkan ke Lifecycle.

VideoCapture API CameraX mengikuti pola desain builder. Aplikasi menggunakan Recorder.Builder untuk membuat Recorder. Anda juga dapat mengonfigurasi resolusi video untuk Recorder melalui objek QualitySelector.

Recorder CameraX mendukung Qualities berikut yang telah ditentukan sebelumnya untuk resolusi video:

  • Quality.UHD untuk ukuran video ultra HD 4K (2160p)
  • Quality.FHD untuk ukuran video full HD (1080p)
  • Quality.HD untuk ukuran video HD (720p)
  • Quality.SD untuk ukuran video SD (480p)

Perhatikan bahwa CameraX juga memilih resolusi lain saat diizinkan oleh aplikasi.

Ukuran video yang tepat dari setiap pilihan bergantung pada kemampuan kamera dan encoder. Untuk mengetahui informasi selengkapnya, baca dokumentasi untuk CamcorderProfile.

Aplikasi dapat mengonfigurasi resolusi dengan membuat QualitySelector. Anda dapat membuat QualitySelector menggunakan salah satu metode berikut:

  • Berikan beberapa resolusi pilihan menggunakan fromOrderedList(), dan sertakan strategi penggantian untuk digunakan jika tidak ada resolusi pilihan yang didukung.

    CameraX dapat menentukan pencocokan penggantian terbaik berdasarkan kemampuan kamera yang dipilih, lihat FallbackStrategy specification QualitySelector untuk detail selengkapnya. Misalnya, kode berikut meminta resolusi tertinggi yang didukung untuk perekaman, dan jika tidak ada resolusi permintaan yang dapat didukung, izinkan CameraX untuk memilih resolusi yang paling mendekati resolusi Quality.SD:

    val qualitySelector = QualitySelector.fromOrderedList(
             listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD),
             FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))
    
  • Buat kueri kapabilitas kamera terlebih dahulu, dan pilih dari resolusi yang didukung menggunakan QualitySelector::from():

    val cameraInfo = cameraProvider.availableCameraInfos.filter {
        Camera2CameraInfo
        .from(it)
        .getCameraCharacteristic(CameraCharacteristics.LENS\_FACING) == CameraMetadata.LENS_FACING_BACK
    }
    
    val supportedQualities = QualitySelector.getSupportedQualities(cameraInfo[0])
    val filteredQualities = arrayListOf (Quality.UHD, Quality.FHD, Quality.HD, Quality.SD)
                           .filter { supportedQualities.contains(it) }
    
    // Use a simple ListView with the id of simple_quality_list_view
    viewBinding.simpleQualityListView.apply {
        adapter = ArrayAdapter(context,
                               android.R.layout.simple_list_item_1,
                               filteredQualities.map { it.qualityToString() })
    
        // Set up the user interaction to manually show or hide the system UI.
        setOnItemClickListener { _, _, position, _ ->
            // Inside View.OnClickListener,
            // convert Quality.* constant to QualitySelector
            val qualitySelector = QualitySelector.from(filteredQualities[position])
    
            // Create a new Recorder/VideoCapture for the new quality
            // and bind to lifecycle
            val recorder = Recorder.Builder()
                .setQualitySelector(qualitySelector).build()
    
             // ...
        }
    }
    
    // A helper function to translate Quality to a string
    fun Quality.qualityToString() : String {
        return when (this) {
            Quality.UHD -> "UHD"
            Quality.FHD -> "FHD"
            Quality.HD -> "HD"
            Quality.SD -> "SD"
            else -> throw IllegalArgumentException()
        }
    }
    
    

    Perhatikan bahwa kemampuan yang ditampilkan dari QualitySelector.getSupportedQualities() dijamin berfungsi untuk kasus penggunaan VideoCapture ataupun kombinasi kasus penggunaan VideoCapture dan Preview. Saat melakukan binding dengan kasus penggunaan ImageCapture atau ImageAnalysis, CameraX mungkin masih menggagalkan binding jika kombinasi yang diperlukan tidak didukung pada kamera yang diminta.

Setelah Anda memiliki QualitySelector, aplikasi dapat membuat objek VideoCapture dan melakukan binding. Perhatikan bahwa binding ini sama dengan kasus penggunaan lainnya:

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)
}

Perhatikan bahwa bindToLifecycle() menampilkan objek Camera. Lihat panduan ini untuk informasi mengontrol output kamera selengkapnya, seperti zoom dan eksposur.

Recorder akan memilih format yang paling cocok untuk sistem. Codec video yang paling umum adalah H.264 AVC) dengan format penampung MPEG-4.

Mengonfigurasi dan membuat rekaman

Dari Recorder, aplikasi dapat membuat objek perekaman untuk melakukan perekaman video dan audio. Aplikasi membuat rekaman dengan melakukan hal-hal berikut:

  1. Mengonfigurasikan OutputOptions dengan prepareRecording().
  2. (Opsional) Mengaktifkan rekaman audio.
  3. Menggunakan start() untuk mendaftarkan pemroses VideoRecordEvent, dan memulai perekaman video.

Recorder menampilkan objek Recording saat Anda memanggil fungsi start(). Aplikasi Anda dapat menggunakan objek Recording ini untuk menyelesaikan perekaman atau melakukan tindakan lainnya, seperti menjeda atau melanjutkan perekaman.

Recorder mendukung satu objek Recording dalam satu waktu. Anda dapat memulai perekaman baru setelah memanggil Recording.stop() atau Recording.close() pada objek Recording sebelumnya.

Mari kita lihat langkah-langkah ini secara lebih mendetail. Pertama, aplikasi akan mengonfigurasi OutputOptions untuk Perekam dengan Recorder.prepareRecording(). Recorder mendukung jenis OutputOptions berikut:

  • FileDescriptorOutputOptions untuk merekam ke dalam FileDescriptor.
  • FileOutputOptions untuk merekam ke dalam File.
  • MediaStoreOutputOptions untuk merekam ke dalam MediaStore.

Semua jenis OutputOptions memungkinkan Anda untuk menetapkan ukuran file maksimum dengan setFileSizeLimit(). Opsi lainnya khusus untuk jenis output individu, seperti ParcelFileDescriptor untuk FileDescriptorOutputOptions.

prepareRecording() menampilkan objek PendingRecording yang merupakan objek perantara yang digunakan untuk membuat objek Recording yang sesuai. PendingRecording adalah class sementara yang seharusnya tidak terlihat dan jarang di-cache oleh aplikasi.

Aplikasi dapat mengonfigurasi perekaman lebih lanjut, seperti:

  • Mengaktifkan audio dengan withAudioEnabled().
  • Mendaftarkan pemroses untuk menerima peristiwa perekaman video dengan start(Executor, Consumer<VideoRecordEvent>).
  • Izinkan perekaman untuk terus merekam saat VideoCapture yang terpasang dihubungkan kembali ke kamera lain, dengan PendingRecording.asPersistentRecording().

Untuk mulai merekam, panggil PendingRecording.start(). CameraX mengubah PendingRecording menjadi Recording, mengantrekan permintaan perekaman, dan menampilkan objek Recording yang baru dibuat ke aplikasi. Setelah perekaman dimulai pada perangkat Kamera yang sesuai, CameraX akan mengirimkan peristiwa VideoRecordEvent.EVENT_TYPE_START.

Contoh berikut menunjukkan cara merekam video dan audio ke dalam file 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)

Meskipun pratinjau kamera dicerminkan di kamera depan secara default, video yang direkam oleh VideoCapture tidak dicerminkan secara default. Dengan CameraX 1.3, kini Anda dapat mencerminkan rekaman video sehingga pratinjau kamera depan dan video yang direkam cocok.

Ada tiga opsi MirrorMode: MIRROR_MODE_OFF, MIRROR_MODE_ON, dan MIRROR_MODE_ON_FRONT_ONLY. Untuk menyelaraskan dengan pratinjau kamera, Google merekomendasikan penggunaan MIROR_MODE_ON_FRONT_ONLY, yang berarti pencerminan tidak diaktifkan untuk kamera belakang, tetapi diaktifkan untuk kamera depan. Untuk mengetahui informasi selengkapnya tentang MirrorMode, lihat MirrorMode constants.

Cuplikan kode ini menunjukkan cara memanggil VideoCapture.Builder.setMirrorMode() menggunakan MIRROR_MODE_ON_FRONT_ONLY. Untuk mengetahui informasi selengkapnya, lihat setMirrorMode().

Kotlin


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

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

useCases.add(videoCapture);

Java


Recorder.Builder builder = new Recorder.Builder();
if (mVideoQuality != QUALITY_AUTO) {
    builder.setQualitySelector(
        QualitySelector.from(mVideoQuality));
}
  VideoCapture<Recorder> videoCapture = new VideoCapture.Builder<>(builder.build())
      .setMirrorMode(MIRROR_MODE_ON_FRONT_ONLY)
      .build();
    useCases.add(videoCapture);

Mengontrol perekaman aktif

Anda dapat menjeda, melanjutkan, dan menghentikan Recording yang sedang berjalan dengan metode berikut:

  • pause untuk menjeda perekaman aktif saat ini.
  • resume() untuk melanjutkan perekaman aktif yang dijeda.
  • stop() untuk menyelesaikan perekaman dan menghapus semua objek perekaman terkait.
  • mute() untuk membisukan atau membunyikan rekaman saat ini.

Perhatikan bahwa Anda dapat memanggil stop() untuk menghentikan Recording, terlepas apakah perekaman dalam status dijeda atau aktif.

Jika Anda telah mendaftarkan EventListener dengan PendingRecording.start(), Recording akan berkomunikasi menggunakan VideoRecordEvent.

  • VideoRecordEvent.EVENT_TYPE_STATUS digunakan untuk merekam statistik seperti ukuran file saat ini dan rentang waktu yang direkam.
  • VideoRecordEvent.EVENT_TYPE_FINALIZE digunakan untuk hasil perekaman dan mencakup informasi seperti URI file akhir beserta error terkait apa pun.

Setelah aplikasi Anda menerima EVENT_TYPE_FINALIZE yang menunjukkan bahwa sesi perekaman berhasil dilakukan, Anda dapat mengakses video yang direkam dari lokasi yang ditentukan di OutputOptions.

Referensi lainnya

Untuk mempelajari CameraX lebih lanjut, baca referensi tambahan berikut ini: