ארכיטקטורת CameraX

בדף הזה נסביר על הארכיטקטורה של CameraX, כולל המבנה שלה, אופן העבודה עם ה-API, אופן העבודה עם מחזורי חיים ואופן השילוב של תרחישים לדוגמה.

המבנה של CameraX

אפשר להשתמש ב-CameraX כדי ליצור ממשק עם מצלמת המכשיר באמצעות הפשטה שנקראת תרחיש לדוגמה. אלה תרחישים לדוגמה:

  • Preview: מקבלת משטח להצגת תצוגה מקדימה, כמו PreviewView.
  • ניתוח תמונות: מספק מאגרים שזמינים למעבד לצורך ניתוח, למשל לצורך למידת מכונה.
  • צילום תמונה: צילום ושמירה של תמונה.
  • צילום וידאו: צילום וידאו ואודיו באמצעות VideoCapture

אפשר לשלב בין תרחישי לדוגמה ולהפעיל אותם בו-זמנית. לדוגמה, אפליקציה יכולה לאפשר למשתמש לראות את התמונה שמוצגת במצלמה באמצעות תרחיש לדוגמה של תצוגה מקדימה, לכלול תרחיש לדוגמה של ניתוח תמונה כדי לקבוע אם האנשים בתמונה מחייכים, ולכלול תרחיש לדוגמה של צילום תמונה כדי לצלם תמונה ברגע שהאנשים מחייכים.

מודל API

כדי לעבוד עם הספרייה, צריך לציין את הפרטים הבאים:

  • תרחיש לדוגמה עם אפשרויות תצורה.
  • מה עושים עם נתוני הפלט על ידי צירוף מאזינים.
  • התהליך המתוכנן, למשל מתי להפעיל מצלמות ומתי ליצור נתונים, על ידי קישור התרחיש לדוגמה למחזורי החיים של ארכיטקטורת Android.

יש שתי דרכים לכתוב אפליקציית CameraX: CameraController (מצוינת אם אתם רוצים את הדרך הפשוטה ביותר להשתמש ב-CameraX) או CameraProvider (מצוינת אם אתם צריכים גמישות רבה יותר).

CameraController

CameraController מספק את רוב הפונקציונליות של הליבה של CameraX בכיתה אחת. נדרש מעט קוד הגדרה, והוא מטפל באופן אוטומטי בהפעלת המצלמה, בניהול תרחישי שימוש, ברוטציית היעד, בהקשה לצורך התמקדות, בתנועת הצמדה לצורך הגדלה ועוד. הכיתה הקונקרטית שמרחיבה את CameraController היא LifecycleCameraController.

Kotlin

val previewView: PreviewView = viewBinding.previewView
var cameraController = LifecycleCameraController(baseContext)
cameraController.bindToLifecycle(this)
cameraController.cameraSelector = CameraSelector.DEFAULT_BACK_CAMERA
previewView.controller = cameraController

Java

PreviewView previewView = viewBinding.previewView;
LifecycleCameraController cameraController = new LifecycleCameraController(baseContext);
cameraController.bindToLifecycle(this);
cameraController.setCameraSelector(CameraSelector.DEFAULT_BACK_CAMERA);
previewView.setController(cameraController);

ערכי ברירת המחדל של UseCase עבור CameraController הם Preview,‏ ImageCapture ו-ImageAnalysis. כדי להשבית את ImageCapture או ImageAnalysis, או כדי להפעיל את VideoCapture, משתמשים ב-method‏ setEnabledUseCases().

למידע נוסף על CameraController, אפשר לעיין בדוגמה לסורק קודי QR או בסרטון בנושא העקרונות הבסיסיים של CameraController.

CameraProvider

עדיין קל להשתמש ב-CameraProvider, אבל מכיוון שמפתח האפליקציה מטפל בחלק גדול יותר מההגדרה, יש יותר הזדמנויות להתאים אישית את ההגדרות, למשל הפעלת סיבוב של תמונות הפלט או הגדרת הפורמט של תמונות הפלט ב-ImageAnalysis. אפשר גם להשתמש ב-Surface בהתאמה אישית לתצוגה המקדימה של המצלמה, כדי לקבל גמישות רבה יותר. לעומת זאת, ב-CameraController צריך להשתמש ב-PreviewView. כדאי להשתמש בקוד Surface הקיים אם הוא כבר משמש כקלט בחלקים אחרים באפליקציה.

