ארכיטקטורה לצילום וידאו של CameraX

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

תרשים קונספטואלי של מערכת לצילום וידאו והקלטת אודיו
איור 1. תרשים קונספטואלי של מערכת להקלטת וידאו ואודיו.

ב-CameraX, הפתרון לצילום וידאו הוא תרחיש השימוש VideoCapture:

תרשים קונספטואלי שמראה איך CameraX מטפל בתרחיש לדוגמה של צילום וידאו
איור 2. תרשים קונספטואלי שמראה איך CameraX מטפל בתרחיש לדוגמה של VideoCapture.

כמו שמוצג באיור 2, צילום וידאו ב-CameraX כולל כמה רכיבים ארכיטקטוניים ברמה גבוהה:

  • SurfaceProvider למקור הסרטון.
  • AudioSource למקור האודיו.
  • שני מקודדים כדי לקודד ולדחוס את הווידאו והאודיו.
  • ממקסר מדיה כדי למקסר את שני הסטרימים.
  • כלי לשמירת קבצים כדי לכתוב את התוצאה.

‫VideoCapture API מסתיר את מנוע הצילום המורכב ומספק לאפליקציות ממשק API פשוט וישיר יותר.

סקירה כללית על VideoCapture API

‫VideoCapture הוא תרחיש לדוגמה של CameraX שפועל היטב בפני עצמו או בשילוב עם תרחישים אחרים לדוגמה. השילובים הספציפיים שנתמכים תלויים ביכולות החומרה של המצלמה, אבל Preview ו-VideoCapture הם שילוב תקין של תרחישי שימוש בכל המכשירים.

‫VideoCapture API מורכב מהאובייקטים הבאים שמתקשרים עם אפליקציות:

  • ‫VideoCapture היא המחלקה של תרחיש השימוש ברמה העליונה. ‫VideoCapture נקשר ל-LifecycleOwner עם CameraSelector ו-UseCases אחרים של CameraX. מידע נוסף על המושגים והשימושים האלה זמין במאמר ארכיטקטורת CameraX.
  • ‫Recorder הוא הטמעה של VideoOutput שמקושרת באופן הדוק ל-VideoCapture. ‫Recorder משמש לצילום הווידאו והקלטת האודיו. אפליקציה יוצרת הקלטות מ-Recorder.
  • PendingRecording מגדיר הקלטה, ומספק אפשרויות כמו הפעלת אודיו והגדרת רכיב event listener. כדי ליצור PendingRecording, צריך להשתמש ב-Recorder. PendingRecording לא מקליט שום דבר.
  • ההקלטה עצמה מתבצעת באמצעות Recording. כדי ליצור Recording, צריך להשתמש ב-PendingRecording.

באיור 3 מוצגים הקשרים בין האובייקטים האלה:

תרשים שמראה את האינטראקציות שמתרחשות בתרחיש לדוגמה של צילום וידאו
איור 3. תרשים שמציג את האינטראקציות שמתרחשות בתרחיש לדוגמה של VideoCapture.

מקרא:

  1. יצירת Recorder עם QualitySelector.
  2. מגדירים את Recorder באמצעות אחת מOutputOptions.
  3. אם צריך, מפעילים את האודיו באמצעות withAudioEnabled().
  4. כדי להתחיל להקליט, צריך להתקשר אל start() עם מאזין VideoRecordEvent.
  5. אפשר להשתמש ב-pause()/resume()/stop() ב-Recording כדי לשלוט בהקלטה.
  6. מגיבים לאירוע VideoRecordEvents בתוך רכיב ה-event listener.

רשימת ה-API המפורטת נמצאת בקובץ current.txt בתוך קוד המקור.

שימוש ב-VideoCapture API

כדי לשלב את תרחיש השימוש VideoCapture של CameraX באפליקציה:

  1. כריכה של VideoCapture.
  2. מכינים ומגדירים את ההקלטה.
  3. התחלה של הקלטת זמן הריצה ושליטה בה.

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

קישור של VideoCapture

כדי לקשר את תרחיש השימוש VideoCapture:

  1. יוצרים אובייקט Recorder.
  2. יוצרים אובייקט VideoCapture.
  3. קישור אל Lifecycle.

‫CameraX VideoCapture API פועל לפי תבנית העיצוב של ה-builder. אפליקציות משתמשות ב-Recorder.Builder כדי ליצור Recorder. אפשר גם להגדיר את רזולוציית הווידאו של Recorder באמצעות אובייקט QualitySelector.

