สถาปัตยกรรมการจับภาพวิดีโอของ CameraX

โดยทั่วไป ระบบการจับภาพจะบันทึกสตรีมวิดีโอและเสียง บีบอัด มัลติเพล็กซ์สตรีมทั้ง 2 แล้วเขียนสตรีมผลลัพธ์ลงในดิสก์

แผนภาพแนวคิดสำหรับระบบจับภาพวิดีโอและเสียง
รูปที่ 1 แผนภาพแนวคิดสำหรับระบบจับภาพวิดีโอและเสียง

ใน CameraX โซลูชันสำหรับการจับภาพวิดีโอคือ VideoCapture กรณีการใช้งานต่อไปนี้

แผนภาพแนวคิดที่แสดงวิธีที่ CameraX จัดการ
         กรณีการใช้งานการจับภาพวิดีโอ
รูปที่ 2 แผนภาพแนวคิดที่แสดงวิธีที่ CameraX จัดการVideoCapture Use Case

ดังที่แสดงในรูปที่ 2 การจับภาพวิดีโอด้วย CameraX มีคอมโพเนนต์ระดับสูง 2-3 รายการในสถาปัตยกรรม ดังนี้

  • SurfaceProvider สำหรับแหล่งที่มาของวิดีโอ
  • AudioSource สำหรับแหล่งที่มาของเสียง
  • โปรแกรมเปลี่ยนไฟล์ 2 ตัวเพื่อเข้ารหัสและบีบอัดวิดีโอ/เสียง
  • ตัวมัลติเพล็กซ์สื่อเพื่อมัลติเพล็กซ์ 2 สตรีม
  • โปรแกรมบันทึกไฟล์เพื่อเขียนผลลัพธ์

VideoCapture API จะแยกเอนจินการจับภาพที่ซับซ้อนออกและมอบ API ที่ง่ายและตรงไปตรงมามากขึ้นให้แก่แอปพลิเคชัน

ภาพรวม VideoCapture API

VideoCapture เป็น Use Case ของ CameraX ที่ทำงานได้ดีด้วยตัวของมันเองหรือเมื่อ ใช้ร่วมกับ Use Case อื่นๆ ชุดค่าผสมที่รองรับเฉพาะจะขึ้นอยู่กับ ความสามารถของฮาร์ดแวร์กล้อง แต่ Preview และ VideoCapture เป็น ชุดค่าผสมของ Use Case ที่ใช้ได้ในอุปกรณ์ทุกเครื่อง

VideoCapture API ประกอบด้วยออบเจ็กต์ต่อไปนี้ที่สื่อสารกับแอปพลิเคชัน

  • VideoCapture คือ คลาสกรณีการใช้งานระดับบนสุด VideoCapture จะเชื่อมโยงกับ LifecycleOwner ที่มี CameraSelector และ CameraX UseCases อื่นๆ ดูข้อมูลเพิ่มเติมเกี่ยวกับแนวคิดและการใช้งานเหล่านี้ได้ที่ สถาปัตยกรรม CameraX
  • Recorder คือการใช้งาน VideoOutput ที่เชื่อมโยงอย่างใกล้ชิดกับ VideoCapture Recorder ใช้เพื่อบันทึกวิดีโอและเสียง แอปพลิเคชันสร้างการบันทึกจาก Recorder
  • PendingRecording กำหนดค่าการบันทึก โดยมีตัวเลือกต่างๆ เช่น การเปิดใช้เสียงและการตั้งค่า Listener เหตุการณ์ คุณต้องใช้ Recorder เพื่อสร้าง PendingRecording PendingRecording จะไม่บันทึกสิ่งใด
  • Recording จะทำการบันทึกจริง คุณต้องใช้ PendingRecording เพื่อสร้าง Recording

รูปที่ 3 แสดงความสัมพันธ์ระหว่างออบเจ็กต์เหล่านี้

แผนภาพแสดงการโต้ตอบที่เกิดขึ้นในวิดีโอ
         กรณีการใช้งานการจับภาพ
รูปที่ 3 แผนภาพแสดงการโต้ตอบที่เกิดขึ้น ในกรณีการใช้งาน VideoCapture