מגדירים תרחישים לדוגמה באמצעות השיטות set() ומסיימים אותם באמצעות השיטה build(). כל אובייקט של תרחיש לדוגמה מספק קבוצה של ממשקי API ספציפיים לתרחיש לדוגמה. לדוגמה, בתרחיש לדוגמה של צילום תמונות מוצגת קריאה לשיטה takePicture().

במקום שהאפליקציה תציב קריאות ספציפיות לשימוש בשיטות ההפעלה וההפסקה ב-onResume() וב-onPause(), האפליקציה מציינת מחזור חיים שרוצים לשייך למצלמה באמצעות cameraProvider.bindToLifecycle(). מחזור החיים הזה מעדכן את CameraX מתי להגדיר את סשן הצילום במצלמה, ומבטיח שסטטוס המצלמה ישתנה בהתאם למעברים במחזור החיים.

שלבי ההטמעה של כל תרחיש לדוגמה מפורטים במאמרים הבאים: הטמעת תצוגה מקדימה, ניתוח תמונות, צילום תמונות וצילום סרטונים

תרחיש לדוגמה של תצוגה מקדימה מתקשר עם Surface להצגה. אפליקציות יוצרות את תרחיש לדוגמה עם אפשרויות תצורה באמצעות הקוד הבא:

Kotlin

val preview = Preview.Builder().build()
val viewFinder: PreviewView = findViewById(R.id.previewView)

// The use case is bound to an Android Lifecycle with the following code
val camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview)

// PreviewView creates a surface provider and is the recommended provider
preview.setSurfaceProvider(viewFinder.getSurfaceProvider())

Java

Preview preview = new Preview.Builder().build();
PreviewView viewFinder = findViewById(R.id.view_finder);

// The use case is bound to an Android Lifecycle with the following code
Camera camera = cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview);

// PreviewView creates a surface provider, using a Surface from a different
// kind of view will require you to implement your own surface provider.
preview.previewSurfaceProvider = viewFinder.getSurfaceProvider();

דוגמאות נוספות לקוד זמינות באפליקציית הדוגמה הרשמית של CameraX.

מחזורי חיים של CameraX

CameraX עוקב אחרי מחזור חיים כדי לקבוע מתי לפתוח את המצלמה, מתי ליצור סשן צילום ומתי לעצור את הצילום ולסגור את המצלמה. ממשקי API לתרחישים לדוגמה מספקים קריאות ל-method וקריאות חוזרות (callbacks) כדי לעקוב אחרי ההתקדמות.

כפי שמוסבר בקטע שילוב תרחישים לדוגמה, אפשר לשייך שילובים מסוימים של תרחישים לדוגמה למחזור חיים יחיד. אם האפליקציה שלכם צריכה לתמוך בתרחישים לדוגמה שלא ניתן לשלב, תוכלו לבצע אחת מהפעולות הבאות:

  • לקבץ תרחישים לדוגמה תואמים ליותר מקטע אחד, ואז לעבור בין קטעים
  • יצירת רכיב מחזור חיים בהתאמה אישית ולהשתמש בו כדי לשלוט באופן ידני במחזור החיים של המצלמה

אם מבטלים את הקישור בין הבעלים של מחזור החיים של תרחישי השימוש של התצוגה והמצלמה (לדוגמה, אם משתמשים במחזור חיים מותאם אישית או בקטע שמתבצע בו שמירה), צריך לוודא שכל תרחישי השימוש לא מקושרים ל-CameraX באמצעות ProcessCameraProvider.unbindAll() או על ידי ביטול הקישור של כל תרחיש שימוש בנפרד. לחלופין, כשמקשרים תרחישים לדוגמה למחזור חיים, אפשר לאפשר ל-CameraX לנהל את פתיחת סשן הצילום ואת סגירתו, וגם לבטל את הקישור של התרחישים לדוגמה.

