מערכת לכידה בדרך כלל מקליטה זרמי וידאו ואודיו, דוחסת אותם, מבצעת ריבוב של שני הזרמים ואז כותבת את הזרם שמתקבל לדיסק.
ב-CameraX, הפתרון לצילום וידאו הוא תרחיש השימוש VideoCapture:
VideoCapture בתרחיש השימוש.כפי שמוצג באיור 2, צילום וידאו באמצעות CameraX כולל כמה רכיבים ארכיטקטוניים ברמה גבוהה:
SurfaceProviderלמקור הווידאו.-
AudioSourceלמקור האודיו. - שני מקודדים לקידוד ולדחיסה של וידאו ואודיו.
- ממקסס מדיה כדי למקסס את שני הזרמים.
- כלי לשמירת קבצים לכתיבת התוצאה.
ממשק VideoCapture API מפשט את מנוע הלכידה המורכב ומספק לאפליקציות ממשק API פשוט וישיר יותר.
סקירה כללית על VideoCapture API
VideoCapture הוא תרחיש לדוגמה לשימוש ב-CameraX שפועל היטב בפני עצמו או בשילוב עם תרחישים אחרים. השילובים הספציפיים הנתמכים תלויים ביכולות החומרה של המצלמה, אבל Preview ו-VideoCapture הם שילובים תקפים לשימוש בכל המכשירים.
ממשק ה-API של VideoCapture מורכב מהאובייקטים הבאים שמתקשרים עם אפליקציות:
-
VideoCaptureהיא מחלקת תרחישי השימוש ברמה העליונה. VideoCaptureנקשר ל-LifecycleOwnerעםCameraSelectorול-CameraX UseCases אחרים. מידע נוסף על המושגים ועל השימושים האלה זמין במאמר ארכיטקטורת CameraX. -
Recorderהוא הטמעה של VideoOutput שמקושרת באופן הדוק ל-VideoCapture. אפליקצייתRecorderמשמשת לצילום הווידאו והאודיו. אפליקציה יוצרת הקלטות מתוךRecorder. -
PendingRecordingמגדיר הקלטה, ומספק אפשרויות כמו הפעלת אודיו והגדרת event listener. כדי ליצורPendingRecording, צריך להשתמש בRecorder. ההקלטה שלPendingRecordingלא כוללת שום דבר. Recordingמבצע את ההקלטה בפועל. כדי ליצורRecording, צריך להשתמש בPendingRecording.
איור 3 מציג את הקשרים בין האובייקטים האלה:
מקרא:
- יצירת
RecorderעםQualitySelector. - מגדירים את
Recorderבאמצעות אחת מהאפשרויות שלOutputOptions. - אם צריך, מפעילים את האודיו באמצעות
withAudioEnabled(). - מתקשרים אל
start()עם מאזיןVideoRecordEventכדי להתחיל את ההקלטה. - כדי לשלוט בהקלטה, משתמשים בלחצנים
pause()/resume()/stop()ב-Recording. - מגיבים ל-
VideoRecordEventsבתוך ה-event listener.
רשימת ה-API המפורטת מופיעה בקובץ current.txt בתוך קוד המקור.
שימוש ב-VideoCapture API
כדי לשלב את תרחיש השימוש של CameraX VideoCapture באפליקציה, מבצעים את הפעולות הבאות:
- כריכה
VideoCapture. - הכנה והגדרה של ההקלטה.
- מתחילים את ההקלטה של זמן הריצה ושולטים בה.
בקטעים הבאים מפורטות הפעולות שאפשר לבצע בכל שלב כדי לקבל הקלטה של סשן מקצה לקצה.
כריכת VideoCapture
כדי לקשר את תרחיש השימוש VideoCapture:
- יוצרים אובייקט
Recorder. - יצירת אובייקט
VideoCapture. - כבילה ל-
Lifecycle.
ממשק CameraX VideoCapture API פועל לפי דפוס העיצוב של 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 באחת מהשיטות הבאות:
כדאי לספק כמה רזולוציות מועדפות באמצעות
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. כשמשלבים עם תרחיש השימוש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, האפליקציה יכולה ליצור אובייקטים של הקלטה כדי לבצע את הלכידה של הווידאו והאודיו. אפליקציות יוצרות הקלטות על ידי ביצוע הפעולות הבאות:
- מגדירים את
OutputOptionsבאמצעותprepareRecording(). - (אופציונלי) מפעילים את הקלטת האודיו.
- משתמשים ב-
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(). - רושמים 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: MIRROR_MODE_OFF, MIRROR_MODE_ON ו-MIRROR_MODE_ON_FRONT_ONLY. כדי ליישר את התצוגה המקדימה של המצלמה, Google ממליצה להשתמש ב-MIRROR_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 זמין במקורות המידע הבאים:
- Codelab תחילת העבודה עם CameraX
- אפליקציית הדוגמה הרשמית של CameraX
- רשימת הגרסאות האחרונות של CameraX Video Capture API
- הערות לגבי הגרסה של CameraX
- קוד המקור של CameraX