คำอธิบาย:

  1. สร้าง Recorder ด้วย QualitySelector
  2. กำหนดค่า Recorder ด้วยOutputOptions อย่างใดอย่างหนึ่ง
  3. เปิดใช้เสียงด้วย withAudioEnabled() หากจำเป็น
  4. โทรหา start() พร้อมVideoRecordEvent ผู้ฟังเพื่อเริ่มบันทึก
  5. ใช้ pause()/resume()/stop() ใน Recording เพื่อควบคุมการบันทึก
  6. ตอบกลับ VideoRecordEvents ภายใน Listener เหตุการณ์

ดูรายการ API แบบละเอียดได้ใน current.txt ภายในซอร์สโค้ด

การใช้ VideoCapture API

หากต้องการผสานรวม Use Case ของ CameraX VideoCapture เข้ากับแอป ให้ทำดังนี้

  1. เชื่อมโยง VideoCapture
  2. เตรียมและกำหนดค่าการบันทึก
  3. เริ่มและควบคุมการบันทึกรันไทม์

ส่วนต่อไปนี้จะอธิบายสิ่งที่คุณทำได้ในแต่ละขั้นตอนเพื่อให้ได้เซสชันการบันทึกแบบครบวงจร

เชื่อมโยง VideoCapture

หากต้องการเชื่อมโยงกรณีการใช้งาน VideoCapture ให้ทำดังนี้

  1. สร้างออบเจ็กต์ Recorder
  2. สร้างVideoCaptureออบเจ็กต์
  3. เชื่อมโยงกับ Lifecycle

VideoCapture API ของ CameraX เป็นไปตามรูปแบบการออกแบบ Builder แอปพลิเคชัน ใช้ Recorder.Builder เพื่อสร้าง Recorder นอกจากนี้ คุณยังกำหนดค่า ความละเอียดของวิดีโอสำหรับ Recorder ผ่านออบเจ็กต์ QualitySelector ได้ด้วย

CameraX Recorder รองรับQualities ที่กำหนดไว้ล่วงหน้าต่อไปนี้สำหรับความละเอียดของวิดีโอ

  • Quality.UHD สำหรับขนาดวิดีโอ 4K Ultra HD (2160p)
  • Quality.FHD สำหรับขนาดวิดีโอ Full HD (1080p)
  • Quality.HD สำหรับขนาดวิดีโอ HD (720p)
  • Quality.SD สำหรับขนาดวิดีโอ SD (480p)

โปรดทราบว่า CameraX ยังเลือกความละเอียดอื่นๆ ได้ด้วยเมื่อแอปได้รับอนุญาต

ขนาดวิดีโอที่แน่นอนของแต่ละตัวเลือกจะขึ้นอยู่กับความสามารถของกล้องและตัวเข้ารหัส ดูข้อมูลเพิ่มเติมได้ในเอกสารประกอบสำหรับ CamcorderProfile

แอปพลิเคชันสามารถกำหนดค่าความละเอียดได้โดยการสร้าง QualitySelector คุณสร้าง QualitySelector ได้โดยใช้วิธีใดวิธีหนึ่งต่อไปนี้

  • ระบุความละเอียดที่ต้องการ 2-3 รายการโดยใช้ fromOrderedList() และ ใส่กลยุทธ์สำรองไว้ใช้ในกรณีที่ระบบไม่รองรับความละเอียดที่ต้องการ

    CameraX สามารถกำหนดการจับคู่สำรองที่ดีที่สุดตามความสามารถของกล้องที่เลือก โปรดดูรายละเอียดเพิ่มเติมในQualitySelectorFallbackStrategy specification ตัวอย่างเช่น โค้ดต่อไปนี้จะขอความละเอียดสูงสุดที่รองรับ สำหรับการบันทึก และหากไม่มีความละเอียดที่ขอใดที่รองรับได้ ให้สิทธิ์ CameraX เลือกความละเอียดที่ใกล้เคียงกับความละเอียด Quality.SD มากที่สุด

    val qualitySelector = QualitySelector.fromOrderedList(
             listOf(Quality.UHD, Quality.FHD, Quality.HD, Quality.SD),
             FallbackStrategy.lowerQualityOrHigherThan(Quality.SD))
    
  • ก่อนอื่นให้ค้นหาความสามารถของกล้อง แล้วเลือกความละเอียดที่รองรับ โดยใช้ 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()
        }
    }
    
    

    โปรดทราบว่าความสามารถที่ส่งคืนจาก QualitySelector.getSupportedQualities() รับประกันว่าจะใช้งานได้สำหรับกรณีการใช้งาน VideoCapture หรือ การรวมกรณีการใช้งาน VideoCapture และ Preview เมื่อผูกร่วมกับ Use Case ImageCapture หรือ ImageAnalysis CameraX อาจยังคงผูกไม่สำเร็จเมื่อกล้องที่ขอไม่รองรับ การผูกที่จำเป็น