אם כל הפונקציונליות של המצלמה תואמת למחזור החיים של רכיב יחיד שמודע למחזור החיים, כמו AppCompatActivity או קטע AppCompat, שימוש במחזור החיים של הרכיב הזה בזמן קישור כל התרחישי השימוש הרצויים יבטיח שפונקציית המצלמה תהיה מוכנה כשהרכיב שמודע למחזור החיים פעיל, ותתבצע מחיקת הרכיב בצורה בטוחה, ללא צריכת משאבים אחרת.

LifecycleOwners בהתאמה אישית

במקרים מתקדמים, אפשר ליצור LifecycleOwner בהתאמה אישית כדי לאפשר לאפליקציה לשלוט באופן מפורש במחזור החיים של הסשן ב-CameraX, במקום לקשר אותו ל-LifecycleOwner רגיל של Android.

דוגמת הקוד הבאה מראה איך ליצור LifecycleOwner פשוט בהתאמה אישית:

Kotlin

class CustomLifecycle : LifecycleOwner {
    private val lifecycleRegistry: LifecycleRegistry

    init {
        lifecycleRegistry = LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED)
    }
    ...
    fun doOnResume() {
        lifecycleRegistry.markState(State.RESUMED)
    }
    ...
    override fun getLifecycle(): Lifecycle {
        return lifecycleRegistry
    }
}

Java

public class CustomLifecycle implements LifecycleOwner {
    private LifecycleRegistry lifecycleRegistry;
    public CustomLifecycle() {
        lifecycleRegistry = new LifecycleRegistry(this);
        lifecycleRegistry.markState(Lifecycle.State.CREATED);
    }
   ...
   public void doOnResume() {
        lifecycleRegistry.markState(State.RESUMED);
    }
   ...
    public Lifecycle getLifecycle() {
        return lifecycleRegistry;
    }
}

באמצעות LifecycleOwner, האפליקציה יכולה למקם את מעברי המצב בנקודות הרצויות בקוד שלה. מידע נוסף על הטמעת הפונקציונליות הזו באפליקציה זמין במאמר הטמעת LifecycleOwner בהתאמה אישית.

תרחישים לדוגמה של שימוש בו-זמני

אפשר להריץ תרחישי לדוגמה בו-זמנית. אפשר לקשר תרחישים לדוגמה ברצף למחזור חיים, אבל עדיף לקשר את כל התרחישים לדוגמה באמצעות קריאה אחת ל-CameraProcessProvider.bindToLifecycle(). מידע נוסף על שיטות מומלצות לשינויים בהגדרות זמין במאמר טיפול בשינויים בהגדרות.

בדוגמת הקוד הבאה, האפליקציה מציינת את שני תרחישים לדוגמה שייווצרו ויופעלו בו-זמנית. הוא גם מציין את מחזור החיים שבו יש להשתמש בשני תרחישים לדוגמה, כך שהם יתחילו ויפסיקו בהתאם למחזור החיים.

Kotlin

private lateinit var imageCapture: ImageCapture

override fun onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    setContentView(R.layout.activity_main)

    val cameraProviderFuture = ProcessCameraProvider.getInstance(this)

    cameraProviderFuture.addListener(Runnable {
        // Camera provider is now guaranteed to be available
        val cameraProvider = cameraProviderFuture.get()

        // Set up the preview use case to display camera preview.
        val preview = Preview.Builder().build()

        // Set up the capture use case to allow users to take photos.
        imageCapture = ImageCapture.Builder()
                .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                .build()

        // Choose the camera by requiring a lens facing
        val cameraSelector = CameraSelector.Builder()
                .requireLensFacing(CameraSelector.LENS_FACING_FRONT)
                .build()

        // Attach use cases to the camera with the same lifecycle owner
        val camera = cameraProvider.bindToLifecycle(
                this as LifecycleOwner, cameraSelector, preview, imageCapture)

        // Connect the preview use case to the previewView
        preview.setSurfaceProvider(
                previewView.getSurfaceProvider())
    }, ContextCompat.getMainExecutor(this))
}

Java

private ImageCapture imageCapture;