‫CameraX Recorder תומך ב-Qualities המוגדרים מראש הבאים לרזולוציות של סרטונים:

  • Quality.UHD לגודל סרטון 4K Ultra HD‏ (2160p)
  • Quality.QHD לגודל וידאו Quad HD‏ (1440p), שהוצג ב-CameraX 1.7.0-alpha02
  • ‫Quality.FHD לסרטון בגודל Full HD ‏ (1080p)
  • Quality.HD לסרטון באיכות HD ‏ (720p)
  • ‫Quality.SD לגודל וידאו SD‏ (480p)

חשוב לזכור ש-CameraX יכולה לבחור גם רזולוציות אחרות אם האפליקציה מאשרת זאת.

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

כדי להגדיר את הרזולוציה באפליקציות, צריך ליצור QualitySelector. אפשר ליצור QualitySelector באחת מהשיטות הבאות:

  • אפשר לספק כמה רזולוציות מועדפות באמצעות fromOrderedList(), ולכלול אסטרטגיית חלופית למקרה שאף אחת מהרזולוציות המועדפות לא נתמכת.

    ‫CameraX יכולה להחליט מהי ההתאמה החלופית הטובה ביותר על סמך היכולת של המצלמה שנבחרה. פרטים נוספים זמינים ב-FallbackStrategy specification של QualitySelector. לדוגמה, הקוד הבא מבקש את הרזולוציה הגבוהה ביותר שנתמכת להקלטה, ואם אף אחת מהרזולוציות המבוקשות לא נתמכת, מאשר ל-CameraX לבחור את הרזולוציה שהכי קרובה לרזולוציה Quality.SD:

    val qualitySelector = QualitySelector.fromOrderedList(
             listOf(Quality.UHD, Quality.QHD, 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.QHD, 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.QHD -> "QHD"
            Quality.FHD -> "FHD"
            Quality.HD -> "HD"
            Quality.SD -> "SD"
            else -> throw IllegalArgumentException()
        }
    }
    
    

    שימו לב שהיכולת שמוחזרת מ-QualitySelector.getSupportedQualities() תעבוד בוודאות בתרחיש לדוגמה VideoCapture או בשילוב של התרחישים לדוגמה VideoCapture ו-Preview. כשמבצעים קישור יחד עם תרחיש לדוגמה של 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, ומתחילים לצלם את הסרטון.

ה-Recorder מחזיר אובייקט Recording כשמפעילים את הפונקציה start(). האפליקציה יכולה להשתמש באובייקט Recording הזה כדי לסיים את ההקלטה או לבצע פעולות אחרות, כמו השהיה או המשך.

‫Recorder תומך באובייקט Recording אחד בכל פעם. אפשר להתחיל הקלטה חדשה אחרי שמפעילים את 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().
  • רושמים מאזין כדי לקבל אירועים של הקלטת סרטונים באמצעות 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‏: 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);

טווח דינמי (סרטון HDR)

‫CameraX תומך בהקלטת וידאו בטווח דינמי גבוה (HDR) של 10 ביט, כמו HLG10,‏ HDR10,‏ HDR10+‎ או Dolby Vision, באמצעות DynamicRange.

כדי להשתמש בצילום וידאו ב-HDR, פועלים לפי השלבים הבאים:

  1. שולחים שאילתה לגבי הטווחים הדינמיים שהמצלמה תומכת בהם באמצעות VideoCapabilities.getSupportedDynamicRanges().
  2. אופציונלי: אם מגדירים QualitySelector בהתאמה אישית, בודקים אילו איכויות נתמכות בטווח הדינמי הזה באמצעות getSupportedQualities(DynamicRange).
  3. מגדירים את VideoCapture.Builder באמצעות setDynamicRange().

בדוגמה הבאה, מופעלת הקלטת וידאו HDR באיכות 10 ביט בפורמט HLG כשהמצלמה תומכת בכך:

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();

ייצוב וידאו

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

כדי להפעיל את ייצוב הווידאו:

  1. בודקים אם המצלמה תומכת בייצוב באמצעות VideoCapabilities.isStabilizationSupported().
  2. מפעילים אותה ב-VideoCapture.Builder באמצעות setVideoStabilizationEnabled(true).

בדוגמה הבאה, הייצוב מופעל כשהמצלמה תומכת בו:

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();

שליטה בהקלטה פעילה

אפשר להשהות, להמשיך ולהפסיק 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.