เมื่อมี QualitySelector แล้ว แอปพลิเคชันจะสร้างออบเจ็กต์ VideoCaptureและทำการเชื่อมโยงได้ โปรดทราบว่าการเชื่อมโยงนี้จะเหมือนกับกรณีการใช้งานอื่นๆ ดังนี้

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

โปรดทราบว่า bindToLifecycle() จะแสดงผลออบเจ็กต์ Camera ดูข้อมูลเพิ่มเติมเกี่ยวกับการควบคุมเอาต์พุตของกล้อง เช่น การซูมและการเปิดรับแสง ได้ที่คู่มือนี้

Recorder จะเลือกรูปแบบที่เหมาะสมที่สุดสำหรับระบบ ตัวแปลงรหัสวิดีโอที่ใช้กันมากที่สุดคือ H.264 AVC) ที่มีรูปแบบคอนเทนเนอร์ MPEG-4

กำหนดค่าและสร้างการบันทึก

จาก Recorder แอปพลิเคชันจะสร้างออบเจ็กต์การบันทึกเพื่อ บันทึกวิดีโอและเสียงได้ แอปพลิเคชันสร้างการบันทึกโดยทำดังนี้

  1. กำหนดค่า OutputOptions ด้วย prepareRecording()
  2. (ไม่บังคับ) เปิดใช้การบันทึกเสียง
  3. ใช้ start() เพื่อลงทะเบียน VideoRecordEvent Listener และเริ่มจับภาพวิดีโอ

Recorder จะแสดงผลออบเจ็กต์ Recording เมื่อคุณเรียกใช้ฟังก์ชัน start() แอปพลิเคชันของคุณสามารถใช้Recordingออบเจ็กต์นี้เพื่อสิ้นสุด การจับภาพหรือเพื่อดำเนินการอื่นๆ เช่น หยุดชั่วคราวหรือดำเนินการต่อ

Recorder รองรับออบเจ็กต์ Recording ได้ครั้งละ 1 รายการ คุณเริ่มบันทึกใหม่ได้เมื่อโทรหา Recording.stop() หรือ Recording.close() ในออบเจ็กต์ Recording ก่อนหน้า

มาดูรายละเอียดขั้นตอนเหล่านี้กัน ก่อนอื่น แอปพลิเคชันจะกำหนดค่า OutputOptions สำหรับเครื่องบันทึกด้วย Recorder.prepareRecording() Recorder รองรับOutputOptions ประเภทต่อไปนี้

  • FileDescriptorOutputOptions สำหรับการจับภาพลงใน FileDescriptor
  • FileOutputOptions สำหรับการจับภาพลงใน File
  • MediaStoreOutputOptions สำหรับการจับภาพลงใน MediaStore

OutputOptions ทุกประเภทช่วยให้คุณกำหนดขนาดไฟล์สูงสุดได้ด้วย setFileSizeLimit() ส่วนตัวเลือกอื่นๆ จะเฉพาะเจาะจงกับเอาต์พุตแต่ละประเภท เช่น ParcelFileDescriptor สำหรับ FileDescriptorOutputOptions

prepareRecording() จะแสดงผลออบเจ็กต์ PendingRecording ซึ่งเป็นออบเจ็กต์ ระดับกลางที่ใช้สร้างออบเจ็กต์ Recording ที่เกี่ยวข้อง PendingRecording เป็นคลาสชั่วคราวที่ควร มองไม่เห็นในกรณีส่วนใหญ่ และแอปไม่ค่อยแคช

แอปพลิเคชันสามารถกำหนดค่าการบันทึกเพิ่มเติมได้ เช่น

  • เปิดใช้เสียงด้วย withAudioEnabled()
  • ลงทะเบียน Listener เพื่อรับเหตุการณ์การบันทึกวิดีโอ ด้วย start(Executor, Consumer<VideoRecordEvent>)
  • อนุญาตให้การบันทึกบันทึกอย่างต่อเนื่องขณะที่ VideoCapture ที่แนบมา จะผูกกับกล้องอื่นด้วย PendingRecording.asPersistentRecording()