@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
    super.onCreate(savedInstanceState);
    setContentView(R.layout.activity_main);

    PreviewView previewView = findViewById(R.id.previewView);

    ListenableFuture<ProcessCameraProvider> cameraProviderFuture =
            ProcessCameraProvider.getInstance(this);

    cameraProviderFuture.addListener(() -> {
        try {
            // Camera provider is now guaranteed to be available
            ProcessCameraProvider cameraProvider = cameraProviderFuture.get();

            // Set up the view finder use case to display camera preview
            Preview preview = new Preview.Builder().build();

            // Set up the capture use case to allow users to take photos
            imageCapture = new ImageCapture.Builder()
                    .setCaptureMode(ImageCapture.CAPTURE_MODE_MINIMIZE_LATENCY)
                    .build();

            // Choose the camera by requiring a lens facing
            CameraSelector cameraSelector = new CameraSelector.Builder()
                    .requireLensFacing(lensFacing)
                    .build();

            // Attach use cases to the camera with the same lifecycle owner
            Camera camera = cameraProvider.bindToLifecycle(
                    ((LifecycleOwner) this),
                    cameraSelector,
                    preview,
                    imageCapture);

            // Connect the preview use case to the previewView
            preview.setSurfaceProvider(
                    previewView.getSurfaceProvider());
        } catch (InterruptedException | ExecutionException e) {
            // Currently no exceptions thrown. cameraProviderFuture.get()
            // shouldn't block since the listener is being called, so no need to
            // handle InterruptedException.
        }
    }, ContextCompat.getMainExecutor(this));
}

ב-CameraX אפשר להשתמש בו-זמנית במכונה אחת של כל אחד מהרכיבים Preview,‏ VideoCapture,‏ ImageAnalysis ו-ImageCapture. בנוסף,

  • כל תרחיש לדוגמה יכול לפעול בפני עצמו. לדוגמה, אפליקציה יכולה להקליט סרטון בלי להשתמש בתצוגה המקדימה.
  • כשהתוספים מופעלים, רק השילוב של ImageCapture ו-Preview מובטח לפעול. בהתאם להטמעה של יצרן הציוד המקורי, יכול להיות שלא תהיה אפשרות להוסיף גם את ImageAnalysis. אי אפשר להפעיל תוספים בתרחיש לדוגמה VideoCapture. לפרטים נוספים, עיינו במסמך העזרה של התוסף.
  • בהתאם ליכולות של המצלמה, יכול להיות שחלק מהמצלמות יתמכו בשילוב הזה במצבי רזולוציה נמוכים יותר, אבל לא יוכלו לתמוך באותו שילוב ברזולוציות גבוהות יותר.
  • במכשירים עם חומרת מצלמה ברמה FULL ומטה, שילוב של Preview,‏ VideoCapture ו-ImageCapture או ImageAnalysis עלול לאלץ את CameraX להכפיל את שידור ה-PRIV של המצלמה עבור Preview ו-VideoCapture. הכפילות הזו, שנקראת 'שיתוף סטרימינג', מאפשרת להשתמש בו-זמנית בתכונות האלה, אבל היא גורמת לעלייה בדרישה לעיבוד. כתוצאה מכך, ייתכן שתבחינו בזמן אחזור ארוך יותר ובחיי סוללה קצרים יותר.

אפשר לאחזר את רמת החומרה הנתמכת מ-Camera2CameraInfo. לדוגמה, הקוד הבא בודק אם המצלמה האחורית שמוגדרת כברירת מחדל היא מכשיר LEVEL_3:

Kotlin

@androidx.annotation.OptIn(ExperimentalCamera2Interop::class)
fun isBackCameraLevel3Device(cameraProvider: ProcessCameraProvider) : Boolean {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        return CameraSelector.DEFAULT_BACK_CAMERA
            .filter(cameraProvider.availableCameraInfos)
            .firstOrNull()
            ?.let { Camera2CameraInfo.from(it) }
            ?.getCameraCharacteristic(CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL) ==
            CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3
    }
    return false
}

Java

@androidx.annotation.OptIn(markerClass = ExperimentalCamera2Interop.class)
Boolean isBackCameraLevel3Device(ProcessCameraProvider cameraProvider) {
    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
        List\ filteredCameraInfos = CameraSelector.DEFAULT_BACK_CAMERA
                .filter(cameraProvider.getAvailableCameraInfos());
        if (!filteredCameraInfos.isEmpty()) {
            return Objects.equals(
                Camera2CameraInfo.from(filteredCameraInfos.get(0)).getCameraCharacteristic(
                        CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL),
                CameraCharacteristics.INFO_SUPPORTED_HARDWARE_LEVEL_3);
        }
    }
    return false;
}

