הערה: הדף הזה מתייחס לחבילה camera2. מומלץ להשתמש ב-CameraX, אלא אם לאפליקציה שלך נדרשים תכונות ספציפיות ברמה נמוכה. גם CameraX וגם Camera2 תומכים ב-Android 5.0 (רמת API 21) ואילך.
במכשיר Android אחד יכולות להיות כמה מצלמות. כל מצלמה
CameraDevice
ו-CameraDevice
יכול פלט של יותר משידור אחד בו-זמנית.
אחת מהסיבות לעשות את זה היא כדי ליצור סטרימינג אחד של פריימים עוקבים
מ-CameraDevice
, עבר אופטימיזציה לביצוע משימה ספציפית, כמו הצגת
עינית, ואילו אחרים עשויים להשתמש כדי לצלם תמונה או ליצור סרטון
הקלטות. הזרמים פועלים כצינורות עיבוד נתונים מקבילים שמעבדים פריימים גולמיים.
פריים אחד בכל פעם:
עיבוד מקביל מצביע על כך שיכולות להיות מגבלות ביצועים בהתאם את עוצמת העיבוד הזמינה מהמעבד (CPU), מה-GPU או ממעבד אחר. אם צינור עיבוד הנתונים לא יכול לעמוד בקצב של הפריימים הנכנסים, הוא מתחיל להסיר אותם.
לכל צינור עיבוד נתונים יש פורמט פלט משלו. הנתונים הגולמיים שמתקבלים
שיומר אוטומטית
פורמט פלט באמצעות לוגיקה מרומזת
שמשויכים לכל צינור עיבוד נתונים. בCameraDevice
נעשה שימוש לאורך כל הדף
דוגמאות הקוד הן לא ספציפיות, לכן קודם צריך לספור
לפני המשך הפעולה, כל המצלמות הזמינות.
אפשר להשתמש ב-CameraDevice
כדי ליצור
CameraCaptureSession
,
שספציפי לאותו CameraDevice
. CameraDevice
חייב לקבל
של פריים לכל פריים גולמי באמצעות CameraCaptureSession
.
תצורה מציינת את מאפייני המצלמה כגון מיקוד אוטומטי, צמצם, אפקטים
והחשיפה. עקב אילוצי חומרה, רק הגדרה אחת
פעיל בחיישן המצלמה בכל זמן נתון, שנקרא
הגדרה פעילה.
עם זאת, תרחישים לדוגמה של סטרימינג משפרים ומרחיבים דרכים קודמות לשימוש ב-CameraDevice
כדי לבצע סטרימינג של פעילויות צילום, וכך לבצע אופטימיזציה של זרימת המצלמה
והוא תרחיש לדוגמה ספציפי. לדוגמה, ההגדרה יכולה לשפר את חיי הסוללה כשמבצעים אופטימיזציה
שיחות וידאו.
CameraCaptureSession
מתאר את כל צינורות עיבוד הנתונים האפשריים שקשורים
CameraDevice
. כשיוצרים סשן, אי אפשר להוסיף או להסיר צינורות עיבוד נתונים.
השדה CameraCaptureSession
נשמר בתור של
CaptureRequest
,
שהופך את התצורה הפעילה.
CaptureRequest
מוסיף הגדרה אישית לתור ובוחר הגדרה אחת, יותר מ-
צינור עיבוד נתונים אחד, או את כל צינורות עיבוד הנתונים הזמינים לקבלת מסגרת
CameraDevice
. אפשר לשלוח הרבה בקשות לתיעוד במהלך חיי הצילום
סשן. כל בקשה יכולה לשנות את המערך הפעיל של הגדרות אישיות ושל הפלט
מקבלים את התמונה הגולמית.
שימוש בתרחישים לדוגמה של 'עדכונים לכיתה' לביצועים טובים יותר
תרחישים לדוגמה של סטרימינג מאפשרים לשפר את הביצועים של צילום Camera2 סשנים. הם מספקים למכשיר החומרה יותר מידע לגבי כוונון הפרמטרים, שמספק חוויית שימוש טובה יותר במצלמה לביצוע משימה ספציפית.
הזה
מאפשר למכשיר המצלמה לבצע אופטימיזציה לצינורות עיבוד נתונים של חומרה ותוכנה של המצלמה
על סמך תרחישי משתמשים בכל מקור נתונים. למידע נוסף על השימוש בסטרימינג
פניות: setStreamUseCase
תרחישים לדוגמה של סטרימינג מאפשרים לכם לציין איך ייעשה שימוש בשידור מסוים מהמצלמה
בנוסף להגדרת תבנית
CameraDevice.createCaptureRequest()
הפעולה הזו מאפשרת לחומרת המצלמה לבצע אופטימיזציה
פרמטרים שונים, כגון כוונון, מצב חיישן או הגדרות של חיישן מצלמה, על סמך
האיזון בין האיכות או זמן האחזור שמתאימים למקרי שימוש ספציפיים.
דוגמאות לתרחישים לדוגמה של עדכוני תוכן:
DEFAULT
: מכסה את כל פעולות ההתנהגות הקיימות של אפליקציות. היא מקבילה ל- הגדרת כל תרחיש לדוגמה של סטרימינג.PREVIEW
: מומלץ לצורך ניתוח תמונות בעינית או בתוך האפליקציה.STILL_CAPTURE
: עבר אופטימיזציה לצילום ברזולוציה גבוהה באיכות גבוהה, ולא צפויה לשמור על קצב פריימים כמו תצוגה מקדימה.VIDEO_RECORD
: בוצעה אופטימיזציה לצילום וידאו באיכות גבוהה, כולל באיכות גבוהה של ייצוב התמונה, אם הוא נתמך על ידי המכשיר ומופעל על ידי האפליקציה. האפשרות הזו עשויה ליצור פריימים של פלט עם עיכוב משמעותי בזמן אמת, כדי לאפשר ייצוב באיכות הגבוהה ביותר או עיבוד אחר.VIDEO_CALL
: מומלץ לשימושים ממושכים במצלמה כאשר התרוקנות הסוללה חשש.PREVIEW_VIDEO_STILL
: מומלץ לאפליקציות של רשתות חברתיות או לשימוש בסטרימינג יחיד במקרים שונים. זהו שידור רב-תכליתי.VENDOR_START
: משמש לתרחישים לדוגמה שהוגדרו על ידי ה-OEM.
יצירה של סשן צילום מצלמה
כדי ליצור סשן של המצלמה, צריך לספק לו מאגר נתונים זמני אחד או יותר האפליקציה שלך יכולה לכתוב פריימים של פלט. כל מאגר נתונים זמני מייצג צינור עיבוד נתונים. צריך לעשות זאת לפני שמתחילים להשתמש במצלמה, כדי שה-framework יוכל להגדיר את צינורות עיבוד הנתונים הפנימיים של המכשיר ולהקצות מאגרי זיכרון זמניים לשליחת פריימים ליעדי הפלט הנדרשים.
בקטע הקוד הבא מוסבר איך להכין סשן צילום באמצעות
מאגרי פלט, ששייך
SurfaceView
ועוד אחד
ImageReader
הוספת תרחיש לדוגמה של סטרימינג של PREVIEW
אל previewSurface
וגם
STILL_CAPTURE
שימוש בסטרימינג
הכיסוי של imReaderSurface
מאפשר לחומרת המכשיר לבצע אופטימיזציה של השידורים האלה אפילו
עוד יותר.
Kotlin
// Retrieve the target surfaces, which might be coming from a number of places: // 1. SurfaceView, if you want to display the image directly to the user // 2. ImageReader, if you want to read each frame or perform frame-by-frame // analysis // 3. OpenGL Texture or TextureView, although discouraged for maintainability reasons // 4. RenderScript.Allocation, if you want to do parallel processing val surfaceView = findViewById<SurfaceView>(...) val imageReader = ImageReader.newInstance(...) // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() val previewSurface = surfaceView.holder.surface val imReaderSurface = imageReader.surface val targets = listOf(previewSurface, imReaderSurface) // Create a capture session using the predefined targets; this also involves // defining the session state callback to be notified of when the session is // ready // Setup Stream Use Case while setting up your Output Configuration. @RequiresApi(Build.VERSION_CODES.TIRAMISU) fun configureSession(device: CameraDevice, targets: List<Surface>){ val configs = mutableListOf<OutputConfiguration>() val streamUseCase = CameraMetadata .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL targets.forEach { val config = OutputConfiguration(it) config.streamUseCase = streamUseCase.toLong() configs.add(config) } ... device.createCaptureSession(session) }
Java
// Retrieve the target surfaces, which might be coming from a number of places: // 1. SurfaceView, if you want to display the image directly to the user // 2. ImageReader, if you want to read each frame or perform frame-by-frame analysis // 3. RenderScript.Allocation, if you want to do parallel processing // 4. OpenGL Texture or TextureView, although discouraged for maintainability reasons Surface surfaceView = findViewById<SurfaceView>(...); ImageReader imageReader = ImageReader.newInstance(...); // Remember to call this only *after* SurfaceHolder.Callback.surfaceCreated() Surface previewSurface = surfaceView.getHolder().getSurface(); Surface imageSurface = imageReader.getSurface(); List<Surface> targets = Arrays.asList(previewSurface, imageSurface); // Create a capture session using the predefined targets; this also involves defining the // session state callback to be notified of when the session is ready private void configureSession(CameraDevice device, List<Surface> targets){ ArrayList<OutputConfiguration> configs= new ArrayList() String streamUseCase= CameraMetadata .SCALER_AVAILABLE_STREAM_USE_CASES_PREVIEW_VIDEO_STILL for(Surface s : targets){ OutputConfiguration config = new OutputConfiguration(s) config.setStreamUseCase(String.toLong(streamUseCase)) configs.add(config) } device.createCaptureSession(session) }
בשלב הזה לא הגדרתם את ההגדרה הפעילה של המצלמה. כאשר הסשן מוגדר, אפשר ליצור תיעוד ולשלוח אותו. בקשות לעשות זאת.
הטרנספורמציה שמוחלת על קלט בזמן שהם נכתבים במאגר הנתונים הזמני
נקבע לפי הסוג של כל יעד, שחייב להיות
Surface
פלטפורמת Android יודעת
להמיר תמונה גולמית בתצורה הפעילה לפורמט שמתאים עבור
לכל יעד. ההמרה נקבעת לפי פורמט הפיקסלים והגודל של
ספציפי ל-Surface
.
תוכנת ה-framework עושה כמיטב יכולתה, אבל כמה Surface
שילובי הגדרות אישיות עשויים לא לפעול, מה שעלול לגרום לבעיות כמו הסשן
לא נוצרת, דבר שיוביל לשגיאת זמן ריצה בעת שליחת בקשה, או
והירידה בביצועים שלהם. המסגרת מספקת אחריות
שילוב של פרמטרים של מכשיר, פלטפורמה ובקשה. התיעוד עבור
createCaptureSession()
מספק מידע נוסף.
בקשות TakeRequests בודדות
ההגדרות האישיות של כל פריים מקודדות במסגרת CaptureRequest
,
נשלחו למצלמה. כדי ליצור בקשה לתיעוד, אפשר להשתמש באחד
מוגדר מראש
templates,
או להשתמש ב-TEMPLATE_MANUAL
כדי לקבל שליטה מלאה. כשבוחרים
עליך לספק מאגר נתונים זמני אחד או יותר לשימוש,
לבקשה. אפשר להשתמש רק במאגרי נתונים זמניים שכבר הוגדרו בצילום
שבו אתם מתכוונים להשתמש.
בקשות איסוף משתמשות
תבנית build
ולתת למפתחים את ההזדמנות להגדיר
אפשרויות, כולל
חשיפה אוטומטית,
מיקוד אוטומטי,
וגם
צמצם עדשה.
לפני שמגדירים שדה, צריך לוודא שהאפשרות הספציפית זמינה
המכשיר באמצעות התקשרות
CameraCharacteristics.getAvailableCaptureRequestKeys()
ושהערך הרצוי נתמך על ידי בדיקת המצלמה המתאימה
מאפיין, כמו החשיפה האוטומטית הזמינה
מצבים.
כדי ליצור בקשת תיעוד של SurfaceView
באמצעות התבנית
שמיועד לתצוגה מקדימה ללא שינויים,
CameraDevice.TEMPLATE_PREVIEW
:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest = session.device.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW) captureRequest.addTarget(previewSurface)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest.Builder captureRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); captureRequest.addTarget(previewSurface);
לאחר הגדרה של בקשת תיעוד אפשר עכשיו לשלוח הודעה אותו להפעלת המצלמה:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest = ... // from CameraDevice.createCaptureRequest() // The first null argument corresponds to the capture callback, which you // provide if you want to retrieve frame metadata or keep track of failed capture // requests that can indicate dropped frames; the second null argument // corresponds to the Handler used by the asynchronous callback, which falls // back to the current thread's looper if null session.capture(captureRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest captureRequest = ...; // from CameraDevice.createCaptureRequest() // The first null argument corresponds to the capture callback, which you // provide if you want to retrieve frame metadata or keep track of failed // capture // requests that can indicate dropped frames; the second null argument // corresponds to the Handler used by the asynchronous callback, which falls // back to the current thread's looper if null session.capture(captureRequest.build(), null, null);
כשמסגרת פלט מוכנסת למאגר הנתונים הזמני הספציפי, צילום
התקשרות חזרה
מופעלת. במקרים רבים קריאות נוספות לפעולה, כמו
ImageReader.OnImageAvailableListener
מופעלת כשהפריים שהוא מכיל מעובד. זה בשעה
ובשלב הזה אפשר לאחזר נתוני תמונה מהמאגר הזמני.
חזרה על בקשות צילום
קל לבצע בקשות למצלמה יחידה, אבל כדי להציג שידור חי תצוגה מקדימה או סרטון, לא מאוד שימושיות. במקרה כזה, צריך לקבל זרם רציף של פריימים, ולא רק פריימים בודדים. קטע הקוד הבא מראים איך להוסיף בקשה חוזרת לפעילות:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback val captureRequest: CaptureRequest = ... // from CameraDevice.createCaptureRequest() // This keeps sending the capture request as frequently as possible until // the // session is torn down or session.stopRepeating() is called // session.setRepeatingRequest(captureRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback CaptureRequest captureRequest = ...; // from CameraDevice.createCaptureRequest() // This keeps sending the capture request as frequently as possible until the // session is torn down or session.stopRepeating() is called // session.setRepeatingRequest(captureRequest.build(), null, null);
בקשת צילום חוזרת גורמת למכשיר המצלמה לצלם באופן רציף
תמונות באמצעות ההגדרות שבמוצר CaptureRequest
שסופק. ממשק API של Camera2
מאפשר למשתמשים לצלם וידאו מהמצלמה על ידי שליחת
חוזר על CaptureRequests
כפי שמוצג כאן
דוגמה של Camera2
ב-GitHub. הוא יכול גם לעבד סרטון בהילוך איטי על ידי צילום
סרטון במהירות גבוהה (בהילוך איטי) עם רצף חוזר של CaptureRequests
כפי שמוצג באפליקציה לדוגמה של סרטונים בהילוך איטי של Camera2
ב-GitHub.
שילוב בקשה לצילום של מאפיינים
כדי לשלוח בקשה שנייה לתיעוד בזמן שבקשת ההקלטה החוזרת פעילה: למשל כדי להציג עינית ולאפשר למשתמשים לצלם תמונה, להפסיק את הבקשה החוזרת המתמשכת. במקום זאת, יוצרים צילום שאינו חוזר בזמן שהבקשה החוזרת ממשיכה לפעול.
צריך להגדיר כל מאגר נתונים זמני של פלט כחלק מסשן המצלמה כאשר הסשן נוצר לראשונה. לבקשות חוזרות יש עדיפות נמוכה מ- בקשות חד-פעמיות או בקשות רצף, שמאפשרות לדגימה הבאה לעבוד:
Kotlin
val session: CameraCaptureSession = ... // from CameraCaptureSession.StateCallback // Create the repeating request and dispatch it val repeatingRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_PREVIEW) repeatingRequest.addTarget(previewSurface) session.setRepeatingRequest(repeatingRequest.build(), null, null) // Some time later... // Create the single request and dispatch it // NOTE: This can disrupt the ongoing repeating request momentarily val singleRequest = session.device.createCaptureRequest( CameraDevice.TEMPLATE_STILL_CAPTURE) singleRequest.addTarget(imReaderSurface) session.capture(singleRequest.build(), null, null)
Java
CameraCaptureSession session = ...; // from CameraCaptureSession.StateCallback // Create the repeating request and dispatch it CaptureRequest.Builder repeatingRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW); repeatingRequest.addTarget(previewSurface); session.setRepeatingRequest(repeatingRequest.build(), null, null); // Some time later... // Create the single request and dispatch it // NOTE: This can disrupt the ongoing repeating request momentarily CaptureRequest.Builder singleRequest = session.getDevice().createCaptureRequest(CameraDevice.TEMPLATE_STILL_CAPTURE); singleRequest.addTarget(imReaderSurface); session.capture(singleRequest.build(), null, null);
עם זאת, לגישה הזו יש חיסרון: לא יודעים בדיוק מתי מתרחשת הבקשה היחידה. באיור הבא, אם A הוא חזרה בקשת הצילום וה-B היא בקשת הצילום של פריים יחיד. כך הסשן מעבד את תור הבקשות:
אי אפשר להבטיח את זמן האחזור בין הבקשה החוזרת האחרונה של א לפני הפעלה של ב' ובפעם הבאה שמשתמשים ב-A שוב, אז יכול להיות שיופיעו כמה פריימים שעליהם דילגתם. יש כמה דברים כדי להפחית את הבעיה:
מוסיפים את יעדי הפלט מבקשה א כדי לבקש ב. כך, המסגרת של B מוכנה, היא מועתקת ליעדי הפלט של A. לדוגמה, השלב הזה חיוני כשמבצעים תמונות מצב של סרטונים כדי לשמור קצב פריימים יציב. בקוד הקודם,
singleRequest.addTarget(previewSurface)
לפני יצירת הבקשה.משתמשים בשילוב של תבניות שמיועדות לתרחיש המסוים הזה, כמו אפס רעידות.