หากต้องการเริ่มบันทึก ให้โทรไปที่ PendingRecording.start() CameraX จะเปลี่ยน PendingRecording เป็น Recording จัดคําขอการบันทึก และส่งคืนออบเจ็กต์ Recording ที่สร้างขึ้นใหม่ไปยังแอปพลิเคชัน เมื่อการบันทึกเริ่มขึ้นในอุปกรณ์กล้องที่เกี่ยวข้อง CameraX จะส่งเหตุการณ์ VideoRecordEvent.EVENT_TYPE_START

ตัวอย่างต่อไปนี้แสดงวิธีบันทึกวิดีโอและเสียงลงในไฟล์ 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)

แม้ว่าตัวอย่างกล้องจะปรับให้เหมือนภาพในกระจกในกล้องหน้าโดยค่าเริ่มต้น แต่วิดีโอที่บันทึกโดย VideoCapture จะไม่ปรับให้เหมือนภาพในกระจกโดยค่าเริ่มต้น ใน CameraX 1.3 คุณสามารถ บันทึกวิดีโอแบบมิเรอร์ได้แล้ว เพื่อให้ตัวอย่างกล้องหน้าและวิดีโอที่บันทึกตรงกัน

ตัวเลือก MirrorMode มี 3 ตัวเลือก ได้แก่ MIRROR_MODE_OFF, MIRROR_MODE_ON และ MIRROR_MODE_ON_FRONT_ONLY หากต้องการจัดแนวให้ตรงกับการแสดงตัวอย่างกล้อง Google ขอแนะนำให้ใช้ MIROR_MODE_ON_FRONT_ONLY ซึ่งหมายความว่า ระบบจะไม่ได้เปิดใช้การมิเรอร์สำหรับกล้องหลัง แต่จะเปิดใช้สำหรับกล้องหน้า ดูข้อมูลเพิ่มเติมเกี่ยวกับ MirrorMode ได้ที่ MirrorMode constants

ข้อมูลโค้ดนี้แสดงวิธีเรียกใช้ VideoCapture.Builder.setMirrorMode() โดยใช้ MIRROR_MODE_ON_FRONT_ONLY ดูข้อมูลเพิ่มเติมได้ที่ 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);

ควบคุมการบันทึกที่ใช้งานอยู่

คุณหยุดชั่วคราว เล่นต่อ และหยุดRecordingที่กำลังดำเนินการได้โดย ใช้วิธีต่อไปนี้

  • pause เพื่อหยุดการบันทึกที่ใช้งานอยู่ชั่วคราว
  • resume() เพื่อบันทึกต่อจากที่หยุดไว้ชั่วคราว
  • stop() เพื่อสิ้นสุดการบันทึกและล้างออบเจ็กต์การบันทึกที่เชื่อมโยง
  • mute() เพื่อปิดหรือเปิดเสียงการบันทึกปัจจุบัน

โปรดทราบว่าคุณสามารถโทรหา stop() เพื่อสิ้นสุด Recording ได้ไม่ว่าการบันทึกจะอยู่ในสถานะหยุดชั่วคราวหรือสถานะบันทึกที่ใช้งานอยู่

หากคุณลงทะเบียน EventListener กับ PendingRecording.start() Recording จะสื่อสาร โดยใช้ VideoRecordEvent

  • VideoRecordEvent.EVENT_TYPE_STATUS ใช้สำหรับบันทึกสถิติต่างๆ เช่น ขนาดไฟล์ปัจจุบันและระยะเวลาที่บันทึก
  • VideoRecordEvent.EVENT_TYPE_FINALIZE ใช้สำหรับผลการบันทึก และมีข้อมูล เช่น URI ของไฟล์สุดท้ายพร้อมกับ ข้อผิดพลาดที่เกี่ยวข้อง

เมื่อแอปได้รับ EVENT_TYPE_FINALIZE ที่ระบุว่าเซสชันการบันทึกสำเร็จ แล้ว คุณจะเข้าถึงวิดีโอที่บันทึกได้จากตำแหน่ง ที่ระบุใน OutputOptions

แหล่งข้อมูลเพิ่มเติม

ดูข้อมูลเพิ่มเติมเกี่ยวกับ CameraX ได้ที่แหล่งข้อมูลเพิ่มเติมต่อไปนี้