הרשאות

האפליקציה תצטרך את ההרשאה CAMERA. כדי לשמור תמונות בקבצים, נדרשת גם ההרשאה WRITE_EXTERNAL_STORAGE, מלבד במכשירים עם Android מגרסה 10 ואילך.

מידע נוסף על הגדרת הרשאות לאפליקציה זמין במאמר בקשת הרשאות לאפליקציה.

הדרישות

אלה דרישות הגרסה המינימליות של CameraX:

  • Android API ברמה 21
  • רכיבי הארכיטקטורה של Android 1.1.1

לפעילויות עם תמיכה במחזור חיים, משתמשים ב-FragmentActivity או ב-AppCompatActivity.

הצהרת יחסי תלות

כדי להוסיף יחסי תלות ל-CameraX, צריך להוסיף לפרויקט את מאגר Google Maven.

פותחים את הקובץ settings.gradle של הפרויקט ומוסיפים את המאגר google(), כפי שמתואר בהמשך:

Groovy

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

Kotlin

dependencyResolutionManagement {
    repositoriesMode.set(RepositoriesMode.FAIL_ON_PROJECT_REPOS)
    repositories {
        google()
        mavenCentral()
    }
}

מוסיפים את הטקסט הבא לסוף הבלוק של Android:

Groovy

android {
    compileOptions {
        sourceCompatibility JavaVersion.VERSION_1_8
        targetCompatibility JavaVersion.VERSION_1_8
    }
    // For Kotlin projects
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

Kotlin

android {
    compileOptions {
        sourceCompatibility = JavaVersion.VERSION_1_8
        targetCompatibility = JavaVersion.VERSION_1_8
    }
    // For Kotlin projects
    kotlinOptions {
        jvmTarget = "1.8"
    }
}

מוסיפים את הטקסט הבא לקובץ build.gradle של כל מודול באפליקציה:

Groovy

dependencies {
  // CameraX core library using the camera2 implementation
  def camerax_version = "1.5.0-alpha03"
  // The following line is optional, as the core library is included indirectly by camera-camera2
  implementation "androidx.camera:camera-core:${camerax_version}"
  implementation "androidx.camera:camera-camera2:${camerax_version}"
  // If you want to additionally use the CameraX Lifecycle library
  implementation "androidx.camera:camera-lifecycle:${camerax_version}"
  // If you want to additionally use the CameraX VideoCapture library
  implementation "androidx.camera:camera-video:${camerax_version}"
  // If you want to additionally use the CameraX View class
  implementation "androidx.camera:camera-view:${camerax_version}"
  // If you want to additionally add CameraX ML Kit Vision Integration
  implementation "androidx.camera:camera-mlkit-vision:${camerax_version}"
  // If you want to additionally use the CameraX Extensions library
  implementation "androidx.camera:camera-extensions:${camerax_version}"
}

Kotlin

dependencies {
    // CameraX core library using the camera2 implementation
    val camerax_version = "1.5.0-alpha03"
    // The following line is optional, as the core library is included indirectly by camera-camera2
    implementation("androidx.camera:camera-core:${camerax_version}")
    implementation("androidx.camera:camera-camera2:${camerax_version}")
    // If you want to additionally use the CameraX Lifecycle library
    implementation("androidx.camera:camera-lifecycle:${camerax_version}")
    // If you want to additionally use the CameraX VideoCapture library
    implementation("androidx.camera:camera-video:${camerax_version}")
    // If you want to additionally use the CameraX View class
    implementation("androidx.camera:camera-view:${camerax_version}")
    // If you want to additionally add CameraX ML Kit Vision Integration
    implementation("androidx.camera:camera-mlkit-vision:${camerax_version}")
    // If you want to additionally use the CameraX Extensions library
    implementation("androidx.camera:camera-extensions:${camerax_version}")
}

מידע נוסף על הגדרת האפליקציה כך שתתאים לדרישות האלה זמין במאמר הצהרה על יחסי תלות.

יכולת פעולה הדדית של CameraX עם Camera2

CameraX מבוסס על Camera2, ו-CameraX חושף דרכים לקרוא ואפילו לכתוב מאפיינים בהטמעה של Camera2. פרטים מלאים זמינים בחבילת ה-Interop.

למידע נוסף על האופן שבו CameraX מגדיר את המאפיינים של Camera2, תוכלו להשתמש ב-Camera2CameraInfo כדי לקרוא את CameraCharacteristics הבסיסי. אפשר גם לכתוב את המאפיינים הבסיסיים של Camera2 באחת משתי הדרכים הבאות:

בדוגמת הקוד הבאה נעשה שימוש בתרחישי שימוש של סטרימינג כדי לבצע אופטימיזציה לשיחות וידאו. משתמשים ב-Camera2CameraInfo כדי לאחזר אם תרחיש לדוגמה של שיחת וידאו בסטרימינג זמין. לאחר מכן, משתמשים ב-Camera2Interop.Extender כדי להגדיר את תרחיש השימוש הבסיסי של הסטרימינג.

Kotlin

// Set underlying Camera2 stream use case to optimize for video calls.

val videoCallStreamId =
    CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL.toLong()

// Check available CameraInfos to find the first one that supports
// the video call stream use case.
val frontCameraInfo = cameraProvider.getAvailableCameraInfos()
    .first { cameraInfo ->
        val isVideoCallStreamingSupported = Camera2CameraInfo.from(cameraInfo)
            .getCameraCharacteristic(
                CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES
            )?.contains(videoCallStreamId)
        val isFrontFacing = (cameraInfo.getLensFacing() == 
                             CameraSelector.LENS_FACING_FRONT)
        (isVideoCallStreamingSupported == true) && isFrontFacing
    }

val cameraSelector = frontCameraInfo.cameraSelector

// Start with a Preview Builder.
val previewBuilder = Preview.Builder()
    .setTargetAspectRatio(screenAspectRatio)
    .setTargetRotation(rotation)

// Use Camera2Interop.Extender to set the video call stream use case.
Camera2Interop.Extender(previewBuilder).setStreamUseCase(videoCallStreamId)

// Bind the Preview UseCase and the corresponding CameraSelector.
val preview = previewBuilder.build()
camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)