עוצמת השמע והמצב בזמן אמת

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

  • הפונקציה AudioStats.getAudioAmplitude() מחזירה את עוצמת השיא הנוכחית של האודיו, בטווח שבין 0.0 ל-1.0. אפשר להשתמש בערך הזה כדי להפעיל מדי ממשק משתמש או רכיבי visualizer של צורות גל של אודיו.
  • ‫AudioStats.getAudioState(): הסטטוס הנוכחי של שידור האודיו, למשל AUDIO_STATE_ACTIVE, ‏AUDIO_STATE_MUTED או AUDIO_STATE_SOURCE_SILENCED.

בדוגמה הבאה, המערכת קוראת את עוצמת הקול מאירוע סטטוס:

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

סרטונים במהירות גבוהה ובהילוך איטי

‫CameraX תומך בצילום וידאו במהירות גבוהה, כמו 120, 240 או 480 פריימים לשנייה, שמאפשר שני תרחישי שימוש שונים:

  • סרטון בקצב פריימים גבוה (הילוך איטי שמנוהל על ידי הנגן): CameraX מצלם ושומר פריימים בקצב פריימים גבוה (לדוגמה, 120fps). בנגן הווידאו יש כפתורים לשינוי מהירות ההפעלה, שמאפשרים למשתמשים לעבור בין מהירות רגילה להילוך איטי או לנתח את הסרטון פריים אחרי פריים.
  • סרטון בהילוך איטי שמוכן להפעלה: CameraX מעבד את הסטרימינג במהירות גבוהה ומוציא קובץ וידאו רגיל של 30fps עם חותמות זמן מותאמות של פריימים. אפקט ההילוך האיטי "מוטמע" בסרטון, כך שהוא מופעל בהילוך איטי באופן אוטומטי בכל נגן וידאו רגיל, בלי צורך בתמיכה מיוחדת של הנגן. לדוגמה, צילום בקצב של 120 פריימים לשנייה שמופעל בקצב של 30 פריימים לשנייה יוצר סרטון בהילוך איטי במהירות של 1/4x.

בדיקת היכולות של המהירות הגבוהה

כדי לצלם וידאו במהירות גבוהה, נדרשת תמיכה בחומרה והגדרות CamcorderProfile תואמות מיצרני המכשירים, כמו CamcorderProfile.QUALITY_HIGH_SPEED_1080P או QUALITY_HIGH_SPEED_720P.

לפני שמגדירים צילום במהירות גבוהה, צריך לשלוח שאילתה לגבי היכולות של המכשיר באמצעות 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);

הגדרת HighSpeedVideoSessionConfig

כדי להשתמש בצילום במהירות גבוהה, צריך לשלב את תרחישי השימוש של VideoCapture ו-Preview ב-HighSpeedVideoSessionConfig. כדי להגדיר את הסשן, מבצעים את השלבים הבאים:

  1. יוצרים תרחיש שימוש אחד של VideoCapture ותרחיש שימוש אחד של Preview.
  2. מאתחלים את HighSpeedVideoSessionConfig.Builder עם תרחיש השימוש VideoCapture ומצרפים את Preview.
  3. שולחים שאילתה לגבי טווחי קצב הפריימים הנתמכים באמצעות CameraInfo.getSupportedFrameRateRanges(SessionConfig) ומגדירים את הטווח שנבחר.
  4. בוחרים בין סרטון עם קצב פריימים גבוה לבין סרטון בהילוך איטי שמוכן להפעלה:
    • כדי ליצור סרטון בקצב פריימים גבוה, משאירים את הערך setSlowMotionEnabled(false) (ברירת המחדל).
    • כדי ליצור סרטון בהילוך איטי שמוכן להפעלה, צריך להפעיל את setSlowMotionEnabled(true).
  5. מקשרים את הגדרת הסשן באמצעות cameraProvider.bindToLifecycle().

בדוגמה הבאה מגדירים ומקשרים סשן של הילוך איטי שמוכן להפעלה:

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

אפשר גם להשתמש ב-Kotlin DSL, שהוצג ב-CameraX 1.7.0-alpha02, כדי להגדיר סשנים במהירות גבוהה בצורה תמציתית:

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

איך מצלמים סרטונים במהירות גבוהה או בהילוך איטי

אחרי שמחברים את HighSpeedVideoSessionConfig, מתחילים לצלם באמצעות VideoCapture באותו אופן שבו מצלמים סרטונים רגילים:

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

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

איפה אפשר למצוא מידע נוסף על CameraX?