Java

// Set underlying Camera2 stream use case to optimize for video calls.

Long videoCallStreamId =
    CameraMetadata.SCALER_AVAILABLE_STREAM_USE_CASES_VIDEO_CALL.toLong();

// Check available CameraInfos to find the first one that supports
// the video call stream use case.
List<CameraInfo> cameraInfos = cameraProvider.getAvailableCameraInfos();
CameraInfo frontCameraInfo = null;
for (cameraInfo in cameraInfos) {
    Long[] availableStreamUseCases = Camera2CameraInfo.from(cameraInfo)
        .getCameraCharacteristic(
            CameraCharacteristics.SCALER_AVAILABLE_STREAM_USE_CASES
        );
    boolean isVideoCallStreamingSupported = Arrays.List(availableStreamUseCases)
                .contains(videoCallStreamId);
    boolean isFrontFacing = (cameraInfo.getLensFacing() ==
                             CameraSelector.LENS_FACING_FRONT);

    if (isVideoCallStreamingSupported && isFrontFacing) {
        frontCameraInfo = cameraInfo;
    }
}

if (frontCameraInfo == null) {
    // Handle case where video call streaming is not supported.
}

CameraSelector cameraSelector = frontCameraInfo.getCameraSelector();

// Start with a Preview Builder.
Preview.Builder previewBuilder = Preview.Builder()
    .setTargetAspectRatio(screenAspectRatio)
    .setTargetRotation(rotation);

// Use Camera2Interop.Extender to set the video call stream use case.
Camera2Interop.Extender(previewBuilder).setStreamUseCase(videoCallStreamId);

// Bind the Preview UseCase and the corresponding CameraSelector.
Preview preview = previewBuilder.build()
Camera camera = cameraProvider.bindToLifecycle(this, cameraSelector, preview)

מקורות מידע נוספים

מידע נוסף על CameraX זמין במקורות המידע הבאים.

Codelab

  • תחילת העבודה עם CameraX
  • דוגמת קוד

  • אפליקציות לדוגמה של CameraX